1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Minor rework & cleanup of combat replays

This commit is contained in:
Ivan Savenko 2023-07-27 18:40:47 +03:00
parent b8110218c0
commit aed8c411fc
20 changed files with 90 additions and 67 deletions

View File

@ -826,7 +826,7 @@ void CBattleAI::evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcas
ps.value = totalGain;
}
void CBattleAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side)
void CBattleAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side, bool replayAllowed)
{
LOG_TRACE(logAi);
side = Side;

View File

@ -83,7 +83,7 @@ public:
BattleAction selectStackAction(const CStack * stack);
std::optional<PossibleSpellcast> findBestCreatureSpell(const CStack *stack);
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool Side) override;
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool Side, bool replayAllowed) override;
//void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero
//void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero
//void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack

View File

@ -809,7 +809,7 @@ void AIGateway::makeTurn()
for (auto h : cb->getHeroesInfo())
{
if (h->movementPointsRemaining())
logAi->warn("Hero %s has %d MP left", h->getNameTranslated(), h->movementPointsRemaining());
logAi->info("Hero %s has %d MP left", h->getNameTranslated(), h->movementPointsRemaining());
}
#if NKAI_TRACE_LEVEL == 0
}
@ -1065,14 +1065,14 @@ void AIGateway::recruitCreatures(const CGDwelling * d, const CArmedInstance * re
}
}
void AIGateway::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side)
void AIGateway::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed)
{
NET_EVENT_HANDLER;
assert(playerID > PlayerColor::PLAYER_LIMIT || status.getBattle() == UPCOMING_BATTLE);
status.setBattle(ONGOING_BATTLE);
const CGObjectInstance * presumedEnemy = vstd::backOrNull(cb->getVisitableObjs(tile)); //may be nullptr in some very are cases -> eg. visited monolith and fighting with an enemy at the FoW covered exit
battlename = boost::str(boost::format("Starting battle of %s attacking %s at %s") % (hero1 ? hero1->getNameTranslated() : "a army") % (presumedEnemy ? presumedEnemy->getObjectName() : "unknown enemy") % tile.toString());
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side);
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side, replayAllowed);
}
void AIGateway::battleEnd(const BattleResult * br, QueryID queryID)
@ -1083,12 +1083,16 @@ void AIGateway::battleEnd(const BattleResult * br, QueryID queryID)
bool won = br->winner == myCb->battleGetMySide();
logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.getStr(), (won ? "won" : "lost"), battlename);
battlename.clear();
status.addQuery(queryID, "Combat result dialog");
const int confirmAction = 0;
requestActionASAP([=]()
if (queryID != -1)
{
answerQuery(queryID, confirmAction);
});
status.addQuery(queryID, "Combat result dialog");
const int confirmAction = 0;
requestActionASAP([=]()
{
answerQuery(queryID, confirmAction);
});
}
CAdventureAI::battleEnd(br, queryID);
}
@ -1175,7 +1179,7 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
if(startHpos == dst)
{
//FIXME: this assertion fails also if AI moves onto defeated guarded object
assert(cb->getVisitableObjs(dst).size() > 1); //there's no point in revisiting tile where there is no visitable object
//assert(cb->getVisitableObjs(dst).size() > 1); //there's no point in revisiting tile where there is no visitable object
cb->moveHero(*h, h->convertFromVisitablePos(dst));
afterMovementCheck(); // TODO: is it feasible to hero get killed there if game work properly?
// If revisiting, teleport probing is never done, and so the entries into the list would remain unused and uncleared

View File

@ -169,7 +169,7 @@ public:
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) override;
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) override;
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) override;
void battleEnd(const BattleResult * br, QueryID queryID) override;
void makeTurn();

View File

