/** @file world/world.cpp Implementace hlavickoveho souboru /world/world.h @author Petr Wolf */ #include "world/world.h" #include "common/rm/rmmap.h" #include "common/rm/rmbuildingi.h" #include "common/rm/rmadvxmli.h" #include "common/rm/rmxmli.h" #include "common/da.h" using namespace World; using namespace rm; using namespace ai_ns::diplomacy_ns; TWorld::TWorld() { rules_ownership = false; rules = NULL; } TWorld::~TWorld() { // struktury sveta reset(); // pravidla if (rules_ownership) delete rules; rules = NULL; } void TWorld::init(TRules * external_rules) { if (external_rules) { rules = external_rules; rules_ownership = false; } else { rules = new TRules(); rules_ownership = true; rules->init(); } } void TWorld::reset() { weather.reset(); kingdoms.reset(); towns.reset(); units.reset(); corpses.reset(); buildings.reset(); ruins.reset(); visibility_maps.reset(); map.reset(); players.reset(); } void TWorld::fillRelations(bool refresh) { // ulozim do jednotek cisla hexu a budov, kde se nachazeji // ulozim do budov cisla hexu, na kterych stoji // ulozim do hexu cisla jednotek, ktere je tam mozno kupovat THex * hex; TUnitsIterator uit; TLivingUnitsIterator luit; TBuildingsIterator bit; TLivingBuildingsIterator lbit; TTownsIterator tit; TVisibilityMapsIterator vmit; int hex_id; int i, j, k; if (refresh) { for (i = 0; i < map.width(); i++) { for (j = 0; j < map.height(); j++) { hex = map.getHexAt(i, j); hex->unit_occurrence.clear(); } } for (luit = units.begin(); luit != units.end(); luit++) { luit->second->_hex = 0; luit->second->_building = 0; } for (lbit = buildings.begin(); lbit != buildings.end(); lbit++) { lbit->second->_hex = 0; lbit->second->_unit = 0; } } for (i = 0; i < map.width(); i++) { for (j = 0; j < map.height(); j++) { hex = map.getHexAt(i, j); map.coordsToId(i, j, &hex_id); if (hex->data().unit) { // na poli je jednotka - necham jednotku si to zapamatovat units[hex->data().unit]->_hex = hex_id; } if (hex->data().building) { // na poli je budova - necham budovu si to zapamatovat buildings[hex->data().building]->_hex = hex_id; if (hex->data().unit) { // na poli je jednotka v budove, necham obe si to zapamatovat; units[hex->data().unit]->_building = hex->data().building; buildings[hex->data().building]->_unit = hex->data().unit; } } // vyskyt jednotek na hexu for (k = 0; k < hex->data().unit_occurrence.length; k++) { hex->unit_occurrence.push_back(hex->data().unit_occurrence.data[k]); } // implicitni vyskyt jednotek for (uit = rules->unit_types.begin(); uit != rules->unit_types.end(); uit++) { if (uit->second->data().default_occurrence) hex->unit_occurrence.push_back(uit->first); } } } // doplnim do hexu, nalezajicich se na uzemi mest, odkazy na mesta for (tit = towns.begin(); tit != towns.end(); tit++) { for (i = 0; i < tit->second->data().citysize; i++) { hex = map.getHexById(tit->second->data().position[i]); hex->_town = tit->first; } } // doplnim do jednotek, budov a hexu odkaz na svet for (luit = units.begin(); luit != units.end(); luit++) luit->second->_world = this; for (lbit = buildings.begin(); lbit != buildings.end(); lbit++) lbit->second->_world = this; for (vmit = visibility_maps.begin(); vmit != visibility_maps.end(); vmit++) { for (i = 0; i < map.width(); i++) { for (j = 0; j < map.height(); j++) { vmit->second->getHexAt(i, j)._world = this; } } } } void TWorld::nextTurn(MONEY moneyGain, TIntArray & movementPoints, TIntArray & buildingLives, TIntContainer & unitsInterrupted, TIntArray * menDied) { TIntArray::iterator it; TIntIterator iit; TLivingBuilding * building; TLivingUnitsIterator unit; int i, j; int wounds[MAX_LIVES]; int player_id = players.playerIdOnTurn(); // pricteni penez players[player_id]->data().game_data.money += moneyGain; players.moneyGained(player_id, moneyGain); // pricteni bodu pohybu for (it = movementPoints.begin(); it != movementPoints.end(); it++) { units[it->first]->data().points_of_movement += it->second; } // pricteni zivotu budov for (it = buildingLives.begin(); it != buildingLives.end(); it++) { building = buildings[it->first]; building->data().lives += it->second; if (building->data().lives == rules->building_types[building->data().type]->data().lives) { // budova byla dokoncena - zmenim jeji stav a stav jednotky building->data().construction_duration = 0; units[building->unit()]->data().state = US_IDLE; } } // pozastaveni stavby budov for (iit = unitsInterrupted.begin(); iit != unitsInterrupted.end(); iit++) { stopBuilding(*iit); } // zmena stavu jednotek for (unit = units.begin(); unit != units.end(); unit++) { if (unit->second->data().player == player_id) { if (unit->second->data().state == US_HAS_STARTED_BUILDING) unit->second->data().state = US_BUILDING; else if (unit->second->data().state != US_BUILDING) unit->second->data().state = US_IDLE; } } // navyseni zraneni jednotek for (unit = units.begin(); unit != units.end(); unit++) { if (unit->second->data().player == player_id) { j = 0; for (i = 0; i < MAX_LIVES; i++) { switch (unit->second->data().wounds[i]) { case UW_OK: // bez zraneni wounds[j] = UW_OK; j++; break; case UW_KO: // ubytek zivota unit->second->data().lives -= 1; if (menDied) (*menDied)[unit->first]++; break; default: // navyseni zraneni wounds[j] = unit->second->data().wounds[i] + 1; j++; break; } } for (i = 0; i < j; i++) unit->second->data().wounds[i] = wounds[i]; for (i = j; i < MAX_LIVES; i++) unit->second->data().wounds[i] = UW_OK; } } } void TWorld::playerDefeated(PLAYER_ID player_id) { players[player_id]->data().game_data.state = PS_DEFEATED; } TLivingUnit * TWorld::unitCreated(BUILDING_ID building_id, UNIT_TYPE_ID unit_type, UNIT_ID unit_id, MONEY cost) { TLivingUnit * unit; TLivingBuilding * building = buildings[building_id]; THex * hex = map.getHexById(building->hex()); // vytvoreni jednotky LIVING_UNIT * data = (LIVING_UNIT * )KMemAlloc(sizeof(LIVING_UNIT)); LIVING_UNIT_Init(data); data->type = unit_type; data->player = building->data().player; snprintf(data->name, MAX_STRLEN, "%d", unit_id); data->lives = rules->unit_types[unit_type]->data().max_lives; data->state = US_EXHAUSTED; unit = new TLivingUnit(data, unit_id); units[unit_id] = unit; // aktualizace vazeb (budova, hex) unit->_hex = building->hex(); unit->_building = building_id; unit->_world = this; buildings[building_id]->_unit = unit_id; hex->data().unit = unit_id; // odecteni penez players[data->player]->data().game_data.money -= cost; // zaznam do statistik players.unitCreated(data->player); return unit; } void TWorld::unitStep(UNIT_ID unit_id, HEX_ID to) { TLivingUnit * unit = units[unit_id]; THex * previous = map.getHexById(unit->hex()); THex * actual = map.getHexById(to); previous->data().unit = 0; actual->data().unit = unit_id; unit->_hex = to; unit->_building = 0; if (previous->data().building != 0) { TLivingBuilding * previousBuilding = buildings[previous->data().building]; previousBuilding->_unit = 0; } if (actual->data().building != 0) { TLivingBuilding * actualBuilding = buildings[actual->data().building]; actualBuilding->_unit = unit_id; unit->_building = actual->data().building; } } void TWorld::unitMove(UNIT_ID unit_id, HEX_ID to, TPath * path) { TLivingUnit * unit = units[unit_id]; // 1. smazu ze startovniho hexu THex * source = map.getHexById(unit->hex()); source->data().unit = 0; unit->_building = 0; // pokud byla na hexu budova, zrusim jeji vazbu na jednotku if (source->data().building) buildings[source->data().building]->_unit = 0; // 2. pridam na cilovy hex THex * target = map.getHexById(to); target->data().unit = unit_id; // pokud je v cilovem hexu budova, pridam vazbu na jednotku if (target->data().building) { buildings[target->data().building]->_unit = unit_id; unit->_building = target->data().building; } // 3. aktualizuji zkratku z TUnit unit->_hex = to; // 4. aktualizuji viditelnost a odecitam BP if (path) { TPath::iterator i; TIntContainer::iterator j; int player_id = unit->data().player; for (i = path->begin(); i != path->end(); i++) { // odkryte hexy if (i->shown.size()) { for (j = i->shown.begin(); j != i->shown.end(); j++) visibility_maps[player_id]->getHexById(*j).setActual(); } // skryte hexy if (i->hidden.size()) { for (j = i->hidden.begin(); j != i->hidden.end(); j++) visibility_maps[player_id]->getHexById(*j).removeActual(); } // BP unit->data().points_of_movement -= i->cost; } } // zmena stavu jednotky if (unit->data().state == US_IDLE) unit->data().state = US_HAS_MOVED; else if (unit->data().state == US_HAS_FOUGHT) unit->data().state = US_HAS_FOUGHT_AND_MOVED; } void World::TWorld::unitDied(UNIT_ID unit_id) { TLivingUnit * unit = units[unit_id]; THex * hex = map.getHexById(unit->hex()); // aktualizuji odkaz na jednotku z hexu hex->data().unit = 0; // aktualizuji odkaz na jednotku z budovy if (hex->data().building) { TLivingBuilding * building = buildings[hex->data().building]; building->_unit = 0; } // pridam jednotku do mrtvych corpses[unit_id] = unit; // odeberu jednotku z zivych units.erase(unit_id); } TLivingBuilding * TWorld::buildingCreated(UNIT_ID unit_id, BUILDING_TYPE_ID building_type, BUILDING_ID building_id, HEX_ID location, int lives, MONEY cost, int orientation) { TLivingUnit * unit = units[unit_id]; TLivingBuilding * building; THex * hex = map.getHexById(location); // vytvoreni budovy LIVING_BUILDING * data = (LIVING_BUILDING *)KMemAlloc(sizeof(LIVING_BUILDING)); LIVING_BUILDING_Init(data); data->type = building_type; data->player = unit->data().player; snprintf(data->name, MAX_STRLEN, "%d", building_id); data->lives = lives; data->construction_duration = 1; data->orientation = orientation; building = new TLivingBuilding(data, building_id); buildings[building_id] = building; // aktualizace vazeb (budova, hex) building->_world = this; building->_hex = location; hex->data().building = building_id; unit->_building = building_id; building->_unit = unit_id; // posunula se jednotka? // pokud ano, aktualizuji jeji vazby if (unit->_hex != location) unitStep(unit_id, location); // konec bodu pohybu jednotky unit->data().points_of_movement = 0; // zmena stavu jednotky unit->data().state = US_HAS_STARTED_BUILDING; // odecteni penez players[data->player]->data().game_data.money -= cost; // zaznam do statistik players.buildingBuilt(data->player); return building; } void TWorld::buildingRepaired(UNIT_ID unit_id, BUILDING_ID building_id, int lives, MONEY cost) { TLivingUnit * unit = units[unit_id]; TLivingBuilding * building = buildings[building_id]; // zmena stavu jednotky unit->data().state = US_HAS_STARTED_BUILDING; // pricteni zivotu building->data().lives += lives; // posunula se jednotka? // pokud ano, aktualizuji jeji vazby if (unit->_hex != building->_hex) unitStep(unit_id, building->hex()); // konec bodu pohybu jednotky unit->data().points_of_movement = 0; // odecteni penez players[unit->data().player]->data().game_data.money -= cost; } void TWorld::buildingDestroyed(BUILDING_ID building_id) { TLivingBuilding * building = buildings[building_id]; THex * hex = map.getHexById(building->hex()); // aktualizuji odkaz na budovu z hexu hex->data().building = 0; // pridam budovu do mrtvych ruins[building_id] = building; // odeberu jednotku z zivych buildings.erase(building_id); } bool TWorld::diplomacyChange(int source_diplomat_world_id, int target_diplomat_world_id, ai_ns::diplomacy_ns::TRelationship new_relationship, ai_ns::diplomacy_ns::TRelationship new_offered_relationship, MONEY source_diplomat_penalty, MONEY target_diplomat_penalty) { TDipRelation * relation = players[source_diplomat_world_id]->data().game_data.relations[target_diplomat_world_id]; if ((relation->rs != new_relationship) || (relation->offeredrs != new_offered_relationship)) { players[source_diplomat_world_id]->data().game_data.relations[target_diplomat_world_id]->rs = new_relationship; players[source_diplomat_world_id]->data().game_data.relations[target_diplomat_world_id]->offeredrs = new_offered_relationship; players[target_diplomat_world_id]->data().game_data.relations[source_diplomat_world_id]->rs = new_relationship; players[source_diplomat_world_id]->data().game_data.money -= source_diplomat_penalty; players[target_diplomat_world_id]->data().game_data.money -= target_diplomat_penalty; players.moneyGained(source_diplomat_world_id, -source_diplomat_penalty); players.moneyGained(target_diplomat_world_id, -target_diplomat_penalty); return true; } else { return false; } } void TWorld::setWeather(WEATHER_STATE state) { if (state == weather.data().state) weather.data().duration++; else { weather.data().duration = 0; weather.data().state = state; } } void TWorld::stopBuilding(UNIT_ID unit_id) { TLivingUnit * unit = units[unit_id]; unit->data().state = US_IDLE; unit->_building = 0; } void TWorld::sellBonus(BUILDING_ID building_id, UNIT_PROPERTY_BONUS_ID bonus_id, MONEY cost, UNIT_PROPERTY_BONUS_ID replace) { TLivingBuilding * building = buildings[building_id]; TLivingUnit * unit = units[building->unit()]; int i; // pridani bonusu if (replace) { // nahrazuji stavajici bonus for (i = 0; i < unit->data().bonuses.length; i++) { if (unit->data().bonuses.data[i] == replace) { // na i-tem miste se nachazi nahrazovany bonus unit->data().bonuses.data[i] = bonus_id; break; } } } else { // pridavam novy bonus // alokace noveho pole UNIT_PROPERTY_BONUS_ID * nove = (UNIT_PROPERTY_BONUS_ID *)KMemAlloc((unit->data().bonuses.length + 1) * sizeof(UNIT_PROPERTY_BONUS_ID)); // prekopirovani stavajicich dat for (i = 0; i < unit->data().bonuses.length; i++) { nove[i] = unit->data().bonuses.data[i]; } // pridani nove polozky nove[unit->data().bonuses.length] = bonus_id; if (unit->data().bonuses.data) KMemFree(unit->data().bonuses.data); unit->data().bonuses.data = nove; unit->data().bonuses.length += 1; } // odecteni penez players[unit->data().player]->data().game_data.money -= cost; // zmena stavu unit->data().state = US_EXHAUSTED; } void TWorld::unitWound(UNIT_ID unit_id, int wounds) { TLivingUnit * unit = units[unit_id]; for (int i = 0; (i < unit->data().lives) && (wounds > 0); i++) { if (unit->data().wounds[i] == UW_OK) { unit->data().wounds[i] = 1; wounds--; } } } void TWorld::unitExperience(UNIT_ID unit_id, int experience, int level) { TLivingUnit * unit = units[unit_id]; unit->data().experience = experience; unit->data().level = level; } void TWorld::unitHeal(UNIT_ID unit_id, int lives) { TLivingUnit * unit = units[unit_id]; // vyleceni jednotky for (int i = 0; (i < unit->data().lives) && (lives > 0); i++) { if (unit->data().wounds[i] != UW_OK) { unit->data().wounds[i] = UW_OK; lives--; } } // zmena stavu jednotky unit->data().state = US_EXHAUSTED; } void TWorld::unitRecruit(UNIT_ID unit_id, int lives, int cost, int level, int experience) { TLivingUnit * unit = units[unit_id]; // rekrutovani muzu unit->data().lives += lives; unit->data().level = level; unit->data().experience = experience; // vyleceni zranenych for (int i = 0; i < unit->data().lives; i++) unit->data().wounds[i] = UW_OK; // odecteni penez players[unit->data().player]->data().game_data.money -= cost; // zmena stavu unit->data().state = US_EXHAUSTED; } void TWorld::unitRecruitElite(UNIT_ID unit_id, int lives, int cost) { TLivingUnit * unit = units[unit_id]; // rekrutovani muzu unit->data().lives += lives; // vyleceni zranenych for (int i = 0; i < unit->data().lives; i++) unit->data().wounds[i] = UW_OK; // odecteni penez players[unit->data().player]->data().game_data.money -= cost; // zmena stavu unit->data().state = US_EXHAUSTED; } void TWorld::visibilityShow(PLAYER_ID player_id, TIntContainer & shown) { if (player_id) { TIntIterator it; for (it = shown.begin(); it != shown.end(); it++) visibility_maps[player_id]->getHexById(*it).setActual(); } } void TWorld::visibilityHide(PLAYER_ID player_id, TIntContainer & hidden) { if (player_id) { TIntIterator it; for (it = hidden.begin(); it != hidden.end(); it++) visibility_maps[player_id]->getHexById(*it).removeActual(); } } bool TWorld::playerOperational(PLAYER_ID player_id) { if (players.playerIdOnTurn() != player_id) return false; if (players[player_id]->data().game_data.state != PS_ACTIVE) return false; return true; } bool TWorld::unitOperational(UNIT_ID unit_id, PLAYER_ID player_id) { if (units.count(unit_id) == 0) return false; if (units[unit_id]->data().player != player_id) return false; if (!playerOperational(player_id)) return false; return true; } bool TWorld::buildingOperational(BUILDING_ID building_id, PLAYER_ID player_id) { if (buildings.count(building_id) == 0) return false; if (buildings[building_id]->data().player != player_id) return false; if (!playerOperational(player_id)) return false; return true; } int TWorld::lock() { return mmin(3, units.lock(), buildings.lock(), map.lock()); } int TWorld::unlock() { return mmax(3, units.unlock(), buildings.unlock(), map.unlock()); }