From c576438a958b7787f0f768130bd6f9d1f6879c1d Mon Sep 17 00:00:00 2001 From: kodobi Date: Wed, 16 Oct 2024 14:14:47 +0200 Subject: [PATCH] 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. --- lib/battle/BattleInfo.cpp | 97 ++++++++++++++---------------- server/battles/BattleProcessor.cpp | 20 ++++-- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 8a8eb633c..10a4988f5 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -161,54 +161,45 @@ struct RangeGenerator BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const BattleField & battlefieldType, BattleSideArray armies, BattleSideArray 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 & stacks = (curB->stacks); + std::vector & 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(fortification.wallsHealth); + currentBattle->si.wallState[wall] = static_cast(fortification.wallsHealth); if (fortification.citadelHealth != 0) - curB->si.wallState[EWallPart::KEEP] = static_cast(fortification.citadelHealth); + currentBattle->si.wallState[EWallPart::KEEP] = static_cast(fortification.citadelHealth); if (fortification.upperTowerHealth != 0) - curB->si.wallState[EWallPart::UPPER_TOWER] = static_cast(fortification.upperTowerHealth); + currentBattle->si.wallState[EWallPart::UPPER_TOWER] = static_cast(fortification.upperTowerHealth); if (fortification.lowerTowerHealth != 0) - curB->si.wallState[EWallPart::BOTTOM_TOWER] = static_cast(fortification.lowerTowerHealth); + currentBattle->si.wallState[EWallPart::BOTTOM_TOWER] = static_cast(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(); obstPtr->obstacleType = CObstacleInstance::ABSOLUTE_OBSTACLE; obstPtr->ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle); - obstPtr->uniqueID = static_cast(curB->obstacles.size()); - curB->obstacles.push_back(obstPtr); + obstPtr->uniqueID = static_cast(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(); obstPtr->ID = obid; obstPtr->pos = posgenerator.getSuchNumber(validPosition); - obstPtr->uniqueID = static_cast(curB->obstacles.size()); - curB->obstacles.push_back(obstPtr); + obstPtr->uniqueID = static_cast(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 : bgInfo->bonuses) { - curB->addNewBonus(bonus); + currentBattle->addNewBonus(bonus); } //native terrain bonuses auto nativeTerrain = std::make_shared(); - curB->addNewBonus(std::make_shared(BonusDuration::ONE_BATTLE, BonusType::STACKS_SPEED, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID())->addLimiter(nativeTerrain)); - curB->addNewBonus(std::make_shared(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::ATTACK))->addLimiter(nativeTerrain)); - curB->addNewBonus(std::make_shared(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::DEFENSE))->addLimiter(nativeTerrain)); + currentBattle->addNewBonus(std::make_shared(BonusDuration::ONE_BATTLE, BonusType::STACKS_SPEED, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID())->addLimiter(nativeTerrain)); + currentBattle->addNewBonus(std::make_shared(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::ATTACK))->addLimiter(nativeTerrain)); + currentBattle->addNewBonus(std::make_shared(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 diff --git a/server/battles/BattleProcessor.cpp b/server/battles/BattleProcessor.cpp index 87b9deb74..597933013 100644 --- a/server/battles/BattleProcessor.cpp +++ b/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, BattleSideArraygetTile(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);