nordsoft 3 سال پیش
والد
کامیت
219a282916

+ 1 - 1
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<BattleStackAttacked> & 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

+ 2 - 2
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()

+ 1 - 1
AI/Nullkiller/AIGateway.h

@@ -169,7 +169,7 @@ public:
 	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 battleEnd(const BattleResult * br) override;
+	void battleEnd(const BattleResult * br, QueryID queryID) override;
 
 	void makeTurn();
 

+ 1 - 1
AI/StupidAI/StupidAI.cpp

@@ -182,7 +182,7 @@ void CStupidAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bs
 	print("battleStacksAttacked called");
 }
 
-void CStupidAI::battleEnd(const BattleResult *br)
+void CStupidAI::battleEnd(const BattleResult *br, QueryID queryID)
 {
 	print("battleEnd called");
 }

+ 1 - 1
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<BattleStackAttacked> & 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

+ 2 - 2
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()

+ 1 - 1
AI/VCAI/VCAI.h

@@ -201,7 +201,7 @@ public:
 	void showWorldViewEx(const std::vector<ObjectPosInfo> & 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();

+ 11 - 4
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<const CGObjectInstance *>(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<CBattleResultWindow>(*br, *this);
+			auto wnd = std::make_shared<CBattleResultWindow>(*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();
 }
 

+ 1 - 1
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<MetaString> & lines) override;

+ 1 - 1
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<CPlayerInterface>(playerint[leftSide.color]);

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

+ 17 - 7
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<CBattleResultWindow>(*bresult, *(this->curInt)));
+	auto wnd = std::make_shared<CBattleResultWindow>(*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;
 }

+ 2 - 2
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

+ 24 - 1
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<CButton>(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, SDLK_RETURN);
 	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
 	{
@@ -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<CBattleInterface*>(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);

+ 4 - 1
client/battle/CBattleInterfaceClasses.h

@@ -118,14 +118,17 @@ private:
 	std::shared_ptr<CPicture> background;
 	std::vector<std::shared_ptr<CLabel>> labels;
 	std::shared_ptr<CButton> exit;
+	std::shared_ptr<CButton> repeat;
 	std::vector<std::shared_ptr<CAnimImage>> icons;
 	std::shared_ptr<CTextBox> 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<void(int result)> resultCallback; //callback receiving which button was pressed
 
 	void activate() override;
 	void show(SDL_Surface * to = 0) override;

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

+ 1 - 1
lib/CGameInterface.h

@@ -163,7 +163,7 @@ public:
 	virtual void battleStackMoved(const CStack * stack, std::vector<BattleHex> 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<UnitChanges> & units, const std::vector<CustomEffectInfo> & customEffects) override;
 
 	virtual void saveGame(BinarySerializer & h, const int version) override;

+ 1 - 1
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<BattleStackAttacked> & 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<MetaString> & lines){};

+ 3 - 1
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<ui32,si32> casualties[2]; //first => casualties of attackers - map crid => number
@@ -1455,6 +1456,7 @@ struct BattleResult : public CPackForClient
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
+		h & queryID;
 		h & result;
 		h & winner;
 		h & casualties[0];

+ 4 - 0
server/CGameHandler.cpp

@@ -818,6 +818,10 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
 			}
 		}
 	}
+	
+	auto battleDialogQuery = std::make_shared<CDialogQuery>(this);
+	battleResult.data->queryID = battleDialogQuery->queryID;
+	queries.addQuery(battleDialogQuery);
 	sendAndApply(battleResult.data); //after this point casualties objects are destroyed
 
 	if (arts.size()) //display loot

+ 10 - 0
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);

+ 6 - 0
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
 {