Просмотр исходного кода

Add setting to disable spell hit & run in first battle round when attacking

Dydzio 1 месяц назад
Родитель
Сommit
21b45da1f8

+ 2 - 0
config/gameConfig.json

@@ -610,6 +610,8 @@
 			"oneHexTriggersObstacles": false,
 			// Allow area shooters with SPELL_LIKE_ATTACK bonus such as liches or magogs to target empty hexes
 			"areaShotCanTargetEmptyHex" : false,
+			// Disallow fleeing/surrendering in first turn as attacker after casting spell - part of established H3 PvP standard rules
+			"noSpellHitAndRun" : false,
 			
 			// Positions of units on start of the combat
 			// If battle does not defines specific configuration, 'default' configuration will be used

+ 2 - 1
config/schemas/gameSettings.json

@@ -97,7 +97,8 @@
 				"defensePointDamageFactorCap" : { "type" : "number" },
 				"oneHexTriggersObstacles" :     { "type" : "boolean" },
 				"layouts" :                     { "type" : "object" },
-				"areaShotCanTargetEmptyHex" :   { "type" : "boolean" }
+				"areaShotCanTargetEmptyHex" :   { "type" : "boolean" },
+				"noSpellHitAndRun" :            { "type" : "boolean" }
 			}
 		},
 		"creatures": {

+ 1 - 0
lib/GameSettings.cpp

@@ -62,6 +62,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
 		{EGameSettings::COMBAT_LUCK_BIAS,                                 "combat",    "luckBias"                             },
 		{EGameSettings::COMBAT_LAYOUTS,                                   "combat",    "layouts"                              },
 		{EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES,                "combat",    "oneHexTriggersObstacles"              },
+		{EGameSettings::COMBAT_NO_SPELL_HIT_AND_RUN,                      "combat",    "noSpellHitAndRun"                     },
 		{EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH,             "creatures", "allowAllForDoubleMonth"               },
 		{EGameSettings::CREATURES_ALLOW_JOINING_FOR_FREE,                 "creatures", "allowJoiningForFree"                  },
 		{EGameSettings::CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS,             "creatures", "allowRandomSpecialWeeks"              },

+ 1 - 0
lib/IGameSettings.h

@@ -35,6 +35,7 @@ enum class EGameSettings
 	COMBAT_LUCK_BIAS,
 	COMBAT_LAYOUTS,
 	COMBAT_ONE_HEX_TRIGGERS_OBSTACLES,
+	COMBAT_NO_SPELL_HIT_AND_RUN,
 	CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH,
 	CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS,
 	CREATURES_DAILY_STACK_EXPERIENCE,

+ 6 - 1
lib/battle/BattleInfo.cpp

@@ -167,7 +167,7 @@ std::unique_ptr<BattleInfo> BattleInfo::setupBattle(IGameInfoCallback *cb, const
 	currentBattle->tile = tile;
 	currentBattle->terrainType = terrain;
 	currentBattle->battlefieldType = battlefieldType;
-	currentBattle->round = -2;
+	currentBattle->round = 0;
 	currentBattle->activeStack = -1;
 	currentBattle->replayAllowed = false;
 	if (town)
@@ -578,6 +578,11 @@ BattleSide BattleInfo::getTacticsSide() const
 	return tacticsSide;
 }
 
+int32_t BattleInfo::getRound() const
+{
+	return round;
+}
+
 const CGTownInstance * BattleInfo::getDefendedTown() const
 {
 	if (townID.hasValue())

+ 2 - 1
lib/battle/BattleInfo.h

@@ -31,12 +31,12 @@ class DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallba
 {
 	BattleSideArray<SideInBattle> sides; //sides[0] - attacker, sides[1] - defender
 	std::unique_ptr<BattleLayout> layout;
+	si32 round;
 
 	void postDeserialize();
 public:
 	BattleID battleID = BattleID(0);
 
-	si32 round;
 	si32 activeStack;
 	ObjectInstanceID townID; //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
@@ -103,6 +103,7 @@ public:
 
 	ui8 getTacticDist() const override;
 	BattleSide getTacticsSide() const override;
+	int32_t getRound() const override;
 
 	const CGTownInstance * getDefendedTown() const override;
 	EWallState getWallState(EWallPart partOfWall) const override;

+ 5 - 0
lib/battle/BattleProxy.cpp

@@ -90,6 +90,11 @@ BattleSide BattleProxy::getTacticsSide() const
 	return subject->battleGetTacticsSide();
 }
 
+int32_t BattleProxy::getRound() const
+{
+	return subject->battleGetRound();
+}
+
 const CGTownInstance * BattleProxy::getDefendedTown() const
 {
 	return subject->battleGetDefendedTown();

+ 1 - 0
lib/battle/BattleProxy.h

@@ -44,6 +44,7 @@ public:
 
 	ui8 getTacticDist() const override;
 	BattleSide getTacticsSide() const override;
+	int32_t getRound() const override;
 
 	const CGTownInstance * getDefendedTown() const override;
 	EWallState getWallState(EWallPart partOfWall) const override;

+ 15 - 2
lib/battle/CBattleInfoEssentials.cpp

@@ -13,6 +13,8 @@
 #include "../CStack.h"
 #include "BattleInfo.h"
 #include "CObstacleInstance.h"
+#include "GameLibrary.h"
+#include "IGameSettings.h"
 
 #include "../constants/EntityIdentifiers.h"
 #include "../entities/building/TownFortifications.h"
@@ -230,6 +232,12 @@ BattleSide CBattleInfoEssentials::battleGetTacticsSide() const
 	return getBattle()->getTacticsSide();
 }
 
+int32_t CBattleInfoEssentials::battleGetRound() const
+{
+	RETURN_IF_NOT_BATTLE(-1);
+	return getBattle()->getRound();
+}
+
 const CGHeroInstance * CBattleInfoEssentials::battleGetFightingHero(BattleSide side) const
 {
 	RETURN_IF_NOT_BATTLE(nullptr);
@@ -295,14 +303,19 @@ bool CBattleInfoEssentials::battleCanFlee(const PlayerColor & player) const
 
 	const CGHeroInstance * myHero = battleGetFightingHero(side);
 
-	//current player have no hero
+	//current player has no hero
 	if(!myHero)
 		return false;
 
-	//eg. one of heroes is wearing shakles of war
+	//eg. one of heroes is wearing shackles of war
 	if(myHero->hasBonusOfType(BonusType::BATTLE_NO_FLEEING) && battleHasHero(otherSide(side)))
 		return false;
 
+	//with hit & run disabled - cannot flee after casting spell in 1st turn as attacker
+	if(LIBRARY->engineSettings()->getBoolean(EGameSettings::COMBAT_NO_SPELL_HIT_AND_RUN)
+		&& side == BattleSide::ATTACKER && getBattle()->getRound() == 1 && battleHasHero(otherSide(side)) && getBattle()->getCastSpells(side) >= 1)
+		return false;
+
 	//we are besieged defender
 	if(side == BattleSide::DEFENDER && getBattle()->getDefendedTown() != nullptr)
 	{

+ 1 - 0
lib/battle/CBattleInfoEssentials.h

@@ -67,6 +67,7 @@ public:
 
 	si8 battleTacticDist() const override; //returns tactic distance in current tactics phase; 0 if not in tactics phase
 	BattleSide battleGetTacticsSide() const override; //returns which side is in tactics phase, undefined if none (?)
+	int32_t battleGetRound() const;
 
 	bool battleCanFlee(const PlayerColor & player) const;
 	bool battleCanSurrender(const PlayerColor & player) const;

+ 2 - 0
lib/battle/IBattleState.h

@@ -75,6 +75,8 @@ public:
 
 	virtual int3 getLocation() const = 0;
 	virtual BattleLayout getLayout() const = 0;
+
+	virtual int32_t getRound() const = 0;
 };
 
 class DLL_LINKAGE IBattleState : public IBattleInfo