1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Intermediate commit

This commit is contained in:
nordsoft 2023-04-06 19:34:07 +04:00
parent 307065a633
commit ce3028bd73
27 changed files with 214 additions and 64 deletions

View File

@ -81,7 +81,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

View File

@ -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()

View File

@ -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();

View File

@ -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");
} }

View File

@ -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

View File

@ -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()

View File

@ -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();

View File

@ -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 replay = (lastBattleArmies.first == army1 && lastBattleArmies.second == army2); //will be true if player already refused auto-battle result
lastBattleArmies.first = army1;
lastBattleArmies.second = army2;
//quick combat with neutral creatures only
auto * army2_object = dynamic_cast<const CGObjectInstance *>(army2);
if((!replay && !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 replay = allowBattleReplay && !settings["adventure"]["alwaysSkipCombat"].Bool(); //do not allow manual replay
allowBattleReplay = false;
auto wnd = std::make_shared<CBattleResultWindow>(*br, *this, replay);
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();
} }

View File

@ -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;

View File

@ -553,13 +553,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())
@ -579,18 +595,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))
{ {
boost::thread(&CClient::commenceTacticPhaseForInt, this, battleints[info->sides[info->tacticsSide].color]); boost::thread(&CClient::commenceTacticPhaseForInt, this, battleints[info->sides[info->tacticsSide].color]);

View File

@ -735,7 +735,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, queryID);
cl.battleFinished(); cl.battleFinished();
} }

View File

@ -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;
} }

View File

@ -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

View File

@ -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,12 @@ 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);
}
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 +575,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 +591,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)

View File

@ -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;

View File

@ -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();
} }

View File

@ -162,7 +162,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;

View File

@ -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){};

View File

@ -1466,12 +1466,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?)]
@ -1483,6 +1502,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];

View File

@ -2190,15 +2190,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)
@ -2214,14 +2210,19 @@ 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++) army1->battle = nullptr;
gs->curB->battleGetArmyObject(i)->battle = nullptr; army2->battle = nullptr;
gs->curB.dellNull(); gs->curB.dellNull();
} }

View File

@ -561,7 +561,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
{ {

View File

@ -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;

View File

@ -266,6 +266,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>();

View File

@ -832,6 +832,8 @@ 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);
@ -914,6 +916,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)
@ -2627,6 +2631,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);
@ -2640,8 +2647,17 @@ 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
queries.addQuery(battleQuery); auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(queries.topQuery(gs->curB->sides[0].color));
if(battleQuery)
{
battleQuery->bi = gs->curB;
battleQuery->result = boost::none;
battleQuery->belligerents[0] = gs->curB->sides[0].armyObject;
battleQuery->belligerents[1] = gs->curB->sides[1].armyObject;
}
queries.addQuery(std::make_shared<CBattleQuery>(this, gs->curB));
this->battleThread = std::make_unique<boost::thread>(boost::thread(&CGameHandler::runBattle, this)); this->battleThread = std::make_unique<boost::thread>(boost::thread(&CGameHandler::runBattle, this));
} }
@ -7189,7 +7205,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();
@ -7341,12 +7357,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;
} }

View File

@ -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);
@ -314,8 +315,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;
@ -325,6 +329,7 @@ public:
h & loserHero; h & loserHero;
h & victor; h & victor;
h & loser; h & loser;
h & winnerSide;
h & remainingBattleQueriesCount; h & remainingBattleQueriesCount;
} }
}; };

View File

@ -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);
} }
@ -386,6 +387,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);

View File

@ -145,6 +145,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
{ {