/** @file /world/world_engine.cpp Implementace hlavickoveho souboru /world/world_engine.h @author Petr Wolf */ #include "math.h" #include "common/exc.h" #include "common/TCL/tcl_script.h" #include "world/world_engine.h" #include "world/world_server.h" #include "ai/PathFind/pathenv.h" using namespace World; using namespace std; using namespace ai_ns::pathfind_ns; /// konstanta urcuje, jestli se vyhodnoceni vzdalenosti bude provadet v TCL (1), nebo v C (0) #define GET_DISTANCE_TCL 0 #define GET_UNIT_VISIBILITY_TCL 0 TWorldEngine::TWorldEngine(TWorld * world) : _world(* world) { } TWorldEngine::~TWorldEngine() { } void TWorldEngine::init(TWorld * world) { // zapis pravidel do prostredi TCL interpreteru if (world->rules) world->rules->writeToTCL(interpreter); } int TWorldEngine::lock() { _world.lock(); return TMutex::lock(); } int TWorldEngine::unlock() { _world.unlock(); return TMutex::unlock(); } TIntContainer * TWorldEngine::getRange(int unit_id) { lock(); // Obdobou Dijkstrova algoritmu urcim oblast, kam je dana jednotka // schopna momentalne dojit. TLivingUnit * unit = _world.units[unit_id]; TIntContainer * result = new TIntContainer(); TIntContainer * neighbours; TIntIterator i; std::map explored; std::map waiting; std::map::iterator it; int center, cost, closestHex; if (center = unit->hex()) { // upravena varianta dijkstrova algoritmu // inicializace explored[center] = 0; // dosazeni sousedu startovniho hexu neighbours = _world.map.getNeighbours(center); for (i = neighbours->begin(); i != neighbours->end(); i++) { cost = getDistance(unit_id, 0, center, *i); if ((cost > 0) && (cost <= unit->data().points_of_movement)) { waiting[*i] = cost; } } delete neighbours; // prohledani prostoru while (waiting.size()) { // vyber minima z cekajicih uzlu cost = -1; for (it = waiting.begin(); it != waiting.end(); it++) { if ((cost == -1) || (it->second < cost)) { cost = it->second; closestHex = it->first; } } // pridani minima do prozkoumanych uzlu explored[closestHex] = waiting[closestHex]; // pridani sousedu minima do cekajicich uzlu neighbours = _world.map.getNeighbours(closestHex); for (i = neighbours->begin(); i != neighbours->end(); i++) { if (explored.find(*i) == explored.end()) { // uzel jeste nebyl definitivne zkouman cost = getDistance(unit_id, waiting[closestHex], closestHex, *i); if ( (cost > 0) && (cost <= unit->data().points_of_movement - waiting[closestHex]) && (_world.visibility_maps[unit->data().player]->getHexById(*i).getVisibility() == VI_IN_SIGHT) ) { if (waiting.find(*i) != waiting.end()) { waiting[*i] = MIN(waiting[*i], waiting[closestHex] + cost); } else { waiting[*i] = waiting[closestHex] + cost; } } } } delete neighbours; // odstraneni minima z cekajicih uzlu waiting.erase(closestHex); } // vypis vysledku (nehlede na cenu) for (it = explored.begin(); it != explored.end(); it++) { if (it->first != center) result->push_back(it->first); } unlock(); return result; } else { unlock(); THROW(E_8K, "Chyba v konzistenci dat."); } unlock(); return result; } TIntContainer TWorldEngine::getAttackRange(int unit_id) { lock(); // v kruhovem okoli o polomeru danem dosahem utoku jednotky // vypisu ty hexy, na nichz stoji nejaky objekt (nepratelska jednotka, budova, ...) // na ktery je mozno utocit TLivingUnit * source_unit = _world.units[unit_id]; // utocici jednotka TIntContainer result; // vysledek funkce TIntContainer range; TIntIterator it; int target_hex_id, attack; THex * target_hex; TLivingUnit * target_unit; TLivingBuilding * target_building; // TTCL_Script script(& interpreter); // skript pro vyhodnoceni v TCL // script.loadStruct(_world.rules->scripts[TS_UNIT_ATTACK_RANGE]); // script.setVar("unit", unit); // int temp = -1; // script.setVar("source_id", &temp); // pres vsechny hexy v dosahu vyhodnotim moznost utoku range = _world.map.getCircle(source_unit->hex(), _world.rules->unit_types[source_unit->data().type]->data().attack_range + source_unit->attackRangeBonus()); for (it = range.begin(); it != range.end(); it++) { target_hex_id = *it; // vyhodnoceni dostupnosti pro ztec a vybuch if ((_world.rules->unit_types[source_unit->data().type]->data().fight_type == FT_FACE_TO_FACE) || (_world.rules->unit_types[source_unit->data().type]->data().fight_type == FT_BLAST)) { if (getDistance(unit_id, _world.rules->unit_types[source_unit->data().type]->data().max_points_of_movement + source_unit->movementPointsBonus(), source_unit->hex(), target_hex_id, GCT_IgnoreAllUnits) == 0) continue; } // vyhodnoceni moznosti utoku v TCL if (_world.map.insideBorders(target_hex_id)) { // script.setVar("target_id", &hex); target_hex = _world.map.getHexById(target_hex_id); try { // script.run(); // script.getVar("attack", &attack); attack=0; // there is a unit at the target hex if (target_hex->data().unit!=0) { target_unit=_world.units[target_hex->data().unit]; if (target_unit->data().player!=source_unit->data().player) attack=1; } // there is a bulding at the target hex if (target_hex->data().building!=0) { target_building=_world.buildings[target_hex->data().building]; if (target_building->data().player!=source_unit->data().player) attack=1; } } catch (E_8K_TCL_Error) { attack = 0; } // pokud je na hexu nepratelsky objekt, pridam do vysledku if (attack) result.push_back(*it); } } unlock(); return result; } TIntContainer World::TWorldEngine::getInverseAttackRange(UNIT_ID unit_id, HEX_ID target_hex_id) { // ZKONTROLOVAT SOURCE A TARGET!!!! lock(); // v kruhovem okoli o polomeru danem dosahem utoku jednotky // vypisu ty hexy, z nichz je mozno utocit na cilovy hex TLivingUnit * source_unit = _world.units[unit_id]; // ? TLivingUnit * target_unit; // ? TLivingBuilding * target_building; TIntContainer result; TIntContainer range; TIntIterator it; int attack; //,attack2; THex * target_hex; // target of the unit (unit_id) THex * source_hex; // where the unit can stand int source_hex_id; target_hex = _world.map.getHexById(target_hex_id); /*********/ // inicializace skriptu // TTCL_Script script(& interpreter); // script.loadStruct(_world.rules->scripts[TS_UNIT_ATTACK_RANGE]); // script.setVar("unit", source_unit); // script.setVar("target_id", &target_hex_id); /*********/ // pres vsechny hexy v dosahu vyhodnotim moznost utoku range = _world.map.getCircle(target_hex_id, _world.rules->unit_types[source_unit->data().type]->data().attack_range + source_unit->attackRangeBonus()); for (it = range.begin(); it != range.end(); it++) { source_hex_id = *it; if (_world.map.insideBorders(source_hex_id)) { /********/ // script.setVar("source_id", &source_hex_id); /********/ try { source_hex = _world.map.getHexById(source_hex_id); // can I stand here? attack decides attack=0; // there is a unit at the target hex if (source_hex->data().unit!=0) { target_unit=_world.units[source_hex->data().unit]; if (target_unit!=NULL) // it should be not, cause there was a unit if (target_unit->data().player!=source_unit->data().player) attack=1; } // there is a bulding at the target hex if (source_hex->data().building!=0) { target_building=_world.buildings[source_hex->data().building]; if (target_building!=NULL) if (target_building->data().player!=source_unit->data().player) attack=1; } if (source_hex_id!=-1) { attack=1; // WHY ?? // I am staying at the same hex if (source_hex_id == target_hex_id) { attack= 0; } if (_world.rules->unit_types[source_unit->data().type]->data().movement_terrain[source_hex->data().terrain]==0) attack = 0; if (source_hex->data().unit!= 0) attack = 0; } /*********/ // script.run(); // script.getVar("attack", &attack2); // if (attack2!=attack) // { // printf("\n\nWRONG TRANSFORMATION!!!!\n\n"); // THROW(E_8K_TCL_Error,"Wrong TCL -> C transformation"); // } /*********/ if (attack) result.push_back(*it); } catch (E_8K_TCL_Error) { // attack=0; } } } unlock(); return result; } TIntContainer * TWorldEngine::getUnitVisibility(int unit_id, int hex_id) { lock(); // Zaplavovym algoritmem vyplnim oblast, na kterou jednotka dohledne a vratim ji // jako seznam hexu. TLivingUnit * unit = _world.units[unit_id]; // jednotka, kterou zkoumam TIntContainer waiting; // zasobnik uzlu cekajicich na expanzi TIntContainer hidden; // zasobnik hexu, ktere jiz byly vyhodnoceny, jako skryte TIntContainer * visible = new TIntContainer(); // kontejner na jiz vyhodnocene uzly TIntContainer * neighbours; // seznam sousednich hexu pro expanzi #if GET_UNIT_VISIBILITY_TCL TTCL_Script script(& interpreter); // skript pro vypocet viditelnosti v TCL #endif int result; // vysledek TCL skriptu int hex = (hex_id == -1) ? unit->hex() : hex_id; // expandovany hex int sx,sy, tx, ty; // promenne pro ulozeni souradnic hexu // nacteni a inicializace skriptu _world.map.idToCoords(hex, &sx, &sy); #if GET_UNIT_VISIBILITY_TCL script.loadStruct(_world.rules->scripts[TS_UNIT_VISIBILITY]); script.setVar("unit", unit); script.setVar("source_x", &sx); script.setVar("source_y", &sy); #endif // inicializace a spusteni algoritmu int distance_x,distance_y,eyesight,in_range,sourcex,sourcey,targetx,targety; eyesight = 2 * _world.rules->unit_types[unit->data().type]->data().eyesight_distance; // prepocet na souradnice stredu hexu sourcex= 4*sx + 2 - 2*(sy%2); sourcey= sy; waiting.push_back(hex); while (!waiting.empty()) { // vyjmu prvni uzel z kontejneru hex = waiting.back(); waiting.pop_back(); // spocitam jeho viditelnost _world.map.idToCoords(hex, &tx, &ty); #if GET_UNIT_VISIBILITY_TCL script.setVar("target_x", &tx); script.setVar("target_y", &ty); try { script.run(); script.getVar("visible", &result); } catch (E_8K_TCL_Error) { result = 0; } #else // varriant in C if ((sx == tx) && (sy == ty)) { // na hexu stojim, nemusim nic pocitat result= 1; } else { // koukam na nejaky hex, musim pocitat // dosah videni jednotky // eyesight= 2 * $unit_types($unit(type), eyesight_distance)]; // eyesight= 8; // prepocet na souradnice stredu hexu targetx= 4*tx + 2 - 2*(ty%2); targety= ty; // vzdalenost stredu hexu distance_x=abs(targetx - sourcex); distance_y=abs(targety - sourcey); if ((distance_x <= eyesight) && (distance_y <= eyesight) && (distance_x*distance_x + distance_y*distance_y < (eyesight + 1)*(eyesight + 1))) { in_range=1; } else { in_range=0; } if (in_range == 0) { // ani mi nestaci dosah, nemusim nic pocitat result=0; } else { result=1; } } #endif // pokud je viditelny, pridam jeho dosud neprozkoumane sousedy do kontejneru // pridam na seznam cekajich if (result) { neighbours = _world.map.getNeighbours(hex); for (TIntContainer::iterator hit = neighbours->begin(); hit != neighbours->end(); hit++) { if (visible->find(*hit) == -1) { if (waiting.find(*hit) == -1) { if (hidden.find(*hit) == -1) { waiting.push_back(*hit); } } } } delete neighbours; visible->push_back(hex); } else { hidden.push_back(hex); } } unlock(); return visible; } TIntContainer * TWorldEngine::getBuildingVisibility(int building_id) { lock(); // Zaplavovym algoritmem vyplnim oblast, na kterou budova dohledne a vratim ji // jako seznam hexu. TLivingBuilding * building = _world.buildings[building_id]; // budova, kterou zkoumam TIntContainer waiting; // zasobnik uzlu cekajicich na expanzi TIntContainer hidden; // zasobnik hexu, ktere jiz byly vyhodnoceny, jako skryte TIntContainer * visible = new TIntContainer(); // kontejner na jiz vyhodnocene uzly TIntContainer * neighbours; // seznam sousednich hexu pro expanzi TTCL_Script script(& interpreter); // skript pro vypocet viditelnosti v TCL int result; // vysledek TCL skriptu int hex; // expandovany hex int sx,sy, tx, ty; // promenne pro ulozeni souradnic hexu // nacteni a inicializace skriptu script.loadStruct(_world.rules->scripts[TS_BUILDING_VISIBILITY]); _world.map.idToCoords(building->hex(), &sx, &sy); script.setVar("building", building); script.setVar("source_x", &sx); script.setVar("source_y", &sy); // inicializace a spusteni algoritmu waiting.push_back(building->hex()); while (!waiting.empty()) { // vyjmu prvni uzel z kontejneru hex = waiting.back(); waiting.pop_back(); // spocitam jeho viditelnost _world.map.idToCoords(hex, &tx, &ty); script.setVar("target_x", &tx); script.setVar("target_y", &ty); try { script.run(); script.getVar("visible", &result); } catch (E_8K_TCL_Error) { result = 0; } // pokud je viditelny, pridam jeho dosud neprozkoumane sousedy do kontejneru // pridam na seznam cekajic if (result) { neighbours = _world.map.getNeighbours(hex); for (TIntContainer::iterator hit = neighbours->begin(); hit != neighbours->end(); hit++) { if (visible->find(*hit) == -1) { if (waiting.find(*hit) == -1) { if (hidden.find(*hit) == -1) { waiting.push_back(*hit); } } } } delete neighbours; visible->push_back(hex); } else { hidden.push_back(hex); } } unlock(); return visible; } TIntContainer * TWorldEngine::getTownVisibility(int town_id) { lock(); TIntContainer * visible = new TIntContainer(); // kontejner na jiz vyhodnocene uzly TTCL_Script script(& interpreter); // skript pro vypocet viditelnosti v TCL try { script.loadStruct(_world.rules->scripts[TS_TOWN_VISIBILITY]); script.setVar("town_id", &town_id); script.run(); script.getVar("visible", (TTCL_Int_List *)visible); } catch (E_8K_TCL_Error) { } unlock(); return visible; } vector TWorldEngine::getBuildingRange(int unit_id, int building_type) { lock(); int _result, _hex; vector result; TPacket_RET_BUILDING_RANGE_Position position; TTCL_Script script(& interpreter); script.loadStruct(_world.rules->scripts[TS_BUILDING_POSITION]); script.setVar("unit_id", &unit_id); script.setVar("building_type", &building_type); TBuilding * building = _world.rules->building_types[building_type]; TLivingUnit * unit = _world.units[unit_id]; switch (building->data().construction_position) { case CP_SAME_HEX: // vyhodnoceni v TCL _hex = unit->hex(); script.setVar("location", &_hex); try { script.run(); script.getVar("result", &_result); script.getVar("orientation", (TTCL_Int_List *)&position.orientation); if (_result && (position.orientation.size() > 0)) { position.position = _hex; result.push_back(position); } } catch (E_8K_TCL_Error) { } break; case CP_ADJACENT_HEX: // ziskani kruhoveho okoli TIntContainer range; TIntIterator it; range = _world.map.getCircle(unit->hex(), 1); // pres vsechny hexy v dosahu vyhodnotim moznost stavby for (it = range.begin(); it != range.end(); it++) { // vyhodnoceni v TCL _hex = *it; if (_world.map.insideBorders(_hex)) { script.setVar("location", &_hex); try { script.run(); script.getVar("result", &_result); script.getVar("orientation", (TTCL_Int_List *)&position.orientation); if (_result && (position.orientation.size() > 0)) { position.position = _hex; result.push_back(position); } } catch (E_8K_TCL_Error) { } } } break; } unlock(); return result; } TIntContainer TWorldEngine::getRepairRange(UNIT_ID unit_id) { lock(); int _result, _hex; TIntContainer range, result; TIntIterator it; TTCL_Script script(& interpreter); script.loadStruct(_world.rules->scripts[TS_REPAIR_POSITION]); script.setVar("unit_id", &unit_id); // pres vsechny hexy v dosahu vyhodnotim moznost stavby TLivingUnit * unit = _world.units[unit_id]; range = _world.map.getCircle(unit->hex(), 1); for (it = range.begin(); it != range.end(); it++) { // vyhodnoceni v TCL _hex = *it; if (_world.map.insideBorders(_hex)) { script.setVar("location", &_hex); try { script.run(); script.getVar("result", &_result); if (_result) result.push_back(_result); } catch (E_8K_TCL_Error) { } } } unlock(); return result; } int TWorldEngine::getDistance(int unit_id, int points_of_movement, int from, int to, TGetCostTransparency transparency) { int result = 0; lock(); // je hex v hratelne casti mapy? if (_world.map.insideBorders(to)) { // hex patri do hratelne casti mapy #if GET_DISTANCE_TCL { // varianta vyhodnoceni v TCL TTCL_Script script(& interpreter); script.loadStruct(_world.rules->scripts[TS_MOVEMENT_COST]); script.setVar("unit", _world.units[unit_id]); script.setVar("source", _world.map.getHexById(from)); script.setVar("target", _world.map.getHexById(to)); script.setVar("movement", &points_of_movement); script.setVar("transparency", &transparency); if (int building_id = _world.map.getHexById(to)->data().building) { script.setVar("building", _world.buildings[building_id]); } try { script.run(); script.getVar("cost", &result); world_log.LogMsg("[WS] getDistance;%d;%d;%d,%d;%d\n", unit_id, points_of_movement, from, to, result); } catch (E_8K_TCL_Error) { result = -1; } } #else { // varianta vyhodnoceni v C int cost = 0; // inicializace a nacteni objektu THex * source = _world.map.getHexById(from); THex * target = _world.map.getHexById(to); TLivingUnit * unit = _world.units[unit_id]; TLivingBuilding * building = (target->data().building) ? _world.buildings[target->data().building] : NULL; // je cilovy hex volny int access = 0; if (target->data().unit == 0) access = 1; else if (transparency == GCT_UnitsNotTransparent) access = 0; else if (transparency == GCT_IgnoreMyUnits) { TLivingUnit * target_unit = _world.units[target->data().unit]; if (target_unit->data().player == unit->data().player) access = 1; else access = 0; } else if ((transparency == GCT_IgnoreAllUnits) || (transparency == GCT_IgnoreRivers) || (transparency == GCT_SampleUnit)) access = 1; if (access) { // hex je volny (nebo ingoruji jednotky), jdu na nej // koeficient pohybu v terenu float coef_terrain; if ((transparency == GCT_IgnoreRivers) && (target->data().terrain == TT_RIVER)) coef_terrain = 1; else if ((transparency == GCT_SampleUnit) && (target->data().terrain != TT_DEEP_FOREST) && (target->data().terrain != TT_SEA)) coef_terrain = 1; else { coef_terrain = _world.rules->unit_types[unit->data().type]->data().movement_terrain[target->data().terrain]; if (coef_terrain == 0) if (target->data().building) if ((building->data().construction_duration == 0) && (_world.rules->building_types[building->data().type]->data().structure_type = ST_OPEN)) coef_terrain = 1; } // koeficient pro pohyb z kopce / do kopce float coef_hill; if (target->data().elevation > source->data().elevation) coef_hill = _world.rules->unit_types[unit->data().type]->data().movement_uphill; else if (target->data().elevation < source->data().elevation) coef_hill = _world.rules->unit_types[unit->data().type]->data().movement_downhill; else coef_hill = 1; int elevation = abs(source->data().elevation - target->data().elevation); float coef_elevation = (elevation + 1) * coef_hill; // koeficient pro pohyb v nadmorske vysce float coef_altitude = _world.rules->unit_types[unit->data().type]->data().movement_elevation[target->data().elevation]; // koeficient pro jizdu podle pocasi float coef_weather = _world.rules->unit_types[unit->data().type]->data().movement_weather[_world.weather.data().state]; // celkovy pocet bodu pohybu cost = (int)ceil(coef_terrain * coef_elevation * coef_altitude * coef_weather); // pokud je na vychozim hexu otevrena budova, zkontroluji, mohu-li ji danym smerem opustit if (source->data().building) { TLivingBuilding * source_building = _world.buildings[source->data().building]; if (_world.rules->building_types[source_building->data().type]->data().structure_type == ST_OPEN) { // Je to otevrena budova - zkontroluji, je-li natocena ve smeru odchodu // ziskam ID hexu, se kterymi sousedi TIntContainer adjacent = _world.map.getNeighboursOrientation(from, source_building->data().orientation); TIntContainer opposite = _world.map.getNeighboursOrientation(from, (6 + source_building->data().orientation) % 12); // pokud mezi nimi neni cilovy hex, neni krok mozny if ((adjacent.find(to) == -1) && (opposite.find(to) == -1)) cost = 0; } } // pokud je na cilovem hexu budova, stoji pohyb cely zbytek bodu pohybu if (target->data().building) { if (_world.rules->building_types[building->data().type]->data().structure_type == ST_OPEN) { // Je to otevrena budova - zkontroluji, je-li natocena smerem ke mne // ziskam ID hexu, se kterymi sousedi TIntContainer adjacent = _world.map.getNeighboursOrientation(to, building->data().orientation); TIntContainer opposite = _world.map.getNeighboursOrientation(to, (6 + building->data().orientation) % 12); // pokud mezi nimi neni startovni hex, neni krok mozny if ((adjacent.find(from) == -1) && (opposite.find(from) == -1)) cost = 0; } else { if (_world.rules->unit_types[unit->data().type]->data().can_enter_building) { // jednotka smi vstupovat do budov if (cost <= unit->data().points_of_movement - points_of_movement) { // bude mne to stat zbytek BP cost = unit->data().points_of_movement - points_of_movement; } else { // stejne bych se tam nedostal, nemam dostatek BP } } else { // jednotka nesmi vstupovat do budov cost = 0; } } } } else { // hex je obsazeny cost = 0; } result = cost; } #endif } else { // hex patri do hranicni casti result = 0; } unlock(); return result; } int TWorldEngine::getUnitActions(UNIT_ID unit_id, int actions[], std::vector & buildings) { lock(); int result = 0; TTCL_Script script(& interpreter); // nasypu data do TCL script.loadStruct(_world.rules->scripts[TS_UNIT_ACTIONS]); script.setVar("unit_id", &unit_id); // vyhodnotim akce v TCL try { script.run(); script.getVar("suicide", &actions[UA_SUICIDE]); script.getVar("move", &actions[UA_MOVE]); script.getVar("attack", &actions[UA_ATTACK]); script.getVar("heal", &actions[UA_HEAL]); script.getVar("recruit", &actions[UA_RECRUIT]); script.getVar("recruit_elite", &actions[UA_RECRUIT_ELITE]); script.getVar("start_building", &actions[UA_START_BUILDING]); script.getVar("repair_building", &actions[UA_REPAIR_BUILDING]); script.getVar("stop_building", &actions[UA_STOP_BUILDING]); script.getVar("buildings", &buildings); } catch (E_8K_TCL_Error) { result = ERR_InvalidInput; } unlock(); return result; } int TWorldEngine::getBuildingActions(BUILDING_ID building_id, int actions[], std::vector & recruitment, std::vector & bonuses) { lock(); int result = 0; TTCL_Script script(& interpreter); // nasypu data do TCL script.loadStruct(_world.rules->scripts[TS_BUILDING_ACTIONS]); script.setVar("building_id", &building_id); // vyhodnotim akce v TCL try { script.run(); script.getVar("suicide", &actions[BA_SUICIDE]); script.getVar("recruit_unit", &actions[BA_RECRUIT_UNIT]); script.getVar("sell_bonus", &actions[BA_SELL_BONUS]); script.getVar("recruitment", &recruitment); script.getVar("bonuses", &bonuses); } catch (E_8K_TCL_Error) { result = ERR_InvalidInput; } unlock(); return result; } TWorldServerEngine::TWorldServerEngine(TWorld * world, THistory * history) : TWorldEngine(world), _history(* history) { } TWorldServerEngine::~TWorldServerEngine() { } int TWorldServerEngine::unitSuicide(int unit_id) { lock(); TPacket_RCT_UNIT_SUICIDE * packet = new TPacket_RCT_UNIT_SUICIDE(); packet->unit_id = unit_id; // promitnu zmeny do sveta TLivingUnit * unit = _world.units[unit_id]; removeUnitVisibility(packet->unit_id, packet->hidden); _world.unitDied(packet->unit_id); // pridam do historie _history.add(AT_UNIT_SUICIDE, packet); // zaznamenam do statistiky _world.players.unitKilled(unit->data().player); // odemknu unlock(); return 0; } int TWorldServerEngine::buildingSuicide(int building_id) { lock(); TPacket_RCT_BUILDING_SUICIDE * packet = new TPacket_RCT_BUILDING_SUICIDE(); packet->building_id = building_id; // promitnu zmeny do sveta TLivingBuilding * building = _world.buildings[building_id]; // byla v budove jednotka? if (building->unit()) { packet->unit_killed = building->unit(); // aktualizuju viditelnost removeUnitVisibility(packet->unit_killed, packet->hidden); // odstranim _world.unitDied(packet->unit_killed); // zaznamenam do statistiky _world.players.unitKilled(building->data().player); } else { packet->unit_killed = 0; } // znicim budovu removeBuildingVisibility(packet->building_id, packet->hidden); _world.buildingDestroyed(packet->building_id); // pridam do historie _history.add(AT_BUILDING_SUICIDE, packet); // zaznamenam do statistiky _world.players.buildingDestroyed(building->data().player); // odemknu svet unlock(); return 0; } int TWorldServerEngine::buildingRecruitUnit(int building_id, int unit_type) { lock(); int result = 0; TPacket_RCT_BUILDING_RECRUIT_UNIT * packet = new TPacket_RCT_BUILDING_RECRUIT_UNIT(); packet->building_id = building_id; packet->unit_type = unit_type; // pusteni skriptu TTCL_Script script(& interpreter); script.loadStruct(_world.rules->scripts[TS_BUILDING_RECRUIT_UNIT]); script.setVar("building_id", &building_id); script.setVar("unit_type", &unit_type); script.run(); script.getVar("result", &result); if (result == 0) { // v poradku, jednotku vyrobim script.getVar("cost", &packet->cost); // zaznamenam do sveta packet->unit_id = _world.units.auto_increment(); TLivingUnit * unit = _world.unitCreated(packet->building_id, packet->unit_type, packet->unit_id, packet->cost); // Aktualizace viditelnosti TIntContainer * visibility = getUnitVisibility(packet->unit_id); TIntIterator it; for (it = visibility->begin(); it != visibility->end(); it++) { if (_world.visibility_maps[unit->data().player]->getHexById(*it).setActual(packet->unit_id)) { packet->shown.push_back(*it); } } delete visibility; // pridam do historie _history.add(AT_BUILDING_RECRUIT_UNIT, packet); } // odemknu svet unlock(); return result; } int TWorldServerEngine::startBuilding(int unit_id, int building_type, int location, int orientation) { int result = 0; int hex; lock(); TPacket_RCT_START_BUILDING * packet = new TPacket_RCT_START_BUILDING(); packet->building_type = building_type; packet->location = location; packet->orientation = orientation; packet->unit_id = unit_id; // pusteni skriptu TTCL_Script script(& interpreter); script.loadStruct(_world.rules->scripts[TS_START_BUILDING]); script.setVar("building_type", &building_type); script.setVar("unit_id", &unit_id); script.setVar("location", &location); script.setVar("orientation", &orientation); script.run(); script.getVar("result", &result); if (result == 0) { // v poradku, budovu vyrobim script.getVar("cost", &packet->cost); script.getVar("lives", &packet->lives); // zaznamenam do sveta vytvoreni budovy hex = _world.units[unit_id]->hex(); packet->building_id = _world.buildings.auto_increment(); TLivingBuilding * building = _world.buildingCreated(packet->unit_id, packet->building_type, packet->building_id, packet->location, packet->lives, packet->cost, packet->orientation); // vypocitam zmeny ve viditelnosti // aktualizace viditelnosti - 1.budova TIntContainer * visibility = getBuildingVisibility(packet->building_id); TIntIterator it; // Pridam hexy, ktere budova vidi do aktualniho vyhledu. for (it = visibility->begin(); it != visibility->end(); it++) { if (_world.visibility_maps[building->data().player]->getHexById(*it).setActual(0, packet->building_id)) { packet->shown.push_back(*it); } } delete visibility; // aktualizace viditelnosti - 2. jednotka if (hex != _world.units[unit_id]->hex()) { // jednotka se pojla TLivingUnit * unit = _world.units[unit_id]; TIntContainer * neighbours1, * neighbours2; TIntContainer shown, hidden; // vypocitam si hexy, ktere jednotka videla, nez se pohla neighbours1 = getUnitVisibility(unit_id, hex); neighbours2 = getUnitVisibility(unit_id); shown = TIntContainer::difference(*neighbours2, *neighbours1); hidden = TIntContainer::difference(*neighbours1, *neighbours2); delete neighbours1; delete neighbours2; // odmazu jednotku z hexu, ktere videla. Pokud byla jedinou entitou, ktera hex videla, // vytvorim HISTORY_HEX a ulozim jej. for (it = hidden.begin(); it != hidden.end(); it++) { if (_world.visibility_maps[unit->data().player]->getHexById(*it).removeActual(unit_id)) { packet->hidden.push_back(*it); } } // Pridam hexy, ktere nyni jednotka vidi, do aktualniho vyhledu. for (it = shown.begin(); it != shown.end(); it++) { if (_world.visibility_maps[unit->data().player]->getHexById(*it).setActual(unit_id)) { packet->shown.push_back(*it); } } } // pridam do historie _history.add(AT_UNIT_BUILD_START, packet); } unlock(); return result; } int TWorldServerEngine::repairBuilding(int unit_id, int building_id) { int result = 0; int hex; lock(); TPacket_RCT_REPAIR_BUILDING * packet = new TPacket_RCT_REPAIR_BUILDING(); packet->building_id = building_id; packet->unit_id = unit_id; // pusteni skriptu TTCL_Script script(& interpreter); script.loadStruct(_world.rules->scripts[TS_REPAIR_BUILDING]); script.setVar("building_id", &building_id); script.setVar("unit_id", &unit_id); script.run(); script.getVar("result", &result); if (result == 0) { // v poradku, budovu zacnu opravovat script.getVar("cost", &packet->cost); script.getVar("lives", &packet->lives); // zaznamenam zmeny do sveta hex = _world.units[unit_id]->hex(); _world.buildingRepaired(packet->unit_id, packet->building_id, packet->lives, packet->cost); // vypocitam zmeny ve viditelnosti if (hex != _world.units[unit_id]->hex()) { // jednotka se pohla TLivingUnit * unit = _world.units[unit_id]; TIntContainer * neighbours1, * neighbours2; TIntContainer shown, hidden; TIntIterator it; // vypocitam si hexy, ktere jednotka videla, nez se pohla neighbours1 = getUnitVisibility(unit_id, hex); neighbours2 = getUnitVisibility(unit_id); shown = TIntContainer::difference(*neighbours2, *neighbours1); hidden = TIntContainer::difference(*neighbours1, *neighbours2); delete neighbours1; delete neighbours2; // odmazu jednotku z hexu, ktere videla. Pokud byla jedinou entitou, ktera hex videla, // vytvorim HISTORY_HEX a ulozim jej. for (it = hidden.begin(); it != hidden.end(); it++) { if (_world.visibility_maps[unit->data().player]->getHexById(*it).removeActual(unit_id)) { packet->hidden.push_back(*it); } } // Pridam hexy, ktere nyni jednotka vidi, do aktualniho vyhledu. for (it = shown.begin(); it != shown.end(); it++) { if (_world.visibility_maps[unit->data().player]->getHexById(*it).setActual(unit_id)) { packet->shown.push_back(*it); } } } // pridam do historie _history.add(AT_UNIT_REPAIR_START, packet); } unlock(); return result; } int TWorldServerEngine::stopBuilding(int unit_id) { lock(); int result = 0; TLivingUnit * unit = _world.units[unit_id]; if (unit->data().state == US_BUILDING) { TPacket_RCT_STOP_BUILDING * packet = new TPacket_RCT_STOP_BUILDING(); packet->unit_id = unit_id; _world.stopBuilding(unit_id); _history.add(AT_UNIT_BUILD_STOP, packet); } else { result = ERR_InvalidInput; } unlock(); return result; } int TWorldServerEngine::sellBonus(int building_id, int bonus_id) { lock(); int result = 0; TTCL_Script script(& interpreter); script.loadStruct(_world.rules->scripts[TS_SELL_BONUS]); script.setVar("building_id", &building_id); script.setVar("bonus_id", &bonus_id); script.run(); script.getVar("result", &result); if (result == 0) { // v poradku TPacket_RCT_SELL_BONUS * packet = new TPacket_RCT_SELL_BONUS(); packet->bonus_id = bonus_id; packet->building_id = building_id; script.getVar("cost", &packet->cost); script.getVar("replace", &packet->replace); _world.sellBonus(packet->building_id, packet->bonus_id, packet->cost, packet->replace); _history.add(AT_BUILDING_SELL_BONUS, packet); } unlock(); return result; } int TWorldServerEngine::unitMove(int unit_id, int to, TPathFindTransparency transparency) { lock(); int result = 0; int points_of_movement = 0; TLivingUnit * unit = _world.units[unit_id]; TStep step; TIntContainer * neighbours1, *neighbours2; TIntContainer shown, hidden; TTCL_Int_List unit_influence, town_influence, new_unit_influence, new_town_influence; int previous, left_town_id, left_kingdom_owner; TIntContainer::iterator it; TTCL_Script script(&interpreter), kingdom_script(&interpreter); script.loadStruct(_world.rules->scripts[TS_OCCUPY]); script.setVar("unit_id", &unit_id); kingdom_script.loadStruct(_world.rules->scripts[TS_KINGDOM_OCCUPY]); kingdom_script.setVar("unit_id", &unit_id); std::map buildings_occuppied, towns_occuppied, kingdoms_occuppied, towns_previous_owners; std::map::iterator occuppied; // kontrola opravneni k pohybu bool can_move = false; if ((unit->data().state == US_IDLE) || (unit->data().state == US_HAS_MOVED)) { can_move = true; } else if ( ((unit->data().state == US_HAS_FOUGHT) || (unit->data().state == US_HAS_FOUGHT_AND_MOVED)) && ((_world.rules->unit_types[unit->data().type]->data().movement_and_fight_combination == MFC_ANYTHING) || (_world.rules->unit_types[unit->data().type]->data().movement_and_fight_combination == MFC_ATTACK_FIRST)) ) { can_move = 1; } if (!can_move) return ERR_NotOperational; TPacket_RCT_UNIT_MOVE * packet = new TPacket_RCT_UNIT_MOVE(); packet->unit_id = unit_id; packet->player_id = 0; // zprava pro PathFind PACKET_PATHFIND_REQUEST msg; msg.unit_id = unit_id; msg.points_of_movement = unit->data().points_of_movement; msg.pathfind_transparency = transparency; _world.map.idToCoords(unit->hex(), &msg.from.x, &msg.from.y); _world.map.idToCoords(to, &msg.to.x, &msg.to.y); // necham PathFind vyresit nejlepsi cestu TPATH * fullPath = (TPATH *)KSendGlobalMessage(MSG_PATHFIND_REQUEST, MOD_WORLD_SERVER, MOD_PATHFIND, &msg); if (fullPath != NULL) { // pridam do historie _history.add(AT_UNIT_MOVE, packet); // provadim krok po kroku previous = unit->hex(); bool continue_path = true; for (TPATH::iterator i = fullPath->begin(); (continue_path) && (i != fullPath->end()); i++) { _world.map.coordsToId(i->x, i->y, &step.hex_id); step.left_hex_id = previous; step.shown.clear(); step.hidden.clear(); if (previous != step.hex_id) { // spocitani narocnosti kroku step.cost = getDistance(unit_id, 0, previous, step.hex_id); if (step.cost == 0) { // krok neni platny (muze se stat pri vypoctu pohybu AI s nejakou transparenci) break; } // odectu BP. Pokud na to uz nemam, koncim if (unit->data().points_of_movement >= step.cost) { unit->data().points_of_movement -= step.cost; points_of_movement += step.cost; } else { break; } // vypocitam si hexy, ktere jednotka vidi pred a po pohybu neighbours1 = getUnitVisibility(unit_id); neighbours2 = getUnitVisibility(unit_id, step.hex_id); shown = TIntContainer::difference(*neighbours2, *neighbours1); hidden = TIntContainer::difference(*neighbours1, *neighbours2); delete neighbours1; delete neighbours2; // odmazu jednotku z hexu, ktere videla. Pokud byla jedinou entitou, ktera hex videla, // vytvorim HISTORY_HEX a ulozim jej. for (it = hidden.begin(); it != hidden.end(); it++) { if (_world.visibility_maps[unit->data().player]->getHexById(*it).removeActual(unit_id)) { // Navic to zapisu do zpravy, aby se to mohlo hezky zobrazit step.hidden.push_back(*it); } } // zmena polohy jednotky _world.unitMove(unit_id, step.hex_id); // vyhodnotim, jestli nedoslo k obsazeni budovy/mesta/kralovstvi script.setVar("previous_hex_id", &previous); script.run(); int player_id, id; // obsazeni budovy script.getVar("building_id", &id); script.getVar("player_id", &player_id); if (id) { buildings_occuppied[id] = player_id; } // obsazeni mesta script.getVar("town_id", &id); if (id) { towns_previous_owners[id] = _world.towns[id]->data().owner; _world.towns[id]->data().owner = player_id; towns_occuppied[id] = player_id; } // zmena majitele opusteneho mesta script.getVar("left_town_id", &left_town_id); if (left_town_id) { script.getVar("left_town_owner", &id); towns_previous_owners[left_town_id] = _world.towns[left_town_id]->data().owner; _world.towns[left_town_id]->data().owner = id; towns_occuppied[left_town_id] = id; } // obsazeni kralovstvi kingdom_script.setVar("previous_hex_id", &previous); kingdom_script.run(); kingdom_script.getVar("player_id", &player_id); kingdom_script.getVar("kingdom_id", &id); if (id) { kingdoms_occuppied[id] = player_id; } // obsazeni opusteneho kralovstvi kingdom_script.getVar("left_kingdom_id", &id); if (id) { kingdom_script.getVar("left_kingdom_owner", &left_kingdom_owner); kingdoms_occuppied[id] = left_kingdom_owner; } // Pridam hexy, ktere nyni jednotka vidi, do aktualniho vyhledu. for (it = shown.begin(); it != shown.end(); it++) { if (_world.visibility_maps[unit->data().player]->getHexById(*it).setActual(unit_id)) { // Pokud je do te doby nevidela, dam o tom vedet GUI. step.shown.push_back(*it); } } } else { step.cost = 0; } previous = step.hex_id; packet->path.push_back(step); } } else { result = ERR_InvalidInput; } // zpracuji vygenerovana obsazeni for (occuppied = buildings_occuppied.begin(); occuppied != buildings_occuppied.end(); occuppied++) buildingOccuppied(occuppied->first, occuppied->second); for (occuppied = towns_occuppied.begin(); occuppied != towns_occuppied.end(); occuppied++) { _world.towns[occuppied->first]->data().owner = towns_previous_owners[occuppied->first]; townOccuppied(occuppied->first, occuppied->second); } for (occuppied = kingdoms_occuppied.begin(); occuppied != kingdoms_occuppied.end(); occuppied++) kingdomOccuppied(occuppied->first, occuppied->second); // odemknu svet unlock(); return result; } int TWorldServerEngine::unitAttack(int attacker, int target) { lock(); int result = 0; int defender = 0; TPacket_RCT_UNIT_ATTACK * packet = new TPacket_RCT_UNIT_ATTACK(); TTCL_Script script(& interpreter); packet->attacker = attacker; packet->target = target; // kontrola dosahu TIntContainer range = getAttackRange(attacker); if (range.find(target) == -1) { unlock(); return ERR_InvalidInput; } // nasypu data do TCL script.loadStruct(_world.rules->scripts[TS_ATTACK]); script.setVar("attacker", &attacker); script.setVar("target", &target); // vyhodnotim souboj v TCL script.run(); script.getVar("result", &result); if (result == 0) { script.getVar("attacker_wounds", &packet->attacker_wounds); script.getVar("attacker_losses", &packet->attacker_losses); script.getVar("defender_wounds", &packet->defender_wounds); script.getVar("defender_losses", &packet->defender_losses); script.getVar("building_losses", &packet->building_losses); script.getVar("attacker_experience", &packet->attacker_experience); script.getVar("attacker_level", &packet->attacker_level); script.getVar("defender_experience", &packet->defender_experience); script.getVar("defender_level", &packet->defender_level); // promitnu zmeny do sveta // 1. utocnik TLivingUnit * unit = _world.units[packet->attacker]; if ((unit->data().lives -= packet->attacker_losses) <= 0) { // utocici jednotka zemrela packet->attacker_killed = packet->attacker; // upravim viditelnost removeUnitVisibility(packet->attacker, packet->attacker_hidden); // aktualizuji svet _world.unitDied(packet->attacker); } else { _world.unitWound(packet->attacker, packet->attacker_wounds); _world.unitExperience(packet->attacker, packet->attacker_experience, packet->attacker_level); if (unit->data().state == US_IDLE) unit->data().state = US_HAS_FOUGHT; else if (unit->data().state == US_HAS_MOVED) unit->data().state = US_EXHAUSTED; } // 2. obrance THex * hex = _world.map.getHexById(packet->target); if (hex->data().building) { // napadena byla budova TLivingBuilding * building = _world.buildings[hex->data().building]; defender = building->data().player; if ((building->data().lives -= packet->building_losses) <= 0) { // napadena budova byla znicena if (hex->data().unit) { // ve znicene budove byla jednotka - ta umira packet->defender_killed = hex->data().unit; // aktualizuju viditelnost removeUnitVisibility(hex->data().unit, packet->defender_hidden); // odstranim _world.unitDied(hex->data().unit); } // zniceni budovy packet->building_destroyed = hex->data().building; removeBuildingVisibility(hex->data().building, packet->defender_hidden); _world.buildingDestroyed(hex->data().building); } else if (hex->data().unit) { // napadena budova utok prezila, ale byla v ni jednotka TLivingUnit * attacked_unit = _world.units[hex->data().unit]; if ((attacked_unit->data().lives -= packet->defender_losses) <= 0) { // napadena jednotka v budove byla zabita packet->defender_killed = hex->data().unit; removeUnitVisibility(hex->data().unit, packet->defender_hidden); _world.unitDied(hex->data().unit); } else { _world.unitWound(hex->data().unit, packet->defender_wounds); _world.unitExperience(hex->data().unit, packet->defender_experience, packet->defender_level); } } } else if (hex->data().unit) { // napadena jednotka na otevrenem prostranstvi (bez budovy) TLivingUnit * attacked_unit = _world.units[hex->data().unit]; defender = attacked_unit->data().player; if ((attacked_unit->data().lives -= packet->defender_losses) <= 0) { // napadena jednotka zemrela packet->defender_killed = hex->data().unit; removeUnitVisibility(hex->data().unit, packet->defender_hidden); _world.unitDied(hex->data().unit); } else { _world.unitWound(hex->data().unit, packet->defender_wounds); _world.unitExperience(hex->data().unit, packet->defender_experience, packet->defender_level); } } // pridam do historie _history.add(AT_UNIT_ATTACK, packet); // dam vedet diplomacii if (defender != 0) { TLivingUnit * attacked_unit = NULL; TLivingBuilding * attacked_building = NULL; if (hex->data().unit || packet->defender_killed) { // na hexu je nebo byla jednotka attacked_unit = packet->defender_killed ? _world.corpses[packet->defender_killed] : _world.units[hex->data().unit]; } if (hex->data().building || packet->building_destroyed) { // na hexu je nebo byla budova attacked_building = packet->building_destroyed ? _world.ruins[packet->building_destroyed] : _world.buildings[hex->data().building]; } TPacket_Diplomacy_AttackInfo attackInfo; attackInfo.attacking_diplomat_world_id = unit->data().player; attackInfo.attacked_diplomat_world_id = defender; attackInfo.attacking_unit_type = unit->data().type; attackInfo.attacked_unit_type = attacked_unit ? attacked_unit->data().type : -1; attackInfo.attacking_unit_cost = _world.rules->unit_types[unit->data().type]->data().cost; attackInfo.attacked_unit_cost = attacked_unit ? _world.rules->unit_types[attacked_unit->data().type]->data().cost : -1; attackInfo.attacked_building_type = attacked_building ? attacked_building->data().type : -1; attackInfo.attacked_building_cost = attacked_building ? _world.rules->building_types[attacked_building->data().type]->data().base_cost : -1; attackInfo.attack_on_city = hex->town() ? true : false; attackInfo.attacking_unit_level = unit->data().level; attackInfo.attacked_unit_level = attacked_unit ? attacked_unit->data().level : -1; attackInfo.attacking_unit_men_number_full = _world.rules->unit_types[unit->data().type]->data().max_lives; attackInfo.attacked_unit_men_number_full = attacked_unit ? _world.rules->unit_types[attacked_unit->data().type]->data().max_lives : -1; attackInfo.attacked_building_lives_full = attacked_building ? _world.rules->building_types[attacked_building->data().type]->data().lives : -1; attackInfo.attacking_unit_men_number_before = unit->data().lives + packet->attacker_losses; attackInfo.attacked_unit_men_number_before = attacked_unit ? attacked_unit->data().lives + packet->defender_losses : -1; attackInfo.attacked_building_lives_before = attacked_building ? attacked_building->data().lives + packet->building_losses : -1; attackInfo.attacking_unit_men_number_after = unit->data().lives; attackInfo.attacked_unit_men_number_after = attacked_unit ? attacked_unit->data().lives : -1; attackInfo.attacked_building_lives_after = attacked_building ? attacked_building->data().lives : -1; KSendGlobalMessage(MSG_DIPLOMACY_ATTACK_INFO, MOD_WORLD_SERVER, MOD_DIPLOMACY, &attackInfo); } // zapisu do statistik if (packet->defender_killed) _world.players.unitKilled(defender, unit->data().player); if (packet->attacker_killed) _world.players.unitKilled(unit->data().player, defender); if (packet->building_destroyed) _world.players.buildingDestroyed(defender, unit->data().player); } // zkontroluji, jestli se vysledkem souboje nezmenilo vlastnictvi nekterych mest // nebo celych kralovstvi int town_id, town_owner, kingdom_id, kingdom_owner, temp; if (packet->attacker_killed) { // zemrel utocnik - proverim zmeny tykajici se jeho hexu temp = _world.corpses[packet->attacker_killed]->hex(); // zmena majitele mesta script.loadStruct(_world.rules->scripts[TS_NEW_TOWN_OWNER]); script.setVar("hex_id", &temp); script.run(); script.getVar("town_id", &town_id); if (town_id) { script.getVar("town_owner", &town_owner); townOccuppied(town_id, town_owner); } // zmena majitele kralovstvi script.loadStruct(_world.rules->scripts[TS_NEW_KINGDOM_OWNER]); script.setVar("hex_id", &temp); script.run(); script.getVar("kingdom_id", &kingdom_id); if (kingdom_id) { script.getVar("kingdom_owner", &kingdom_owner); kingdomOccuppied(kingdom_id, kingdom_owner); } } if (packet->defender_killed) { // zemrel obrance - proverim zmeny tykajici se jeho hexu temp = _world.corpses[packet->defender_killed]->hex(); // zmena majitele mesta script.loadStruct(_world.rules->scripts[TS_NEW_TOWN_OWNER]); script.setVar("hex_id", &temp); script.run(); script.getVar("town_id", &town_id); if (town_id) { script.getVar("town_owner", &town_owner); townOccuppied(town_id, town_owner); } // zmena majitele kralovstvi script.loadStruct(_world.rules->scripts[TS_NEW_KINGDOM_OWNER]); script.setVar("hex_id", &temp); script.run(); script.getVar("kingdom_id", &kingdom_id); if (kingdom_id) { script.getVar("kingdom_owner", &kingdom_owner); kingdomOccuppied(kingdom_id, kingdom_owner); } } // odemknu unlock(); return result; } int TWorldServerEngine::unitHeal(int unit_id) { int result = 0; // zamknu lock(); // priprava TCL skriptu TTCL_Script script(&interpreter); script.loadStruct(_world.rules->scripts[TS_UNIT_HEAL]); script.setVar("unit_id", &unit_id); // pustim skript script.run(); script.getVar("result", &result); if (result == 0) { TPacket_RCT_UNIT_HEAL * packet = new TPacket_RCT_UNIT_HEAL(); packet->unit_id = unit_id; script.getVar("lives", &packet->lives); // promitnu zmeny do sveta _world.unitHeal(unit_id, packet->lives); // zapisu do historie _history.add(AT_UNIT_HEAL, packet); } // odemknu unlock(); return result; } int TWorldServerEngine::unitRecruit(int unit_id) { int result = 0; // zamknu lock(); // priprava TCL skriptu TTCL_Script script(&interpreter); script.loadStruct(_world.rules->scripts[TS_UNIT_RECRUIT]); script.setVar("unit_id", &unit_id); // pustim skript script.run(); script.getVar("result", &result); if (result == 0) { TPacket_RCT_UNIT_RECRUIT * packet = new TPacket_RCT_UNIT_RECRUIT(); packet->unit_id = unit_id; script.getVar("experience", &packet->experience); script.getVar("level", &packet->level); script.getVar("lives", &packet->lives); script.getVar("cost", &packet->cost); // promitnu zmeny do sveta _world.unitRecruit(unit_id, packet->lives, packet->cost, packet->level, packet->experience); // zapisu do historie _history.add(AT_UNIT_RECRUIT, packet); } // odemknu unlock(); return result; } int TWorldServerEngine::unitRecruitElite(int unit_id) { int result = 0; // zamknu lock(); // priprava TCL skriptu TTCL_Script script(&interpreter); script.loadStruct(_world.rules->scripts[TS_UNIT_RECRUIT_ELITE]); script.setVar("unit_id", &unit_id); // pustim skript script.run(); script.getVar("result", &result); if (result == 0) { TPacket_RCT_UNIT_RECRUIT_ELITE * packet = new TPacket_RCT_UNIT_RECRUIT_ELITE(); packet->unit_id = unit_id; script.getVar("lives", &packet->lives); script.getVar("cost", &packet->cost); // promitnu zmeny do sveta _world.unitRecruitElite(unit_id, packet->lives, packet->cost); // zapisu do historie _history.add(AT_UNIT_RECRUIT_ELITE, packet); } // odemknu unlock(); return result; } int TWorldServerEngine::nextTurn() { lock(); // printf("Next TURN FCE"); int result = 0; int player_id; TTCL_Script script(& interpreter); TPacket_RCT_NEXT_TURN * packet = new TPacket_RCT_NEXT_TURN(); // ukonceni diplomatickeho kola (pouze, pokud hrac na tahu neprohral) if (_world.players.playerOnTurn()->data().game_data.state != PS_DEFEATED) { TPacket_Diplomacy_DiplomatTurnEnd turn_end; turn_end.diplomat_world_id = _world.players.playerIdOnTurn(); KSendGlobalMessage(MSG_DIPLOMACY_DIPLOMAT_TURN_END, MOD_WORLD_SERVER, MOD_DIPLOMACY, &turn_end); } // predani kola nasledujicimu hraci if (_world.players.next()) { // doslo k posunu do dalsiho kola packet->newRound = true; _world.turn_id++; script.loadStruct(_world.rules->scripts[TS_NEW_ROUND]); script.run(); script.getVar("weather_state", &packet->weatherState); _history.add(AT_NEXT_ROUND, packet); } else { // zustavam ve stejnem kole packet->newRound = false; packet->weatherState = _world.weather.data().state; _history.add(AT_NEXT_TURN, packet); } packet->player_id_on_turn = _world.players.playerIdOnTurn(); _world.setWeather(packet->weatherState); player_id = _world.players.playerIdOnTurn(); // vyhodnoceni noveho tahu v TCL script.loadStruct(_world.rules->scripts[TS_NEXT_TURN]); script.setVar("player_id", &player_id); script.run(); script.getVar("money_gain", &packet->moneyGain); script.getVar("money_lack", &packet->moneyLack); script.getVar("units_deserted", (vector *)&packet->unitsDeserted); script.getVar("buildings_interrupted", (vector *)&packet->unitsInterrupted); script.getVar("units_wounded", (vector *)&packet->unitsWounded); script.getVar("units_died", (vector *)&packet->unitsDied); script.joinIntLists("movement_points_ids", "movement_points_gain", packet->movementPoints); script.joinIntLists("building_lives_ids", "building_lives_gain", packet->buildingLives); // promitnuti zmen do sveta a ziskani seznamu mrtvych muzu _world.nextTurn(packet->moneyGain, packet->movementPoints, packet->buildingLives, packet->unitsInterrupted, &packet->menDied); // smrt jednotek TIntIterator it; for (it = packet->unitsDeserted.begin(); it != packet->unitsDeserted.end(); it++) { removeUnitVisibility(*it, packet->hidden); _world.unitDied(*it); _world.players.unitKilled(_world.players.playerIdOnTurn()); } for (it = packet->unitsDied.begin(); it != packet->unitsDied.end(); it++) { removeUnitVisibility(*it, packet->hidden); _world.unitDied(*it); _world.players.unitKilled(_world.players.playerIdOnTurn()); } // zahajeni diplomatickeho kola TPacket_Diplomacy_DiplomatTurnBegin turn_begin; turn_begin.diplomat_world_id = _world.players.playerIdOnTurn(); TPacket_DIPLOMACY_CHANGE * change = (TPacket_DIPLOMACY_CHANGE *)KSendGlobalMessage(MSG_DIPLOMACY_DIPLOMAT_TURN_BEGIN, MOD_WORLD_SERVER, MOD_DIPLOMACY, &turn_begin); if (change) KSendGlobalMessage(MSG_DIPLOMACY_CHANGE, MOD_WORLD_SERVER, MOD_WORLD_SERVER, change); // odemknu unlock(); return result; } void TWorldServerEngine::buildingOccuppied(int building_id, int new_player_id) { TPacket_BUILDING_OCCUPIED * building_occupied = new TPacket_BUILDING_OCCUPIED(); building_occupied->building_id = building_id; building_occupied->new_player_id = new_player_id; building_occupied->former_player_id = _world.buildings[building_id]->data().player; // odmazu viditelnost predchoziho hrace // a soucasne pridam viditelnost noveho majitele TIntContainer * neighbours1 = getBuildingVisibility(building_id); for (TIntIterator it = neighbours1->begin(); it != neighbours1->end(); it++) { if (building_occupied->former_player_id) { if (_world.visibility_maps[building_occupied->former_player_id]->getHexById(*it).removeActual(0, building_id)) { building_occupied->hidden.push_back(*it); } } if (_world.visibility_maps[new_player_id]->getHexById(*it).setActual(0, building_id)) { building_occupied->shown.push_back(*it); } } delete neighbours1; // zmenim majitele _world.buildings[building_id]->data().player = building_occupied->new_player_id; // pridam do historie _history.add(AT_BUILDING_OCCUPIED, building_occupied); // zaznamenam do statistik _world.players.buildingCaptured(building_occupied->new_player_id, building_occupied->former_player_id); // dam o tom vedet diplomacii if (building_occupied->former_player_id != 0) { TPacket_Diplomacy_ConquerBuilding conquerBuilding; conquerBuilding.attacked_diplomat_world_id = building_occupied->former_player_id; conquerBuilding.attacking_diplomat_world_id = building_occupied->new_player_id; conquerBuilding.building_type = _world.buildings[building_id]->data().type; conquerBuilding.building_cost = _world.rules->building_types[_world.buildings[building_id]->data().type]->data().base_cost; KSendGlobalMessage(MSG_DIPLOMACY_CONQUER_BUILDING, MOD_WORLD_SERVER, MOD_DIPLOMACY, &conquerBuilding); } } void TWorldServerEngine::townOccuppied(int town_id, int new_player_id) { // sestavim zpravu int former_player_id = _world.towns[town_id]->data().owner; TPacket_TOWN_OCCUPIED * town_occupied = new TPacket_TOWN_OCCUPIED(); town_occupied->town_id = town_id; town_occupied->new_player_id = new_player_id; town_occupied->former_player_id = former_player_id; // odmazu viditelnost predchoziho hrace // a soucasne pridam viditelnost noveho majitele TIntContainer * neighbours = getTownVisibility(town_id); for (TIntIterator it = neighbours->begin(); it != neighbours->end(); it++) { if (town_occupied->former_player_id) { if (_world.visibility_maps[town_occupied->former_player_id]->getHexById(*it).removeActual(0, 0, town_id)) { town_occupied->hidden.push_back(*it); } } if (_world.visibility_maps[town_occupied->new_player_id]->getHexById(*it).setActual(0, 0, town_id)) { town_occupied->shown.push_back(*it); } } delete neighbours; // zmenim majitele _world.towns[town_id]->data().owner = new_player_id; // zaznamenam do historie _history.add(AT_TOWN_OCCUPIED, town_occupied); // zaznamenam do statistik _world.players.townCaptured(town_occupied->new_player_id, town_occupied->former_player_id); // dam o tom vedet diplomacii if (former_player_id != 0) { TPacket_Diplomacy_ConquerCity conquerCity; conquerCity.attacked_diplomat_world_id = former_player_id; conquerCity.attacking_diplomat_world_id = new_player_id; conquerCity.payment = _world.towns[town_id]->data().citysize * TOWN_INCOME; KSendGlobalMessage(MSG_DIPLOMACY_CONQUER_CITY, MOD_WORLD_SERVER, MOD_DIPLOMACY, &conquerCity); } } void TWorldServerEngine::kingdomOccuppied(int kingdom_id, int new_player_id) { // sestavim zpravu TPacket_KINGDOM_OCCUPIED * kingdom_occupied = new TPacket_KINGDOM_OCCUPIED(); kingdom_occupied->kingdom_id = kingdom_id; kingdom_occupied->new_player_id = new_player_id; kingdom_occupied->former_player_id = _world.kingdoms[kingdom_id]->data().roleid; // zmenim majitele _world.kingdoms[kingdom_id]->data().roleid = new_player_id; // zaznamenam do historie _history.add(AT_KINGDOM_OCCUPIED, kingdom_occupied); // zaznamenam do statistik _world.players.kingdomCaptured(kingdom_occupied->new_player_id, kingdom_occupied->former_player_id); } int TWorldServerEngine::playerDefeatedTest(PLAYER_ID player_id) { int result = 0; TTCL_Script script(& interpreter); script.loadStruct(_world.rules->scripts[TS_PLAYER_DEFEATED]); script.setVar("player_id", &player_id); script.run(); script.getVar("result", &result); if (result == 0) { int defeated; script.getVar("defeated", &defeated); if (defeated) { // zapisu do sveta _world.playerDefeated(player_id); // uvedomim diploma TPacket_Diplomacy_RemoveDiplomat msg; msg.diplomat_world_id = player_id; KSendGlobalMessage(MSG_DIPLOMACY_REMOVE_DIPLOMAT, MOD_WORLD_SERVER, MOD_DIPLOMACY, &msg); // zaznamenam do historie TPacket_PLAYER_DEFEATED * action_data = new TPacket_PLAYER_DEFEATED(); action_data->defeated_player_id = player_id; _history.add(AT_PLAYER_DEFEATED, action_data); } } return result; } bool TWorldServerEngine::endGameTest() { bool game_has_ended = false; int result = 0; lock(); TTCL_Script script(& interpreter); script.loadStruct(_world.rules->scripts[TS_ENDGAME]); script.setVar("end_game_condition", &world_server->end_game_condition); script.run(); script.getVar("result", &result); if (result == 0) { int winner; int reason; script.getVar("winner", &winner); script.getVar("reason", &reason); if ((reason != 0) || (winner != 0)) { endGame((ENDGAME_REASON)reason, winner); game_has_ended = true; } else if (_world.players.numberOfHumanPlayers() == 0) { // nezbyde nikdo zivy, kdo by ocenil krasu hry 8K endGame(ER_CANCEL); game_has_ended = true; } } unlock(); return game_has_ended; } bool TWorldServerEngine::finalTest(PLAYER_ID player1_id, PLAYER_ID player2_id) { bool result = false; lock(); if (player1_id == 0) { // vyhodnocuji pres vsechny aktivni hrace for (TPlayerIterator it = _world.players.begin(); (result == 0) && (it != _world.players.end()); it++) { if (it->second->data().game_data.state == PS_ACTIVE) playerDefeatedTest(it->first); } } else if (player2_id == 0) { // vyhodnocuji pres jednoho hrace playerDefeatedTest(player1_id); } else { // vyhodnocuji pres dva hrace playerDefeatedTest(player1_id); playerDefeatedTest(player2_id); } result = endGameTest(); unlock(); return result; } int TWorldServerEngine::endGame(ENDGAME_REASON reason, PLAYER_ID winner) { int result = 0; // ukonceni tahu AI, pokud prave probiha if (_world.players.playerOnTurn()->data().client_data.type != PT_HUMAN) { KSendGlobalMessage(MSG_AI_WAIT_AI_PLANNING, MOD_WORLD_SERVER, MOD_GUI, NULL); KSendGlobalMessage(MSG_AI_WAIT_AI_PLANNING, MOD_WORLD_SERVER, MOD_STRATEGY, NULL); } // zapsani konce hry do historie TPacket_END_GAME * action_data = new TPacket_END_GAME(); action_data->reason = reason; action_data->winner = winner; _history.add(AT_ENDGAME, action_data); return result; } void TWorldServerEngine::calculateVisibility() { lock(); // vypocitam viditelnost pro kazdeho hrace podle jeho jednotek a budov TPlayerIterator pit; TLivingUnitsIterator luit; TLivingBuildingsIterator buit; TTownsIterator toit; TIntContainer * visible; TIntIterator it; for (luit = _world.units.begin(); luit != _world.units.end(); luit++) { if ((pit = _world.players.find(luit->second->data().player)) != _world.players.end()) { // mam jednotku a jejiho hrace - rozsvitim vsechny hexy viditelneho okoli visible = getUnitVisibility(luit->first); for (it = visible->begin(); it != visible->end(); it++) { _world.visibility_maps[pit->first]->getHexById(*it).setActual(luit->first); } delete visible; } } for (buit = _world.buildings.begin(); buit != _world.buildings.end(); buit++) { if ((pit = _world.players.find(buit->second->data().player)) != _world.players.end()) { // mam budovu i jejiho hrace - rozsvitim vsechny hexy viditelneho okoli visible = getBuildingVisibility(buit->first); for (it = visible->begin(); it != visible->end(); it++) { _world.visibility_maps[pit->first]->getHexById(*it).setActual(0, buit->first); } delete visible; } } for (toit = _world.towns.begin(); toit != _world.towns.end(); toit++) { if ((pit = _world.players.find(toit->second->data().owner)) != _world.players.end()) { // mam mesto i jejho majitele - rozsvitim vsechny hexy mesta visible = getTownVisibility(toit->first); for (it = visible->begin(); it != visible->end(); it++) { _world.visibility_maps[pit->first]->getHexById(*it).setActual(0, 0, toit->first); } delete visible; } } unlock(); } void TWorldServerEngine::removeUnitVisibility(int unit_id, TIntContainer & hidden) { lock(); TIntContainer * neighbours = getUnitVisibility(unit_id); TLivingUnit * unit = _world.units[unit_id]; for (TIntContainer::iterator it = neighbours->begin(); it != neighbours->end(); it++) { if (_world.visibility_maps[unit->data().player]->getHexById(*it).removeActual(unit_id)) { hidden.push_back(*it); } } unlock(); } void TWorldServerEngine::removeBuildingVisibility(int building_id, TIntContainer & hidden) { lock(); TIntContainer * neighbours = getBuildingVisibility(building_id); TLivingBuilding * building = _world.buildings[building_id]; if (building->data().player) { for (TIntContainer::iterator it = neighbours->begin(); it != neighbours->end(); it++) { if (_world.visibility_maps[building->data().player]->getHexById(*it).removeActual(0, building_id)) { hidden.push_back(*it); } } } unlock(); } void TWorldServerEngine::removeTownVisibility(int town_id, TIntContainer & hidden) { lock(); TIntContainer * neighbours = getTownVisibility(town_id); TTown * town = _world.towns[town_id]; if (town->data().owner) { for (TIntContainer::iterator it = neighbours->begin(); it != neighbours->end(); it++) { if (_world.visibility_maps[town->data().owner]->getHexById(*it).removeActual(0, 0, town_id)) { hidden.push_back(*it); } } } unlock(); }