1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-02 00:10:22 +02:00

Big change: Introduced new mechanism to handle queries. It should not cause any visible changes ATM apart from fixing several long-standing bugs realted to handling post-visit/battle/levelup callback, including infamous creature bank issues: #955, #1053, #1063, #1191. Needs testing.

Minor changes:
* default log level set to trace
* LOG_TRACE raii guardian lifetime will last till the end of block
* compile fixes
* minor refactorings
This commit is contained in:
Michał W. Urbańczyk 2013-04-20 11:34:01 +00:00
parent 1fca335a2c
commit e8354908c3
30 changed files with 1481 additions and 875 deletions

View File

@ -43,24 +43,6 @@ struct SetGlobalState
} }
}; };
template <typename Container>
typename Container::value_type backOrNull(const Container &c) //returns last element of container or NULL if it is empty (to be used with containers of pointers)
{
if(c.size())
return c.back();
else
return NULL;
}
template <typename Container>
typename Container::value_type frontOrNull(const Container &c) //returns first element of container or NULL if it is empty (to be used with containers of pointers)
{
if(c.size())
return c.front();
else
return NULL;
}
#define SET_GLOBAL_STATE(ai) SetGlobalState _hlpSetState(ai); #define SET_GLOBAL_STATE(ai) SetGlobalState _hlpSetState(ai);
@ -793,7 +775,7 @@ void VCAI::heroManaPointsChanged(const CGHeroInstance * hero)
void VCAI::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) void VCAI::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val)
{ {
LOG_TRACE_PARAMS(logAi, "which '%', val '%'", which % val); LOG_TRACE_PARAMS(logAi, "which '%d', val '%d'", which % val);
NET_EVENT_HANDLER; NET_EVENT_HANDLER;
} }
@ -2579,6 +2561,7 @@ AIStatus::~AIStatus()
void AIStatus::setBattle(BattleState BS) void AIStatus::setBattle(BattleState BS)
{ {
boost::unique_lock<boost::mutex> lock(mx); boost::unique_lock<boost::mutex> lock(mx);
LOG_TRACE_PARAMS(logAi, "battle state=%d", (int)BS);
battle = BS; battle = BS;
cv.notify_all(); cv.notify_all();
} }

View File

@ -568,6 +568,24 @@ namespace vstd
{ {
obj = (T)(((int)obj) + change); obj = (T)(((int)obj) + change);
} }
template <typename Container>
typename Container::value_type backOrNull(const Container &c) //returns last element of container or NULL if it is empty (to be used with containers of pointers)
{
if(c.size())
return c.back();
else
return NULL;
}
template <typename Container>
typename Container::value_type frontOrNull(const Container &c) //returns first element of container or NULL if it is empty (to be used with containers of pointers)
{
if(c.size())
return c.front();
else
return NULL;
}
} }
using vstd::operator-=; using vstd::operator-=;
using vstd::make_unique; using vstd::make_unique;

View File

@ -17,4 +17,4 @@
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup /> <ItemGroup />
</Project> </Project>

View File

@ -176,7 +176,9 @@ static void prog_help(const po::options_description &opts)
void OSX_checkForUpdates(); void OSX_checkForUpdates();
#endif #endif
#if defined(__APPLE__) #ifdef _WIN32
int wmain(int argc, wchar_t* argv[])
#elif defined(__APPLE__)
int SDL_main(int argc, char *argv[]) int SDL_main(int argc, char *argv[])
#else #else
int main(int argc, char** argv) int main(int argc, char** argv)

View File

@ -230,10 +230,10 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
if(LOCPLINT != this) if(LOCPLINT != this)
return; return;
const CGHeroInstance * ho = cb->getHero(details.id); //object representing this hero const CGHeroInstance * hero = cb->getHero(details.id); //object representing this hero
int3 hp = details.start; int3 hp = details.start;
if(!ho) if(!hero)
{ {
//AI hero left the visible area (we can't obtain info) //AI hero left the visible area (we can't obtain info)
//TODO very evil workaround -> retreive pointer to hero so we could animate it //TODO very evil workaround -> retreive pointer to hero so we could animate it
@ -241,25 +241,25 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
const TerrainTile2 &tile = CGI->mh->ttiles[hp.x-1][hp.y][hp.z]; const TerrainTile2 &tile = CGI->mh->ttiles[hp.x-1][hp.y][hp.z];
for(int i = 0; i < tile.objects.size(); i++) for(int i = 0; i < tile.objects.size(); i++)
if(tile.objects[i].first->id == details.id) if(tile.objects[i].first->id == details.id)
ho = dynamic_cast<const CGHeroInstance *>(tile.objects[i].first); hero = dynamic_cast<const CGHeroInstance *>(tile.objects[i].first);
if(!ho) //still nothing... if(!hero) //still nothing...
return; return;
} }
adventureInt->centerOn(ho); //actualizing screen pos adventureInt->centerOn(hero); //actualizing screen pos
adventureInt->minimap.redraw(); adventureInt->minimap.redraw();
adventureInt->heroList.redraw(); adventureInt->heroList.redraw();
bool directlyAttackingCreature = bool directlyAttackingCreature =
CGI->mh->map->isInTheMap(details.attackedFrom) details.attackedFrom
&& adventureInt->terrain.currentPath //in case if movement has been canceled in the meantime and path was already erased && adventureInt->terrain.currentPath //in case if movement has been canceled in the meantime and path was already erased
&& adventureInt->terrain.currentPath->nodes.size() == 3;//FIXME should be 2 but works nevertheless... && adventureInt->terrain.currentPath->nodes.size() == 3;//FIXME should be 2 but works nevertheless...
if(makingTurn && ho->tempOwner == playerID) //we are moving our hero - we may need to update assigned path if(makingTurn && hero->tempOwner == playerID) //we are moving our hero - we may need to update assigned path
{ {
//We may need to change music - select new track, music handler will change it if needed //We may need to change music - select new track, music handler will change it if needed
CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(ho->visitablePos())->terType, true); CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType, true);
if(details.result == TryMoveHero::TELEPORTATION) if(details.result == TryMoveHero::TELEPORTATION)
{ {
@ -272,42 +272,42 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
&& (nodesIt-1)->coord == CGHeroInstance::convertPosition(details.end, false)) && (nodesIt-1)->coord == CGHeroInstance::convertPosition(details.end, false))
{ {
//path was between entrance and exit of teleport -> OK, erase node as usual //path was between entrance and exit of teleport -> OK, erase node as usual
removeLastNodeFromPath(ho); removeLastNodeFromPath(hero);
} }
else else
{ {
//teleport was not along current path, it'll now be invalid (hero is somewhere else) //teleport was not along current path, it'll now be invalid (hero is somewhere else)
eraseCurrentPathOf(ho); eraseCurrentPathOf(hero);
} }
} }
adventureInt->heroList.update(ho); adventureInt->heroList.update(hero);
return; //teleport - no fancy moving animation return; //teleport - no fancy moving animation
//TODO: smooth disappear / appear effect //TODO: smooth disappear / appear effect
} }
if (ho->pos != details.end //hero didn't change tile but visit succeeded if (hero->pos != details.end //hero didn't change tile but visit succeeded
|| directlyAttackingCreature) // or creature was attacked from endangering tile. || directlyAttackingCreature) // or creature was attacked from endangering tile.
{ {
eraseCurrentPathOf(ho, false); eraseCurrentPathOf(hero, false);
} }
else if(adventureInt->terrain.currentPath && ho->pos == details.end) //&& hero is moving else if(adventureInt->terrain.currentPath && hero->pos == details.end) //&& hero is moving
{ {
if(details.start != details.end) //so we don't touch path when revisiting with spacebar if(details.start != details.end) //so we don't touch path when revisiting with spacebar
removeLastNodeFromPath(ho); removeLastNodeFromPath(hero);
} }
} }
if (details.result != TryMoveHero::SUCCESS) //hero failed to move if (details.result != TryMoveHero::SUCCESS) //hero failed to move
{ {
ho->isStanding = true; hero->isStanding = true;
stillMoveHero.setn(STOP_MOVE); stillMoveHero.setn(STOP_MOVE);
GH.totalRedraw(); GH.totalRedraw();
adventureInt->heroList.update(ho); adventureInt->heroList.update(hero);
return; return;
} }
initMovement(details, ho, hp); initMovement(details, hero, hp);
//first initializing done //first initializing done
GH.mainFPSmng->framerateDelay(); // after first move GH.mainFPSmng->framerateDelay(); // after first move
@ -316,7 +316,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
//main moving //main moving
for(int i=1; i<32; i+=2*speed) for(int i=1; i<32; i+=2*speed)
{ {
movementPxStep(details, i, hp, ho); movementPxStep(details, i, hp, hero);
adventureInt->updateScreen = true; adventureInt->updateScreen = true;
adventureInt->show(screen); adventureInt->show(screen);
CSDL_Ext::update(screen); CSDL_Ext::update(screen);
@ -325,12 +325,12 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
//main moving done //main moving done
//finishing move //finishing move
finishMovement(details, hp, ho); finishMovement(details, hp, hero);
ho->isStanding = true; hero->isStanding = true;
//move finished //move finished
adventureInt->minimap.redraw(); adventureInt->minimap.redraw();
adventureInt->heroList.update(ho); adventureInt->heroList.update(hero);
//check if user cancelled movement //check if user cancelled movement
{ {
@ -358,14 +358,14 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
// Hero attacked creature directly, set direction to face it. // Hero attacked creature directly, set direction to face it.
if (directlyAttackingCreature) { if (directlyAttackingCreature) {
// Get direction to attacker. // Get direction to attacker.
int3 posOffset = details.attackedFrom - details.end + int3(2, 1, 0); int3 posOffset = *details.attackedFrom - details.end + int3(2, 1, 0);
static const ui8 dirLookup[3][3] = { static const ui8 dirLookup[3][3] = {
{ 1, 2, 3 }, { 1, 2, 3 },
{ 8, 0, 4 }, { 8, 0, 4 },
{ 7, 6, 5 } { 7, 6, 5 }
}; };
// FIXME: Avoid const_cast, make moveDir mutable in some other way? // FIXME: Avoid const_cast, make moveDir mutable in some other way?
const_cast<CGHeroInstance *>(ho)->moveDir = dirLookup[posOffset.y][posOffset.x]; const_cast<CGHeroInstance *>(hero)->moveDir = dirLookup[posOffset.y][posOffset.x];
} }
} }
void CPlayerInterface::heroKilled(const CGHeroInstance* hero) void CPlayerInterface::heroKilled(const CGHeroInstance* hero)

View File

@ -170,11 +170,12 @@ public:
void setHoverName(const CGObjectInstance * obj, MetaString * name) OVERRIDE {}; void setHoverName(const CGObjectInstance * obj, MetaString * name) OVERRIDE {};
void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) OVERRIDE {}; void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) OVERRIDE {};
void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) OVERRIDE {}; void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) OVERRIDE {};
void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback) OVERRIDE {};
ui32 showBlockingDialog(BlockingDialog *iw) OVERRIDE {return 0;}; //synchronous version of above void showBlockingDialog(BlockingDialog *iw) OVERRIDE {};
void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) OVERRIDE {}; void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) OVERRIDE {};
void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) OVERRIDE {}; void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) OVERRIDE {};
void giveResource(PlayerColor player, Res::ERes which, int val) OVERRIDE {}; void giveResource(PlayerColor player, Res::ERes which, int val) OVERRIDE {};
virtual void giveResources(PlayerColor player, TResources resources) OVERRIDE {};
void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) OVERRIDE {}; void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) OVERRIDE {};
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) OVERRIDE {}; void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) OVERRIDE {};
@ -199,11 +200,11 @@ public:
void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE {}; void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE {};
//void giveHeroArtifact(int artid, int hid, int position){}; //void giveHeroArtifact(int artid, int hid, int position){};
//void giveNewArtifact(int hid, int position){}; //void giveNewArtifact(int hid, int position){};
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL) OVERRIDE {}; //use hero=NULL for no hero void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = NULL) OVERRIDE {}; //use hero=NULL for no hero
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) OVERRIDE {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
void setAmount(ObjectInstanceID objid, ui32 val) OVERRIDE {}; void setAmount(ObjectInstanceID objid, ui32 val) OVERRIDE {};
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 instant, PlayerColor asker = PlayerColor::NEUTRAL) OVERRIDE {return false;}; bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) OVERRIDE {return false;};
void giveHeroBonus(GiveBonus * bonus) OVERRIDE {}; void giveHeroBonus(GiveBonus * bonus) OVERRIDE {};
void setMovePoints(SetMovePoints * smp) OVERRIDE {}; void setMovePoints(SetMovePoints * smp) OVERRIDE {};
void setManaPoints(ObjectInstanceID hid, int val) OVERRIDE {}; void setManaPoints(ObjectInstanceID hid, int val) OVERRIDE {};