@ -242,7 +242,7 @@ void CStupidAI::battleStacksEffectsSet(const SetStackEffect & sse)
print("battleStacksEffectsSet called");
}
void CStupidAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side)
void CStupidAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side, bool replayAllowed)
{
print("battleStart called");
side = Side;

View File

@ -44,7 +44,7 @@ public:
void battleSpellCast(const BattleSpellCast *sc) override;
void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right
void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
private:

View File

@ -819,7 +819,7 @@ void VCAI::makeTurn()
for (auto h : cb->getHeroesInfo())
{
if (h->movementPointsRemaining())
logAi->warn("Hero %s has %d MP left", h->getNameTranslated(), h->movementPointsRemaining());
logAi->info("Hero %s has %d MP left", h->getNameTranslated(), h->movementPointsRemaining());
}
}
catch (boost::thread_interrupted & e)
@ -1575,14 +1575,14 @@ void VCAI::completeGoal(Goals::TSubgoal goal)
}
void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side)
void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed)
{
NET_EVENT_HANDLER;
assert(playerID > PlayerColor::PLAYER_LIMIT || status.getBattle() == UPCOMING_BATTLE);
status.setBattle(ONGOING_BATTLE);
const CGObjectInstance * presumedEnemy = vstd::backOrNull(cb->getVisitableObjs(tile)); //may be nullptr in some very are cases -> eg. visited monolith and fighting with an enemy at the FoW covered exit
battlename = boost::str(boost::format("Starting battle of %s attacking %s at %s") % (hero1 ? hero1->getNameTranslated() : "a army") % (presumedEnemy ? presumedEnemy->getObjectName() : "unknown enemy") % tile.toString());
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side);
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side, replayAllowed);
}
void VCAI::battleEnd(const BattleResult * br, QueryID queryID)
@ -1593,12 +1593,16 @@ void VCAI::battleEnd(const BattleResult * br, QueryID queryID)
bool won = br->winner == myCb->battleGetMySide();
logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.getStr(), (won ? "won" : "lost"), battlename);
battlename.clear();
status.addQuery(queryID, "Combat result dialog");
const int confirmAction = 0;
requestActionASAP([=]()
if (queryID != -1)
{
answerQuery(queryID, confirmAction);
});
status.addQuery(queryID, "Combat result dialog");
const int confirmAction = 0;
requestActionASAP([=]()
{
answerQuery(queryID, confirmAction);
});
}
CAdventureAI::battleEnd(br, queryID);
}

View File

@ -201,7 +201,7 @@ public:
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) override;
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) override;
void battleEnd(const BattleResult * br, QueryID queryID) override;
void makeTurn();

View File

@ -652,26 +652,20 @@ void CPlayerInterface::battleStartBefore(const CCreatureSet *army1, const CCreat
waitForAllDialogs();
}
void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side)
void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
bool autoBattleResultRefused = (lastBattleArmies.first == army1 && lastBattleArmies.second == army2);
lastBattleArmies.first = army1;
lastBattleArmies.second = army2;
//quick combat with neutral creatures only
auto * army2_object = dynamic_cast<const CGObjectInstance *>(army2);
if((!autoBattleResultRefused && !allowBattleReplay && army2_object
&& (army2_object->getOwner() == PlayerColor::UNFLAGGABLE || army2_object->getOwner() == PlayerColor::NEUTRAL)
&& settings["adventure"]["quickCombat"].Bool())
|| settings["adventure"]["alwaysSkipCombat"].Bool())
bool useQuickCombat = settings["adventure"]["quickCombat"].Bool();
bool forceQuickCombat = settings["adventure"]["forceQuickCombat"].Bool();
if ((replayAllowed && useQuickCombat) || forceQuickCombat)
{
autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String());
autofightingAI->initBattleInterface(env, cb);
autofightingAI->battleStart(army1, army2, int3(0,0,0), hero1, hero2, side);
autofightingAI->battleStart(army1, army2, tile, hero1, hero2, side, false);
isAutoFightOn = true;
cb->registerBattleInterface(autofightingAI);
// Player shouldn't be able to move on adventure map if quick combat is going
allowBattleReplay = true;
}
//Don't wait for dialogs when we are non-active hot-seat player
@ -843,13 +837,17 @@ void CPlayerInterface::battleEnd(const BattleResult *br, QueryID queryID)
if(!battleInt)
{
bool allowManualReplay = allowBattleReplay && !settings["adventure"]["alwaysSkipCombat"].Bool();
allowBattleReplay = false;
bool allowManualReplay = queryID != -1;
auto wnd = std::make_shared<BattleResultWindow>(*br, *this, allowManualReplay);
wnd->resultCallback = [=](ui32 selection)
if (allowManualReplay)
{
cb->selectionMade(selection, queryID);
};
wnd->resultCallback = [=](ui32 selection)
{
cb->selectionMade(selection, queryID);
};
}
GH.windows().pushWindow(wnd);
// #1490 - during AI turn when quick combat is on, we need to display the message and wait for user to close it.
// Otherwise NewTurn causes freeze.

View File

