浏览代码

Merge pull request #4790 from kodobi/bugfix/fix-incorrect-battlefield-while-attacking-town-without-fort

Fix battle setup for sieges without forts
DjWarmonger 11 月之前
父节点
当前提交
59a15c5ff0
共有 2 个文件被更改,包括 59 次插入58 次删除
  1. 44 53
      lib/battle/BattleInfo.cpp
  2. 15 5
      server/battles/BattleProcessor.cpp

+ 44 - 53
lib/battle/BattleInfo.cpp

@@ -161,54 +161,45 @@ struct RangeGenerator
 BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const BattleField & battlefieldType, BattleSideArray<const CArmedInstance *> armies, BattleSideArray<const CGHeroInstance *> heroes, const BattleLayout & layout, const CGTownInstance * town)
 {
 	CMP_stack cmpst;
-	auto * curB = new BattleInfo(layout);
+	auto * currentBattle = new BattleInfo(layout);
 
 	for(auto i : { BattleSide::LEFT_SIDE, BattleSide::RIGHT_SIDE})
-		curB->sides[i].init(heroes[i], armies[i]);
+		currentBattle->sides[i].init(heroes[i], armies[i]);
 
-	std::vector<CStack*> & stacks = (curB->stacks);
+	std::vector<CStack*> & stacks = (currentBattle->stacks);
 
-	curB->tile = tile;
-	curB->battlefieldType = battlefieldType;
-	curB->round = -2;
-	curB->activeStack = -1;
-	curB->replayAllowed = false;
-
-	if(town)
-	{
-		curB->town = town;
-		curB->terrainType = town->getNativeTerrain();
-	}
-	else
-	{
-		curB->town = nullptr;
-		curB->terrainType = terrain;
-	}
+	currentBattle->tile = tile;
+	currentBattle->terrainType = terrain;
+	currentBattle->battlefieldType = battlefieldType;
+	currentBattle->round = -2;
+	currentBattle->activeStack = -1;
+	currentBattle->replayAllowed = false;
+	currentBattle->town = town;
 
 	//setting up siege obstacles
 	if (town && town->fortificationsLevel().wallsHealth != 0)
 	{
 		auto fortification = town->fortificationsLevel();
 
-		curB->si.gateState = EGateState::CLOSED;
+		currentBattle->si.gateState = EGateState::CLOSED;
 
-		curB->si.wallState[EWallPart::GATE] = EWallState::INTACT;
+		currentBattle->si.wallState[EWallPart::GATE] = EWallState::INTACT;
 
 		for(const auto wall : {EWallPart::BOTTOM_WALL, EWallPart::BELOW_GATE, EWallPart::OVER_GATE, EWallPart::UPPER_WALL})
-			curB->si.wallState[wall] = static_cast<EWallState>(fortification.wallsHealth);
+			currentBattle->si.wallState[wall] = static_cast<EWallState>(fortification.wallsHealth);
 
 		if (fortification.citadelHealth != 0)
-			curB->si.wallState[EWallPart::KEEP] = static_cast<EWallState>(fortification.citadelHealth);
+			currentBattle->si.wallState[EWallPart::KEEP] = static_cast<EWallState>(fortification.citadelHealth);
 
 		if (fortification.upperTowerHealth != 0)
-			curB->si.wallState[EWallPart::UPPER_TOWER] = static_cast<EWallState>(fortification.upperTowerHealth);
+			currentBattle->si.wallState[EWallPart::UPPER_TOWER] = static_cast<EWallState>(fortification.upperTowerHealth);
 
 		if (fortification.lowerTowerHealth != 0)
-			curB->si.wallState[EWallPart::BOTTOM_TOWER] = static_cast<EWallState>(fortification.lowerTowerHealth);
+			currentBattle->si.wallState[EWallPart::BOTTOM_TOWER] = static_cast<EWallState>(fortification.lowerTowerHealth);
 	}
 
 	//randomize obstacles
-	if (layout.obstaclesAllowed && !town)
+	if (layout.obstaclesAllowed && (!town || !town->hasFort()))
  	{
 		RandGen r{};
 		auto ourRand = [&](){ return r.rand(); };
@@ -221,12 +212,12 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 		auto appropriateAbsoluteObstacle = [&](int id)
 		{
 			const auto * info = Obstacle(id).getInfo();
-			return info && info->isAbsoluteObstacle && info->isAppropriate(curB->terrainType, battlefieldType);
+			return info && info->isAbsoluteObstacle && info->isAppropriate(currentBattle->terrainType, battlefieldType);
 		};
 		auto appropriateUsualObstacle = [&](int id)
 		{
 			const auto * info = Obstacle(id).getInfo();
-			return info && !info->isAbsoluteObstacle && info->isAppropriate(curB->terrainType, battlefieldType);
+			return info && !info->isAbsoluteObstacle && info->isAppropriate(currentBattle->terrainType, battlefieldType);
 		};
 
 		if(r.rand(1,100) <= 40) //put cliff-like obstacle
@@ -237,8 +228,8 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 				auto obstPtr = std::make_shared<CObstacleInstance>();
 				obstPtr->obstacleType = CObstacleInstance::ABSOLUTE_OBSTACLE;
 				obstPtr->ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle);
-				obstPtr->uniqueID = static_cast<si32>(curB->obstacles.size());
-				curB->obstacles.push_back(obstPtr);
+				obstPtr->uniqueID = static_cast<si32>(currentBattle->obstacles.size());
+				currentBattle->obstacles.push_back(obstPtr);
 
 				for(BattleHex blocked : obstPtr->getBlockedTiles())
 					blockedTiles.push_back(blocked);
@@ -256,7 +247,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 			while(tilesToBlock > 0)
 			{
 				RangeGenerator obidgen(0, VLC->obstacleHandler->size() - 1, ourRand);
-				auto tileAccessibility = curB->getAccessibility();
+				auto tileAccessibility = currentBattle->getAccessibility();
 				const int obid = obidgen.getSuchNumber(appropriateUsualObstacle);
 				const ObstacleInfo &obi = *Obstacle(obid).getInfo();
 
@@ -290,8 +281,8 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 				auto obstPtr = std::make_shared<CObstacleInstance>();
 				obstPtr->ID = obid;
 				obstPtr->pos = posgenerator.getSuchNumber(validPosition);
-				obstPtr->uniqueID = static_cast<si32>(curB->obstacles.size());
-				curB->obstacles.push_back(obstPtr);
+				obstPtr->uniqueID = static_cast<si32>(currentBattle->obstacles.size());
+				currentBattle->obstacles.push_back(obstPtr);
 
 				for(BattleHex blocked : obstPtr->getBlockedTiles())
 					blockedTiles.push_back(blocked);
@@ -315,7 +306,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 			CreatureID cre = warMachineArt->artType->getWarMachine();
 
 			if(cre != CreatureID::NONE)
-				curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(cre, 1), side, SlotID::WAR_MACHINES_SLOT, hex);
+				currentBattle->generateNewStack(currentBattle->nextUnitId(), CStackBasicDescriptor(cre, 1), side, SlotID::WAR_MACHINES_SLOT, hex);
 		}
 	};
 
