diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 09b83c7dc..5809c01c4 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -707,12 +707,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) { EVENT_HANDLER_CALLED_BY_CLIENT; - bool replay = (lastBattleArmies.first == army1 && lastBattleArmies.second == army2); + 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(army2); - if(!replay && army2_object && army2_object->getOwner() == PlayerColor::UNFLAGGABLE && settings["adventure"]["quickCombat"].Bool()) + if((!replay && army2_object + && army2_object->getOwner() == PlayerColor::UNFLAGGABLE + && settings["adventure"]["quickCombat"].Bool()) + || settings["adventure"]["alwaysSkipBattle"].Bool()) { autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String()); autofightingAI->init(env, cb); @@ -938,7 +941,8 @@ void CPlayerInterface::battleEnd(const BattleResult *br, QueryID queryID) if(!battleInt) { - auto wnd = std::make_shared(*br, *this, true); + bool replay = !settings["adventure"]["alwaysSkipCombat"].Bool(); //do not allow manual replay + auto wnd = std::make_shared(*br, *this, replay); wnd->resultCallback = [=](ui32 selection) { cb->selectionMade(selection, queryID); diff --git a/client/Client.cpp b/client/Client.cpp index dff6faac7..49cd93d7f 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -600,7 +600,7 @@ void CClient::battleStarted(const BattleInfo * info) if(vstd::contains(playerint, rightSide.color) && playerint[rightSide.color]->human) def = std::dynamic_pointer_cast(playerint[rightSide.color]); - //If quick combat is not, do not prepare interfaces for battleint + //Remove player interfaces for auto battle (quickCombat option) if(att && att->isAutoFightOn) { att.reset(); diff --git a/client/battle/CBattleInterfaceClasses.cpp b/client/battle/CBattleInterfaceClasses.cpp index 81467d752..1e10c9a12 100644 --- a/client/battle/CBattleInterfaceClasses.cpp +++ b/client/battle/CBattleInterfaceClasses.cpp @@ -570,9 +570,9 @@ void CBattleResultWindow::show(SDL_Surface * to) CCS->videoh->update(pos.x + 107, pos.y + 70, screen, true, false); } -void CBattleResultWindow::bExitf() +void CBattleResultWindow::buttonPressed(int button) { - resultCallback(0); + resultCallback(button); CPlayerInterface &intTmp = owner; //copy reference because "this" will be destructed soon close(); @@ -586,20 +586,14 @@ void CBattleResultWindow::bExitf() CCS->videoh->close(); } +void CBattleResultWindow::bExitf() +{ + buttonPressed(0); +} + 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(); + buttonPressed(1); } Point CClickableHex::getXYUnitAnim(BattleHex hexNum, const CStack * stack, CBattleInterface * cbi) diff --git a/client/battle/CBattleInterfaceClasses.h b/client/battle/CBattleInterfaceClasses.h index 531b83cf2..9b853f028 100644 --- a/client/battle/CBattleInterfaceClasses.h +++ b/client/battle/CBattleInterfaceClasses.h @@ -122,6 +122,8 @@ private: std::vector> icons; std::shared_ptr description; CPlayerInterface & owner; + + void buttonPressed(int button); //internal function for button callbacks public: CBattleResultWindow(const BattleResult & br, CPlayerInterface & _owner, bool allowReplay = false); ~CBattleResultWindow(); diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 7ad183191..e64afd97e 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -124,7 +124,7 @@ "type" : "object", "additionalProperties" : false, "default": {}, - "required" : [ "heroSpeed", "enemySpeed", "scrollSpeed", "heroReminder", "quickCombat" ], + "required" : [ "heroSpeed", "enemySpeed", "scrollSpeed", "heroReminder", "quickCombat", "alwaysSkipCombat" ], "properties" : { "heroSpeed" : { "type" : "number", @@ -144,7 +144,13 @@ }, "quickCombat" : { "type" : "boolean", - "default" : true + "default" : true, + "description" : "enable to allow AI to play combats versus neutrals. Player can refuse battle result and replay it manually. This option can be switched from in-game menu" + }, + "alwaysSkipCombat" : { + "type" : "boolean", + "default" : false, + "description" : "if enabled, all battles will be controlled by AI" } } }, diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 13334afcd..aeb663cb2 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -741,9 +741,9 @@ void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo) CasualtiesAfterBattle cab1(bEndArmy1, battleInfo), cab2(bEndArmy2, battleInfo); //calculate casualties before deleting battle ChangeSpells cs; //for Eagle Eye - if (finishingBattle->winnerHero) + if(!finishingBattle->isDraw() && finishingBattle->winnerHero) { - if (int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_VAL2, SecondarySkill::EAGLE_EYE)) + if(int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_VAL2, SecondarySkill::EAGLE_EYE)) { double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::EAGLE_EYE); for(auto & spellId : battleInfo->sides.at(!battleResult.data->winner).usedSpellsHistory) @@ -756,7 +756,7 @@ void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo) } std::vector 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) { @@ -890,6 +890,11 @@ void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo) RemoveObject ro(finishingBattle->loserHero->id); sendAndApply(&ro); } + if(finishingBattle->isDraw() && finishingBattle->winnerHero) //for draw case both heroes should be removed + { + RemoveObject ro(finishingBattle->winnerHero->id); + sendAndApply(&ro); + } if(battleResult.data->winner == BattleSide::DEFENDER && finishingBattle->winnerHero @@ -900,10 +905,10 @@ void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo) swapGarrisonOnSiege(finishingBattle->winnerHero->visitedTown->id); //return defending visitor from garrison to its rightful place } //give exp - if(battleResult.data->exp[finishingBattle->winnerSide] && finishingBattle->winnerHero) + if(!finishingBattle->isDraw() && battleResult.data->exp[finishingBattle->winnerSide] && finishingBattle->winnerHero) changePrimSkill(finishingBattle->winnerHero, PrimarySkill::EXPERIENCE, battleResult.data->exp[finishingBattle->winnerSide]); - queries.popIfTop(battleQuery); + //queries.popIfTop(battleQuery); BattleResultAccepted raccepted; raccepted.army1 = const_cast(bEndArmy1); @@ -914,6 +919,7 @@ void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo) raccepted.exp[1] = battleResult.data->exp[1]; sendAndApply(&raccepted); + queries.popIfTop(battleQuery); //--> continuation (battleAfterLevelUp) occurs after level-up queries are handled or on removing query (above) } diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 2645d99b4..c475f4dc6 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -308,6 +308,8 @@ public: { FinishingBattleHelper(); FinishingBattleHelper(std::shared_ptr Query, int RemainingBattleQueriesCount); + + inline bool isDraw() const {return winnerSide == 2;} const CGHeroInstance *winnerHero, *loserHero; PlayerColor victor, loser;