View File

@ -545,11 +545,10 @@ void SetObjectProperty::applyCl( CClient *cl )
void HeroLevelUp::applyCl( CClient *cl ) void HeroLevelUp::applyCl( CClient *cl )
{ {
const CGHeroInstance *h = cl->getHero(heroid);
//INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroGotLevel, h, primskill, skills, id); //INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroGotLevel, h, primskill, skills, id);
if(vstd::contains(cl->playerint,h->tempOwner)) if(vstd::contains(cl->playerint,hero->tempOwner))
{ {
cl->playerint[h->tempOwner]->heroGotLevel(h, primskill, skills, queryID); cl->playerint[hero->tempOwner]->heroGotLevel(hero, primskill, skills, queryID);
} }
//else //else
// cb->selectionMade(0, queryID); // cb->selectionMade(0, queryID);
@ -557,9 +556,9 @@ void HeroLevelUp::applyCl( CClient *cl )
void CommanderLevelUp::applyCl( CClient *cl ) void CommanderLevelUp::applyCl( CClient *cl )
{ {
CCommanderInstance * commander = GS(cl)->getHero(heroid)->commander; const CCommanderInstance * commander = hero->commander;
assert (commander); assert (commander);
PlayerColor player = commander->armyObj->tempOwner; PlayerColor player = hero->tempOwner;
if (commander->armyObj && vstd::contains(cl->playerint, player)) //is it possible for Commander to exist beyond armed instance? if (commander->armyObj && vstd::contains(cl->playerint, player)) //is it possible for Commander to exist beyond armed instance?
{ {
cl->playerint[player]->commanderGotLevel(commander, skills, queryID); cl->playerint[player]->commanderGotLevel(commander, skills, queryID);

View File

@ -207,7 +207,7 @@
}, },
"loggers" : { "loggers" : {
"type" : "array", "type" : "array",
"default" : [ { "domain" : "global", "level" : "info" } ], "default" : [ { "domain" : "global", "level" : "trace" } ],
"items" : { "items" : {
"type" : "object", "type" : "object",
"additionalProperties" : false, "additionalProperties" : false,

View File

@ -741,6 +741,11 @@ ArtBearer::ArtBearer CCommanderInstance::bearerType() const
return ArtBearer::COMMANDER; return ArtBearer::COMMANDER;
} }
bool CCommanderInstance::gainsLevel() const
{
return experience >= VLC->heroh->reqExp(level+1);
}
CStackBasicDescriptor::CStackBasicDescriptor() CStackBasicDescriptor::CStackBasicDescriptor()
{ {
type = NULL; type = NULL;

View File

@ -103,6 +103,7 @@ public:
void giveStackExp (TExpType exp); void giveStackExp (TExpType exp);
void levelUp (); void levelUp ();
bool gainsLevel() const; //true if commander has lower level than should upon his experience
ui64 getPower() const {return 0;}; ui64 getPower() const {return 0;};
int getExpRank() const; int getExpRank() const;
int getLevel() const OVERRIDE; int getLevel() const OVERRIDE;

View File

@ -3196,7 +3196,7 @@ CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile
{ {
BOOST_FOREACH(const CGObjectInstance *obj, tinfo->visitableObjects) BOOST_FOREACH(const CGObjectInstance *obj, tinfo->visitableObjects)
{ {
if(obj->getPassableness() & 1<<hero->tempOwner.getNum()) //special object instance specific passableness flag - overwrites other accessibility flags if(obj->passableFor(hero->tempOwner)) //special object instance specific passableness flag - overwrites other accessibility flags
{ {
ret = CGPathNode::ACCESSIBLE; ret = CGPathNode::ACCESSIBLE;
} }

File diff suppressed because it is too large Load Diff

View File

@ -113,6 +113,13 @@ public:
virtual void newTurn() const; virtual void newTurn() const;
virtual void initObj(); //synchr virtual void initObj(); //synchr
virtual void setProperty(ui8 what, ui32 val);//synchr virtual void setProperty(ui8 what, ui32 val);//synchr
//Called when queries created DURING HERO VISIT are resolved
//First parameter is always hero that visited object and triggered the query
virtual void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const;
virtual void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const;
virtual void garrisonDialogClosed(const CGHeroInstance *hero) const;
//unified interface, AI helpers //unified interface, AI helpers
virtual bool wasVisited (PlayerColor player) const; virtual bool wasVisited (PlayerColor player) const;
virtual bool wasVisited (const CGHeroInstance * h) const; virtual bool wasVisited (const CGHeroInstance * h) const;
@ -179,6 +186,7 @@ public:
virtual ui8 getPassableness() const; //bitmap - if the bit is set the corresponding player can pass through the visitable tiles of object, even if it's blockvis; if not set - default properties from definfo are used virtual ui8 getPassableness() const; //bitmap - if the bit is set the corresponding player can pass through the visitable tiles of object, even if it's blockvis; if not set - default properties from definfo are used
virtual int3 getSightCenter() const; //"center" tile from which the sight distance is calculated virtual int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
virtual int getSightRadious() const; //sight distance (should be used if player-owned structure) virtual int getSightRadious() const; //sight distance (should be used if player-owned structure)
bool passableFor(PlayerColor color) const;
void getSightTiles(boost::unordered_set<int3, ShashInt3> &tiles) const; //returns reference to the set void getSightTiles(boost::unordered_set<int3, ShashInt3> &tiles) const; //returns reference to the set
PlayerColor getOwner() const; PlayerColor getOwner() const;
void setOwner(PlayerColor ow); void setOwner(PlayerColor ow);
@ -396,6 +404,8 @@ public:
CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const; CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const; void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 - ECanDig diggingStatus() const; //0 - can dig; 1 - lack of movement; 2 -
std::vector<SecondarySkill> levelUpProposedSkills() const;
bool gainsLevel() const; //true if hero has lower level than should upon his experience
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -471,10 +481,11 @@ public:
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void newTurn() const override; void newTurn() const override;
void setProperty(ui8 what, ui32 val) override; void setProperty(ui8 what, ui32 val) override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
private: private:
void heroAcceptsCreatures(const CGHeroInstance *h, ui32 answer) const; void heroAcceptsCreatures(const CGHeroInstance *h) const;
void fightOver(const CGHeroInstance *h, BattleResult *result) const;
void wantsFight(const CGHeroInstance *h, ui32 answer) const;
}; };
@ -482,25 +493,26 @@ class DLL_LINKAGE CGVisitableOPH : public CGObjectInstance //objects visitable o
{ {
public: public:
std::set<ObjectInstanceID> visitors; //ids of heroes who have visited this obj std::set<ObjectInstanceID> visitors; //ids of heroes who have visited this obj
si8 ttype; //tree type - used only by trees of knowledge: 0 - give level for free; 1 - take 2000 gold; 2 - take 10 gems TResources treePrice; //used only by trees of knowledge: empty, 2000 gold, 10 gems
const std::string & getHoverText() const override; const std::string & getHoverText() const override;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void initObj() override; void initObj() override;
bool wasVisited (const CGHeroInstance * h) const override; bool wasVisited (const CGHeroInstance * h) const override;
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & static_cast<CGObjectInstance&>(*this); h & static_cast<CGObjectInstance&>(*this);
h & visitors & ttype; h & visitors & treePrice;
} }
protected: protected:
void setPropertyDer(ui8 what, ui32 val) override;//synchr void setPropertyDer(ui8 what, ui32 val) override;//synchr
private: private:
void onNAHeroVisit(ObjectInstanceID heroID, bool alreadyVisited) const; void onNAHeroVisit(ObjectInstanceID heroID, bool alreadyVisited) const;
///dialog callbacks ///dialog callbacks
void treeSelected(ObjectInstanceID heroID, int resType, int resVal, TExpType expVal, ui32 result) const; void treeSelected(ObjectInstanceID heroID, ui32 result) const;
void schoolSelected(ObjectInstanceID heroID, ui32 which) const; void schoolSelected(ObjectInstanceID heroID, ui32 which) const;
void arenaSelected(ObjectInstanceID heroID, int primSkill) const; void arenaSelected(ObjectInstanceID heroID, int primSkill) const;
}; };
@ -662,11 +674,9 @@ public:
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void onHeroLeave(const CGHeroInstance * h) const override; void onHeroLeave(const CGHeroInstance * h) const override;
void initObj() override; void initObj() override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
protected: protected:
void setPropertyDer(ui8 what, ui32 val) override; void setPropertyDer(ui8 what, ui32 val) override;
private:
///dialog callbacks
void fightOver(const CGHeroInstance *h, BattleResult *result) const;
}; };
class DLL_LINKAGE CGPandoraBox : public CArmedInstance class DLL_LINKAGE CGPandoraBox : public CArmedInstance
{ {
@ -688,6 +698,8 @@ public:
void initObj() override; void initObj() override;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
@ -696,10 +708,8 @@ public:
& abilities & abilityLevels & artifacts & spells & creatures; & abilities & abilityLevels & artifacts & spells & creatures;
} }
protected: protected:
void endBattle(const CGHeroInstance *h, BattleResult *result) const;
void giveContents(const CGHeroInstance *h, bool afterBattle) const; void giveContents(const CGHeroInstance *h, bool afterBattle) const;
private: private:
void open (const CGHeroInstance * h, ui32 accept) const;
void getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const; void getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const;
void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const; void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const;
}; };
@ -725,6 +735,10 @@ private:
class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
{ {
enum Action {
FIGHT = -2, FLEE = -1, JOIN_FOR_FREE = 0 //values > 0 mean gold price
};
public: public:
ui32 identifier; //unique code for this monster (used in missions) ui32 identifier; //unique code for this monster (used in missions)
si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage) si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
@ -735,11 +749,14 @@ public:
bool notGrowingTeam; //if true, number of units won't grow bool notGrowingTeam; //if true, number of units won't grow
ui64 temppower; //used to handle fractional stack growth for tiny stacks ui64 temppower; //used to handle fractional stack growth for tiny stacks
bool refusedJoining;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
const std::string & getHoverText() const override; const std::string & getHoverText() const override;
void initObj() override; void initObj() override;
void newTurn() const override; void newTurn() const override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
struct DLL_LINKAGE RestoredCreature // info about merging stacks after battle back into one struct DLL_LINKAGE RestoredCreature // info about merging stacks after battle back into one
@ -754,7 +771,8 @@ public:
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & static_cast<CArmedInstance&>(*this); h & static_cast<CArmedInstance&>(*this);
h & identifier & character & message & resources & gainedArtifact & neverFlees & notGrowingTeam & temppower & restore; h & identifier & character & message & resources & gainedArtifact & neverFlees & notGrowingTeam & temppower;
h & refusedJoining & restore;
} }
protected: protected:
void setPropertyDer(ui8 what, ui32 val) override; void setPropertyDer(ui8 what, ui32 val) override;
@ -762,7 +780,6 @@ private:
void fight(const CGHeroInstance *h) const; void fight(const CGHeroInstance *h) const;
void flee( const CGHeroInstance * h ) const; void flee( const CGHeroInstance * h ) const;
void endBattle(BattleResult *result) const;
void fleeDecision(const CGHeroInstance *h, ui32 pursue) const; void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const; void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
@ -815,6 +832,7 @@ public:
const std::string & getHoverText() const override; const std::string & getHoverText() const override;
void newTurn() const override; void newTurn() const override;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
int checkDirection() const; //calculates the region of map where monster is placed int checkDirection() const; //calculates the region of map where monster is placed
void setObjToKill(); //remember creatures / heroes to kill after they are initialized void setObjToKill(); //remember creatures / heroes to kill after they are initialized
@ -888,7 +906,7 @@ public:
ui8 getPassableness() const; ui8 getPassableness() const;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void fightOver (const CGHeroInstance *h, BattleResult *result) const; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
@ -904,8 +922,9 @@ public:
std::string message; std::string message;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void fightForArt(ui32 agreed, const CGHeroInstance *h) const; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
void endBattle(BattleResult *result, const CGHeroInstance *h) const; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
void pick( const CGHeroInstance * h ) const; void pick( const CGHeroInstance * h ) const;
void initObj() override; void initObj() override;
@ -924,10 +943,10 @@ public:
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void initObj() override; void initObj() override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
void collectRes(PlayerColor player) const; void collectRes(PlayerColor player) const;
void fightForRes(ui32 agreed, const CGHeroInstance *h) const;
void endBattle(BattleResult *result, const CGHeroInstance *h) const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
@ -943,7 +962,7 @@ public:
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void initObj() override; void initObj() override;
void chosen(int which, ObjectInstanceID heroID) const; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
@ -972,12 +991,10 @@ class DLL_LINKAGE CGMine : public CArmedInstance
public: public:
Res::ERes producedResource; Res::ERes producedResource;
ui32 producedQuantity; ui32 producedQuantity;
void offerLeavingGuards(const CGHeroInstance *h) const;
void endBattle(BattleResult *result, PlayerColor attackingPlayer) const;
void fight(ui32 agreed, const CGHeroInstance *h) const;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
void flagMine(PlayerColor player) const; void flagMine(PlayerColor player) const;
void newTurn() const override; void newTurn() const override;
@ -1125,13 +1142,12 @@ public:
CGBorderGuard() : IQuestObject(){}; CGBorderGuard() : IQuestObject(){};
void initObj() override; void initObj() override;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = NULL) const; void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = NULL) const;
void getRolloverText (MetaString &text, bool onHover) const; void getRolloverText (MetaString &text, bool onHover) const;
bool checkQuest (const CGHeroInstance * h) const; bool checkQuest (const CGHeroInstance * h) const;
void openGate(const CGHeroInstance *h, ui32 accept) const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & static_cast<IQuestObject&>(*this); h & static_cast<IQuestObject&>(*this);
@ -1184,8 +1200,7 @@ public:
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
const std::string & getHoverText() const override; const std::string & getHoverText() const override;
void initObj() override; void initObj() override;
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
void searchTomb(const CGHeroInstance *h, ui32 accept) const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
@ -1210,9 +1225,8 @@ class DLL_LINKAGE CBank : public CArmedInstance
void newTurn() const override; void newTurn() const override;
bool wasVisited (PlayerColor player) const override; bool wasVisited (PlayerColor player) const override;
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
virtual void fightGuards (const CGHeroInstance *h, ui32 accept) const; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
virtual void endBattle (const CGHeroInstance *h, const BattleResult *result) const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
@ -1231,8 +1245,7 @@ public:
const std::string & getHoverText() const override; const std::string & getHoverText() const override;
void newTurn() const override {}; //empty, no reset void newTurn() const override {}; //empty, no reset
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const OVERRIDE;
void endBattle (const CGHeroInstance *h, const BattleResult *result) const override;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
@ -1270,7 +1283,7 @@ class DLL_LINKAGE CCartographer : public CPlayersVisited
///behaviour varies depending on surface and floor ///behaviour varies depending on surface and floor
public: public:
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void buyMap (const CGHeroInstance *h, ui32 accept) const; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const OVERRIDE;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {

View File

@ -98,3 +98,8 @@ CSpell * SpellID::toSpell() const
//template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id); //template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);
//template std::ostream & operator << <ObjectInstanceID>(std::ostream & os, BaseForID<ObjectInstanceID> id); //template std::ostream & operator << <ObjectInstanceID>(std::ostream & os, BaseForID<ObjectInstanceID> id);
bool PlayerColor::isValidPlayer() const
{
return num < PLAYER_LIMIT_I;
}

View File

@ -215,6 +215,8 @@ class PlayerColor : public BaseForID<PlayerColor, ui8>
DLL_LINKAGE static const int PLAYER_LIMIT_I = 8; //player limit per map DLL_LINKAGE static const int PLAYER_LIMIT_I = 8; //player limit per map
DLL_LINKAGE static const PlayerColor PLAYER_LIMIT; //player limit per map DLL_LINKAGE static const PlayerColor PLAYER_LIMIT; //player limit per map
DLL_LINKAGE bool isValidPlayer() const; //valid means < PLAYER_LIMIT (especially non-neutral)
friend class CGameInfoCallback; friend class CGameInfoCallback;
friend class CNonConstInfoCallback; friend class CNonConstInfoCallback;
}; };

