mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +02:00
Merge pull request #1108 from Nordsoft91/battle-dialog
[HDMod features] Quick combat
This commit is contained in:
commit
3951bbb395
@ -82,7 +82,7 @@ public:
|
|||||||
//void actionStarted(const BattleAction &action) override;//occurs BEFORE 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
|
//void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack
|
||||||
//void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack())
|
//void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack())
|
||||||
//void battleEnd(const BattleResult *br) override;
|
//void battleEnd(const BattleResult *br, QueryID queryID) override;
|
||||||
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
||||||
//void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
//void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
||||||
//void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
//void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
||||||
|
@ -1090,7 +1090,7 @@ void AIGateway::battleStart(const CCreatureSet * army1, const CCreatureSet * arm
|
|||||||
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side);
|
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIGateway::battleEnd(const BattleResult * br)
|
void AIGateway::battleEnd(const BattleResult * br, QueryID queryID)
|
||||||
{
|
{
|
||||||
NET_EVENT_HANDLER;
|
NET_EVENT_HANDLER;
|
||||||
assert(status.getBattle() == ONGOING_BATTLE);
|
assert(status.getBattle() == ONGOING_BATTLE);
|
||||||
@ -1098,7 +1098,13 @@ void AIGateway::battleEnd(const BattleResult * br)
|
|||||||
bool won = br->winner == myCb->battleGetMySide();
|
bool won = br->winner == myCb->battleGetMySide();
|
||||||
logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.getStr(), (won ? "won" : "lost"), battlename);
|
logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.getStr(), (won ? "won" : "lost"), battlename);
|
||||||
battlename.clear();
|
battlename.clear();
|
||||||
CAdventureAI::battleEnd(br);
|
status.addQuery(queryID, "Combat result dialog");
|
||||||
|
const int confirmAction = 0;
|
||||||
|
requestActionASAP([=]()
|
||||||
|
{
|
||||||
|
answerQuery(queryID, confirmAction);
|
||||||
|
});
|
||||||
|
CAdventureAI::battleEnd(br, queryID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIGateway::waitTillFree()
|
void AIGateway::waitTillFree()
|
||||||
|
@ -169,7 +169,7 @@ public:
|
|||||||
boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) override;
|
boost::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) override;
|
||||||
void battleEnd(const BattleResult * br) override;
|
void battleEnd(const BattleResult * br, QueryID queryID) override;
|
||||||
|
|
||||||
void makeTurn();
|
void makeTurn();
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ void CStupidAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bs
|
|||||||
print("battleStacksAttacked called");
|
print("battleStacksAttacked called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStupidAI::battleEnd(const BattleResult *br)
|
void CStupidAI::battleEnd(const BattleResult *br, QueryID queryID)
|
||||||
{
|
{
|
||||||
print("battleEnd called");
|
print("battleEnd called");
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ public:
|
|||||||
|
|
||||||
void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack
|
void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack
|
||||||
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack())
|
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack())
|
||||||
void battleEnd(const BattleResult *br) override;
|
void battleEnd(const BattleResult *br, QueryID queryID) override;
|
||||||
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
||||||
void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
||||||
void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
||||||
|
@ -1580,7 +1580,7 @@ void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, i
|
|||||||
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side);
|
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCAI::battleEnd(const BattleResult * br)
|
void VCAI::battleEnd(const BattleResult * br, QueryID queryID)
|
||||||
{
|
{
|
||||||
NET_EVENT_HANDLER;
|
NET_EVENT_HANDLER;
|
||||||
assert(status.getBattle() == ONGOING_BATTLE);
|
assert(status.getBattle() == ONGOING_BATTLE);
|
||||||
@ -1588,7 +1588,13 @@ void VCAI::battleEnd(const BattleResult * br)
|
|||||||
bool won = br->winner == myCb->battleGetMySide();
|
bool won = br->winner == myCb->battleGetMySide();
|
||||||
logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.getStr(), (won ? "won" : "lost"), battlename);
|
logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.getStr(), (won ? "won" : "lost"), battlename);
|
||||||
battlename.clear();
|
battlename.clear();
|
||||||
CAdventureAI::battleEnd(br);
|
status.addQuery(queryID, "Combat result dialog");
|
||||||
|
const int confirmAction = 0;
|
||||||
|
requestActionASAP([=]()
|
||||||
|
{
|
||||||
|
answerQuery(queryID, confirmAction);
|
||||||
|
});
|
||||||
|
CAdventureAI::battleEnd(br, queryID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCAI::waitTillFree()
|
void VCAI::waitTillFree()
|
||||||
|
@ -201,7 +201,7 @@ public:
|
|||||||
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) 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) override;
|
||||||
void battleEnd(const BattleResult * br) override;
|
void battleEnd(const BattleResult * br, QueryID queryID) override;
|
||||||
|
|
||||||
void makeTurn();
|
void makeTurn();
|
||||||
void mainLoop();
|
void mainLoop();
|
||||||
|
@ -98,6 +98,8 @@
|
|||||||
"vcmi.battleWindow.damageEstimation.kills" : "%d will perish",
|
"vcmi.battleWindow.damageEstimation.kills" : "%d will perish",
|
||||||
"vcmi.battleWindow.damageEstimation.kills.1" : "%d will perish",
|
"vcmi.battleWindow.damageEstimation.kills.1" : "%d will perish",
|
||||||
|
|
||||||
|
"vcmi.battleResultsWindow.applyResultsLabel" : "Apply battle result",
|
||||||
|
|
||||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Show Available Creatures",
|
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Show Available Creatures",
|
||||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Show Available Creatures}\n\nShow the number of creatures available to purchase instead of their growth in town summary (bottom-left corner of town screen).",
|
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Show Available Creatures}\n\nShow the number of creatures available to purchase instead of their growth in town summary (bottom-left corner of town screen).",
|
||||||
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "Show Weekly Growth of Creatures",
|
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "Show Weekly Growth of Creatures",
|
||||||
|
@ -693,7 +693,15 @@ void CPlayerInterface::battleStartBefore(const CCreatureSet *army1, const CCreat
|
|||||||
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)
|
||||||
{
|
{
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
if (settings["adventure"]["quickCombat"].Bool())
|
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
|
||||||
|
&& settings["adventure"]["quickCombat"].Bool())
|
||||||
|
|| settings["adventure"]["alwaysSkipCombat"].Bool())
|
||||||
{
|
{
|
||||||
autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String());
|
autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String());
|
||||||
autofightingAI->initBattleInterface(env, cb);
|
autofightingAI->initBattleInterface(env, cb);
|
||||||
@ -702,6 +710,7 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
|
|||||||
cb->registerBattleInterface(autofightingAI);
|
cb->registerBattleInterface(autofightingAI);
|
||||||
// Player shouldn't be able to move on adventure map if quick combat is going
|
// Player shouldn't be able to move on adventure map if quick combat is going
|
||||||
adventureInt->quickCombatLock();
|
adventureInt->quickCombatLock();
|
||||||
|
allowBattleReplay = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Don't wait for dialogs when we are non-active hot-seat player
|
//Don't wait for dialogs when we are non-active hot-seat player
|
||||||
@ -889,7 +898,7 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlayerInterface::battleEnd(const BattleResult *br)
|
void CPlayerInterface::battleEnd(const BattleResult *br, QueryID queryID)
|
||||||
{
|
{
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
if(isAutoFightOn || autofightingAI)
|
if(isAutoFightOn || autofightingAI)
|
||||||
@ -900,7 +909,14 @@ void CPlayerInterface::battleEnd(const BattleResult *br)
|
|||||||
|
|
||||||
if(!battleInt)
|
if(!battleInt)
|
||||||
{
|
{
|
||||||
GH.pushIntT<BattleResultWindow>(*br, *this);
|
bool allowManualReplay = allowBattleReplay && !settings["adventure"]["alwaysSkipCombat"].Bool();
|
||||||
|
allowBattleReplay = false;
|
||||||
|
auto wnd = std::make_shared<BattleResultWindow>(*br, *this, allowManualReplay);
|
||||||
|
wnd->resultCallback = [=](ui32 selection)
|
||||||
|
{
|
||||||
|
cb->selectionMade(selection, queryID);
|
||||||
|
};
|
||||||
|
GH.pushInt(wnd);
|
||||||
// #1490 - during AI turn when quick combat is on, we need to display the message and wait for user to close it.
|
// #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.
|
// Otherwise NewTurn causes freeze.
|
||||||
waitWhileDialog();
|
waitWhileDialog();
|
||||||
@ -911,7 +927,7 @@ void CPlayerInterface::battleEnd(const BattleResult *br)
|
|||||||
|
|
||||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
BATTLE_EVENT_POSSIBLE_RETURN;
|
||||||
|
|
||||||
battleInt->battleFinished(*br);
|
battleInt->battleFinished(*br, queryID);
|
||||||
adventureInt->quickCombatUnlock();
|
adventureInt->quickCombatUnlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +123,8 @@ public:
|
|||||||
//During battle is quick combat mode is used
|
//During battle is quick combat mode is used
|
||||||
std::shared_ptr<CBattleGameInterface> autofightingAI; //AI that makes decisions
|
std::shared_ptr<CBattleGameInterface> autofightingAI; //AI that makes decisions
|
||||||
bool isAutoFightOn; //Flag, switch it to stop quick combat. Don't touch if there is no battle interface.
|
bool isAutoFightOn; //Flag, switch it to stop quick combat. Don't touch if there is no battle interface.
|
||||||
|
bool allowBattleReplay = false;
|
||||||
|
std::pair<const CCreatureSet *, const CCreatureSet *> lastBattleArmies;
|
||||||
|
|
||||||
struct SpellbookLastSetting
|
struct SpellbookLastSetting
|
||||||
{
|
{
|
||||||
@ -210,7 +212,7 @@ public:
|
|||||||
void actionStarted(const BattleAction& action) override;//occurs BEFORE action taken by active stack or by the hero
|
void actionStarted(const BattleAction& action) override;//occurs BEFORE action taken by active stack or by the hero
|
||||||
BattleAction activeStack(const CStack * stack) override; //called when it's turn of that stack
|
BattleAction activeStack(const CStack * stack) override; //called when it's turn of that stack
|
||||||
void battleAttack(const BattleAttack *ba) override; //stack performs attack
|
void battleAttack(const BattleAttack *ba) override; //stack performs attack
|
||||||
void battleEnd(const BattleResult *br) override; //end of battle
|
void battleEnd(const BattleResult *br, QueryID queryID) override; //end of battle
|
||||||
void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied; used for HP regen handling
|
void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied; used for HP regen handling
|
||||||
void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
||||||
void battleLogMessage(const std::vector<MetaString> & lines) override;
|
void battleLogMessage(const std::vector<MetaString> & lines) override;
|
||||||
|
@ -558,13 +558,29 @@ void CClient::battleStarted(const BattleInfo * info)
|
|||||||
auto & leftSide = info->sides[0], & rightSide = info->sides[1];
|
auto & leftSide = info->sides[0], & rightSide = info->sides[1];
|
||||||
|
|
||||||
//If quick combat is not, do not prepare interfaces for battleint
|
//If quick combat is not, do not prepare interfaces for battleint
|
||||||
if(!settings["adventure"]["quickCombat"].Bool())
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
callBattleStart(leftSide.color, 0);
|
||||||
|
callBattleStart(rightSide.color, 1);
|
||||||
|
callBattleStart(PlayerColor::UNFLAGGABLE, 1);
|
||||||
|
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
||||||
|
callBattleStart(PlayerColor::SPECTATOR, 1);
|
||||||
|
|
||||||
if(vstd::contains(playerint, leftSide.color) && playerint[leftSide.color]->human)
|
if(vstd::contains(playerint, leftSide.color) && playerint[leftSide.color]->human)
|
||||||
att = std::dynamic_pointer_cast<CPlayerInterface>(playerint[leftSide.color]);
|
att = std::dynamic_pointer_cast<CPlayerInterface>(playerint[leftSide.color]);
|
||||||
|
|
||||||
if(vstd::contains(playerint, rightSide.color) && playerint[rightSide.color]->human)
|
if(vstd::contains(playerint, rightSide.color) && playerint[rightSide.color]->human)
|
||||||
def = std::dynamic_pointer_cast<CPlayerInterface>(playerint[rightSide.color]);
|
def = std::dynamic_pointer_cast<CPlayerInterface>(playerint[rightSide.color]);
|
||||||
|
|
||||||
|
//Remove player interfaces for auto battle (quickCombat option)
|
||||||
|
if(att && att->isAutoFightOn)
|
||||||
|
{
|
||||||
|
att.reset();
|
||||||
|
def.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!settings["session"]["headless"].Bool())
|
if(!settings["session"]["headless"].Bool())
|
||||||
@ -584,18 +600,6 @@ 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);
|
|
||||||
};
|
|
||||||
|
|
||||||
callBattleStart(leftSide.color, 0);
|
|
||||||
callBattleStart(rightSide.color, 1);
|
|
||||||
callBattleStart(PlayerColor::UNFLAGGABLE, 1);
|
|
||||||
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
|
||||||
callBattleStart(PlayerColor::SPECTATOR, 1);
|
|
||||||
|
|
||||||
if(info->tacticDistance && vstd::contains(battleints, info->sides[info->tacticsSide].color))
|
if(info->tacticDistance && vstd::contains(battleints, info->sides[info->tacticsSide].color))
|
||||||
{
|
{
|
||||||
PlayerColor color = info->sides[info->tacticsSide].color;
|
PlayerColor color = info->sides[info->tacticsSide].color;
|
||||||
@ -632,6 +636,7 @@ void CClient::battleFinished()
|
|||||||
battleCallbacks[PlayerColor::SPECTATOR]->setBattle(nullptr);
|
battleCallbacks[PlayerColor::SPECTATOR]->setBattle(nullptr);
|
||||||
|
|
||||||
setBattle(nullptr);
|
setBattle(nullptr);
|
||||||
|
gs->curB.dellNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::startPlayerBattleAction(PlayerColor color)
|
void CClient::startPlayerBattleAction(PlayerColor color)
|
||||||
|
@ -737,7 +737,7 @@ void ApplyFirstClientNetPackVisitor::visitBattleUpdateGateState(BattleUpdateGate
|
|||||||
|
|
||||||
void ApplyFirstClientNetPackVisitor::visitBattleResult(BattleResult & pack)
|
void ApplyFirstClientNetPackVisitor::visitBattleResult(BattleResult & pack)
|
||||||
{
|
{
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleEnd, &pack);
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleEnd, &pack, pack.queryID);
|
||||||
cl.battleFinished();
|
cl.battleFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,7 +308,7 @@ void BattleInterface::gateStateChanged(const EGateState state)
|
|||||||
siegeController->gateStateChanged(state);
|
siegeController->gateStateChanged(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleInterface::battleFinished(const BattleResult& br)
|
void BattleInterface::battleFinished(const BattleResult& br, QueryID queryID)
|
||||||
{
|
{
|
||||||
checkForAnimations();
|
checkForAnimations();
|
||||||
stacksController->setActiveStack(nullptr);
|
stacksController->setActiveStack(nullptr);
|
||||||
@ -318,11 +318,18 @@ void BattleInterface::battleFinished(const BattleResult& br)
|
|||||||
|
|
||||||
if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-skip-battle-result"].Bool())
|
if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-skip-battle-result"].Bool())
|
||||||
{
|
{
|
||||||
|
curInt->cb->selectionMade(0, queryID);
|
||||||
windowObject->close();
|
windowObject->close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GH.pushInt(std::make_shared<BattleResultWindow>(br, *(this->curInt)));
|
auto wnd = std::make_shared<BattleResultWindow>(br, *(this->curInt));
|
||||||
|
wnd->resultCallback = [=](ui32 selection)
|
||||||
|
{
|
||||||
|
curInt->cb->selectionMade(selection, queryID);
|
||||||
|
};
|
||||||
|
GH.pushInt(wnd);
|
||||||
|
|
||||||
curInt->waitWhileDialog(); // Avoid freeze when AI end turn after battle. Check bug #1897
|
curInt->waitWhileDialog(); // Avoid freeze when AI end turn after battle. Check bug #1897
|
||||||
CPlayerInterface::battleInt = nullptr;
|
CPlayerInterface::battleInt = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ public:
|
|||||||
void newRoundFirst( int round );
|
void newRoundFirst( int round );
|
||||||
void newRound(int number); //caled when round is ended; number is the number of round
|
void newRound(int number); //caled when round is ended; number is the number of round
|
||||||
void stackIsCatapulting(const CatapultAttack & ca); //called when a stack is attacking walls
|
void stackIsCatapulting(const CatapultAttack & ca); //called when a stack is attacking walls
|
||||||
void battleFinished(const BattleResult& br); //called when battle is finished - battleresult window should be printed
|
void battleFinished(const BattleResult& br, QueryID queryID); //called when battle is finished - battleresult window should be printed
|
||||||
void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell
|
void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell
|
||||||
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
|
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
|
||||||
void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
|
void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
|
||||||
|
@ -397,7 +397,7 @@ HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position)
|
|||||||
labels.push_back(std::make_shared<CLabel>(39, 186, EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(currentSpellPoints) + "/" + std::to_string(maxSpellPoints)));
|
labels.push_back(std::make_shared<CLabel>(39, 186, EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(currentSpellPoints) + "/" + std::to_string(maxSpellPoints)));
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface & _owner)
|
BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface & _owner, bool allowReplay)
|
||||||
: owner(_owner)
|
: owner(_owner)
|
||||||
{
|
{
|
||||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||||
@ -409,6 +409,13 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
|
|||||||
exit = std::make_shared<CButton>(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, SDLK_RETURN);
|
exit = std::make_shared<CButton>(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, SDLK_RETURN);
|
||||||
exit->setBorderColor(Colors::METALLIC_GOLD);
|
exit->setBorderColor(Colors::METALLIC_GOLD);
|
||||||
|
|
||||||
|
if(allowReplay)
|
||||||
|
{
|
||||||
|
repeat = std::make_shared<CButton>(Point(24, 505), "icn6432.def", std::make_pair("", ""), [&](){ bRepeatf();}, SDLK_ESCAPE);
|
||||||
|
repeat->setBorderColor(Colors::METALLIC_GOLD);
|
||||||
|
labels.push_back(std::make_shared<CLabel>(232, 520, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("vcmi.battleResultsWindow.applyResultsLabel")));
|
||||||
|
}
|
||||||
|
|
||||||
if(br.winner == 0) //attacker won
|
if(br.winner == 0) //attacker won
|
||||||
{
|
{
|
||||||
labels.push_back(std::make_shared<CLabel>(59, 124, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[410]));
|
labels.push_back(std::make_shared<CLabel>(59, 124, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[410]));
|
||||||
@ -569,8 +576,9 @@ void BattleResultWindow::show(SDL_Surface * to)
|
|||||||
CCS->videoh->update(pos.x + 107, pos.y + 70, to, true, false);
|
CCS->videoh->update(pos.x + 107, pos.y + 70, to, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleResultWindow::bExitf()
|
void BattleResultWindow::buttonPressed(int button)
|
||||||
{
|
{
|
||||||
|
resultCallback(button);
|
||||||
CPlayerInterface &intTmp = owner; //copy reference because "this" will be destructed soon
|
CPlayerInterface &intTmp = owner; //copy reference because "this" will be destructed soon
|
||||||
|
|
||||||
close();
|
close();
|
||||||
@ -584,6 +592,16 @@ void BattleResultWindow::bExitf()
|
|||||||
CCS->videoh->close();
|
CCS->videoh->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BattleResultWindow::bExitf()
|
||||||
|
{
|
||||||
|
buttonPressed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleResultWindow::bRepeatf()
|
||||||
|
{
|
||||||
|
buttonPressed(1);
|
||||||
|
}
|
||||||
|
|
||||||
StackQueue::StackQueue(bool Embedded, BattleInterface & owner)
|
StackQueue::StackQueue(bool Embedded, BattleInterface & owner)
|
||||||
: embedded(Embedded),
|
: embedded(Embedded),
|
||||||
owner(owner)
|
owner(owner)
|
||||||
|
@ -143,13 +143,18 @@ private:
|
|||||||
std::shared_ptr<CPicture> background;
|
std::shared_ptr<CPicture> background;
|
||||||
std::vector<std::shared_ptr<CLabel>> labels;
|
std::vector<std::shared_ptr<CLabel>> labels;
|
||||||
std::shared_ptr<CButton> exit;
|
std::shared_ptr<CButton> exit;
|
||||||
|
std::shared_ptr<CButton> repeat;
|
||||||
std::vector<std::shared_ptr<CAnimImage>> icons;
|
std::vector<std::shared_ptr<CAnimImage>> icons;
|
||||||
std::shared_ptr<CTextBox> description;
|
std::shared_ptr<CTextBox> description;
|
||||||
CPlayerInterface & owner;
|
CPlayerInterface & owner;
|
||||||
|
|
||||||
|
void buttonPressed(int button); //internal function for button callbacks
|
||||||
public:
|
public:
|
||||||
BattleResultWindow(const BattleResult & br, CPlayerInterface & _owner);
|
BattleResultWindow(const BattleResult & br, CPlayerInterface & _owner, bool allowReplay = false);
|
||||||
|
|
||||||
void bExitf(); //exit button callback
|
void bExitf(); //exit button callback
|
||||||
|
void bRepeatf(); //repeat button callback
|
||||||
|
std::function<void(int result)> resultCallback; //callback receiving which button was pressed
|
||||||
|
|
||||||
void activate() override;
|
void activate() override;
|
||||||
void show(SDL_Surface * to = 0) override;
|
void show(SDL_Surface * to = 0) override;
|
||||||
|
@ -357,14 +357,14 @@ void CGStatusBar::setEnteringMode(bool on)
|
|||||||
|
|
||||||
if (on)
|
if (on)
|
||||||
{
|
{
|
||||||
assert(enteringText == false);
|
//assert(enteringText == false);
|
||||||
alignment = ETextAlignment::TOPLEFT;
|
alignment = ETextAlignment::TOPLEFT;
|
||||||
GH.startTextInput(pos);
|
GH.startTextInput(pos);
|
||||||
setText(consoleText);
|
setText(consoleText);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert(enteringText == true);
|
//assert(enteringText == true);
|
||||||
alignment = ETextAlignment::CENTER;
|
alignment = ETextAlignment::CENTER;
|
||||||
GH.stopTextInput();
|
GH.stopTextInput();
|
||||||
setText(hoverText);
|
setText(hoverText);
|
||||||
|
@ -182,7 +182,7 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"default": {},
|
"default": {},
|
||||||
"required" : [ "heroMoveTime", "enemyMoveTime", "scrollSpeedPixels", "heroReminder", "quickCombat", "objectAnimation", "terrainAnimation" ],
|
"required" : [ "heroMoveTime", "enemyMoveTime", "scrollSpeedPixels", "heroReminder", "quickCombat", "objectAnimation", "terrainAnimation", "alwaysSkipCombat" ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"heroMoveTime" : {
|
"heroMoveTime" : {
|
||||||
"type" : "number",
|
"type" : "number",
|
||||||
@ -211,6 +211,10 @@
|
|||||||
"terrainAnimation" : {
|
"terrainAnimation" : {
|
||||||
"type" : "boolean",
|
"type" : "boolean",
|
||||||
"default" : true
|
"default" : true
|
||||||
|
},
|
||||||
|
"alwaysSkipCombat" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"default" : false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -230,9 +230,9 @@ void CAdventureAI::battleSpellCast(const BattleSpellCast * sc)
|
|||||||
battleAI->battleSpellCast(sc);
|
battleAI->battleSpellCast(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::battleEnd(const BattleResult * br)
|
void CAdventureAI::battleEnd(const BattleResult * br, QueryID queryID)
|
||||||
{
|
{
|
||||||
battleAI->battleEnd(br);
|
battleAI->battleEnd(br, queryID);
|
||||||
battleAI.reset();
|
battleAI.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ public:
|
|||||||
virtual void battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override;
|
virtual void battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override;
|
||||||
virtual void battleAttack(const BattleAttack *ba) override;
|
virtual void battleAttack(const BattleAttack *ba) override;
|
||||||
virtual void battleSpellCast(const BattleSpellCast *sc) override;
|
virtual void battleSpellCast(const BattleSpellCast *sc) override;
|
||||||
virtual void battleEnd(const BattleResult *br) override;
|
virtual void battleEnd(const BattleResult *br, QueryID queryID) override;
|
||||||
virtual void battleUnitsChanged(const std::vector<UnitChanges> & units) override;
|
virtual void battleUnitsChanged(const std::vector<UnitChanges> & units) override;
|
||||||
|
|
||||||
virtual void saveGame(BinarySerializer & h, const int version) override;
|
virtual void saveGame(BinarySerializer & h, const int version) override;
|
||||||
|
@ -60,7 +60,7 @@ public:
|
|||||||
virtual void actionStarted(const BattleAction &action){};//occurs BEFORE every action taken by any stack or by the hero
|
virtual void actionStarted(const BattleAction &action){};//occurs BEFORE every action taken by any stack or by the hero
|
||||||
virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack
|
virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack
|
||||||
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged){}; //called when stack receives damage (after battleAttack())
|
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged){}; //called when stack receives damage (after battleAttack())
|
||||||
virtual void battleEnd(const BattleResult *br){};
|
virtual void battleEnd(const BattleResult *br, QueryID queryID){};
|
||||||
virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied;
|
virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied;
|
||||||
virtual void battleNewRound(int round){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
virtual void battleNewRound(int round){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
||||||
virtual void battleLogMessage(const std::vector<MetaString> & lines){};
|
virtual void battleLogMessage(const std::vector<MetaString> & lines){};
|
||||||
|
@ -1474,12 +1474,31 @@ struct DLL_LINKAGE BattleSetActiveStack : public CPackForClient
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DLL_LINKAGE BattleResult : public CPackForClient
|
struct DLL_LINKAGE BattleResultAccepted : public CPackForClient
|
||||||
|
{
|
||||||
|
void applyGs(CGameState * gs) const;
|
||||||
|
|
||||||
|
CGHeroInstance * hero1 = nullptr;
|
||||||
|
CGHeroInstance * hero2 = nullptr;
|
||||||
|
CArmedInstance * army1 = nullptr;
|
||||||
|
CArmedInstance * army2 = nullptr;
|
||||||
|
TExpType exp[2];
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & hero1;
|
||||||
|
h & hero2;
|
||||||
|
h & army1;
|
||||||
|
h & army2;
|
||||||
|
h & exp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DLL_LINKAGE BattleResult : public Query
|
||||||
{
|
{
|
||||||
enum EResult { NORMAL = 0, ESCAPE = 1, SURRENDER = 2 };
|
enum EResult { NORMAL = 0, ESCAPE = 1, SURRENDER = 2 };
|
||||||
|
|
||||||
void applyFirstCl(CClient * cl);
|
void applyFirstCl(CClient * cl);
|
||||||
void applyGs(CGameState * gs);
|
|
||||||
|
|
||||||
EResult result = NORMAL;
|
EResult result = NORMAL;
|
||||||
ui8 winner = 2; //0 - attacker, 1 - defender, [2 - draw (should be possible?)]
|
ui8 winner = 2; //0 - attacker, 1 - defender, [2 - draw (should be possible?)]
|
||||||
@ -1491,6 +1510,7 @@ struct DLL_LINKAGE BattleResult : public CPackForClient
|
|||||||
|
|
||||||
template <typename Handler> void serialize(Handler & h, const int version)
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
|
h & queryID;
|
||||||
h & result;
|
h & result;
|
||||||
h & winner;
|
h & winner;
|
||||||
h & casualties[0];
|
h & casualties[0];
|
||||||
|
@ -2195,15 +2195,11 @@ void BattleUpdateGateState::applyGs(CGameState * gs) const
|
|||||||
gs->curB->si.gateState = state;
|
gs->curB->si.gateState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleResult::applyGs(CGameState *gs)
|
void BattleResultAccepted::applyGs(CGameState * gs) const
|
||||||
{
|
{
|
||||||
for (auto & elem : gs->curB->stacks)
|
for(auto * h : {hero1, hero2})
|
||||||
delete elem;
|
|
||||||
|
|
||||||
|
|
||||||
for(int i = 0; i < 2; ++i)
|
|
||||||
{
|
{
|
||||||
if(auto * h = gs->curB->battleGetFightingHero(i))
|
if(h)
|
||||||
{
|
{
|
||||||
h->removeBonusesRecursive(Bonus::OneBattle); //remove any "until next battle" bonuses
|
h->removeBonusesRecursive(Bonus::OneBattle); //remove any "until next battle" bonuses
|
||||||
if (h->commander && h->commander->alive)
|
if (h->commander && h->commander->alive)
|
||||||
@ -2219,15 +2215,17 @@ void BattleResult::applyGs(CGameState *gs)
|
|||||||
if(VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
|
if(VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
|
||||||
{
|
{
|
||||||
for(int i = 0; i < 2; i++)
|
for(int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
if(exp[i])
|
if(exp[i])
|
||||||
gs->curB->battleGetArmyObject(i)->giveStackExp(exp[i]);
|
{
|
||||||
|
if(auto * army = (i == 0 ? army1 : army2))
|
||||||
|
army->giveStackExp(exp[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CBonusSystemNode::treeHasChanged();
|
CBonusSystemNode::treeHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < 2; i++)
|
|
||||||
gs->curB->battleGetArmyObject(i)->battle = nullptr;
|
|
||||||
|
|
||||||
gs->curB.dellNull();
|
gs->curB.dellNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,6 +205,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
|
|||||||
curB->battlefieldType = battlefieldType;
|
curB->battlefieldType = battlefieldType;
|
||||||
curB->round = -2;
|
curB->round = -2;
|
||||||
curB->activeStack = -1;
|
curB->activeStack = -1;
|
||||||
|
curB->creatureBank = creatureBank;
|
||||||
|
|
||||||
if(town)
|
if(town)
|
||||||
{
|
{
|
||||||
@ -561,7 +562,15 @@ BattleInfo::BattleInfo():
|
|||||||
setNodeType(BATTLE);
|
setNodeType(BATTLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleInfo::~BattleInfo() = default;
|
BattleInfo::~BattleInfo()
|
||||||
|
{
|
||||||
|
for (auto & elem : stacks)
|
||||||
|
delete elem;
|
||||||
|
|
||||||
|
for(int i = 0; i < 2; i++)
|
||||||
|
if(auto * _armyObj = battleGetArmyObject(i))
|
||||||
|
_armyObj->battle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
int32_t BattleInfo::getActiveStackID() const
|
int32_t BattleInfo::getActiveStackID() const
|
||||||
{
|
{
|
||||||
|
@ -34,6 +34,7 @@ public:
|
|||||||
si32 round, activeStack;
|
si32 round, activeStack;
|
||||||
const CGTownInstance * town; //used during town siege, nullptr if this is not a siege (note that fortless town IS also a siege)
|
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
|
int3 tile; //for background and bonuses
|
||||||
|
bool creatureBank; //auxilary field, do not serialize
|
||||||
std::vector<CStack*> stacks;
|
std::vector<CStack*> stacks;
|
||||||
std::vector<std::shared_ptr<CObstacleInstance> > obstacles;
|
std::vector<std::shared_ptr<CObstacleInstance> > obstacles;
|
||||||
SiegeInfo si;
|
SiegeInfo si;
|
||||||
|
@ -265,6 +265,7 @@ void registerTypesClientPacks2(Serializer &s)
|
|||||||
s.template registerType<CPackForClient, BattleNextRound>();
|
s.template registerType<CPackForClient, BattleNextRound>();
|
||||||
s.template registerType<CPackForClient, BattleSetActiveStack>();
|
s.template registerType<CPackForClient, BattleSetActiveStack>();
|
||||||
s.template registerType<CPackForClient, BattleResult>();
|
s.template registerType<CPackForClient, BattleResult>();
|
||||||
|
s.template registerType<CPackForClient, BattleResultAccepted>();
|
||||||
s.template registerType<CPackForClient, BattleLogMessage>();
|
s.template registerType<CPackForClient, BattleLogMessage>();
|
||||||
s.template registerType<CPackForClient, BattleStackMoved>();
|
s.template registerType<CPackForClient, BattleStackMoved>();
|
||||||
s.template registerType<CPackForClient, BattleAttack>();
|
s.template registerType<CPackForClient, BattleAttack>();
|
||||||
|
@ -585,28 +585,13 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
if(heroDefender)
|
if(heroDefender)
|
||||||
battleResult.data->exp[1] = heroDefender->calculateXp(battleResult.data->exp[1]);
|
battleResult.data->exp[1] = heroDefender->calculateXp(battleResult.data->exp[1]);
|
||||||
|
|
||||||
const CArmedInstance *bEndArmy1 = gs->curB->sides.at(0).armyObject;
|
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(queries.topQuery(gs->curB->sides[0].color));
|
||||||
const CArmedInstance *bEndArmy2 = gs->curB->sides.at(1).armyObject;
|
|
||||||
const BattleResult::EResult result = battleResult.get()->result;
|
|
||||||
|
|
||||||
auto findBattleQuery = [this]() -> std::shared_ptr<CBattleQuery>
|
|
||||||
{
|
|
||||||
for (auto &q : queries.allQueries())
|
|
||||||
{
|
|
||||||
if (auto bq = std::dynamic_pointer_cast<CBattleQuery>(q))
|
|
||||||
if (bq->bi == gs->curB)
|
|
||||||
return bq;
|
|
||||||
}
|
|
||||||
return std::shared_ptr<CBattleQuery>();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto battleQuery = findBattleQuery();
|
|
||||||
if (!battleQuery)
|
if (!battleQuery)
|
||||||
{
|
{
|
||||||
logGlobal->error("Cannot find battle query!");
|
logGlobal->error("Cannot find battle query!");
|
||||||
|
complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0].color) + " has no battle query at the top!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (battleQuery != queries.topQuery(gs->curB->sides[0].color))
|
|
||||||
complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0].color) + " although in battle has no battle query at the top!");
|
|
||||||
|
|
||||||
battleQuery->result = boost::make_optional(*battleResult.data);
|
battleQuery->result = boost::make_optional(*battleResult.data);
|
||||||
|
|
||||||
@ -614,15 +599,43 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
const int queriedPlayers = battleQuery ? (int)boost::count(queries.allQueries(), battleQuery) : 0;
|
const int queriedPlayers = battleQuery ? (int)boost::count(queries.allQueries(), battleQuery) : 0;
|
||||||
finishingBattle = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
|
finishingBattle = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
|
||||||
|
|
||||||
CasualtiesAfterBattle cab1(bEndArmy1, gs->curB), cab2(bEndArmy2, gs->curB); //calculate casualties before deleting battle
|
auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(this, gs->curB);
|
||||||
|
battleResult.data->queryID = battleDialogQuery->queryID;
|
||||||
|
queries.addQuery(battleDialogQuery);
|
||||||
|
|
||||||
|
//set same battle result for all queries
|
||||||
|
for(auto q : queries.allQueries())
|
||||||
|
{
|
||||||
|
auto otherBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(q);
|
||||||
|
if(otherBattleQuery)
|
||||||
|
otherBattleQuery->result = battleQuery->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendAndApply(battleResult.data); //after this point casualties objects are destroyed
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo)
|
||||||
|
{
|
||||||
|
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(queries.topQuery(battleInfo->sides.at(0).color));
|
||||||
|
if(!battleQuery)
|
||||||
|
{
|
||||||
|
logGlobal->trace("No battle query, battle end was confirmed by another player");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CArmedInstance *bEndArmy1 = battleInfo->sides.at(0).armyObject;
|
||||||
|
const CArmedInstance *bEndArmy2 = battleInfo->sides.at(1).armyObject;
|
||||||
|
const BattleResult::EResult result = battleResult.get()->result;
|
||||||
|
|
||||||
|
CasualtiesAfterBattle cab1(bEndArmy1, battleInfo), cab2(bEndArmy2, battleInfo); //calculate casualties before deleting battle
|
||||||
ChangeSpells cs; //for Eagle Eye
|
ChangeSpells cs; //for Eagle Eye
|
||||||
|
|
||||||
if (finishingBattle->winnerHero)
|
if(!finishingBattle->isDraw() && finishingBattle->winnerHero)
|
||||||
{
|
{
|
||||||
if (int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(Bonus::LEARN_BATTLE_SPELL_LEVEL_LIMIT, -1))
|
if (int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(Bonus::LEARN_BATTLE_SPELL_LEVEL_LIMIT, -1))
|
||||||
{
|
{
|
||||||
double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::LEARN_BATTLE_SPELL_CHANCE, 0);
|
double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::LEARN_BATTLE_SPELL_CHANCE, 0);
|
||||||
for(auto & spellId : gs->curB->sides.at(!battleResult.data->winner).usedSpellsHistory)
|
for(auto & spellId : battleInfo->sides.at(!battleResult.data->winner).usedSpellsHistory)
|
||||||
{
|
{
|
||||||
auto spell = spellId.toSpell(VLC->spells());
|
auto spell = spellId.toSpell(VLC->spells());
|
||||||
if(spell && spell->getLevel() <= eagleEyeLevel && !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId()) && getRandomGenerator().nextInt(99) < eagleEyeChance)
|
if(spell && spell->getLevel() <= eagleEyeLevel && !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId()) && getRandomGenerator().nextInt(99) < eagleEyeChance)
|
||||||
@ -632,7 +645,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
}
|
}
|
||||||
std::vector<const CArtifactInstance *> arts; //display them in window
|
std::vector<const CArtifactInstance *> arts; //display them in window
|
||||||
|
|
||||||
if (result == BattleResult::NORMAL && finishingBattle->winnerHero)
|
if(result == BattleResult::NORMAL && !finishingBattle->isDraw() && finishingBattle->winnerHero)
|
||||||
{
|
{
|
||||||
auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma)
|
auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma)
|
||||||
{
|
{
|
||||||
@ -690,7 +703,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks)
|
for (auto armySlot : battleInfo->sides.at(!battleResult.data->winner).armyObject->stacks)
|
||||||
{
|
{
|
||||||
auto artifactsWorn = armySlot.second->artifactsWorn;
|
auto artifactsWorn = armySlot.second->artifactsWorn;
|
||||||
for (auto artSlot : artifactsWorn)
|
for (auto artSlot : artifactsWorn)
|
||||||
@ -705,7 +718,6 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendAndApply(battleResult.data); //after this point casualties objects are destroyed
|
|
||||||
|
|
||||||
if (arts.size()) //display loot
|
if (arts.size()) //display loot
|
||||||
{
|
{
|
||||||
@ -768,41 +780,48 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
cab1.updateArmy(this);
|
cab1.updateArmy(this);
|
||||||
cab2.updateArmy(this); //take casualties after battle is deleted
|
cab2.updateArmy(this); //take casualties after battle is deleted
|
||||||
|
|
||||||
if(battleResult.data->winner != BattleSide::ATTACKER && heroAttacker) //remove beaten Attacker
|
if(finishingBattle->loserHero) //remove beaten hero
|
||||||
{
|
{
|
||||||
RemoveObject ro(heroAttacker->id);
|
RemoveObject ro(finishingBattle->loserHero->id);
|
||||||
sendAndApply(&ro);
|
sendAndApply(&ro);
|
||||||
}
|
}
|
||||||
|
if(finishingBattle->isDraw() && finishingBattle->winnerHero) //for draw case both heroes should be removed
|
||||||
if(battleResult.data->winner != BattleSide::DEFENDER && heroDefender) //remove beaten Defender
|
|
||||||
{
|
{
|
||||||
RemoveObject ro(heroDefender->id);
|
RemoveObject ro(finishingBattle->winnerHero->id);
|
||||||
sendAndApply(&ro);
|
sendAndApply(&ro);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(battleResult.data->winner == BattleSide::DEFENDER
|
if(battleResult.data->winner == BattleSide::DEFENDER
|
||||||
&& heroDefender
|
&& finishingBattle->winnerHero
|
||||||
&& heroDefender->visitedTown
|
&& finishingBattle->winnerHero->visitedTown
|
||||||
&& !heroDefender->inTownGarrison
|
&& !finishingBattle->winnerHero->inTownGarrison
|
||||||
&& heroDefender->visitedTown->garrisonHero == heroDefender)
|
&& finishingBattle->winnerHero->visitedTown->garrisonHero == finishingBattle->winnerHero)
|
||||||
{
|
{
|
||||||
swapGarrisonOnSiege(heroDefender->visitedTown->id); //return defending visitor from garrison to its rightful place
|
swapGarrisonOnSiege(finishingBattle->winnerHero->visitedTown->id); //return defending visitor from garrison to its rightful place
|
||||||
}
|
}
|
||||||
//give exp
|
//give exp
|
||||||
if(battleResult.data->exp[0] && heroAttacker && battleResult.get()->winner == BattleSide::ATTACKER)
|
if(!finishingBattle->isDraw() && battleResult.data->exp[finishingBattle->winnerSide] && finishingBattle->winnerHero)
|
||||||
changePrimSkill(heroAttacker, PrimarySkill::EXPERIENCE, battleResult.data->exp[0]);
|
changePrimSkill(finishingBattle->winnerHero, PrimarySkill::EXPERIENCE, battleResult.data->exp[finishingBattle->winnerSide]);
|
||||||
else if(battleResult.data->exp[1] && heroDefender && battleResult.get()->winner == BattleSide::DEFENDER)
|
|
||||||
changePrimSkill(heroDefender, PrimarySkill::EXPERIENCE, battleResult.data->exp[1]);
|
BattleResultAccepted raccepted;
|
||||||
|
raccepted.army1 = const_cast<CArmedInstance*>(bEndArmy1);
|
||||||
|
raccepted.army2 = const_cast<CArmedInstance*>(bEndArmy2);
|
||||||
|
raccepted.hero1 = const_cast<CGHeroInstance*>(battleInfo->sides.at(0).hero);
|
||||||
|
raccepted.hero2 = const_cast<CGHeroInstance*>(battleInfo->sides.at(1).hero);
|
||||||
|
raccepted.exp[0] = battleResult.data->exp[0];
|
||||||
|
raccepted.exp[1] = battleResult.data->exp[1];
|
||||||
|
sendAndApply(&raccepted);
|
||||||
|
|
||||||
queries.popIfTop(battleQuery);
|
queries.popIfTop(battleQuery);
|
||||||
|
//--> continuation (battleAfterLevelUp) occurs after level-up queries are handled or on removing query
|
||||||
//--> continuation (battleAfterLevelUp) occurs after level-up queries are handled or on removing query (above)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::battleAfterLevelUp(const BattleResult &result)
|
void CGameHandler::battleAfterLevelUp(const BattleResult &result)
|
||||||
{
|
{
|
||||||
LOG_TRACE(logGlobal);
|
LOG_TRACE(logGlobal);
|
||||||
|
|
||||||
|
if(!finishingBattle)
|
||||||
|
return;
|
||||||
|
|
||||||
finishingBattle->remainingBattleQueriesCount--;
|
finishingBattle->remainingBattleQueriesCount--;
|
||||||
logGlobal->trace("Decremented queries count to %d", finishingBattle->remainingBattleQueriesCount);
|
logGlobal->trace("Decremented queries count to %d", finishingBattle->remainingBattleQueriesCount);
|
||||||
@ -885,6 +904,8 @@ void CGameHandler::battleAfterLevelUp(const BattleResult &result)
|
|||||||
sendAndApply(&sah);
|
sendAndApply(&sah);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finishingBattle.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter)
|
void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter)
|
||||||
@ -2598,6 +2619,9 @@ void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedI
|
|||||||
const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank,
|
const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank,
|
||||||
const CGTownInstance *town) //use hero=nullptr for no hero
|
const CGTownInstance *town) //use hero=nullptr for no hero
|
||||||
{
|
{
|
||||||
|
if(gs->curB)
|
||||||
|
gs->curB.dellNull();
|
||||||
|
|
||||||
engageIntoBattle(army1->tempOwner);
|
engageIntoBattle(army1->tempOwner);
|
||||||
engageIntoBattle(army2->tempOwner);
|
engageIntoBattle(army2->tempOwner);
|
||||||
|
|
||||||
@ -2611,7 +2635,35 @@ void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedI
|
|||||||
|
|
||||||
setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
|
setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
|
||||||
|
|
||||||
auto battleQuery = std::make_shared<CBattleQuery>(this, gs->curB);
|
//existing battle query for retying auto-combat
|
||||||
|
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(queries.topQuery(gs->curB->sides[0].color));
|
||||||
|
if(battleQuery)
|
||||||
|
{
|
||||||
|
for(int i : {0, 1})
|
||||||
|
{
|
||||||
|
if(heroes[i])
|
||||||
|
{
|
||||||
|
SetMana restoreInitialMana;
|
||||||
|
restoreInitialMana.val = battleQuery->initialHeroMana[i];
|
||||||
|
restoreInitialMana.hid = heroes[i]->id;
|
||||||
|
sendAndApply(&restoreInitialMana);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
battleQuery->bi = gs->curB;
|
||||||
|
battleQuery->result = boost::none;
|
||||||
|
battleQuery->belligerents[0] = gs->curB->sides[0].armyObject;
|
||||||
|
battleQuery->belligerents[1] = gs->curB->sides[1].armyObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
battleQuery = std::make_shared<CBattleQuery>(this, gs->curB);
|
||||||
|
for(int i : {0, 1})
|
||||||
|
{
|
||||||
|
if(heroes[i])
|
||||||
|
{
|
||||||
|
battleQuery->initialHeroMana[i] = heroes[i]->mana;
|
||||||
|
}
|
||||||
|
}
|
||||||
queries.addQuery(battleQuery);
|
queries.addQuery(battleQuery);
|
||||||
|
|
||||||
this->battleThread = std::make_unique<boost::thread>(boost::thread(&CGameHandler::runBattle, this));
|
this->battleThread = std::make_unique<boost::thread>(boost::thread(&CGameHandler::runBattle, this));
|
||||||
@ -7188,7 +7240,7 @@ void CGameHandler::showInfoDialog(const std::string & msg, PlayerColor player)
|
|||||||
showInfoDialog(&iw);
|
showInfoDialog(&iw);
|
||||||
}
|
}
|
||||||
|
|
||||||
CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat):
|
CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, const BattleInfo * bat):
|
||||||
army(_army)
|
army(_army)
|
||||||
{
|
{
|
||||||
heroWithDeadCommander = ObjectInstanceID();
|
heroWithDeadCommander = ObjectInstanceID();
|
||||||
@ -7340,12 +7392,14 @@ CGameHandler::FinishingBattleHelper::FinishingBattleHelper(std::shared_ptr<const
|
|||||||
loserHero = result.winner != 0 ? info.sides[0].hero : info.sides[1].hero;
|
loserHero = result.winner != 0 ? info.sides[0].hero : info.sides[1].hero;
|
||||||
victor = info.sides[result.winner].color;
|
victor = info.sides[result.winner].color;
|
||||||
loser = info.sides[!result.winner].color;
|
loser = info.sides[!result.winner].color;
|
||||||
|
winnerSide = result.winner;
|
||||||
remainingBattleQueriesCount = RemainingBattleQueriesCount;
|
remainingBattleQueriesCount = RemainingBattleQueriesCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGameHandler::FinishingBattleHelper::FinishingBattleHelper()
|
CGameHandler::FinishingBattleHelper::FinishingBattleHelper()
|
||||||
{
|
{
|
||||||
winnerHero = loserHero = nullptr;
|
winnerHero = loserHero = nullptr;
|
||||||
|
winnerSide = 0;
|
||||||
remainingBattleQueriesCount = 0;
|
remainingBattleQueriesCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ struct CasualtiesAfterBattle
|
|||||||
TSummoned summoned;
|
TSummoned summoned;
|
||||||
ObjectInstanceID heroWithDeadCommander; //TODO: unify stack locations
|
ObjectInstanceID heroWithDeadCommander; //TODO: unify stack locations
|
||||||
|
|
||||||
CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat);
|
CasualtiesAfterBattle(const CArmedInstance * _army, const BattleInfo * bat);
|
||||||
void updateArmy(CGameHandler *gh);
|
void updateArmy(CGameHandler *gh);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -131,6 +131,7 @@ public:
|
|||||||
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
|
||||||
|
void endBattleConfirm(const BattleInfo * battleInfo);
|
||||||
|
|
||||||
void makeAttack(const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter);
|
void makeAttack(const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter);
|
||||||
|
|
||||||
@ -316,8 +317,11 @@ public:
|
|||||||
FinishingBattleHelper();
|
FinishingBattleHelper();
|
||||||
FinishingBattleHelper(std::shared_ptr<const CBattleQuery> Query, int RemainingBattleQueriesCount);
|
FinishingBattleHelper(std::shared_ptr<const CBattleQuery> Query, int RemainingBattleQueriesCount);
|
||||||
|
|
||||||
|
inline bool isDraw() const {return winnerSide == 2;}
|
||||||
|
|
||||||
const CGHeroInstance *winnerHero, *loserHero;
|
const CGHeroInstance *winnerHero, *loserHero;
|
||||||
PlayerColor victor, loser;
|
PlayerColor victor, loser;
|
||||||
|
ui8 winnerSide;
|
||||||
|
|
||||||
int remainingBattleQueriesCount;
|
int remainingBattleQueriesCount;
|
||||||
|
|
||||||
@ -327,6 +331,7 @@ public:
|
|||||||
h & loserHero;
|
h & loserHero;
|
||||||
h & victor;
|
h & victor;
|
||||||
h & loser;
|
h & loser;
|
||||||
|
h & winnerSide;
|
||||||
h & remainingBattleQueriesCount;
|
h & remainingBattleQueriesCount;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -288,7 +288,7 @@ QueryPtr Queries::getQuery(QueryID queryID)
|
|||||||
|
|
||||||
void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
|
void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
|
||||||
{
|
{
|
||||||
assert(result);
|
if(result)
|
||||||
objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
|
objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,6 +318,7 @@ bool CBattleQuery::blocksPack(const CPack * pack) const
|
|||||||
|
|
||||||
void CBattleQuery::onRemoval(PlayerColor color)
|
void CBattleQuery::onRemoval(PlayerColor color)
|
||||||
{
|
{
|
||||||
|
if(result)
|
||||||
gh->battleAfterLevelUp(*result);
|
gh->battleAfterLevelUp(*result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,6 +393,28 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const
|
|||||||
return CDialogQuery::blocksPack(pack);
|
return CDialogQuery::blocksPack(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi):
|
||||||
|
CDialogQuery(owner)
|
||||||
|
{
|
||||||
|
bi = Bi;
|
||||||
|
|
||||||
|
for(auto & side : bi->sides)
|
||||||
|
addPlayer(side.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBattleDialogQuery::onRemoval(PlayerColor color)
|
||||||
|
{
|
||||||
|
assert(answer);
|
||||||
|
if(*answer == 1)
|
||||||
|
{
|
||||||
|
gh->startBattlePrimary(bi->sides[0].armyObject, bi->sides[1].armyObject, bi->tile, bi->sides[0].hero, bi->sides[1].hero, bi->creatureBank, bi->town);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gh->endBattleConfirm(bi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
|
void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
|
||||||
{
|
{
|
||||||
assert(answer);
|
assert(answer);
|
||||||
|
@ -97,6 +97,7 @@ class CBattleQuery : public CGhQuery
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::array<const CArmedInstance *,2> belligerents;
|
std::array<const CArmedInstance *,2> belligerents;
|
||||||
|
std::array<int, 2> initialHeroMana;
|
||||||
|
|
||||||
const BattleInfo *bi;
|
const BattleInfo *bi;
|
||||||
boost::optional<BattleResult> result;
|
boost::optional<BattleResult> result;
|
||||||
@ -145,6 +146,16 @@ public:
|
|||||||
virtual bool blocksPack(const CPack *pack) const override;
|
virtual bool blocksPack(const CPack *pack) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CBattleDialogQuery : public CDialogQuery
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi);
|
||||||
|
|
||||||
|
const BattleInfo * bi;
|
||||||
|
|
||||||
|
virtual void onRemoval(PlayerColor color) override;
|
||||||
|
};
|
||||||
|
|
||||||
//yes/no and component selection dialogs
|
//yes/no and component selection dialogs
|
||||||
class CBlockingDialogQuery : public CDialogQuery
|
class CBlockingDialogQuery : public CDialogQuery
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user