Forráskód Böngészése

BattleAI: retreat

Andrii Danylchenko 3 éve
szülő
commit
82a9f82e1c

+ 22 - 3
AI/BattleAI/BattleAI.cpp

@@ -17,6 +17,7 @@
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/spells/ISpellMechanics.h"
+#include "../../lib/battle/BattleStateInfoForRetreat.h"
 #include "../../lib/CStack.h" // TODO: remove
                               // Eventually only IBattleInfoCallback and battle::Unit should be used,
                               // CUnitState should be private and CStack should be removed completely
@@ -728,13 +729,31 @@ void CBattleAI::print(const std::string &text) const
 
 boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
 {
-	if(cb->battleCanSurrender(playerID))
+	BattleStateInfoForRetreat bs;
+
+	bs.canFlee = cb->battleCanFlee();
+	bs.canSurrender = cb->battleCanSurrender(playerID);
+	bs.ourSide = cb->battleGetMySide();
+	bs.ourHero = cb->battleGetMyHero();
+	bs.enemyHero = cb->battleGetFightingHero(!bs.ourSide);
+
+	for(auto stack : cb->battleGetAllStacks(false))
 	{
+		if(stack->alive())
+		{
+			if(stack->side == bs.ourSide)
+				bs.ourStacks.push_back(stack);
+			else
+				bs.enemyStacks.push_back(stack);
+		}
 	}
-	if(cb->battleCanFlee())
+
+	if(!bs.canFlee || !bs.canSurrender)
 	{
+		return boost::none;
 	}
-	return boost::none;
+
+	return cb->makeSurrenderRetreatDecision(bs);
 }
 
 

+ 18 - 0
AI/Nullkiller/AIGateway.cpp

@@ -19,6 +19,7 @@
 #include "../../lib/serializer/CTypeList.h"
 #include "../../lib/serializer/BinarySerializer.h"
 #include "../../lib/serializer/BinaryDeserializer.h"
+#include "../../lib/battle/BattleStateInfoForRetreat.h"
 
 #include "AIGateway.h"
 #include "Goals/Goals.h"
@@ -488,6 +489,23 @@ void AIGateway::showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositio
 	NET_EVENT_HANDLER;
 }
 
+boost::optional<BattleAction> AIGateway::makeSurrenderRetreatDecision(
+	const BattleStateInfoForRetreat & battleState)
+{
+	LOG_TRACE(logAi);
+	NET_EVENT_HANDLER;
+
+	double fightRatio = battleState.getOurStrength() / (double)battleState.getEnemyStrength();
+
+	if(fightRatio < 0.3 && battleState.canFlee)
+	{
+		return BattleAction::makeRetreat(battleState.ourSide);
+	}
+
+	return boost::none;
+}
+
+
 void AIGateway::init(std::shared_ptr<Environment> env, std::shared_ptr<CCallback> CB)
 {
 	LOG_TRACE(logAi);

+ 1 - 0
AI/Nullkiller/AIGateway.h

@@ -166,6 +166,7 @@ public:
 	void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
 	void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
 	void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) 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 battleEnd(const BattleResult * br) override;

+ 6 - 0
CCallback.cpp

@@ -387,3 +387,9 @@ bool CBattleCallback::battleMakeTacticAction( BattleAction * action )
 	sendRequest(&ma);
 	return true;
 }
+
+boost::optional<BattleAction> CBattleCallback::makeSurrenderRetreatDecision(
+	const BattleStateInfoForRetreat & battleState)
+{
+	return cl->playerint[getPlayerID().get()]->makeSurrenderRetreatDecision(battleState);
+}

+ 3 - 0
CCallback.h

@@ -32,6 +32,7 @@ struct CPackForServer;
 class IBattleEventsReceiver;
 class IGameEventsReceiver;
 struct ArtifactLocation;
+class BattleStateInfoForRetreat;
 
 VCMI_LIB_NAMESPACE_END
 
@@ -48,6 +49,7 @@ public:
 	//battle
 	virtual int battleMakeAction(const BattleAction * action) = 0;//for casting spells by hero - DO NOT use it for moving active stack
 	virtual bool battleMakeTacticAction(BattleAction * action) = 0; // performs tactic phase actions
+	virtual boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) = 0;
 };
 
 class IGameActionCallback
@@ -104,6 +106,7 @@ public:
 	CBattleCallback(boost::optional<PlayerColor> Player, CClient *C);
 	int battleMakeAction(const BattleAction * action) override;//for casting spells by hero - DO NOT use it for moving active stack
 	bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions
+	boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) override;
 
 #if SCRIPTING_ENABLED
 	scripting::Pool * getContextPool() const override;

+ 2 - 0
cmake_modules/VCMI_lib.cmake

@@ -12,6 +12,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/battle/BattleHex.cpp
 		${MAIN_LIB_DIR}/battle/BattleInfo.cpp
 		${MAIN_LIB_DIR}/battle/BattleProxy.cpp
+		${MAIN_LIB_DIR}/battle/BattleStateInfoForRetreat.cpp
 		${MAIN_LIB_DIR}/battle/CBattleInfoCallback.cpp
 		${MAIN_LIB_DIR}/battle/CBattleInfoEssentials.cpp
 		${MAIN_LIB_DIR}/battle/CCallbackBase.cpp