View File

@ -220,11 +220,11 @@ public:
virtual void setHoverName(const CGObjectInstance * obj, MetaString * name)=0; virtual void setHoverName(const CGObjectInstance * obj, MetaString * name)=0;
virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false)=0; virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false)=0;
virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0; virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0;
virtual void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback)=0; virtual void showBlockingDialog(BlockingDialog *iw) =0;
virtual ui32 showBlockingDialog(BlockingDialog *iw) =0; //synchronous version of above //TODO:
virtual void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) =0; //cb will be called when player closes garrison window virtual void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) =0; //cb will be called when player closes garrison window
virtual void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) =0; virtual void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) =0;
virtual void giveResource(PlayerColor player, Res::ERes which, int val)=0; virtual void giveResource(PlayerColor player, Res::ERes which, int val)=0;
virtual void giveResources(PlayerColor player, TResources resources)=0;
virtual void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) =0; virtual void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) =0;
virtual void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) =0; virtual void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) =0;
@ -247,11 +247,11 @@ public:
virtual void showCompInfo(ShowInInfobox * comp)=0; virtual void showCompInfo(ShowInInfobox * comp)=0;
virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0; virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0;
virtual void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0; virtual void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0;
virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL)=0; //use hero=NULL for no hero virtual void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = NULL)=0; //use hero=NULL for no hero
virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false)=0; //if any of armies is hero, hero will be used virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false)=0; //if any of armies is hero, hero will be used
virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
virtual void setAmount(ObjectInstanceID objid, ui32 val)=0; virtual void setAmount(ObjectInstanceID objid, ui32 val)=0;
virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 instant, PlayerColor asker = PlayerColor::NEUTRAL)=0; virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL)=0;
virtual void giveHeroBonus(GiveBonus * bonus)=0; virtual void giveHeroBonus(GiveBonus * bonus)=0;
virtual void setMovePoints(SetMovePoints * smp)=0; virtual void setMovePoints(SetMovePoints * smp)=0;
virtual void setManaPoints(ObjectInstanceID hid, int val)=0; virtual void setManaPoints(ObjectInstanceID hid, int val)=0;

