1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

Fix battle setup for sieges without forts

- Update the if statement in BattleProcessor::SetupBattle to allow
  obstacles in battlefields during sieges without forts.
- Ensure towns without forts use the native battlegrounds.
- Refactor variable name from curB to currentBattle for clarity.
This commit is contained in:
kodobi 2024-10-16 14:14:47 +02:00
parent 52e0899d6f
commit c576438a95
2 changed files with 59 additions and 58 deletions

View File

@ -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) 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; CMP_stack cmpst;
auto * curB = new BattleInfo(layout); auto * currentBattle = new BattleInfo(layout);
for(auto i : { BattleSide::LEFT_SIDE, BattleSide::RIGHT_SIDE}) 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; currentBattle->tile = tile;
curB->battlefieldType = battlefieldType; currentBattle->terrainType = terrain;
curB->round = -2; currentBattle->battlefieldType = battlefieldType;
curB->activeStack = -1; currentBattle->round = -2;
curB->replayAllowed = false; currentBattle->activeStack = -1;
currentBattle->replayAllowed = false;
if(town) currentBattle->town = town;
{
curB->town = town;
curB->terrainType = town->getNativeTerrain();
}
else
{
curB->town = nullptr;
curB->terrainType = terrain;
}
//setting up siege obstacles //setting up siege obstacles
if (town && town->fortificationsLevel().wallsHealth != 0) if (town && town->fortificationsLevel().wallsHealth != 0)
{ {
auto fortification = town->fortificationsLevel(); 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}) 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) 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) 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) 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 //randomize obstacles
if (layout.obstaclesAllowed && !town) if (layout.obstaclesAllowed && (!town || !town->hasFort()))
{ {
RandGen r{}; RandGen r{};
auto ourRand = [&](){ return r.rand(); }; auto ourRand = [&](){ return r.rand(); };
@ -221,12 +212,12 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
auto appropriateAbsoluteObstacle = [&](int id) auto appropriateAbsoluteObstacle = [&](int id)
{ {
const auto * info = Obstacle(id).getInfo(); 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) auto appropriateUsualObstacle = [&](int id)
{ {
const auto * info = Obstacle(id).getInfo(); 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 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>(); auto obstPtr = std::make_shared<CObstacleInstance>();
obstPtr->obstacleType = CObstacleInstance::ABSOLUTE_OBSTACLE; obstPtr->obstacleType = CObstacleInstance::ABSOLUTE_OBSTACLE;
obstPtr->ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle); obstPtr->ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle);
obstPtr->uniqueID = static_cast<si32>(curB->obstacles.size()); obstPtr->uniqueID = static_cast<si32>(currentBattle->obstacles.size());
curB->obstacles.push_back(obstPtr); currentBattle->obstacles.push_back(obstPtr);
for(BattleHex blocked : obstPtr->getBlockedTiles()) for(BattleHex blocked : obstPtr->getBlockedTiles())
blockedTiles.push_back(blocked); blockedTiles.push_back(blocked);
@ -256,7 +247,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
while(tilesToBlock > 0) while(tilesToBlock > 0)
{ {
RangeGenerator obidgen(0, VLC->obstacleHandler->size() - 1, ourRand); RangeGenerator obidgen(0, VLC->obstacleHandler->size() - 1, ourRand);
auto tileAccessibility = curB->getAccessibility(); auto tileAccessibility = currentBattle->getAccessibility();
const int obid = obidgen.getSuchNumber(appropriateUsualObstacle); const int obid = obidgen.getSuchNumber(appropriateUsualObstacle);
const ObstacleInfo &obi = *Obstacle(obid).getInfo(); 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>(); auto obstPtr = std::make_shared<CObstacleInstance>();
obstPtr->ID = obid; obstPtr->ID = obid;
obstPtr->pos = posgenerator.getSuchNumber(validPosition); obstPtr->pos = posgenerator.getSuchNumber(validPosition);
obstPtr->uniqueID = static_cast<si32>(curB->obstacles.size()); obstPtr->uniqueID = static_cast<si32>(currentBattle->obstacles.size());
curB->obstacles.push_back(obstPtr); currentBattle->obstacles.push_back(obstPtr);
for(BattleHex blocked : obstPtr->getBlockedTiles()) for(BattleHex blocked : obstPtr->getBlockedTiles())
blockedTiles.push_back(blocked); blockedTiles.push_back(blocked);
@ -315,7 +306,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
CreatureID cre = warMachineArt->artType->getWarMachine(); CreatureID cre = warMachineArt->artType->getWarMachine();
if(cre != CreatureID::NONE) 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); const BattleHex & pos = layout.units.at(side).at(k);
if (pos.isValid()) 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) 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) if (currentBattle->town->fortificationsLevel().citadelHealth != 0)
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_CENTRAL_TOWER); 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) if (currentBattle->town->fortificationsLevel().upperTowerHealth != 0)
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_UPPER_TOWER); 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) if (currentBattle->town->fortificationsLevel().lowerTowerHealth != 0)
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_BOTTOM_TOWER); 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 //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) for(const std::shared_ptr<Bonus> & bonus : bgInfo->bonuses)
{ {
curB->addNewBonus(bonus); currentBattle->addNewBonus(bonus);
} }
//native terrain bonuses //native terrain bonuses
auto nativeTerrain = std::make_shared<CreatureTerrainLimiter>(); 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)); currentBattle->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)); currentBattle->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::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::DEFENSE))->addLimiter(nativeTerrain));
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//tactics //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!"); logGlobal->warn("Double tactics is not implemented, only attacker will have tactics!");
if(tacticsSkillDiffAttacker > 0) 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 //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) 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 //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 else
curB->tacticDistance = 0; currentBattle->tacticDistance = 0;
} }
return curB; return currentBattle;
} }
const CGHeroInstance * BattleInfo::getHero(const PlayerColor & player) const const CGHeroInstance * BattleInfo::getHero(const PlayerColor & player) const

View File

@ -28,10 +28,12 @@
#include "../../lib/gameState/CGameState.h" #include "../../lib/gameState/CGameState.h"
#include "../../lib/mapping/CMap.h" #include "../../lib/mapping/CMap.h"
#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/modding/IdentifierStorage.h" #include "../../lib/modding/IdentifierStorage.h"
#include "../../lib/networkPacks/PacksForClient.h" #include "../../lib/networkPacks/PacksForClient.h"
#include "../../lib/networkPacks/PacksForClientBattle.h" #include "../../lib/networkPacks/PacksForClientBattle.h"
#include "../../lib/CPlayerState.h" #include "../../lib/CPlayerState.h"
#include "vstd/RNG.h"
BattleProcessor::BattleProcessor(CGameHandler * gameHandler) BattleProcessor::BattleProcessor(CGameHandler * gameHandler)
: gameHandler(gameHandler) : gameHandler(gameHandler)
@ -156,16 +158,24 @@ BattleID BattleProcessor::setupBattle(int3 tile, BattleSideArray<const CArmedIns
{ {
const auto & t = *gameHandler->getTile(tile); const auto & t = *gameHandler->getTile(tile);
TerrainId terrain = t.terType->getId(); 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; terrain = ETerrainId::SAND;
BattleField terType = gameHandler->gameState()->battleGetBattlefieldType(tile, gameHandler->getRandomGenerator()); BattleField battlefieldType = 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")); 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 //send info about battles
BattleStart bs; 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; bs.battleID = gameHandler->gameState()->nextBattleID;
engageIntoBattle(bs.info->getSide(BattleSide::ATTACKER).color); engageIntoBattle(bs.info->getSide(BattleSide::ATTACKER).color);