@@ -353,7 +344,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 			const BattleHex & pos = layout.units.at(side).at(k);
 
 			if (pos.isValid())
-				curB->generateNewStack(curB->nextUnitId(), *i->second, side, i->first, pos);
+				currentBattle->generateNewStack(currentBattle->nextUnitId(), *i->second, side, i->first, pos);
 		}
 	}
 
@@ -362,20 +353,20 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 	{
 		if (heroes[i] && heroes[i]->commander && heroes[i]->commander->alive)
 		{
-			curB->generateNewStack(curB->nextUnitId(), *heroes[i]->commander, i, SlotID::COMMANDER_SLOT_PLACEHOLDER, layout.commanders.at(i));
+			currentBattle->generateNewStack(currentBattle->nextUnitId(), *heroes[i]->commander, i, SlotID::COMMANDER_SLOT_PLACEHOLDER, layout.commanders.at(i));
 		}
 	}
 
-	if (curB->town)
+	if (currentBattle->town)
 	{
-		if (curB->town->fortificationsLevel().citadelHealth != 0)
-			curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_CENTRAL_TOWER);
+		if (currentBattle->town->fortificationsLevel().citadelHealth != 0)
+			currentBattle->generateNewStack(currentBattle->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_CENTRAL_TOWER);
 
-		if (curB->town->fortificationsLevel().upperTowerHealth != 0)
-			curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_UPPER_TOWER);
+		if (currentBattle->town->fortificationsLevel().upperTowerHealth != 0)
+			currentBattle->generateNewStack(currentBattle->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_UPPER_TOWER);
 
-		if (curB->town->fortificationsLevel().lowerTowerHealth != 0)
-			curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_BOTTOM_TOWER);
+		if (currentBattle->town->fortificationsLevel().lowerTowerHealth != 0)
+			currentBattle->generateNewStack(currentBattle->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_BOTTOM_TOWER);
 
 		//Moat generating is done on server
 	}
@@ -390,15 +381,15 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 
 	for(const std::shared_ptr<Bonus> & bonus : bgInfo->bonuses)
 	{
-		curB->addNewBonus(bonus);
+		currentBattle->addNewBonus(bonus);
 	}
 
 	//native terrain bonuses
 	auto nativeTerrain = std::make_shared<CreatureTerrainLimiter>();
 	