View File

@ -633,7 +633,7 @@ struct RemoveObject : public CPackForClient //500
}; };
struct TryMoveHero : public CPackForClient //501 struct TryMoveHero : public CPackForClient //501
{ {
TryMoveHero(){type = 501;humanKnows=false; attackedFrom = int3(-1, -1, -1);}; TryMoveHero(){type = 501;humanKnows=false;};
void applyFirstCl(CClient *cl); void applyFirstCl(CClient *cl);
void applyCl(CClient *cl); void applyCl(CClient *cl);
void applyGs(CGameState *gs); void applyGs(CGameState *gs);
@ -648,7 +648,7 @@ struct TryMoveHero : public CPackForClient //501
EResult result; //uses EResult EResult result; //uses EResult
int3 start, end; //h3m format int3 start, end; //h3m format
boost::unordered_set<int3, ShashInt3> fowRevealed; //revealed tiles boost::unordered_set<int3, ShashInt3> fowRevealed; //revealed tiles
int3 attackedFrom; // Set when stepping into endangered tile. boost::optional<int3> attackedFrom; // Set when stepping into endangered tile.
bool humanKnows; //used locally during applying to client bool humanKnows; //used locally during applying to client
@ -1168,7 +1168,7 @@ struct InfoWindow : public CPackForClient //103 - displays simple info window
namespace ObjProperty namespace ObjProperty
{ {
enum {OWNER = 1, BLOCKVIS = 2, PRIMARY_STACK_COUNT = 3, VISITORS = 4, VISITED = 5, ID = 6, AVAILABLE_CREATURE = 7, SUBID = 8, enum {OWNER = 1, BLOCKVIS = 2, PRIMARY_STACK_COUNT = 3, VISITORS = 4, VISITED = 5, ID = 6, AVAILABLE_CREATURE = 7, SUBID = 8,
MONSTER_COUNT = 10, MONSTER_POWER = 11, MONSTER_EXP = 12, MONSTER_RESTORE_TYPE = 13, MONSTER_COUNT = 10, MONSTER_POWER = 11, MONSTER_EXP = 12, MONSTER_RESTORE_TYPE = 13, MONSTER_REFUSED_JOIN,
//town-specific //town-specific
STRUCTURE_ADD_VISITING_HERO, STRUCTURE_CLEAR_VISITORS, STRUCTURE_ADD_GARRISONED_HERO, //changing buildings state STRUCTURE_ADD_VISITING_HERO, STRUCTURE_CLEAR_VISITORS, STRUCTURE_ADD_GARRISONED_HERO, //changing buildings state
@ -1216,7 +1216,7 @@ struct HeroLevelUp : public Query//2000
void applyCl(CClient *cl); void applyCl(CClient *cl);
DLL_LINKAGE void applyGs(CGameState *gs); DLL_LINKAGE void applyGs(CGameState *gs);
ObjectInstanceID heroid; const CGHeroInstance *hero;
PrimarySkill::PrimarySkill primskill; PrimarySkill::PrimarySkill primskill;
ui8 level; ui8 level;
std::vector<SecondarySkill> skills; std::vector<SecondarySkill> skills;
@ -1225,7 +1225,7 @@ struct HeroLevelUp : public Query//2000
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & queryID & heroid & primskill & level & skills; h & queryID & hero & primskill & level & skills;
} }
}; };
@ -1234,8 +1234,7 @@ struct CommanderLevelUp : public Query
void applyCl(CClient *cl); void applyCl(CClient *cl);
DLL_LINKAGE void applyGs(CGameState *gs); DLL_LINKAGE void applyGs(CGameState *gs);
ObjectInstanceID heroid; //for commander attached to hero const CGHeroInstance *hero;
StackLocation sl; //for commander not on the hero?
std::vector<ui32> skills; //0-5 - secondary skills, val-100 - special skill std::vector<ui32> skills; //0-5 - secondary skills, val-100 - special skill
@ -1243,7 +1242,7 @@ struct CommanderLevelUp : public Query
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & queryID & heroid & sl & skills; h & queryID & hero & skills;
} }
}; };
@ -1302,6 +1301,12 @@ struct BlockingDialog : public Query//2003
soundID = 0; soundID = 0;
}; };
void addResourceComponents(TResources resources)
{
for(TResources::nziterator i(resources); i.valid(); i++)
components.push_back(Component(Component::RESOURCE, i->resType, i->resVal, 0));
}
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & queryID & text & components & player & flags & soundID; h & queryID & text & components & player & flags & soundID;