@ -65,8 +65,6 @@ class CPlayerInterface : public CGameInterface, public IUpdateable
int firstCall;
int autosaveCount;
std::pair<const CCreatureSet *, const CCreatureSet *> lastBattleArmies;
bool allowBattleReplay = false;
std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
const BattleAction *curAction; //during the battle - action currently performed by active stack (or nullptr)
@ -169,7 +167,7 @@ protected: // Call-ins from server, should not be called directly, but only via
void battleTriggerEffect(const BattleTriggerEffect & bte) override; //various one-shot effect
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override;
void battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) override; //called by engine just before battle starts; side=0 - left, side=1 - right
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right
void battleUnitsChanged(const std::vector<UnitChanges> & units) override;
void battleObstaclesChanged(const std::vector<ObstacleChanges> & obstacles) override;
void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack

View File

@ -580,7 +580,7 @@ void CClient::battleStarted(const BattleInfo * info)
auto callBattleStart = [&](PlayerColor color, ui8 side)
{
if(vstd::contains(battleints, color))
battleints[color]->battleStart(leftSide.armyObject, rightSide.armyObject, info->tile, leftSide.hero, rightSide.hero, side);
battleints[color]->battleStart(leftSide.armyObject, rightSide.armyObject, info->tile, leftSide.hero, rightSide.hero, side, info->replayAllowed);
};
callBattleStart(leftSide.color, 0);

View File

@ -501,7 +501,7 @@ void BattleWindow::bAutofightf()
auto ai = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String());
ai->initBattleInterface(owner.curInt->env, owner.curInt->cb);
ai->battleStart(owner.army1, owner.army2, int3(0,0,0), owner.attackingHeroInstance, owner.defendingHeroInstance, owner.curInt->cb->battleGetMySide());
ai->battleStart(owner.army1, owner.army2, int3(0,0,0), owner.attackingHeroInstance, owner.defendingHeroInstance, owner.curInt->cb->battleGetMySide(), false);
owner.curInt->autofightingAI = ai;
owner.curInt->cb->registerBattleInterface(ai);

View File

@ -223,7 +223,7 @@
"type" : "object",
"additionalProperties" : false,
"default" : {},
"required" : [ "heroMoveTime", "enemyMoveTime", "scrollSpeedPixels", "heroReminder", "quickCombat", "objectAnimation", "terrainAnimation", "alwaysSkipCombat", "borderScroll", "leftButtonDrag" ],
"required" : [ "heroMoveTime", "enemyMoveTime", "scrollSpeedPixels", "heroReminder", "quickCombat", "objectAnimation", "terrainAnimation", "forceQuickCombat", "borderScroll", "leftButtonDrag" ],
"properties" : {
"heroMoveTime" : {
"type" : "number",
@ -253,7 +253,7 @@
"type" : "boolean",
"default" : true
},
"alwaysSkipCombat" : {
"forceQuickCombat" : {
"type" : "boolean",
"default" : false
},

View File

@ -168,13 +168,13 @@ void CAdventureAI::battleCatapultAttacked(const CatapultAttack & ca)
}
void CAdventureAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile,
const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side)
const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed)
{
assert(!battleAI);
assert(cbc);
battleAI = CDynLibHandler::getNewBattleAI(getBattleAIName());
battleAI->initBattleInterface(env, cbc);
battleAI->battleStart(army1, army2, tile, hero1, hero2, side);
battleAI->battleStart(army1, army2, tile, hero1, hero2, side, replayAllowed);
}
void CAdventureAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged)

View File

@ -149,7 +149,7 @@ public:
virtual void yourTacticPhase(int distance) override;
virtual void battleNewRound(int round) override;
virtual void battleCatapultAttacked(const CatapultAttack & ca) override;
virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override;
virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed) override;
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override;
virtual void actionStarted(const BattleAction &action) override;
virtual void battleNewRoundFirst(int round) override;

View File

@ -69,7 +69,7 @@ public:
virtual void battleStacksEffectsSet(const SetStackEffect & sse){};//called when a specific effect is set to stacks
virtual void battleTriggerEffect(const BattleTriggerEffect & bte){}; //called for various one-shot effects
virtual void battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) {}; //called just before battle start
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 battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side, bool replayAllowed){}; //called by engine when battle starts; side=0 - left, side=1 - right
virtual void battleUnitsChanged(const std::vector<UnitChanges> & units){};
virtual void battleObstaclesChanged(const std::vector<ObstacleChanges> & obstacles){};
virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack

View File

@ -207,6 +207,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
curB->round = -2;
curB->activeStack = -1;
curB->creatureBank = creatureBank;
curB->replayAllowed = false;
if(town)
{

View File

@ -36,6 +36,7 @@ public:
const CGTownInstance * town; //used during town siege, nullptr if this is not a siege (note that fortless town IS also a siege)
int3 tile; //for background and bonuses
bool creatureBank; //auxilary field, do not serialize
bool replayAllowed;
std::vector<CStack*> stacks;
std::vector<std::shared_ptr<CObstacleInstance> > obstacles;
SiegeInfo si;
@ -61,6 +62,10 @@ public:
h & tacticsSide;
h & tacticDistance;
h & static_cast<CBonusSystemNode&>(*this);
if (version > 824)
h & replayAllowed;
else
replayAllowed = false;
}
//////////////////////////////////////////////////////////////////////////

View File

@ -14,7 +14,7 @@
VCMI_LIB_NAMESPACE_BEGIN
const ui32 SERIALIZATION_VERSION = 824;
const ui32 SERIALIZATION_VERSION = 825;
const ui32 MINIMAL_SERIALIZATION_VERSION = 824;
const std::string SAVEGAME_MAGIC = "VCMISVG";

View File

@ -607,9 +607,15 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
const int queriedPlayers = battleQuery ? (int)boost::count(queries.allQueries(), battleQuery) : 0;
finishingBattle = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(this, gs->curB);
battleResult.data->queryID = battleDialogQuery->queryID;
queries.addQuery(battleDialogQuery);
// in battles against neutrals, 1st player can ask to replay battle manually
if (!gs->curB->sides[1].color.isValidPlayer())
{
auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(this, gs->curB);
battleResult.data->queryID = battleDialogQuery->queryID;
queries.addQuery(battleDialogQuery);
}
else
battleResult.data->queryID = -1;
//set same battle result for all queries
for(auto q : queries.allQueries())
@ -620,6 +626,9 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
}
sendAndApply(battleResult.data); //after this point casualties objects are destroyed
if (battleResult.data->queryID == -1)
endBattleConfirm(gs->curB);
}
void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo)
@ -2118,6 +2127,10 @@ void CGameHandler::setupBattle(int3 tile, const CArmedInstance *armies[2], const
//send info about battles
BattleStart bs;
bs.info = BattleInfo::setupBattle(tile, terrain, terType, armies, heroes, creatureBank, town);
auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(queries.topQuery(bs.info->sides[0].color));
bs.info->replayAllowed = lastBattleQuery == nullptr && !bs.info->sides[1].color.isValidPlayer();
sendAndApply(&bs);
}
@ -2587,39 +2600,39 @@ void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedI
heroes[0] = hero1;
heroes[1] = hero2;
setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(queries.topQuery(gs->curB->sides[0].color));
//existing battle query for retying auto-combat
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(queries.topQuery(gs->curB->sides[0].color));
if(battleQuery)
if(lastBattleQuery)
{
for(int i : {0, 1})
{
if(heroes[i])
{
SetMana restoreInitialMana;
restoreInitialMana.val = battleQuery->initialHeroMana[i];
restoreInitialMana.val = lastBattleQuery->initialHeroMana[i];
restoreInitialMana.hid = heroes[i]->id;
sendAndApply(&restoreInitialMana);
}
}
battleQuery->bi = gs->curB;
battleQuery->result = std::nullopt;
battleQuery->belligerents[0] = gs->curB->sides[0].armyObject;
battleQuery->belligerents[1] = gs->curB->sides[1].armyObject;
lastBattleQuery->bi = gs->curB;
lastBattleQuery->result = std::nullopt;
lastBattleQuery->belligerents[0] = gs->curB->sides[0].armyObject;
lastBattleQuery->belligerents[1] = gs->curB->sides[1].armyObject;
}
battleQuery = std::make_shared<CBattleQuery>(this, gs->curB);
auto nextBattleQuery = std::make_shared<CBattleQuery>(this, gs->curB);
for(int i : {0, 1})
{
if(heroes[i])
{
battleQuery->initialHeroMana[i] = heroes[i]->mana;
nextBattleQuery->initialHeroMana[i] = heroes[i]->mana;
}
}
queries.addQuery(battleQuery);
queries.addQuery(nextBattleQuery);
this->battleThread = std::make_unique<boost::thread>(boost::thread(&CGameHandler::runBattle, this));
}