diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index a890d1196..21de4ec5e 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -7,6 +7,7 @@ #include "../../lib/CCreatureHandler.h" #include //#include +#include "../../lib/CHeroHandler.h" CBattleCallback * cbc; @@ -204,16 +205,8 @@ void CStupidAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2 { print("battleStart called"); side = Side; - TStacks myStacks = cb->battleGetStacks(CBattleCallback::ONLY_MINE); - std::cout << "My side: " << side << std::endl - << "I have " << myStacks.size() << " stacks. They are:\n"; + printOpeningReport(); - for(int i = 0; i < myStacks.size(); i++) - { - const CStack *s = myStacks.at(i); - tlog5 << format("%2d) Stack of %4d %s.\n\tAttack:\t%4d, \n\tDefense:\t%4d, \n\tHP:\t%4d\n\tDamage:\t%4d-%d\n") - % i % s->count % s->getCreature()->namePl % s->Attack() % s->Defense() % s->MaxHealth() % s->getMinDamage() % s->getMaxDamage(); - } } void CStupidAI::battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) @@ -276,3 +269,29 @@ BattleAction CStupidAI::goTowards(const CStack * stack, THex hex) } } +void CStupidAI::printOpeningReport() +{ + TStacks myStacks = cb->battleGetStacks(CBattleCallback::ONLY_MINE); + tlog5 << "My side: " << side << std::endl; + if(const CGHeroInstance *h = cb->battleGetFightingHero(side)) + { + tlog5 << boost::format("I have a hero named %s (Type ID=%d)\n") % h->name % h->type->ID; + tlog5 << boost::format("Hero skills: Attack %d, Defense %d, Spell Power %d, Knowledge %d, Mana %d/%d\n") + % h->Attack() % h->Defense() % h->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + % h->getPrimSkillLevel(PrimarySkill::KNOWLEDGE) % h->mana % h->manaLimit(); + tlog5 << "Number of known spells: " << h->spells.size() << std::endl; + } + else + tlog5 << "I do not have a hero.\n"; + + + + tlog5 << "I have " << myStacks.size() << " stacks. They are:\n"; + + for(int i = 0; i < myStacks.size(); i++) + { + const CStack *s = myStacks.at(i); + tlog5 << format("%2d) Stack of %4d %s.\n\tAttack:\t\t%4d, \n\tDefense:\t%4d, \n\tHP:\t\t%4d\n\tDamage:\t\t%4d-%d\n") + % (i+1) % s->count % s->getCreature()->namePl % s->Attack() % s->Defense() % s->MaxHealth() % s->getMinDamage() % s->getMaxDamage(); + } +} diff --git a/AI/StupidAI/StupidAI.h b/AI/StupidAI/StupidAI.h index b9621fe2b..36c29d37a 100644 --- a/AI/StupidAI/StupidAI.h +++ b/AI/StupidAI/StupidAI.h @@ -6,6 +6,7 @@ class CStupidAI : public CBattleGameInterface CBattleCallback *cb; void print(const std::string &text) const; + void printOpeningReport(); public: CStupidAI(void); ~CStupidAI(void); diff --git a/VCMI_BattleAiHost/CheckTime.h b/VCMI_BattleAiHost/CheckTime.h index 63f38444f..19dcd68fd 100644 --- a/VCMI_BattleAiHost/CheckTime.h +++ b/VCMI_BattleAiHost/CheckTime.h @@ -58,13 +58,13 @@ struct CheckTime //all ms const int PROCESS_INFO_TIME = 5; -const int MAKE_DECIDION_TIME = 75; +const int MAKE_DECIDION_TIME = 125; const int MEASURE_MARGIN = 1; const int HANGUP_TIME = 50; const int STARTUP_TIME = 100; void postInfoCall(int timeUsed); -void postDecisionCall(int timeUsed); +void postDecisionCall(int timeUsed, const std::string &text = "AI was thinking over an action"); struct Bomb { diff --git a/VCMI_BattleAiHost/NetPacksRunner.cpp b/VCMI_BattleAiHost/NetPacksRunner.cpp index 543582754..9fa853be9 100644 --- a/VCMI_BattleAiHost/NetPacksRunner.cpp +++ b/VCMI_BattleAiHost/NetPacksRunner.cpp @@ -22,9 +22,9 @@ void postInfoCall(int timeUsed, int limit) } } -void postDecisionCall(int timeUsed) +void postDecisionCall(int timeUsed, const std::string &text/* = "AI was thinking over an action"*/) { - tlog0 << "AI was thinking over an action for " << timeUsed << " ms.\n"; + tlog0 << text << " for " << timeUsed << " ms.\n"; if(timeUsed > MAKE_DECIDION_TIME + MEASURE_MARGIN) { tlog1 << "That's too long! AI is disqualified!\n"; diff --git a/VCMI_BattleAiHost/main.cpp b/VCMI_BattleAiHost/main.cpp index 923ac65b3..1f0016322 100644 --- a/VCMI_BattleAiHost/main.cpp +++ b/VCMI_BattleAiHost/main.cpp @@ -20,6 +20,7 @@ #include "../lib/BattleState.h" #include "../lib/NetPacks.h" #include "../lib/CThreadHelper.h" +#include "CheckTime.h" using namespace std; using namespace boost; @@ -88,10 +89,17 @@ int main(int argc, char** argv) tlog0 << "Cbc created\n"; if(battleAIName.size()) { + Bomb *b = new Bomb(55 + HANGUP_TIME); + CheckTime timer; + ////////////////////////////////////////////////////////////////////////// cl.ai = CDynLibHandler::getNewBattleAI(battleAIName); cl.color = color; tlog0 << "AI created\n"; cl.ai->init(cbc); + ////////////////////////////////////////////////////////////////////////// + postDecisionCall(timer.timeSinceStart(), "AI was being created"); + b->disarm(); + BattleStart bs; bs.info = gs->curB; diff --git a/b1.json b/b1.json index 0d8df0120..6e7710266 100644 --- a/b1.json +++ b/b1.json @@ -9,6 +9,7 @@ "side" : 0, "army" : [[10, 40]], "heroid" : 0, + "heroPrimSkills" : [4, 4, 3, 2], "spells" : [1,2,3,4] }, { diff --git a/lib/BattleState.h b/lib/BattleState.h index c39062294..9e8c23bbf 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -75,9 +75,10 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode h & static_cast(*this); } - ////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// //void getBonuses(BonusList &out, const CSelector &selector, const CBonusSystemNode *root = NULL) const; - ////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + const CStack * getNextStack() const; //which stack will have turn after current one void getStackQueue(std::vector &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action @@ -146,40 +147,87 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode class DLL_EXPORT CStack : public CBonusSystemNode, public CStackBasicDescriptor { public: - const CStackInstance *base; +/// pointer to structure describing stack in garrison that was source of this stack, may be NULL (eg. summoned creatures and War Machines) + const CStackInstance *base; + + +/// unique ID of stack + ui32 ID; + +/// how many creatures stack counted at the beginning of battle + ui32 baseAmount; + +/// HP of first creature in stack + ui32 firstHPleft; + +/// owner - player colour (255 for neutrals), slot - position in garrison (may be 255 for neutrals/called creatures) + ui8 owner, slot; + +/// if true, this stack is owned by attakcer (this one from left hand side of battle) + ui8 attackerOwned; + +/// position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower + THex position; + +/// how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1) + ui8 counterAttacks; + +/// how many shots left + si16 shots; + +/// how many casts left + ui8 casts; - ui32 ID; //unique ID of stack - ui32 baseAmount; - ui32 firstHPleft; //HP of first creature in stack - ui8 owner, slot; //owner - player colour (255 for neutrals), slot - position in garrison (may be 255 for neutrals/called creatures) - ui8 attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle) - THex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower - ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1) - si16 shots; //how many shots left - ui8 casts; //how many casts left std::set state; - //overrides const CCreature* getCreature() const {return type;} - CStack(const CStackInstance *base, int O, int I, bool AO, int S); //c-tor - CStack(const CStackBasicDescriptor *stack, int O, int I, bool AO, int S = 255); //c-tor - CStack(); //c-tor +/// c-tor + CStack(const CStackInstance *base, int O, int I, bool AO, int S); + +/// c-tor + CStack(const CStackBasicDescriptor *stack, int O, int I, bool AO, int S = 255); + +/// c-tor + CStack(); + ~CStack(); std::string nodeName() const OVERRIDE; - void init(); //set initial (invalid) values - void postInit(); //used to finish initialization when inheriting creature parameters is working - const Bonus * getEffect(ui16 id, int turn = 0) const; //effect id (SP) - ui8 howManyEffectsSet(ui16 id) const; //returns amount of effects with given id set for this stack - bool willMove(int turn = 0) const; //if stack has remaining move this turn - bool ableToRetaliate() const; //if stack can retaliate after attacked - bool moved(int turn = 0) const; //if stack was already moved this turn - bool canMove(int turn = 0) const; //if stack can move - ui32 Speed(int turn = 0) const; //get speed of creature with all modificators +/// set initial (invalid) values + void init(); + +/// used to finish initialization when inheriting creature parameters is working + void postInit(); + +/// effect id (SP) + const Bonus * getEffect(ui16 id, int turn = 0) const; + +/// returns amount of effects with given id set for this stack + ui8 howManyEffectsSet(ui16 id) const; + +/// if stack has remaining move this turn + bool willMove(int turn = 0) const; + +/// if stack can retaliate after attacked + bool ableToRetaliate() const; + +/// if stack was already moved this turn + bool moved(int turn = 0) const; + +/// if stack can move + bool canMove(int turn = 0) const; + +/// get speed of creature with all modificators + ui32 Speed(int turn = 0) const; + static void stackEffectToFeature(std::vector & sf, const Bonus & sse); - std::vector activeSpells() const; //returns vector of active spell IDs sorted by time of cast - const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, NULL otherwise +/// returns vector of active spell IDs sorted by time of cast + std::vector activeSpells() const; + +/// if stack belongs to hero (directly or was by him summoned) returns hero, NULL otherwise + const CGHeroInstance *getMyHero() const; + static inline Bonus featureGenerator(Bonus::BonusType type, si16 subtype, si32 value, ui16 turnsRemain, si32 additionalInfo = 0, si32 limit = Bonus::NO_LIMIT) { @@ -199,13 +247,25 @@ public: static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, THex attackerPos = THex::INVALID, THex defenderPos = THex::INVALID); - bool doubleWide() const; - THex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1 - std::vector getHexes() const; //up to two occupied hexes, starting from front - bool coversPos(THex position) const; //checks also if unit is double-wide - std::vector getSurroundingHexes(THex attackerPos = THex::INVALID) const; // get six or 8 surrounding hexes depending on creature size +/// checks if stack is double wide (occupies two hexes) + bool doubleWide() const; + +/// returns number of occupied hex (not the position) if stack is double wide; otherwise -1 + THex occupiedHex() const; + +/// up to two occupied hexes, starting from front + std::vector getHexes() const; + +/// checks also if unit is double-wide + bool coversPos(THex position) const; + +/// get six or 8 surrounding hexes depending on creature size + std::vector getSurroundingHexes(THex attackerPos = THex::INVALID) const; + + +/// requires bsa.damageAmout filled + void prepareAttacked(BattleStackAttacked &bsa) const; - void prepareAttacked(BattleStackAttacked &bsa) const; //requires bsa.damageAmout filled template void serialize(Handler &h, const int version) { @@ -236,7 +296,9 @@ public: } } - bool alive() const //determines if stack is alive +/// determines if stack is alive + bool alive() const + { return vstd::contains(state,ALIVE); } diff --git a/lib/CBattleCallback.h b/lib/CBattleCallback.h index 7491e5669..ab038f2b2 100644 --- a/lib/CBattleCallback.h +++ b/lib/CBattleCallback.h @@ -19,11 +19,19 @@ public: class DLL_EXPORT IBattleCallback { public: - bool waitTillRealize; //if true, request functions will return after they are realized by server - bool unlockGsWhenWaiting;//if true after sending each request, gs mutex will be unlocked so the changes can be applied; NOTICE caller must have gs mx locked prior to any call to actiob callback! +/// if true, request functions will return after they are realized by server + bool waitTillRealize; + +/// if true after sending each request, gs mutex will be unlocked so the changes can be applied; NOTICE caller must have gs mx locked prior to any call to actiob callback! + bool unlockGsWhenWaiting; + //battle - virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack - virtual bool battleMakeTacticAction(BattleAction * action) =0; // performs tactic phase actions +/// for casting spells by hero - DO NOT use it for moving active stack + virtual int battleMakeAction(BattleAction* action)=0; + +/// performs tactic phase actions + virtual bool battleMakeTacticAction(BattleAction * action) =0; + }; class DLL_EXPORT CBattleCallback : public IBattleCallback, public CBattleInfoCallback @@ -35,8 +43,12 @@ protected: public: CBattleCallback(CGameState *GS, int Player, IConnectionHandler *C); - int battleMakeAction(BattleAction* action) OVERRIDE;//for casting spells by hero - DO NOT use it for moving active stack - bool battleMakeTacticAction(BattleAction * action) OVERRIDE; // performs tactic phase actions +/// for casting spells by hero - DO NOT use it for moving active stack + int battleMakeAction(BattleAction* action) OVERRIDE; + +/// performs tactic phase actions + bool battleMakeTacticAction(BattleAction * action) OVERRIDE; + friend class CCallback; friend class CClient; diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index 47a1df7bc..a30618f10 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -22,7 +22,7 @@ template rett * createAny(std::string dllname, std::string methodName) { - char temp[50]; + char temp[500]; rett * ret=NULL; rett*(*getAI)(); void(*getName)(char*); diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index ace228b65..d89d6e598 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -33,6 +33,7 @@ #include "../lib/JsonNode.h" #include #include "BattleAction.h" +//#include boost::rand48 ran; class CGObjectInstance; @@ -967,10 +968,14 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed ) CArmedInstance *obj = NULL; if(dp.sides[i].heroId >= 0) { + const DuelParameters::SideSettings &ss = dp.sides[i]; CGHeroInstance *h = new CGHeroInstance(); armies[i] = heroes[i] = h; obj = h; - h->subID = dp.sides[i].heroId; + h->subID = ss.heroId; + for(int i = 0; i < ss.heroPrimSkills.size(); i++) + h->pushPrimSkill(i, ss.heroPrimSkills[i]); + h->initHero(h->subID); obj->initObj(); } @@ -2812,7 +2817,23 @@ DuelParameters DuelParameters::fromJSON(const std::string &fname) i++; } - ss.heroId = n["heroid"].Float(); + if(n["heroid"].getType() == JsonNode::DATA_FLOAT) + ss.heroId = n["heroid"].Float(); + else + ss.heroId = -1; + +// int msgboxID = MessageBox( +// NULL, +// "Resource not available\nDo you want to try again?", +// "Account Details", +// MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2 +// ); + + BOOST_FOREACH(const JsonNode &n, n["heroPrimSkills"].Vector()) + ss.heroPrimSkills.push_back(n.Float()); + + assert(ss.heroPrimSkills.empty() || ss.heroPrimSkills.size() == PRIMARY_SKILLS); + if(ss.heroId != -1) BOOST_FOREACH(const JsonNode &spell, n["spells"].Vector()) ss.spells.insert(spell.Float()); diff --git a/lib/CGameState.h b/lib/CGameState.h index 107f98ac5..64a21472b 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -297,12 +297,13 @@ struct DLL_EXPORT DuelParameters } stacks[ARMY_SIZE]; si32 heroId; //-1 if none + std::vector heroPrimSkills; //may be empty std::set spells; SideSettings(); template void serialize(Handler &h, const int version) { - h & stacks & heroId & spells; + h & stacks & heroId & heroPrimSkills & spells; } } sides[2]; diff --git a/lib/CThreadHelper.cpp b/lib/CThreadHelper.cpp index 0de0da70e..db5de4d77 100644 --- a/lib/CThreadHelper.cpp +++ b/lib/CThreadHelper.cpp @@ -49,8 +49,10 @@ void CThreadHelper::processTasks() } } +static std::list names; void setThreadName(long threadID, const std::string &name) { + names.push_back(name); #ifdef _WIN32 //follows http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx const DWORD MS_VC_EXCEPTION=0x406D1388; @@ -65,10 +67,12 @@ void setThreadName(long threadID, const std::string &name) #pragma pack(pop) THREADNAME_INFO info; info.dwType = 0x1000; - info.szName = name.c_str(); + info.szName = names.back().c_str(); info.dwThreadID = threadID; info.dwFlags = 0; + tlog5 << "Thread " << GetCurrentThreadId() << " will be known as " << name << std::endl; + __try { RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index b44d97572..c5dbc31c3 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -88,38 +88,95 @@ public: ONLY_MINE, ONLY_ENEMY, MINE_AND_ENEMY }; - //battle - int battleGetBattlefieldType(); // 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines 8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields 15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog 21. "favourable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship - int battleGetObstaclesAtTile(THex tile); //returns bitfield - std::vector battleGetAllObstacles(); //returns all obstacles on the battlefield - const CStack * battleGetStackByID(int ID, bool onlyAlive = true); //returns stack info by given ID - const CStack * battleGetStackByPos(THex pos, bool onlyAlive = true); //returns stack info by given pos - THex battleGetPos(int stack); //returns position (tile ID) of stack - TStacks battleGetStacks(EStackOwnership whose = MINE_AND_ENEMY, bool onlyAlive = true); //returns stacks on battlefield - void getStackQueue( std::vector &out, int howMany ); //returns vector of stack in order of their move sequence - void battleGetStackCountOutsideHexes(bool *ac); // returns hexes which when in front of a stack cause us to move the amount box back - std::vector battleGetAvailableHexes(const CStack * stack, bool addOccupiable, std::vector * attackable = NULL); //returns numbers of hexes reachable by creature with id ID - std::vector battleGetDistances(const CStack * stack, THex hex = THex::INVALID, THex * predecessors = NULL); //returns vector of distances to [dest hex number] +/// Return value: 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines 8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields 15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog 21. "favourable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship + + + int battleGetBattlefieldType(); + +/// returns bitfield + int battleGetObstaclesAtTile(THex tile); + +/// returns all obstacles on the battlefield + std::vector battleGetAllObstacles(); + +/// returns stack info by given ID + const CStack * battleGetStackByID(int ID, bool onlyAlive = true); + +/// returns stack info by given pos + const CStack * battleGetStackByPos(THex pos, bool onlyAlive = true); + +/// returns position (tile ID) of stack + THex battleGetPos(int stack); + +/// returns stacks on battlefield + TStacks battleGetStacks(EStackOwnership whose = MINE_AND_ENEMY, bool onlyAlive = true); + +/// returns vector of stack in order of their move sequence + void getStackQueue( std::vector &out, int howMany ); + +/// returns hexes which when in front of a stack cause us to move the amount box back + void battleGetStackCountOutsideHexes(bool *ac); + +/// returns numbers of hexes reachable by creature with id ID + std::vector battleGetAvailableHexes(const CStack * stack, bool addOccupiable, std::vector * attackable = NULL); + +/// returns vector of distances to [dest hex number] + std::vector battleGetDistances(const CStack * stack, THex hex = THex::INVALID, THex * predecessors = NULL); + std::set battleGetAttackedHexes(const CStack* attacker, THex destinationTile, THex attackerPos = THex::INVALID); - bool battleCanShoot(const CStack * stack, THex dest); //returns true if unit with id ID can shoot to dest - bool battleCanCastSpell(); //returns true, if caller can cast a spell - SpellCasting::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell); //determines if given spell can be casted (and returns problem description) - bool battleCanFlee(); //returns true if caller can flee from the battle - int battleGetSurrenderCost(); //returns cost of surrendering battle, -1 if surrendering is not possible - const CGTownInstance * battleGetDefendedTown(); //returns defended town if current battle is a siege, NULL instead - ui8 battleGetWallState(int partOfWall); //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle - int battleGetWallUnderHex(THex hex); //returns part of destructible wall / gate / keep under given hex or -1 if not found - TDmgRange battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg = NULL); //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair - ui8 battleGetSiegeLevel(); //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle - const CGHeroInstance * battleGetFightingHero(ui8 side) const; //returns hero corresponding to given side (0 - attacker, 1 - defender) - si8 battleHasDistancePenalty(const CStack * stack, THex destHex); //checks if given stack has distance penalty - si8 battleHasWallPenalty(const CStack * stack, THex destHex); //checks if given stack has wall penalty - si8 battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel); //checks if teleportation of given stack to given position can take place - si8 battleGetTacticDist(); //returns tactic distance for calling player or 0 if player is not in tactic phase - ui8 battleGetMySide(); //return side of player in battle (attacker/defender) +/// returns true if unit with id ID can shoot to dest + bool battleCanShoot(const CStack * stack, THex dest); + +/// returns true, if caller can cast a spell + bool battleCanCastSpell(); + +/// determines if given spell can be casted (and returns problem description) + SpellCasting::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell); + +/// returns true if caller can flee from the battle + bool battleCanFlee(); + +/// returns cost of surrendering battle, -1 if surrendering is not possible + int battleGetSurrenderCost(); + +/// returns defended town if current battle is a siege, NULL instead + const CGTownInstance * battleGetDefendedTown(); + +/// for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle + ui8 battleGetWallState(int partOfWall); + +/// returns part of destructible wall / gate / keep under given hex or -1 if not found + int battleGetWallUnderHex(THex hex); + +/// estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair + TDmgRange battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg = NULL); + +/// returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle + ui8 battleGetSiegeLevel(); + +/// returns hero corresponding to given side (0 - attacker, 1 - defender) + const CGHeroInstance * battleGetFightingHero(ui8 side) const; + +/// checks if given stack has distance penalty + si8 battleHasDistancePenalty(const CStack * stack, THex destHex); + +/// checks if given stack has wall penalty + si8 battleHasWallPenalty(const CStack * stack, THex destHex); + +/// checks if teleportation of given stack to given position can take place + si8 battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel); + +/// returns tactic distance for calling player or 0 if player is not in tactic phase + si8 battleGetTacticDist(); + +/// return side of player in battle (attacker/defender) + ui8 battleGetMySide(); + //convenience methods using the ones above - TStacks battleGetAllStacks() //returns all stacks, alive or dead or undead or mechanical :) +/// returns all stacks, alive or dead or undead or mechanical :) + TStacks battleGetAllStacks() + { return battleGetStacks(MINE_AND_ENEMY, false); } @@ -191,7 +248,9 @@ public: const CGTownInstance * getTownInfo(int val, bool mode)const; //mode = 0 -> val = player town serial; mode = 1 -> val = object id (serial) std::vector getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited std::string getTavernGossip(const CGObjectInstance * townOrTavern) const; - int canBuildStructure(const CGTownInstance *t, int ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements +/// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements + int canBuildStructure(const CGTownInstance *t, int ID);/ + std::set getBuildingRequiments(const CGTownInstance *t, int ID); virtual bool getTownInfo(const CGObjectInstance *town, InfoAboutTown &dest) const; const CTown *getNativeTown(int color) const; @@ -314,6 +373,8 @@ public: }; /// Interface class for handling general game logic and actions + + class DLL_EXPORT IGameCallback : public CPrivilagedInfoCallback, public IGameEventCallback { public: diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index 36f2fff2a..36596955a 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -35,38 +35,68 @@ struct SetStackEffect; class DLL_EXPORT IBattleEventsReceiver { public: - virtual void actionFinished(const BattleAction *action){};//occurs AFTER every action taken by any stack or by the hero - virtual void actionStarted(const BattleAction *action){};//occurs BEFORE every action taken by any stack or by the hero - virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack - virtual void battleStacksAttacked(const std::vector & bsa){}; //called when stack receives damage (after battleAttack()) +/// occurs AFTER every action taken by any stack or by the hero + virtual void actionFinished(const BattleAction *action){}; + +/// occurs BEFORE every action taken by any stack or by the hero + virtual void actionStarted(const BattleAction *action){}; + +/// called when stack is performing attack + virtual void battleAttack(const BattleAttack *ba){}; + +/// called when stack receives damage (after battleAttack()) + virtual void battleStacksAttacked(const std::vector & bsa){}; + virtual void battleEnd(const BattleResult *br){}; - virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied; - virtual void battleNewRound(int round){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn - virtual void battleStackMoved(const CStack * stack, std::vector dest, int distance){}; - virtual void battleSpellCast(const BattleSpellCast *sc){}; - virtual void battleStacksEffectsSet(const SetStackEffect & sse){};//called when a specific effect is set to stacks - virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right - virtual void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp - virtual void battleNewStackAppeared(const CStack * stack){}; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned - virtual void battleObstaclesRemoved(const std::set & removedObstacles){}; //called when a certain set of obstacles is removed from batlefield; IDs of them are given - virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack - virtual void battleStacksRemoved(const BattleStacksRemoved & bsr){}; //called when certain stack is completely removed from battlefield +/// called at the beginning of each turn before changes are applied; + virtual void battleNewRoundFirst(int round){}; + +/// called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn + virtual void battleNewRound(int round){}; + +/// called when stack moves between tiles; dest is vector of tiles traversed + virtual void battleStackMoved(const CStack * stack, std::vector dest, int distance){}; + +/// called each time after spell is cast + virtual void battleSpellCast(const BattleSpellCast *sc){}; + +/// called when a specific effect is set to stacks + virtual void battleStacksEffectsSet(const SetStackEffect & sse){}; + +/// called by engine when battle starts; side=0 - left, side=1 - right + virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; + +/// called when stacks are healed / resurrected first element of pair - stack id, second - healed hp + virtual void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom){}; + +/// not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned + virtual void battleNewStackAppeared(const CStack * stack){}; + +/// called when a certain set of obstacles is removed from batlefield; IDs of them are given + virtual void battleObstaclesRemoved(const std::set & removedObstacles){}; + +/// called when catapult makes an attack + virtual void battleCatapultAttacked(const CatapultAttack & ca){}; + +/// called when certain stack is completely removed from battlefield + virtual void battleStacksRemoved(const BattleStacksRemoved & bsr){}; + }; class DLL_EXPORT IGameEventsReceiver { public: - virtual void buildChanged(const CGTownInstance *town, int buildingID, int what){}; //what: 1 - built, 2 - demolished + virtual void buildChanged(const CGTownInstance *town, int buildingID, int what){}; /// what: 1 - built, 2 - demolished - virtual void battleResultsApplied(){}; //called when all effects of last battle are applied + virtual void battleResultsApplied(){}; /// called when all effects of last battle are applied //garrison operations - virtual void stackChagedCount(const StackLocation &location, const TQuantity &change, bool isAbsolute){}; //if absolute, change is the new count; otherwise count was modified by adding change - virtual void stackChangedType(const StackLocation &location, const CCreature &newType){}; //used eg. when upgrading creatures - virtual void stacksErased(const StackLocation &location){}; //stack removed from previously filled slot + virtual void stackChagedCount(const StackLocation &location, const TQuantity &change, bool isAbsolute){}; /// if absolute, change is the new count; otherwise count was modified by adding change + virtual void stackChangedType(const StackLocation &location, const CCreature &newType){}; /// used eg. when upgrading creatures + virtual void stacksErased(const StackLocation &location){}; /// stack removed from previously filled slot virtual void stacksSwapped(const StackLocation &loc1, const StackLocation &loc2){}; - virtual void newStackInserted(const StackLocation &location, const CStackInstance &stack){}; //new stack inserted at given (previously empty position) - virtual void stacksRebalanced(const StackLocation &src, const StackLocation &dst, TQuantity count){}; //moves creatures from src stack to dst slot, may be used for merging/splittint/moving stacks + virtual void newStackInserted(const StackLocation &location, const CStackInstance &stack){}; /// new stack inserted at given (previously empty position) + virtual void stacksRebalanced(const StackLocation &src, const StackLocation &dst, TQuantity count){}; /// moves creatures from src stack to dst slot, may be used for merging/splittint/moving stacks //virtual void garrisonChanged(const CGObjectInstance * obj){}; //artifacts operations @@ -82,33 +112,33 @@ public: virtual void heroMoved(const TryMoveHero & details){}; virtual void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val){}; virtual void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val){}; - virtual void heroManaPointsChanged(const CGHeroInstance * hero){} //not called at the beginning of turn and after spell casts - virtual void heroMovePointsChanged(const CGHeroInstance * hero){} //not called at the beginning of turn and after movement + virtual void heroManaPointsChanged(const CGHeroInstance * hero){} /// not called at the beginning of turn and after spell casts + virtual void heroMovePointsChanged(const CGHeroInstance * hero){} /// not called at the beginning of turn and after movement virtual void heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town){}; virtual void receivedResource(int type, int val){}; virtual void showInfoDialog(const std::string &text, const std::vector &components, int soundID){}; virtual void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level){} - virtual void showShipyardDialog(const IShipyard *obj){} //obj may be town or shipyard; state: 0 - can buid, 1 - lack of resources, 2 - dest tile is blocked, 3 - no water + virtual void showShipyardDialog(const IShipyard *obj){} /// obj may be town or shipyard; state: 0 - can buid, 1 - lack of resources, 2 - dest tile is blocked, 3 - no water virtual void showPuzzleMap(){}; virtual void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor){}; virtual void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor){}; virtual void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor){}; virtual void showTavernWindow(const CGObjectInstance *townOrTavern){}; - virtual void advmapSpellCast(const CGHeroInstance * caster, int spellID){}; //called when a hero casts a spell + virtual void advmapSpellCast(const CGHeroInstance * caster, int spellID){}; /// called when a hero casts a spell virtual void tileHidden(const boost::unordered_set &pos){}; virtual void tileRevealed(const boost::unordered_set &pos){}; - virtual void newObject(const CGObjectInstance * obj){}; //eg. ship built in shipyard - virtual void availableArtifactsChanged(const CGBlackMarket *bm = NULL){}; //bm may be NULL, then artifacts are changed in the global pool (used by merchants in towns) + virtual void newObject(const CGObjectInstance * obj){}; /// eg. ship built in shipyard + virtual void availableArtifactsChanged(const CGBlackMarket *bm = NULL){}; /// bm may be NULL, then artifacts are changed in the global pool (used by merchants in towns) virtual void centerView (int3 pos, int focusTime){}; virtual void availableCreaturesChanged(const CGDwelling *town){}; - virtual void heroBonusChanged(const CGHeroInstance *hero, const Bonus &bonus, bool gain){};//if gain hero received bonus, else he lost it - virtual void playerBonusChanged(const Bonus &bonus, bool gain){};//if gain hero received bonus, else he lost it + virtual void heroBonusChanged(const CGHeroInstance *hero, const Bonus &bonus, bool gain){};/// if gain hero received bonus, else he lost it + virtual void playerBonusChanged(const Bonus &bonus, bool gain){};/// if gain hero received bonus, else he lost it virtual void requestRealized(PackageApplied *pa){}; virtual void heroExchangeStarted(si32 hero1, si32 hero2){}; - virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged - virtual void objectRemoved(const CGObjectInstance *obj){}; //eg. collected resource, picked artifact, beaten hero - virtual void playerBlocked(int reason){}; //reason: 0 - upcoming battle - virtual void gameOver(ui8 player, bool victory){}; //player lost or won the game + virtual void objectPropertyChanged(const SetObjectProperty * sop){}; /// eg. mine has been flagged + virtual void objectRemoved(const CGObjectInstance *obj){}; /// eg. collected resource, picked artifact, beaten hero + virtual void playerBlocked(int reason){}; /// reason: 0 - upcoming battle + virtual void gameOver(ui8 player, bool victory){}; /// player lost or won the game virtual void playerStartsTurn(ui8 player){}; }; \ No newline at end of file diff --git a/lib/VCMI_lib.vcxproj b/lib/VCMI_lib.vcxproj index 483e614ad..8cfd9b5a3 100644 --- a/lib/VCMI_lib.vcxproj +++ b/lib/VCMI_lib.vcxproj @@ -281,6 +281,9 @@ + + + diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 3b858b8dd..7eca65456 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -645,15 +645,23 @@ void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, cons void CGameHandler::disqualifyPlayer(int side) { - tlog0 << "The side " << (int)side << " will be disqualified!\n"; - boost::unique_lock lock(*gs->mx); - setBattleResult(3, !side); - battleMadeAction.setn(true); + if(!battleResult.get()) + { + tlog0 << "The side " << (int)side << " will be disqualified!\n"; + boost::unique_lock lock(*gs->mx); + setBattleResult(3, !side); + battleMadeAction.setn(true); + } + else + { + tlog0 << "The side " << side << " won't be disqualified since battle is over.\n"; + } } void CGameHandler::handleConnection(std::set players, CConnection &c) { - setThreadName(-1, "CGameHandler::handleConnection"); + int player = players.size() ? *players.begin() : -1; + setThreadName(-1, "CGameHandler::handleConnection" + boost::lexical_cast(player)); srand(time(NULL)); CPack *pack = NULL; @@ -663,7 +671,7 @@ void CGameHandler::handleConnection(std::set players, CConnection &c) { if(gs->curB && gs->initialOpts->mode == StartInfo::DUEL) { - onException = boost::bind(&CGameHandler::disqualifyPlayer, this, *players.begin()); + onException = boost::bind(&CGameHandler::disqualifyPlayer, this, player); } while(1)//server should never shut connection first //was: while(!end2) @@ -673,7 +681,7 @@ void CGameHandler::handleConnection(std::set players, CConnection &c) int packType = typeList.getTypeID(pack); //get the id of type if(packType == typeList.getTypeID()) { - tlog0 << "Ending listening thread for side " << *players.begin() << std::endl; + tlog0 << "Ending listening thread for side " << player << std::endl; break; } @@ -716,6 +724,7 @@ void CGameHandler::handleConnection(std::set players, CConnection &c) } catch(boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection { + tlog2 << "Exception when handling connection for player " << player << std::endl; boost::unique_lock lock(gsm); if(gs->scenarioOps->mode != StartInfo::DUEL) { @@ -725,7 +734,15 @@ void CGameHandler::handleConnection(std::set players, CConnection &c) end2 = true; if(onException) onException(); } - HANDLE_EXCEPTIONC(boost::unique_lock lock(gsm);end2 = true; if(onException) {onException();return;}); + HANDLE_EXCEPTIONC( + tlog2 << "Unknown exception when handling connection for player " << player << std::endl; + boost::unique_lock lock(gsm); + end2 = true; + if(onException) + { + onException(); + return; + }); tlog1 << "Ended handling connection\n"; } diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 6198168e4..6537f10ad 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -523,6 +523,7 @@ bool memViolated(const int pid, const int refpid, const int limit) { void memoryMonitor(int lAIpid, int rAIpid, int refPid) { + setThreadName(-1, "memoryMonitor"); const int MAX_MEM = 20000; //in blocks (of, I hope, 4096 B) monitringRes = 2; tlog0 << "Monitor is activated\n";