From 219a282916ee7f40fbc1403a5e25457e3eb0aaed Mon Sep 17 00:00:00 2001 From: nordsoft Date: Thu, 3 Nov 2022 21:38:49 +0400 Subject: [PATCH] First step --- AI/BattleAI/BattleAI.h | 2 +- AI/Nullkiller/AIGateway.cpp | 4 ++-- AI/Nullkiller/AIGateway.h | 2 +- AI/StupidAI/StupidAI.cpp | 2 +- AI/StupidAI/StupidAI.h | 2 +- AI/VCAI/VCAI.cpp | 4 ++-- AI/VCAI/VCAI.h | 2 +- client/CPlayerInterface.cpp | 15 ++++++++++---- client/CPlayerInterface.h | 2 +- client/Client.cpp | 2 +- client/NetPacksClient.cpp | 2 +- client/battle/CBattleInterface.cpp | 24 +++++++++++++++------- client/battle/CBattleInterface.h | 4 ++-- client/battle/CBattleInterfaceClasses.cpp | 25 ++++++++++++++++++++++- client/battle/CBattleInterfaceClasses.h | 5 ++++- lib/CGameInterface.cpp | 4 ++-- lib/CGameInterface.h | 2 +- lib/IGameEventsReceiver.h | 2 +- lib/NetPacks.h | 4 +++- server/CGameHandler.cpp | 4 ++++ server/CQuery.cpp | 10 +++++++++ server/CQuery.h | 6 ++++++ 22 files changed, 97 insertions(+), 32 deletions(-) diff --git a/AI/BattleAI/BattleAI.h b/AI/BattleAI/BattleAI.h index 82cecacc8..552340543 100644 --- a/AI/BattleAI/BattleAI.h +++ b/AI/BattleAI/BattleAI.h @@ -81,7 +81,7 @@ public: //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 battleStacksAttacked(const std::vector & bsa) 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 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 diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 6a6dfd299..f04202705 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -1067,7 +1067,7 @@ void AIGateway::battleStart(const CCreatureSet * army1, const CCreatureSet * arm 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; assert(status.getBattle() == ONGOING_BATTLE); @@ -1075,7 +1075,7 @@ void AIGateway::battleEnd(const BattleResult * br) bool won = br->winner == myCb->battleGetMySide(); logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.getStr(), (won ? "won" : "lost"), battlename); battlename.clear(); - CAdventureAI::battleEnd(br); + CAdventureAI::battleEnd(br, queryID); } void AIGateway::waitTillFree() diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index 54854d702..af42cf684 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -169,7 +169,7 @@ public: boost::optional 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 battleEnd(const BattleResult * br) override; + void battleEnd(const BattleResult * br, QueryID queryID) override; void makeTurn(); diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index 8a2bf194e..d9ccf0654 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -182,7 +182,7 @@ void CStupidAI::battleStacksAttacked(const std::vector & bs print("battleStacksAttacked called"); } -void CStupidAI::battleEnd(const BattleResult *br) +void CStupidAI::battleEnd(const BattleResult *br, QueryID queryID) { print("battleEnd called"); } diff --git a/AI/StupidAI/StupidAI.h b/AI/StupidAI/StupidAI.h index 07a2ebcac..8cb33369c 100644 --- a/AI/StupidAI/StupidAI.h +++ b/AI/StupidAI/StupidAI.h @@ -32,7 +32,7 @@ public: void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack void battleStacksAttacked(const std::vector & bsa) 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 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 diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 7f42b8d70..851c7b70f 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1583,7 +1583,7 @@ void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, i 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; assert(status.getBattle() == ONGOING_BATTLE); @@ -1591,7 +1591,7 @@ void VCAI::battleEnd(const BattleResult * br) bool won = br->winner == myCb->battleGetMySide(); logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.getStr(), (won ? "won" : "lost"), battlename); battlename.clear(); - CAdventureAI::battleEnd(br); + CAdventureAI::battleEnd(br, queryID); } void VCAI::waitTillFree() diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index acb6e5f5d..a859e14bd 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -201,7 +201,7 @@ public: void showWorldViewEx(const std::vector & objectPositions) 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 mainLoop(); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index af40aa43f..f461ce914 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -707,7 +707,9 @@ 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) { EVENT_HANDLER_CALLED_BY_CLIENT; - if (settings["adventure"]["quickCombat"].Bool()) + //quick combat with neutral creatures only + auto * army2_object = dynamic_cast(army2); + if(army2_object && army2_object->getOwner() == PlayerColor::UNFLAGGABLE && settings["adventure"]["quickCombat"].Bool()) { autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String()); autofightingAI->init(env, cb); @@ -922,7 +924,7 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i return ret; } -void CPlayerInterface::battleEnd(const BattleResult *br) +void CPlayerInterface::battleEnd(const BattleResult *br, QueryID queryID) { EVENT_HANDLER_CALLED_BY_CLIENT; if(isAutoFightOn || autofightingAI) @@ -933,7 +935,12 @@ void CPlayerInterface::battleEnd(const BattleResult *br) if(!battleInt) { - GH.pushIntT(*br, *this); + auto wnd = std::make_shared(*br, *this, true); + 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. // Otherwise NewTurn causes freeze. waitWhileDialog(); @@ -944,7 +951,7 @@ void CPlayerInterface::battleEnd(const BattleResult *br) BATTLE_EVENT_POSSIBLE_RETURN; - battleInt->battleFinished(*br); + battleInt->battleFinished(*br, queryID); adventureInt->quickCombatUnlock(); } diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index f7907594c..9878b9ff4 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -189,7 +189,7 @@ public: 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 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 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 & lines) override; diff --git a/client/Client.cpp b/client/Client.cpp index 5ee3c6cb0..a84849537 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -583,7 +583,7 @@ void CClient::battleStarted(const BattleInfo * info) auto & leftSide = info->sides[0], & rightSide = info->sides[1]; //If quick combat is not, do not prepare interfaces for battleint - if(!settings["adventure"]["quickCombat"].Bool()) + if(rightSide.color != PlayerColor::NEUTRAL || !settings["adventure"]["quickCombat"].Bool()) { if(vstd::contains(playerint, leftSide.color) && playerint[leftSide.color]->human) att = std::dynamic_pointer_cast(playerint[leftSide.color]); diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index d44434b18..d9826a3da 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -708,7 +708,7 @@ void BattleUpdateGateState::applyFirstCl(CClient * cl) void BattleResult::applyFirstCl(CClient *cl) { - callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleEnd, this); + callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleEnd, this, queryID); cl->battleFinished(); } diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 6606e6dd8..ba7c9cb7c 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -859,11 +859,14 @@ void CBattleInterface::reallySurrender() void CBattleInterface::bAutofightf() { - if (spellDestSelectMode) //we are casting a spell + //if(bresult) //battle is already finished + //return; + + if(spellDestSelectMode) //we are casting a spell return; //Stop auto-fight mode - if (curInt->isAutoFightOn) + if(curInt->isAutoFightOn) { assert(curInt->autofightingAI); curInt->isAutoFightOn = false; @@ -1129,7 +1132,8 @@ void CBattleInterface::newRoundFirst( int round ) void CBattleInterface::newRound(int number) { - console->addText(CGI->generaltexth->allTexts[412]); + if(console) + console->addText(CGI->generaltexth->allTexts[412]); } void CBattleInterface::giveCommand(EActionType action, BattleHex tile, si32 additional) @@ -1254,7 +1258,7 @@ void CBattleInterface::stackIsCatapulting(const CatapultAttack & ca) } } -void CBattleInterface::battleFinished(const BattleResult& br) +void CBattleInterface::battleFinished(const BattleResult& br, QueryID queryID) { bresult = &br; { @@ -1262,19 +1266,25 @@ void CBattleInterface::battleFinished(const BattleResult& br) animsAreDisplayed.waitUntil(false); } setActiveStack(nullptr); - displayBattleFinished(); + displayBattleFinished(queryID); } -void CBattleInterface::displayBattleFinished() +void CBattleInterface::displayBattleFinished(QueryID queryID) { CCS->curh->changeGraphic(ECursor::ADVENTURE,0); if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-skip-battle-result"].Bool()) { + curInt->cb->selectionMade(0, queryID); close(); return; } - GH.pushInt(std::make_shared(*bresult, *(this->curInt))); + auto wnd = std::make_shared(*bresult, *(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 CPlayerInterface::battleInt = nullptr; } diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index 0fdd232fe..186a3283c 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -353,8 +353,8 @@ public: void newRound(int number); //caled when round is ended; number is the number of round void hexLclicked(int whichOne); //hex only call-in 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 displayBattleFinished(); //displays battle result + void battleFinished(const BattleResult& br, QueryID queryID); //called when battle is finished - battleresult window should be printed + void displayBattleFinished(QueryID queryID); //displays battle result 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 castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook diff --git a/client/battle/CBattleInterfaceClasses.cpp b/client/battle/CBattleInterfaceClasses.cpp index f9902989f..b0b0b47df 100644 --- a/client/battle/CBattleInterfaceClasses.cpp +++ b/client/battle/CBattleInterfaceClasses.cpp @@ -394,7 +394,7 @@ void CBattleOptionsWindow::bExitf() close(); } -CBattleResultWindow::CBattleResultWindow(const BattleResult & br, CPlayerInterface & _owner) +CBattleResultWindow::CBattleResultWindow(const BattleResult & br, CPlayerInterface & _owner, bool allowReplay) : owner(_owner) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); @@ -405,6 +405,12 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult & br, CPlayerInterfa exit = std::make_shared(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, SDLK_RETURN); exit->setBorderColor(Colors::METALLIC_GOLD); + + if(allowReplay) + { + repeat = std::make_shared(Point(24, 505), "icn6432.def", std::make_pair("", ""), [&](){ bRepeatf();}, SDLK_ESCAPE); + repeat->setBorderColor(Colors::METALLIC_GOLD); + } if(br.winner == 0) //attacker won { @@ -566,6 +572,7 @@ void CBattleResultWindow::show(SDL_Surface * to) void CBattleResultWindow::bExitf() { + resultCallback(0); CPlayerInterface &intTmp = owner; //copy reference because "this" will be destructed soon close(); @@ -579,6 +586,22 @@ void CBattleResultWindow::bExitf() CCS->videoh->close(); } +void CBattleResultWindow::bRepeatf() +{ + resultCallback(1); + CPlayerInterface &intTmp = owner; //copy reference because "this" will be destructed soon + + close(); + + if(dynamic_cast(GH.topInt().get())) + GH.popInts(1); //pop battle interface if present + + //Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle, + //so we can be sure that there is no dialogs left on GUI stack. + intTmp.showingDialog->setn(false); + CCS->videoh->close(); +} + Point CClickableHex::getXYUnitAnim(BattleHex hexNum, const CStack * stack, CBattleInterface * cbi) { assert(cbi); diff --git a/client/battle/CBattleInterfaceClasses.h b/client/battle/CBattleInterfaceClasses.h index c0b657d27..531b83cf2 100644 --- a/client/battle/CBattleInterfaceClasses.h +++ b/client/battle/CBattleInterfaceClasses.h @@ -118,14 +118,17 @@ private: std::shared_ptr background; std::vector> labels; std::shared_ptr exit; + std::shared_ptr repeat; std::vector> icons; std::shared_ptr description; CPlayerInterface & owner; public: - CBattleResultWindow(const BattleResult & br, CPlayerInterface & _owner); + CBattleResultWindow(const BattleResult & br, CPlayerInterface & _owner, bool allowReplay = false); ~CBattleResultWindow(); void bExitf(); //exit button callback + void bRepeatf(); //repeat button callback + std::function resultCallback; //callback receiving which button was pressed void activate() override; void show(SDL_Surface * to = 0) override; diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index f47fa1bdf..34b78b0eb 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -219,9 +219,9 @@ void CAdventureAI::battleSpellCast(const 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(); } diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index 99c166c3f..4d1d2c003 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -163,7 +163,7 @@ public: virtual void battleStackMoved(const CStack * stack, std::vector dest, int distance) override; virtual void battleAttack(const BattleAttack *ba) 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 & units, const std::vector & customEffects) override; virtual void saveGame(BinarySerializer & h, const int version) override; diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index 632489c76..c55916be2 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -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 battleAttack(const BattleAttack *ba){}; //called when stack is performing attack virtual void battleStacksAttacked(const std::vector & bsa){}; //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 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 & lines){}; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 171ebdace..19b8f4c7a 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1434,7 +1434,7 @@ struct BattleSetActiveStack : public CPackForClient } }; -struct BattleResult : public CPackForClient +struct BattleResult : public Query { enum EResult {NORMAL = 0, ESCAPE = 1, SURRENDER = 2}; @@ -1447,6 +1447,7 @@ struct BattleResult : public CPackForClient void applyFirstCl(CClient *cl); void applyGs(CGameState *gs); + EResult result; ui8 winner; //0 - attacker, 1 - defender, [2 - draw (should be possible?)] std::map casualties[2]; //first => casualties of attackers - map crid => number @@ -1455,6 +1456,7 @@ struct BattleResult : public CPackForClient template void serialize(Handler &h, const int version) { + h & queryID; h & result; h & winner; h & casualties[0]; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index a4f8dedad..00d82a9ab 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -818,6 +818,10 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con } } } + + auto battleDialogQuery = std::make_shared(this); + battleResult.data->queryID = battleDialogQuery->queryID; + queries.addQuery(battleDialogQuery); sendAndApply(battleResult.data); //after this point casualties objects are destroyed if (arts.size()) //display loot diff --git a/server/CQuery.cpp b/server/CQuery.cpp index 4f8270a6f..d907c03ca 100644 --- a/server/CQuery.cpp +++ b/server/CQuery.cpp @@ -380,6 +380,16 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const return CDialogQuery::blocksPack(pack); } +void CBattleDialogQuery::onRemoval(PlayerColor color) +{ + assert(answer); + if(*answer == 1) + { + int a = 0; + ++a; + } +} + void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const { assert(answer); diff --git a/server/CQuery.h b/server/CQuery.h index aa5446f3c..03705a622 100644 --- a/server/CQuery.h +++ b/server/CQuery.h @@ -144,6 +144,12 @@ public: virtual bool blocksPack(const CPack *pack) const override; }; +class CBattleDialogQuery : public CDialogQuery +{ +public: + virtual void onRemoval(PlayerColor color) override; +}; + //yes/no and component selection dialogs class CBlockingDialogQuery : public CDialogQuery {