diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 49ec83555..7da1abd3b 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -481,6 +481,13 @@ void AIGateway::heroSecondarySkillChanged(const CGHeroInstance * hero, int which } void AIGateway::battleResultsApplied() +{ + LOG_TRACE(logAi); + NET_EVENT_HANDLER; + assert(status.getBattle() == ENDING_BATTLE); +} + +void AIGateway::battleEnded() { LOG_TRACE(logAi); NET_EVENT_HANDLER; diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index 637c5d1fd..774c90edd 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -141,6 +141,7 @@ public: void heroManaPointsChanged(const CGHeroInstance * hero) override; void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override; void battleResultsApplied() override; + void battleEnded() override; void beforeObjectPropertyChanged(const SetObjectProperty * sop) override; void objectPropertyChanged(const SetObjectProperty * sop) override; void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override; diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 915b89f78..cf980b60a 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -548,6 +548,13 @@ void VCAI::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int } void VCAI::battleResultsApplied() +{ + LOG_TRACE(logAi); + NET_EVENT_HANDLER; + assert(status.getBattle() == ENDING_BATTLE); +} + +void VCAI::battleEnded() { LOG_TRACE(logAi); NET_EVENT_HANDLER; diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 84054cc96..f2229f8cf 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -182,6 +182,7 @@ public: void heroManaPointsChanged(const CGHeroInstance * hero) override; void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override; void battleResultsApplied() override; + void battleEnded() override; void beforeObjectPropertyChanged(const SetObjectProperty * sop) override; void objectPropertyChanged(const SetObjectProperty * sop) override; void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override; diff --git a/client/ClientNetPackVisitors.h b/client/ClientNetPackVisitors.h index 1a2813a02..30828bb48 100644 --- a/client/ClientNetPackVisitors.h +++ b/client/ClientNetPackVisitors.h @@ -86,6 +86,7 @@ public: void visitSetStackEffect(SetStackEffect & pack) override; void visitStacksInjured(StacksInjured & pack) override; void visitBattleResultsApplied(BattleResultsApplied & pack) override; + void visitBattleEnded(BattleEnded & pack) override; void visitBattleUnitsChanged(BattleUnitsChanged & pack) override; void visitBattleObstaclesChanged(BattleObstaclesChanged & pack) override; void visitCatapultAttack(CatapultAttack & pack) override; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index af8243e98..dfbd1f03c 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -868,6 +868,13 @@ void ApplyClientNetPackVisitor::visitBattleResultsApplied(BattleResultsApplied & callInterfaceIfPresent(cl, PlayerColor::SPECTATOR, &IGameEventsReceiver::battleResultsApplied); } +void ApplyClientNetPackVisitor::visitBattleEnded(BattleEnded & pack) +{ + callInterfaceIfPresent(cl, pack.victor, &IGameEventsReceiver::battleEnded); + callInterfaceIfPresent(cl, pack.loser, &IGameEventsReceiver::battleEnded); + callInterfaceIfPresent(cl, PlayerColor::SPECTATOR, &IGameEventsReceiver::battleEnded); +} + void ApplyClientNetPackVisitor::visitBattleUnitsChanged(BattleUnitsChanged & pack) { callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleUnitsChanged, pack.battleID, pack.changedStacks); diff --git a/lib/callback/IGameEventsReceiver.h b/lib/callback/IGameEventsReceiver.h index 966c6f7d7..6626af3a6 100644 --- a/lib/callback/IGameEventsReceiver.h +++ b/lib/callback/IGameEventsReceiver.h @@ -41,6 +41,7 @@ public: virtual void buildChanged(const CGTownInstance *town, BuildingID buildingID, int what){}; //what: 1 - built, 2 - demolished virtual void battleResultsApplied(){}; //called when all effects of last battle are applied + virtual void battleEnded(){}; //called when a battle has ended virtual void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2){}; diff --git a/lib/gameState/GameStatePackVisitor.cpp b/lib/gameState/GameStatePackVisitor.cpp index 4fc72af4c..cda891320 100644 --- a/lib/gameState/GameStatePackVisitor.cpp +++ b/lib/gameState/GameStatePackVisitor.cpp @@ -1427,7 +1427,14 @@ void GameStatePackVisitor::visitBattleResultsApplied(BattleResultsApplied & pack hero->mana = std::min(hero->mana, currentBattle.getSide(i).initialMana); } } +} +void GameStatePackVisitor::visitBattleEnded(BattleEnded & pack) +{ + auto battleIter = boost::range::find_if(gs.currentBattles, [&](const auto & battle) + { + return battle->battleID == pack.battleID; + }); assert(battleIter != gs.currentBattles.end()); gs.currentBattles.erase(battleIter); } diff --git a/lib/gameState/GameStatePackVisitor.h b/lib/gameState/GameStatePackVisitor.h index 8cb0642a7..785eb4493 100644 --- a/lib/gameState/GameStatePackVisitor.h +++ b/lib/gameState/GameStatePackVisitor.h @@ -102,6 +102,7 @@ public: void visitBattleNextRound(BattleNextRound & pack) override; void visitBattleCancelled(BattleCancelled & pack) override; void visitBattleResultsApplied(BattleResultsApplied & pack) override; + void visitBattleEnded(BattleEnded & pack) override; void visitBattleResultAccepted(BattleResultAccepted & pack) override; void visitTurnTimeUpdate(TurnTimeUpdate & pack) override; }; diff --git a/lib/networkPacks/NetPackVisitor.h b/lib/networkPacks/NetPackVisitor.h index 88cbde6b1..1e62a7870 100644 --- a/lib/networkPacks/NetPackVisitor.h +++ b/lib/networkPacks/NetPackVisitor.h @@ -113,6 +113,7 @@ public: virtual void visitSetStackEffect(SetStackEffect & pack) {} virtual void visitStacksInjured(StacksInjured & pack) {} virtual void visitBattleResultsApplied(BattleResultsApplied & pack) {} + virtual void visitBattleEnded(BattleEnded & pack) {} virtual void visitBattleObstaclesChanged(BattleObstaclesChanged & pack) {} virtual void visitBattleSetStackProperty(BattleSetStackProperty & pack) {} virtual void visitBattleTriggerEffect(BattleTriggerEffect & pack) {} diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 43cd343a1..69a14b974 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -483,6 +483,11 @@ void BattleResultsApplied::visitTyped(ICPackVisitor & visitor) visitor.visitBattleResultsApplied(*this); } +void BattleEnded::visitTyped(ICPackVisitor & visitor) +{ + visitor.visitBattleEnded(*this); +} + void BattleObstaclesChanged::visitTyped(ICPackVisitor & visitor) { visitor.visitBattleObstaclesChanged(*this); diff --git a/lib/networkPacks/PacksForClientBattle.h b/lib/networkPacks/PacksForClientBattle.h index 6d20cb7e1..7e6100357 100644 --- a/lib/networkPacks/PacksForClientBattle.h +++ b/lib/networkPacks/PacksForClientBattle.h @@ -415,6 +415,22 @@ struct DLL_LINKAGE BattleResultsApplied : public CPackForClient } }; +struct DLL_LINKAGE BattleEnded : public CPackForClient +{ + BattleID battleID = BattleID::NONE; + PlayerColor victor; + PlayerColor loser; + void visitTyped(ICPackVisitor & visitor) override; + + template void serialize(Handler & h) + { + h & battleID; + h & victor; + h & loser; + assert(battleID != BattleID::NONE); + } +}; + struct DLL_LINKAGE BattleObstaclesChanged : public CPackForClient { BattleID battleID = BattleID::NONE; diff --git a/lib/serializer/RegisterTypes.h b/lib/serializer/RegisterTypes.h index be48a67c1..a76b921a4 100644 --- a/lib/serializer/RegisterTypes.h +++ b/lib/serializer/RegisterTypes.h @@ -294,6 +294,7 @@ void registerTypes(Serializer &s) s.template registerType(252); s.template registerType(253); s.template registerType(254); + s.template registerType(255); } VCMI_LIB_NAMESPACE_END diff --git a/server/battles/BattleResultProcessor.cpp b/server/battles/BattleResultProcessor.cpp index 9fedd7057..5e1f6b6b7 100644 --- a/server/battles/BattleResultProcessor.cpp +++ b/server/battles/BattleResultProcessor.cpp @@ -558,6 +558,7 @@ void BattleResultProcessor::battleFinalize(const BattleID & battleID, const Batt resultsApplied.battleID = battleID; resultsApplied.victor = finishingBattle->victor; resultsApplied.loser = finishingBattle->loser; + //BattleResultsApplied does not end the battle, it only applies most of its consequences gameHandler->sendAndApply(resultsApplied); // Remove beaten hero @@ -612,6 +613,13 @@ void BattleResultProcessor::battleFinalize(const BattleID & battleID, const Batt gameHandler->heroPool->onHeroEscaped(finishingBattle->loser, loserHero); } + //notify all players that battle has ended after all consequences are applied + BattleEnded ended; + ended.battleID = battleID; + ended.victor = finishingBattle->victor; + ended.loser = finishingBattle->loser; + gameHandler->sendAndApply(ended); + //handle victory/loss of engaged players gameHandler->checkVictoryLossConditions({finishingBattle->loser, finishingBattle->victor}); diff --git a/test/vcai/mock_VCAI.h b/test/vcai/mock_VCAI.h index 0ab80be4e..b846f6bf4 100644 --- a/test/vcai/mock_VCAI.h +++ b/test/vcai/mock_VCAI.h @@ -78,6 +78,7 @@ public: MOCK_METHOD1(heroManaPointsChanged, void(const CGHeroInstance * hero)); MOCK_METHOD3(heroSecondarySkillChanged, void(const CGHeroInstance * hero, int which, int val)); MOCK_METHOD0(battleResultsApplied, void()); + MOCK_METHOD0(battleEnded, void()); MOCK_METHOD1(objectPropertyChanged, void(const SetObjectProperty * sop)); MOCK_METHOD3(buildChanged, void(const CGTownInstance * town, BuildingID buildingID, int what)); MOCK_METHOD3(heroBonusChanged, void(const CGHeroInstance * hero, const Bonus & bonus, bool gain));