View File

@ -994,7 +994,7 @@ DLL_LINKAGE void SetHoverName::applyGs( CGameState *gs )
DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs ) DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs )
{ {
CGHeroInstance* h = gs->getHero(heroid); CGHeroInstance* h = gs->getHero(hero->id);
h->level = level; h->level = level;
//specialty //specialty
h->Updatespecialty(); h->Updatespecialty();
@ -1002,7 +1002,7 @@ DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs )
DLL_LINKAGE void CommanderLevelUp::applyGs (CGameState *gs) DLL_LINKAGE void CommanderLevelUp::applyGs (CGameState *gs)
{ {
CCommanderInstance * commander = gs->getHero(heroid)->commander; CCommanderInstance * commander = gs->getHero(hero->id)->commander;
assert (commander); assert (commander);
commander->levelUp(); commander->levelUp();
} }

View File

@ -92,7 +92,7 @@ void Res::ResourceSet::nziterator::advance()
{ {
do do
{ {
cur.resType++; vstd::advance(cur.resType, +1);
} while(cur.resType < GameConstants::RESOURCE_QUANTITY && !(cur.resVal=rs[cur.resType])); } while(cur.resType < GameConstants::RESOURCE_QUANTITY && !(cur.resVal=rs[cur.resType]));
if(cur.resType >= GameConstants::RESOURCE_QUANTITY) if(cur.resType >= GameConstants::RESOURCE_QUANTITY)
@ -102,8 +102,8 @@ void Res::ResourceSet::nziterator::advance()
Res::ResourceSet::nziterator::nziterator(const ResourceSet &RS) Res::ResourceSet::nziterator::nziterator(const ResourceSet &RS)
: rs(RS) : rs(RS)
{ {
cur.resType = 0; cur.resType = WOOD;
cur.resVal = rs[0]; cur.resVal = rs[WOOD];
if(!valid()) if(!valid())
advance(); advance();

View File

@ -98,6 +98,14 @@ namespace Res
return *this; return *this;
} }
ResourceSet operator-() const
{
ResourceSet ret;
for(int i = 0; i < size(); i++)
ret[i] = -at(i);
return ret;
}
// WARNING: comparison operators are used for "can afford" relation: a <= b means that foreach i a[i] <= b[i] // WARNING: comparison operators are used for "can afford" relation: a <= b means that foreach i a[i] <= b[i]
// that doesn't work the other way: a > b doesn't mean that a cannot be afforded with b, it's still b can afford a // that doesn't work the other way: a > b doesn't mean that a cannot be afforded with b, it's still b can afford a
// bool operator<(const ResourceSet &rhs) // bool operator<(const ResourceSet &rhs)
@ -124,7 +132,8 @@ namespace Res
{ {
struct ResEntry struct ResEntry
{ {
TResourceCap resType, resVal; Res::ERes resType;
TResourceCap resVal;
} cur; } cur;
const ResourceSet &rs; const ResourceSet &rs;
void advance(); void advance();

View File

@ -128,7 +128,7 @@ extern DLL_LINKAGE CLogger * logAi;
/// RAII class for tracing the program execution. /// RAII class for tracing the program execution.
/// It prints "Leaving function XYZ" automatically when the object gets destructed. /// It prints "Leaving function XYZ" automatically when the object gets destructed.
class DLL_LINKAGE CTraceLogger class DLL_LINKAGE CTraceLogger : boost::noncopyable
{ {
public: public:
CTraceLogger(const CLogger * logger, const std::string & beginMessage, const std::string & endMessage); CTraceLogger(const CLogger * logger, const std::string & beginMessage, const std::string & endMessage);
@ -141,10 +141,20 @@ private:
/// Macros for tracing the control flow of the application conveniently. If the LOG_TRACE macro is used it should be /// Macros for tracing the control flow of the application conveniently. If the LOG_TRACE macro is used it should be
/// the first statement in the function. Logging traces via this macro have almost no impact when the trace is disabled. /// the first statement in the function. Logging traces via this macro have almost no impact when the trace is disabled.
#define LOG_TRACE(logger) if(logger->isTraceEnabled()) CTraceLogger ctl00(logger, boost::str(boost::format("Entering %s.") % BOOST_CURRENT_FUNCTION), \ ///
boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION)) #define RAII_TRACE(logger, onEntry, onLeave) \
#define LOG_TRACE_PARAMS(logger, formatStr, params) if(logger->isTraceEnabled()) CTraceLogger ctl00(logger, boost::str(boost::format("Entering %s: " + \ unique_ptr<CTraceLogger> ctl00; \
std::string(formatStr) + ".") % BOOST_CURRENT_FUNCTION % params), boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION)) if(logger->isTraceEnabled()) \
ctl00 = make_unique<CTraceLogger>(logger, onEntry, onLeave);
#define LOG_TRACE(logger) RAII_TRACE(logger, \
boost::str(boost::format("Entering %s.") % BOOST_CURRENT_FUNCTION), \
boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION))
#define LOG_TRACE_PARAMS(logger, formatStr, params) RAII_TRACE(logger, \
boost::str(boost::format("Entering %s: " + std::string(formatStr) + ".") % BOOST_CURRENT_FUNCTION % params), \
boost::str(boost::format("Leaving %s.") % BOOST_CURRENT_FUNCTION))
/* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */
/* Implementation/Detail classes, Private API */ /* Implementation/Detail classes, Private API */

View File