-	curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::STACKS_SPEED, BonusSource::TERRAIN_NATIVE, 1,  BonusSourceID())->addLimiter(nativeTerrain));
-	curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::ATTACK))->addLimiter(nativeTerrain));
-	curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::DEFENSE))->addLimiter(nativeTerrain));
+	currentBattle->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::STACKS_SPEED, BonusSource::TERRAIN_NATIVE, 1,  BonusSourceID())->addLimiter(nativeTerrain));
+	currentBattle->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::ATTACK))->addLimiter(nativeTerrain));
+	currentBattle->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::DEFENSE))->addLimiter(nativeTerrain));
 	//////////////////////////////////////////////////////////////////////////
 
 	//tactics
@@ -428,21 +419,21 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
 			logGlobal->warn("Double tactics is not implemented, only attacker will have tactics!");
 		if(tacticsSkillDiffAttacker > 0)
 		{
-			curB->tacticsSide = BattleSide::ATTACKER;
+			currentBattle->tacticsSide = BattleSide::ATTACKER;
 			//bonus specifies distance you can move beyond base row; this allows 100% compatibility with HMM3 mechanics
-			curB->tacticDistance = 1 + tacticsSkillDiffAttacker;
+			currentBattle->tacticDistance = 1 + tacticsSkillDiffAttacker;
 		}
 		else if(tacticsSkillDiffDefender > 0)
 		{
-			curB->tacticsSide = BattleSide::DEFENDER;
+			currentBattle->tacticsSide = BattleSide::DEFENDER;
 			//bonus specifies distance you can move beyond base row; this allows 100% compatibility with HMM3 mechanics
-			curB->tacticDistance = 1 + tacticsSkillDiffDefender;
+			currentBattle->tacticDistance = 1 + tacticsSkillDiffDefender;
 		}
 		else
-			curB->tacticDistance = 0;
+			currentBattle->tacticDistance = 0;
 	}
 
-	return curB;
+	return currentBattle;
 }
 
 const CGHeroInstance * BattleInfo::getHero(const PlayerColor & player) const

+ 15 - 5
server/battles/BattleProcessor.cpp

@@ -28,10 +28,12 @@
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/mapping/CMap.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/modding/IdentifierStorage.h"
 #include "../../lib/networkPacks/PacksForClient.h"
 #include "../../lib/networkPacks/PacksForClientBattle.h"
 #include "../../lib/CPlayerState.h"
+#include <vstd/RNG.h>
 
 BattleProcessor::BattleProcessor(CGameHandler * gameHandler)
 	: gameHandler(gameHandler)
@@ -156,16 +158,24 @@ BattleID BattleProcessor::setupBattle(int3 tile, BattleSideArray<const CArmedIns
 {
 	const auto & t = *gameHandler->getTile(tile);
 	TerrainId terrain = t.terType->getId();
-	if (gameHandler->gameState()->map->isCoastalTile(tile)) //coastal tile is always ground
+	if (town)
+		terrain = town->getNativeTerrain();
+	else if (gameHandler->gameState()->map->isCoastalTile(tile)) //coastal tile is always ground
 		terrain = ETerrainId::SAND;
 
-	BattleField terType = gameHandler->gameState()->battleGetBattlefieldType(tile, gameHandler->getRandomGenerator());
-	if (heroes[BattleSide::ATTACKER] && heroes[BattleSide::ATTACKER]->boat && heroes[BattleSide::DEFENDER] && heroes[BattleSide::DEFENDER]->boat)
-		terType = BattleField(*VLC->identifiers()->getIdentifier("core", "battlefield.ship_to_ship"));
+	BattleField battlefieldType = gameHandler->gameState()->battleGetBattlefieldType(tile, gameHandler->getRandomGenerator());
+
+	if (town)
+	{
+		const TerrainType* terrainData = VLC->terrainTypeHandler->getById(terrain);
+		battlefieldType = BattleField(*RandomGeneratorUtil::nextItem(terrainData->battleFields, gameHandler->getRandomGenerator()));
+	}
+	else if (heroes[BattleSide::ATTACKER] && heroes[BattleSide::ATTACKER]->boat && heroes[BattleSide::DEFENDER] && heroes[BattleSide::DEFENDER]->boat)
+		battlefieldType = BattleField(*VLC->identifiers()->getIdentifier("core", "battlefield.ship_to_ship"));
 
 	//send info about battles
 	BattleStart bs;
-	bs.info = BattleInfo::setupBattle(tile, terrain, terType, armies, heroes, layout, town);
+	bs.info = BattleInfo::setupBattle(tile, terrain, battlefieldType, armies, heroes, layout, town);
 	bs.battleID = gameHandler->gameState()->nextBattleID;
 
 	engageIntoBattle(bs.info->getSide(BattleSide::ATTACKER).color);