@@ -241,6 +242,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/battle/BattleAttackInfo.h
 		${MAIN_LIB_DIR}/battle/BattleHex.h
 		${MAIN_LIB_DIR}/battle/BattleInfo.h
+		${MAIN_LIB_DIR}/battle/BattleStateInfoForRetreat.h
 		${MAIN_LIB_DIR}/battle/BattleProxy.h
 		${MAIN_LIB_DIR}/battle/CBattleInfoCallback.h
 		${MAIN_LIB_DIR}/battle/CBattleInfoEssentials.h

+ 8 - 1
lib/CGameInterface.h

@@ -58,7 +58,9 @@ class CLoadFile;
 class CSaveFile;
 class BinaryDeserializer;
 class BinarySerializer;
+class BattleStateInfo;
 struct ArtifactLocation;
+class BattleStateInfoForRetreat;
 
 #if SCRIPTING_ENABLED
 namespace scripting
@@ -93,7 +95,7 @@ public:
 
 	//pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
 	virtual void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID)=0;
-	virtual	void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)=0;
+	virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)=0;
 
 	// Show a dialog, player must take decision. If selection then he has to choose between one of given components,
 	// if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called
@@ -108,6 +110,11 @@ public:
 
 	virtual void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions){};
 
+	virtual boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState)
+	{
+		return boost::none;
+	}
+
 	virtual void saveGame(BinarySerializer & h, const int version) = 0;
 	virtual void loadGame(BinaryDeserializer & h, const int version) = 0;
 };

+ 16 - 0
lib/battle/BattleAction.cpp

@@ -105,6 +105,22 @@ BattleAction BattleAction::makeEndOFTacticPhase(ui8 side)
 	return ba;
 }
 
+BattleAction BattleAction::makeSurrender(ui8 side)
+{
+	BattleAction ba;
+	ba.side = side;
+	ba.actionType = EActionType::SURRENDER;
+	return ba;
+}
+
+BattleAction BattleAction::makeRetreat(ui8 side)
+{
+	BattleAction ba;
+	ba.side = side;
+	ba.actionType = EActionType::RETREAT;
+	return ba;
+}
+
 std::string BattleAction::toString() const
 {
 	std::stringstream actionTypeStream;

+ 2 - 0
lib/battle/BattleAction.h

@@ -40,6 +40,8 @@ public:
 	static BattleAction makeCreatureSpellcast(const battle::Unit * stack, const battle::Target & target, SpellID spellID);
 	static BattleAction makeMove(const battle::Unit * stack, BattleHex dest);
 	static BattleAction makeEndOFTacticPhase(ui8 side);
+	static BattleAction makeRetreat(ui8 side);
+	static BattleAction makeSurrender(ui8 side);
 
 	std::string toString() const;
 

+ 52 - 0
lib/battle/BattleStateInfoForRetreat.cpp

@@ -0,0 +1,52 @@
+/*
+ * BattleStateInfoForRetreat.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "BattleStateInfoForRetreat.h"
+#include "Unit.h"
+#include "CBattleInfoCallback.h"
+#include "../CCreatureSet.h"
+#include "../mapObjects/CGHeroInstance.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+BattleStateInfoForRetreat::BattleStateInfoForRetreat()
+: canFlee(false), canSurrender(false), isLastTurnBeforeDie(false), ourStacks(), enemyStacks(), ourHero(nullptr), enemyHero(nullptr), ourSide(-1)
+{
+}
+
+uint64_t getFightingStrength(std::vector<const battle::Unit *> stacks, const CGHeroInstance * hero = nullptr)
+{
+	uint64_t result = 0;
+
+	for(const battle::Unit * stack : stacks)
+	{
+		result += stack->creatureId().toCreature()->AIValue * stack->getCount();
+	}
+
+	if(hero)
+	{
+		result = (uint64_t)(result * hero->getFightingStrength());
+	}
+
+	return result;
+}
+
+uint64_t BattleStateInfoForRetreat::getOurStrength() const
+{
+	return getFightingStrength(ourStacks, ourHero);
+}
+
+uint64_t BattleStateInfoForRetreat::getEnemyStrength() const
+{
+	return getFightingStrength(enemyStacks, enemyHero);
+}
+
+VCMI_LIB_NAMESPACE_END

+ 38 - 0
lib/battle/BattleStateInfoForRetreat.h

@@ -0,0 +1,38 @@
+/*
+ * BattleStateInfoForRetreat.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+namespace battle
+{
+	class Unit;
+}
+
+class CGHeroInstance;
+
+class DLL_LINKAGE BattleStateInfoForRetreat
+{
+public:
+	bool canFlee;
+	bool canSurrender;
+	bool isLastTurnBeforeDie;
+	ui8 ourSide;
+	std::vector<const battle::Unit *> ourStacks;
+	std::vector<const battle::Unit *> enemyStacks;
+	const CGHeroInstance * ourHero;
+	const CGHeroInstance * enemyHero;
+
+	BattleStateInfoForRetreat();
+	uint64_t getOurStrength() const;
+	uint64_t getEnemyStrength() const;
+};
+
+VCMI_LIB_NAMESPACE_END