@ -55,7 +55,7 @@ protected:
}; };
/// The CMapUndoManager provides the functionality to save operations and undo/redo them. /// The CMapUndoManager provides the functionality to save operations and undo/redo them.
class CMapUndoManager class CMapUndoManager : boost::noncopyable
{ {
public: public:
CMapUndoManager(); CMapUndoManager();
@ -87,7 +87,7 @@ private:
/// The map edit manager provides functionality for drawing terrain and placing /// The map edit manager provides functionality for drawing terrain and placing
/// objects on the map. /// objects on the map.
class DLL_LINKAGE CMapEditManager class DLL_LINKAGE CMapEditManager : boost::noncopyable
{ {
public: public:
CMapEditManager(CMap * map); CMapEditManager(CMap * map);

View File

@ -56,7 +56,7 @@ class DLL_LINKAGE CMapGenOptions
{ {
public: public:
/// The player settings class maps the player color, starting town and human player flag. /// The player settings class maps the player color, starting town and human player flag.
class CPlayerSettings class DLL_LINKAGE CPlayerSettings
{ {
public: public:
CPlayerSettings(); CPlayerSettings();

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,8 @@
#include "../lib/IGameCallback.h" #include "../lib/IGameCallback.h"
#include "../lib/BattleAction.h" #include "../lib/BattleAction.h"
#include "../lib/NetPacks.h" #include "../lib/NetPacks.h"
#include "CQuery.h"
/* /*
* CGameHandler.h, part of VCMI engine * CGameHandler.h, part of VCMI engine
@ -41,13 +43,12 @@ extern boost::mutex gsm;
struct PlayerStatus struct PlayerStatus
{ {
bool makingTurn, engagedIntoBattle; bool makingTurn;
std::set<ui32> queries;
PlayerStatus():makingTurn(false),engagedIntoBattle(false){}; PlayerStatus():makingTurn(false){};
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & makingTurn & engagedIntoBattle & queries; h & makingTurn;
} }
}; };
class PlayerStatuses class PlayerStatuses
@ -59,11 +60,8 @@ public:
void addPlayer(PlayerColor player); void addPlayer(PlayerColor player);
PlayerStatus operator[](PlayerColor player); PlayerStatus operator[](PlayerColor player);
int getQueriesCount(PlayerColor player); //returns 0 if there is no such player
bool checkFlag(PlayerColor player, bool PlayerStatus::*flag); bool checkFlag(PlayerColor player, bool PlayerStatus::*flag);
void setFlag(PlayerColor player, bool PlayerStatus::*flag, bool val); void setFlag(PlayerColor player, bool PlayerStatus::*flag, bool val);
void addQuery(PlayerColor player, ui32 id);
void removeQuery(PlayerColor player, ui32 id);
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & players; h & players;
@ -85,7 +83,6 @@ class CGameHandler : public IGameCallback, CBattleInfoCallback
{ {
private: private:
void makeStackDoNothing(const CStack * next); void makeStackDoNothing(const CStack * next);
bool isAllowedExchangeForQuery(ObjectInstanceID id1, ObjectInstanceID id2);
public: public:
CVCMIServer *s; CVCMIServer *s;
std::map<PlayerColor, CConnection*> connections; //player color -> connection to client with interface of that player std::map<PlayerColor, CConnection*> connections; //player color -> connection to client with interface of that player
@ -95,25 +92,22 @@ public:
//queries stuff //queries stuff
boost::recursive_mutex gsm; boost::recursive_mutex gsm;
ui32 QID; ui32 QID;
Queries queries;
//TODO get rid of cfunctionlist (or similar) and use serialziable callback structure //TODO get rid of cfunctionlist (or similar) and use serialziable callback structure
std::map<ui32, CFunctionList<void(ui32)> > callbacks; //query id => callback function - for selection and yes/no dialogs std::map<ui32, CFunctionList<void(ui32)> > callbacks; //query id => callback function - for selection and yes/no dialogs
std::map<ui32, std::pair<ObjectInstanceID, ObjectInstanceID> > allowedExchanges;
bool isBlockedByQueries(const CPack *pack, int packType, PlayerColor player); bool isValidObject(const CGObjectInstance *obj) const;
bool isBlockedByQueries(const CPack *pack, PlayerColor player);
bool isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2); bool isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2);
bool isAllowedArrangePack(const ArrangeStacks *pack);
void giveSpells(const CGTownInstance *t, const CGHeroInstance *h); void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
int moveStack(int stack, BattleHex dest); //returned value - travelled distance int moveStack(int stack, BattleHex dest); //returned value - travelled distance
void startBattle(const CArmedInstance *armies[2], int3 tile, const CGHeroInstance *heroes[2], bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town = NULL); //use hero=NULL for no hero
void runBattle(); void runBattle();
void checkLossVictory(PlayerColor player); void checkLossVictory(PlayerColor player);
void winLoseHandle(ui8 players=255); //players: bit field - colours of players to be checked; default: all void winLoseHandle(ui8 players=255); //players: bit field - colours of players to be checked; default: all
void getLossVicMessage(PlayerColor player, si8 standard, bool victory, InfoWindow &out) const; void getLossVicMessage(PlayerColor player, si8 standard, bool victory, InfoWindow &out) const;
////used only in endBattle - don't touch elsewhere ////used only in endBattle - don't touch elsewhere
boost::function<void(BattleResult*)> * battleEndCallback;
//const CArmedInstance * bEndArmy1, * bEndArmy2;
bool visitObjectAfterVictory; bool visitObjectAfterVictory;
// //
void endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //ends battle void endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //ends battle
@ -137,11 +131,12 @@ public:
void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) OVERRIDE; void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) OVERRIDE;
void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) OVERRIDE; void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) OVERRIDE;
//void showInfoDialog(InfoWindow *iw) OVERRIDE; //void showInfoDialog(InfoWindow *iw) OVERRIDE;
void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback) OVERRIDE;
ui32 showBlockingDialog(BlockingDialog *iw) OVERRIDE; //synchronous version of above void showBlockingDialog(BlockingDialog *iw) OVERRIDE;
void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) OVERRIDE; void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits, const boost::function<void()> &cb) OVERRIDE;
void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) OVERRIDE; void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) OVERRIDE;
void giveResource(PlayerColor player, Res::ERes which, int val) OVERRIDE; void giveResource(PlayerColor player, Res::ERes which, int val) OVERRIDE;
void giveResources(PlayerColor player, TResources resources) OVERRIDE;
void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) OVERRIDE; void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) OVERRIDE;
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) OVERRIDE; void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) OVERRIDE;
@ -165,11 +160,11 @@ public:
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE; void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE;
void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE; void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE;
//bool removeArtifact(const CArtifact* art, int hid) OVERRIDE; //bool removeArtifact(const CArtifact* art, int hid) OVERRIDE;
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL) OVERRIDE; //use hero=NULL for no hero void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = NULL) OVERRIDE; //use hero=NULL for no hero
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE; //if any of armies is hero, hero will be used void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) OVERRIDE; //if any of armies is hero, hero will be used
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function<void(BattleResult*)> cb = 0, bool creatureBank = false) OVERRIDE; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle//void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb) OVERRIDE; //for hero<=>neutral army void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) OVERRIDE; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle//void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb) OVERRIDE; //for hero<=>neutral army
void setAmount(ObjectInstanceID objid, ui32 val) OVERRIDE; void setAmount(ObjectInstanceID objid, ui32 val) OVERRIDE;
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 instant, PlayerColor asker = PlayerColor::NEUTRAL) OVERRIDE; bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) OVERRIDE;
void giveHeroBonus(GiveBonus * bonus) OVERRIDE; void giveHeroBonus(GiveBonus * bonus) OVERRIDE;
void setMovePoints(SetMovePoints * smp) OVERRIDE; void setMovePoints(SetMovePoints * smp) OVERRIDE;
void setManaPoints(ObjectInstanceID hid, int val) OVERRIDE; void setManaPoints(ObjectInstanceID hid, int val) OVERRIDE;
@ -179,7 +174,6 @@ public:
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void useScholarSkill(ObjectInstanceID hero1, ObjectInstanceID hero2); void useScholarSkill(ObjectInstanceID hero1, ObjectInstanceID hero2);
void setPortalDwelling(const CGTownInstance * town, bool forced, bool clear); void setPortalDwelling(const CGTownInstance * town, bool forced, bool clear);
bool tryAttackingGuard(const int3 &guardPos, const CGHeroInstance * h);
void visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h); void visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h);
bool teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui8 source, PlayerColor asker = PlayerColor::NEUTRAL); bool teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui8 source, PlayerColor asker = PlayerColor::NEUTRAL);
void vistiCastleObjects (const CGTownInstance *t, const CGHeroInstance *h); void vistiCastleObjects (const CGTownInstance *t, const CGHeroInstance *h);
@ -187,7 +181,8 @@ public:
void levelUpHero(const CGHeroInstance * hero);//initial call - check if hero have remaining levelups & handle them void levelUpHero(const CGHeroInstance * hero);//initial call - check if hero have remaining levelups & handle them
void levelUpCommander (const CCommanderInstance * c, int skill); //secondary skill 1 to 6, special skill : skill - 100 void levelUpCommander (const CCommanderInstance * c, int skill); //secondary skill 1 to 6, special skill : skill - 100
void levelUpCommander (const CCommanderInstance * c); void levelUpCommander (const CCommanderInstance * c);
void afterBattleCallback(); // called after level-ups are finished
void expGiven(const CGHeroInstance *hero); //triggers needed level-ups, handles also commander of this hero
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void commitPackage(CPackForClient *pack) OVERRIDE; void commitPackage(CPackForClient *pack) OVERRIDE;
@ -233,6 +228,7 @@ public:
void handleTownEvents(CGTownInstance *town, NewTurn &n); void handleTownEvents(CGTownInstance *town, NewTurn &n);
bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h ); void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h );
void objectVisitEnded(const CObjectVisitQuery &query);
void engageIntoBattle( PlayerColor player ); void engageIntoBattle( PlayerColor player );
bool dig(const CGHeroInstance *h); bool dig(const CGHeroInstance *h);
bool castSpell(const CGHeroInstance *h, SpellID spellID, const int3 &pos); bool castSpell(const CGHeroInstance *h, SpellID spellID, const int3 &pos);
@ -240,15 +236,11 @@ public:
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & QID & states; h & QID & states & finishingBattle;
} }
ui32 getQueryResult(PlayerColor player, int queryID);
void sendMessageToAll(const std::string &message); void sendMessageToAll(const std::string &message);
void sendMessageTo(CConnection &c, const std::string &message); void sendMessageTo(CConnection &c, const std::string &message);
void applyAndAsk(Query * sel, PlayerColor player, boost::function<void(ui32)> &callback);
void prepareNewQuery(Query * queryPack, PlayerColor player, const boost::function<void(ui32)> &callback = 0); //generates unique query id and writes it to the pack; blocks the player till query is answered (then callback is called)
void ask(Query * sel, PlayerColor player, const CFunctionList<void(ui32)> &callback);
void sendToAllClients(CPackForClient * info); void sendToAllClients(CPackForClient * info);
void sendAndApply(CPackForClient * info); void sendAndApply(CPackForClient * info);
void applyAndSend(CPackForClient * info); void applyAndSend(CPackForClient * info);
@ -257,6 +249,28 @@ public:
void sendAndApply(SetResources * info); void sendAndApply(SetResources * info);
void sendAndApply(NewStructures * info); void sendAndApply(NewStructures * info);
struct FinishingBattleHelper
{
FinishingBattleHelper();
FinishingBattleHelper(shared_ptr<const CBattleQuery> Query, bool Duel, int RemainingBattleQueriesCount);
shared_ptr<const CBattleQuery> query;
const CGHeroInstance *winnerHero, *loserHero;
PlayerColor victor, loser;
bool duel;
int remainingBattleQueriesCount;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & query & winnerHero & loserHero & victor & loser & duel & remainingBattleQueriesCount;
}
};
unique_ptr<FinishingBattleHelper> finishingBattle;
void battleAfterLevelUp(const BattleResult &result);
void run(bool resume); void run(bool resume);
void newTurn(); void newTurn();
void handleAttackBeforeCasting (const BattleAttack & bat); void handleAttackBeforeCasting (const BattleAttack & bat);

View File

@ -6,6 +6,7 @@ include_directories(${Boost_INCLUDE_DIRS})
set(server_SRCS set(server_SRCS
CGameHandler.cpp CGameHandler.cpp
CQuery.cpp
CVCMIServer.cpp CVCMIServer.cpp
NetPacksServer.cpp NetPacksServer.cpp
) )

342
server/CQuery.cpp Normal file
View File

@ -0,0 +1,342 @@
#include "StdInc.h"
#include "CQuery.h"
#include "CGameHandler.h"
#include "..\lib\BattleState.h"
boost::mutex Queries::mx;
template <typename Container>
std::string formatContainer(const Container &c, std::string delimeter=", ", std::string opener="(", std::string closer=")")
{
std::string ret = opener;
auto itr = boost::begin(c);
if(itr != boost::end(c))
{
ret += boost::lexical_cast<std::string>(*itr);
while(++itr != boost::end(c))
{
ret += delimeter;
ret += boost::lexical_cast<std::string>(*itr);
}
}
ret += closer;
return ret;
}
std::ostream & operator<<(std::ostream &out, const CQuery &query)
{
return out << query.toString();
}
std::ostream & operator<<(std::ostream &out, QueryPtr query)
{
return out << "[" << query.get() << "] " << query->toString();
}
CQuery::CQuery(void)
{
boost::unique_lock<boost::mutex> l(Queries::mx);
static TQueryID QID = 1;
queryID = QID++;
logGlobal->traceStream() << "Created a new query with id " << queryID;
}
CQuery::~CQuery(void)
{
logGlobal->traceStream() << "Destructed the query with id " << queryID;
}
void CQuery::addPlayer(PlayerColor color)
{
if(color.isValidPlayer())
{
players.push_back(color);
}
}
std::string CQuery::toString() const
{
std::string ret = boost::str(boost::format("A query of type %s and qid=%d affecting players %s") % typeid(*this).name() % queryID % formatContainer(players));
return ret;
}
bool CQuery::endsByPlayerAnswer() const
{
return false;
}
void CQuery::onRemoval(CGameHandler *gh, PlayerColor color)
{
}
bool CQuery::blocksPack(const CPack *pack) const
{
return false;
}
void CQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
{
}
void CQuery::onExposure(CGameHandler *gh, QueryPtr topQuery)
{
gh->queries.popQuery(*this);
}
CObjectVisitQuery::CObjectVisitQuery(const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile)
: visitedObject(Obj), visitingHero(Hero), tile(Tile)
{
addPlayer(Hero->tempOwner);
}
bool CObjectVisitQuery::blocksPack(const CPack *pack) const
{
//During the visit itself ALL actions are blocked.
//(However, the visit may trigger a query above that'll pass some.)
return true;
}
void CObjectVisitQuery::onRemoval(CGameHandler *gh, PlayerColor color)
{
gh->objectVisitEnded(*this);
}
void CObjectVisitQuery::onExposure(CGameHandler *gh, QueryPtr topQuery)
{
//Object may have been removed and deleted.
if(gh->isValidObject(visitedObject))
topQuery->notifyObjectAboutRemoval(*this);
gh->queries.popQuery(*this);
}
void Queries::popQuery(PlayerColor player, QueryPtr query)
{
LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query);
if(topQuery(player) != query)
{
logGlobal->traceStream() << "Cannot remove, not a top!";
return;
}
queries[player] -= query;
auto nextQuery = topQuery(player);
query->onRemoval(gh, player);
//Exposure on query below happens only if removal didnt trigger any new query
if(nextQuery && nextQuery == topQuery(player))
{
nextQuery->onExposure(gh, query);
}
}
void Queries::popQuery(const CQuery &query)
{
LOG_TRACE_PARAMS(logGlobal, "query='%s'", query);
assert(query.players.size());
BOOST_FOREACH(auto player, query.players)
{
auto top = topQuery(player);
if(top.get() == &query)
popQuery(top);
else
logGlobal->traceStream() << "Cannot remove query " << query;
}
}
void Queries::popQuery(QueryPtr query)
{
BOOST_FOREACH(auto player, query->players)
popQuery(player, query);
}
void Queries::addQuery(QueryPtr query)
{
BOOST_FOREACH(auto player, query->players)
addQuery(player, query);
}
void Queries::addQuery(PlayerColor player, QueryPtr query)
{
LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query);
queries[player].push_back(query);
}
QueryPtr Queries::topQuery(PlayerColor player)
{
return vstd::backOrNull(queries[player]);
}
void Queries::popIfTop(QueryPtr query)
{
popIfTop(*query);
}
void Queries::popIfTop(const CQuery &query)
{
BOOST_FOREACH(PlayerColor color, query.players)
if(topQuery(color).get() == &query)
popQuery(color, topQuery(color));
}
std::vector<shared_ptr<const CQuery>> Queries::allQueries() const
{
std::vector<shared_ptr<const CQuery>> ret;
BOOST_FOREACH(auto &playerQueries, queries)
BOOST_FOREACH(auto &query, playerQueries.second)
ret.push_back(query);
return ret;
}
std::vector<shared_ptr<CQuery>> Queries::allQueries()
{
//TODO code duplication with const function :(
std::vector<shared_ptr<CQuery>> ret;
BOOST_FOREACH(auto &playerQueries, queries)
BOOST_FOREACH(auto &query, playerQueries.second)
ret.push_back(query);
return ret;
}
void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
{
assert(result);
objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
}
CBattleQuery::CBattleQuery(const BattleInfo *Bi)
{
belligerents[0] = Bi->belligerents[0];
belligerents[1] = Bi->belligerents[1];
bi = Bi;
BOOST_FOREACH(PlayerColor side, bi->sides)
addPlayer(side);
}
CBattleQuery::CBattleQuery()
{
}
bool CBattleQuery::blocksPack(const CPack *pack) const
{
return !dynamic_cast<const MakeAction*>(pack) && !dynamic_cast<const MakeCustomAction*>(pack);
}
void CBattleQuery::onRemoval(CGameHandler *gh, PlayerColor color)
{
gh->battleAfterLevelUp(*result);
}
void CGarrisonDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
{
objectVisit.visitedObject->garrisonDialogClosed(objectVisit.visitingHero);
}
CGarrisonDialogQuery::CGarrisonDialogQuery(const CArmedInstance *up, const CArmedInstance *down)
{
exchangingArmies[0] = up;
exchangingArmies[1] = down;
addPlayer(up->tempOwner);
addPlayer(down->tempOwner);
}
bool CGarrisonDialogQuery::blocksPack(const CPack *pack) const
{
if(auto stacks = dynamic_cast<const ArrangeStacks*>(pack))
{
std::set<ObjectInstanceID> ourIds;
ourIds.insert(this->exchangingArmies[0]->id);
ourIds.insert(this->exchangingArmies[1]->id);
return !vstd::contains(ourIds, stacks->id1) || !vstd::contains(ourIds, stacks->id2);
}
return CDialogQuery::blocksPack(pack);
}
void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
{
assert(answer);
objectVisit.visitedObject->blockingDialogAnswered(objectVisit.visitingHero, *answer);
}
CBlockingDialogQuery::CBlockingDialogQuery(const BlockingDialog &bd)
{
this->bd = bd;
addPlayer(bd.player);
}
CHeroLevelUpDialogQuery::CHeroLevelUpDialogQuery(const HeroLevelUp &Hlu)
{
hlu = Hlu;
addPlayer(hlu.hero->tempOwner);
}
void CHeroLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color)
{
assert(answer);
logGlobal->traceStream() << "Completing hero level-up query. " << hlu.hero->getHoverText() << " gains skill " << answer;
gh->levelUpHero(hlu.hero, hlu.skills[*answer]);
}
CCommanderLevelUpDialogQuery::CCommanderLevelUpDialogQuery(const CommanderLevelUp &Clu)
{
clu = Clu;
addPlayer(clu.hero->tempOwner);
}
void CCommanderLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color)
{
assert(answer);
logGlobal->traceStream() << "Completing commander level-up query. Commander of hero " << clu.hero->getHoverText() << " gains skill " << answer;
gh->levelUpCommander(clu.hero->commander, clu.skills[*answer]);
}
bool CDialogQuery::endsByPlayerAnswer() const
{
return true;
}
bool CDialogQuery::blocksPack(const CPack *pack) const
{
//We accept only query replies from correct player
if(auto reply = dynamic_cast<const QueryReply *>(pack))
{
return !vstd::contains(players, reply->player);
}
return true;
}
CHeroMovementQuery::CHeroMovementQuery(const TryMoveHero &Tmh, const CGHeroInstance *Hero, bool VisitDestAfterVictory)
: tmh(Tmh), visitDestAfterVictory(VisitDestAfterVictory), hero(Hero)
{
players.push_back(hero->tempOwner);
}
void CHeroMovementQuery::onExposure(CGameHandler *gh, QueryPtr topQuery)
{
assert(players.size() == 1);
if(visitDestAfterVictory && hero->tempOwner == players[0]) //hero still alive, so he won with the guard
//TODO what if there were H4-like escape? we should also check pos
{
logGlobal->traceStream() << "Hero " << hero->name << " after victory over guard finishes visit to " << tmh.end;
//finish movement
visitDestAfterVictory = false;
gh->visitObjectOnTile(*gh->getTile(CGHeroInstance::convertPosition(tmh.end, false)), hero);
}
gh->queries.popIfTop(*this);
}

177
server/CQuery.h Normal file
View File

@ -0,0 +1,177 @@
#pragma once
#include "..\lib\GameConstants.h"
#include "..\lib\int3.h"
#include "..\lib\NetPacks.h"
class CGObjectInstance;
class CGHeroInstance;
class CArmedInstance;
struct CPack;
class CGameHandler;
class CObjectVisitQuery;
struct TryMoveHero;
class CQuery;
typedef si32 TQueryID;
typedef shared_ptr<CQuery> QueryPtr;
// This class represents any kind of prolonged interaction that may need to do something special after it is over.
// It does not necessarily has to be "query" requiring player action, it can be also used internally within server.
// Examples:
// - all kinds of blocking dialog windows
// - battle
// - object visit
// - hero movement
// Queries can cause another queries, forming a stack of queries for each player. Eg: hero movement -> object visit -> dialog.
class CQuery
{
protected:
void addPlayer(PlayerColor color);
public:
std::vector<PlayerColor> players; //players that are affected (often "blocked") by query
TQueryID queryID;
CQuery(void);
virtual bool blocksPack(const CPack *pack) const; //query can block attempting actions by player. Eg. he can't move hero during the battle.
virtual bool endsByPlayerAnswer() const; //query is removed after player gives answer (like dialogs)
virtual void onRemoval(CGameHandler *gh, PlayerColor color); //called after query is removed from stack
virtual void onExposure(CGameHandler *gh, QueryPtr topQuery);//called when query immediately above is removed and this is exposed (becomes top)
virtual std::string toString() const;
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const;
virtual ~CQuery(void);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & players & queryID;
}
};
std::ostream &operator<<(std::ostream &out, const CQuery &query);
std::ostream &operator<<(std::ostream &out, QueryPtr query);
//Created when hero visits object.
//Removed when query above is resolved (or immediately after visit if no queries were created)
class CObjectVisitQuery : public CQuery
{
public:
const CGObjectInstance *visitedObject;
const CGHeroInstance *visitingHero;
int3 tile; //may be different than hero pos -> eg. visit via teleport
CObjectVisitQuery(const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile);
virtual bool blocksPack(const CPack *pack) const OVERRIDE;
virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
virtual void onExposure(CGameHandler *gh, QueryPtr topQuery) OVERRIDE;
};
class CBattleQuery : public CQuery
{
public:
std::array<const CArmedInstance *,2> belligerents;
const BattleInfo *bi;
boost::optional<BattleResult> result;
CBattleQuery();
CBattleQuery(const BattleInfo *Bi); //TODO
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const OVERRIDE;
virtual bool blocksPack(const CPack *pack) const OVERRIDE;
virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
};
//Created when hero attempts move and something happens
//(not necessarily position change, could be just an object interaction).
class CHeroMovementQuery : public CQuery
{
public:
TryMoveHero tmh;
bool visitDestAfterVictory; //if hero moved to guarded tile and it should be visited once guard is defeated
const CGHeroInstance *hero;
virtual void onExposure(CGameHandler *gh, QueryPtr topQuery);
CHeroMovementQuery(const TryMoveHero &Tmh, const CGHeroInstance *Hero, bool VisitDestAfterVictory = false);
};
class CDialogQuery : public CQuery
{
public:
boost::optional<ui32> answer;
virtual bool endsByPlayerAnswer() const OVERRIDE;
virtual bool blocksPack(const CPack *pack) const OVERRIDE;
};
class CGarrisonDialogQuery : public CDialogQuery
{
public:
std::array<const CArmedInstance *,2> exchangingArmies;
CGarrisonDialogQuery(const CArmedInstance *up, const CArmedInstance *down);
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const OVERRIDE;
virtual bool blocksPack(const CPack *pack) const OVERRIDE;
};
//yes/no and component selection dialogs
class CBlockingDialogQuery : public CDialogQuery
{
public:
BlockingDialog bd; //copy of pack... debug purposes
CBlockingDialogQuery(const BlockingDialog &bd);
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const OVERRIDE;
};
class CHeroLevelUpDialogQuery : public CDialogQuery
{
public:
CHeroLevelUpDialogQuery(const HeroLevelUp &Hlu);
virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
HeroLevelUp hlu;
};
class CCommanderLevelUpDialogQuery : public CDialogQuery
{
public:
CCommanderLevelUpDialogQuery(const CommanderLevelUp &Clu);
virtual void onRemoval(CGameHandler *gh, PlayerColor color) OVERRIDE;
CommanderLevelUp clu;
};
struct Queries
{
private:
void addQuery(PlayerColor player, QueryPtr query);
void popQuery(PlayerColor player, QueryPtr query);
std::map<PlayerColor, std::vector<QueryPtr>> queries; //player => stack of queries
public:
CGameHandler *gh;
static boost::mutex mx;
void addQuery(QueryPtr query);
void popQuery(const CQuery &query);
void popQuery(QueryPtr query);
void popIfTop(const CQuery &query); //removes this query if it is at the top (otherwise, do nothing)
void popIfTop(QueryPtr query); //removes this query if it is at the top (otherwise, do nothing)
QueryPtr topQuery(PlayerColor player);
std::vector<shared_ptr<const CQuery>> allQueries() const;
std::vector<shared_ptr<CQuery>> allQueries();
//void removeQuery
};

View File

@ -69,8 +69,8 @@ bool EndTurn::applyGh( CGameHandler *gh )
{ {
PlayerColor player = GS(gh)->currentPlayer; PlayerColor player = GS(gh)->currentPlayer;
ERROR_IF_NOT(player); ERROR_IF_NOT(player);
if(gh->states.checkFlag(player, &PlayerStatus::engagedIntoBattle)) if(gh->queries.topQuery(player))
COMPLAIN_AND_RETURN("Cannot end turn when in battle!"); COMPLAIN_AND_RETURN("Cannot end turn before resolving queries!");
gh->states.setFlag(GS(gh)->currentPlayer,&PlayerStatus::makingTurn,false); gh->states.setFlag(GS(gh)->currentPlayer,&PlayerStatus::makingTurn,false);
return true; return true;

View File

@ -145,6 +145,7 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="CGameHandler.cpp" /> <ClCompile Include="CGameHandler.cpp" />
<ClCompile Include="CQuery.cpp" />
<ClCompile Include="CVCMIServer.cpp" /> <ClCompile Include="CVCMIServer.cpp" />
<ClCompile Include="NetPacksServer.cpp" /> <ClCompile Include="NetPacksServer.cpp" />
<ClCompile Include="StdInc.cpp"> <ClCompile Include="StdInc.cpp">
@ -158,6 +159,7 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="..\Global.h" /> <ClInclude Include="..\Global.h" />
<ClInclude Include="CGameHandler.h" /> <ClInclude Include="CGameHandler.h" />
<ClInclude Include="CQuery.h" />
<ClInclude Include="CVCMIServer.h" /> <ClInclude Include="CVCMIServer.h" />
<ClInclude Include="StdInc.h" /> <ClInclude Include="StdInc.h" />
</ItemGroup> </ItemGroup>