mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Merge pull request #4530 from IvanSavenko/town_fortifications
Support for configurable town fortifications
This commit is contained in:
commit
39e5ba32f6
@ -229,7 +229,7 @@ BattleAction CBattleAI::useCatapult(const BattleID & battleID, const CStack * st
|
||||
{
|
||||
auto wallState = cb->getBattle(battleID)->battleGetWallState(wallPart);
|
||||
|
||||
if(wallState == EWallState::REINFORCED || wallState == EWallState::INTACT || wallState == EWallState::DAMAGED)
|
||||
if(wallState != EWallState::NONE && wallState != EWallState::DESTROYED)
|
||||
{
|
||||
targetHex = cb->getBattle(battleID)->wallPartToBattleHex(wallPart);
|
||||
break;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../../lib/CStopWatch.h"
|
||||
#include "../../lib/CThreadHelper.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/entities/building/TownFortifications.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/spells/ISpellMechanics.h"
|
||||
#include "../../lib/battle/BattleStateInfoForRetreat.h"
|
||||
@ -265,7 +266,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
|
||||
if(score <= EvaluationResult::INEFFECTIVE_SCORE
|
||||
&& !stack->hasBonusOfType(BonusType::FLYING)
|
||||
&& stack->unitSide() == BattleSide::ATTACKER
|
||||
&& cb->getBattle(battleID)->battleGetSiegeLevel() >= CGTownInstance::CITADEL)
|
||||
&& cb->getBattle(battleID)->battleGetFortifications().hasMoat)
|
||||
{
|
||||
auto brokenWallMoat = getBrokenWallMoatHexes();
|
||||
|
||||
|
@ -161,7 +161,7 @@ ECreatureAnimType AttackAnimation::findValidGroup( const std::vector<ECreatureAn
|
||||
const CCreature * AttackAnimation::getCreature() const
|
||||
{
|
||||
if (attackingStack->unitType()->getId() == CreatureID::ARROW_TOWERS)
|
||||
return owner.siegeController->getTurretCreature();
|
||||
return owner.siegeController->getTurretCreature(attackingStack->initialPosition);
|
||||
else
|
||||
return attackingStack->unitType();
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ BattleInterface::BattleInterface(const BattleID & battleID, const CCreatureSet *
|
||||
this->army2 = army2;
|
||||
|
||||
const CGTownInstance *town = getBattle()->battleGetDefendedTown();
|
||||
if(town && town->hasFort())
|
||||
if(town && town->fortificationsLevel().wallsHealth > 0)
|
||||
siegeController.reset(new BattleSiegeController(*this, town));
|
||||
|
||||
windowObject = std::make_shared<BattleWindow>(*this);
|
||||
|
@ -160,7 +160,7 @@ const CCreature & BattleProjectileController::getShooter(const CStack * stack) c
|
||||
const CCreature * creature = stack->unitType();
|
||||
|
||||
if(creature->getId() == CreatureID::ARROW_TOWERS)
|
||||
creature = owner.siegeController->getTurretCreature();
|
||||
creature = owner.siegeController->getTurretCreature(stack->initialPosition);
|
||||
|
||||
if(creature->animation.missileFrameAngles.empty())
|
||||
{
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CStack.h"
|
||||
#include "../../lib/entities/building/TownFortifications.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||
|
||||
@ -34,30 +35,27 @@ ImagePath BattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisual
|
||||
{
|
||||
auto getImageIndex = [&]() -> int
|
||||
{
|
||||
bool isTower = (what == EWallVisual::KEEP || what == EWallVisual::BOTTOM_TOWER || what == EWallVisual::UPPER_TOWER);
|
||||
int health = static_cast<int>(state);
|
||||
|
||||
switch (state)
|
||||
switch (what)
|
||||
{
|
||||
case EWallState::REINFORCED :
|
||||
return 1;
|
||||
case EWallState::INTACT :
|
||||
if (town->hasBuilt(BuildingID::CASTLE))
|
||||
return 2; // reinforced walls were damaged
|
||||
else
|
||||
return 1;
|
||||
case EWallState::DAMAGED :
|
||||
// towers don't have separate image here - INTACT and DAMAGED is 1, DESTROYED is 2
|
||||
if (isTower)
|
||||
return 1;
|
||||
else
|
||||
return 2;
|
||||
case EWallState::DESTROYED :
|
||||
if (isTower)
|
||||
return 2;
|
||||
else
|
||||
case EWallVisual::KEEP:
|
||||
case EWallVisual::BOTTOM_TOWER:
|
||||
case EWallVisual::UPPER_TOWER:
|
||||
if (health > 0)
|
||||
return 1;
|
||||
else
|
||||
return 2;
|
||||
default:
|
||||
{
|
||||
int healthTotal = town->fortificationsLevel().wallsHealth;
|
||||
if (healthTotal == health)
|
||||
return 1;
|
||||
if (health > 0)
|
||||
return 2;
|
||||
return 3;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const std::string & prefix = town->town->clientInfo.siegePrefix;
|
||||
@ -128,16 +126,15 @@ ImagePath BattleSiegeController::getBattleBackgroundName() const
|
||||
|
||||
bool BattleSiegeController::getWallPieceExistence(EWallVisual::EWallVisual what) const
|
||||
{
|
||||
//FIXME: use this instead of buildings test?
|
||||
//ui8 siegeLevel = owner.curInt->cb->battleGetSiegeLevel();
|
||||
const auto & fortifications = town->fortificationsLevel();
|
||||
|
||||
switch (what)
|
||||
{
|
||||
case EWallVisual::MOAT: return town->hasBuilt(BuildingID::CITADEL) && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT).isValid();
|
||||
case EWallVisual::MOAT_BANK: return town->hasBuilt(BuildingID::CITADEL) && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT_BANK).isValid();
|
||||
case EWallVisual::KEEP_BATTLEMENT: return town->hasBuilt(BuildingID::CITADEL) && owner.getBattle()->battleGetWallState(EWallPart::KEEP) != EWallState::DESTROYED;
|
||||
case EWallVisual::UPPER_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && owner.getBattle()->battleGetWallState(EWallPart::UPPER_TOWER) != EWallState::DESTROYED;
|
||||
case EWallVisual::BOTTOM_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && owner.getBattle()->battleGetWallState(EWallPart::BOTTOM_TOWER) != EWallState::DESTROYED;
|
||||
case EWallVisual::MOAT: return fortifications.hasMoat && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT).isValid();
|
||||
case EWallVisual::MOAT_BANK: return fortifications.hasMoat && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT_BANK).isValid();
|
||||
case EWallVisual::KEEP_BATTLEMENT: return fortifications.citadelHealth > 0 && owner.getBattle()->battleGetWallState(EWallPart::KEEP) != EWallState::DESTROYED;
|
||||
case EWallVisual::UPPER_BATTLEMENT: return fortifications.upperTowerHealth > 0 && owner.getBattle()->battleGetWallState(EWallPart::UPPER_TOWER) != EWallState::DESTROYED;
|
||||
case EWallVisual::BOTTOM_BATTLEMENT: return fortifications.lowerTowerHealth > 0 && owner.getBattle()->battleGetWallState(EWallPart::BOTTOM_TOWER) != EWallState::DESTROYED;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
@ -186,9 +183,19 @@ BattleSiegeController::BattleSiegeController(BattleInterface & owner, const CGTo
|
||||
}
|
||||
}
|
||||
|
||||
const CCreature *BattleSiegeController::getTurretCreature() const
|
||||
const CCreature *BattleSiegeController::getTurretCreature(BattleHex position) const
|
||||
{
|
||||
return town->town->clientInfo.siegeShooter.toCreature();
|
||||
switch (position)
|
||||
{
|
||||
case BattleHex::CASTLE_CENTRAL_TOWER:
|
||||
return town->fortificationsLevel().citadelShooter.toCreature();
|
||||
case BattleHex::CASTLE_UPPER_TOWER:
|
||||
return town->fortificationsLevel().upperTowerShooter.toCreature();
|
||||
case BattleHex::CASTLE_BOTTOM_TOWER:
|
||||
return town->fortificationsLevel().lowerTowerShooter.toCreature();
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unable to select shooter for tower at " + std::to_string(position.hex));
|
||||
}
|
||||
|
||||
Point BattleSiegeController::getTurretCreaturePosition( BattleHex position ) const
|
||||
|
@ -104,7 +104,7 @@ public:
|
||||
/// queries from other battle controllers
|
||||
bool isAttackableByCatapult(BattleHex hex) const;
|
||||
ImagePath getBattleBackgroundName() const;
|
||||
const CCreature *getTurretCreature() const;
|
||||
const CCreature *getTurretCreature(BattleHex turretPosition) const;
|
||||
Point getTurretCreaturePosition( BattleHex position ) const;
|
||||
|
||||
const CGTownInstance *getSiegedTown() const;
|
||||
|
@ -191,7 +191,7 @@ void BattleStacksController::stackAdded(const CStack * stack, bool instant)
|
||||
{
|
||||
assert(owner.siegeController);
|
||||
|
||||
const CCreature *turretCreature = owner.siegeController->getTurretCreature();
|
||||
const CCreature *turretCreature = owner.siegeController->getTurretCreature(stack->initialPosition);
|
||||
|
||||
stackAnimation[stack->unitId()] = AnimationControls::getAnimation(turretCreature);
|
||||
stackAnimation[stack->unitId()]->pos.h = turretCreatureAnimationHeight;
|
||||
|
@ -19,9 +19,31 @@
|
||||
]
|
||||
},
|
||||
"shipyard": { "id" : 6 },
|
||||
"fort": { "id" : 7 },
|
||||
"citadel": { "id" : 8, "upgrades" : "fort" },
|
||||
"castle": { "id" : 9, "upgrades" : "citadel" },
|
||||
"fort": {
|
||||
"id" : 7,
|
||||
"fortifications" : {
|
||||
"wallsHealth" : 2
|
||||
}
|
||||
},
|
||||
|
||||
"citadel": {
|
||||
"id" : 8,
|
||||
"upgrades" : "fort",
|
||||
"fortifications" : {
|
||||
"citadelHealth" : 2,
|
||||
"hasMoat" : true
|
||||
}
|
||||
},
|
||||
|
||||
"castle": {
|
||||
"id" : 9,
|
||||
"upgrades" : "citadel",
|
||||
"fortifications" : {
|
||||
"wallsHealth" : 3,
|
||||
"upperTowerHealth" : 2,
|
||||
"lowerTowerHealth" : 2
|
||||
}
|
||||
},
|
||||
|
||||
"villageHall": {
|
||||
"id" : 10,
|
||||
|
@ -65,6 +65,22 @@
|
||||
"description" : "Optional, configuration of building that can be activated by visiting hero",
|
||||
"$ref" : "rewardable.json"
|
||||
},
|
||||
"firtufications" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"description" : "Fortifications provided by this buildings, if any",
|
||||
"properties" : {
|
||||
"citadelShooter" : { "type" : "string", "description" : "Creature ID of shooter located in central keep (citadel). Used only if citadel is present." },
|
||||
"upperTowerShooter" : { "type" : "string", "description" : "Creature ID of shooter located in upper tower. Used only if upper tower is present." },
|
||||
"lowerTowerShooter" : { "type" : "string", "description" : "Creature ID of shooter located in lower tower. Used only if lower tower is present." },
|
||||
|
||||
"wallsHealth" : { "type" : "number", "description" : "Maximum health of destructible walls. Walls are only present if their health is above zero" },
|
||||
"citadelHealth" : { "type" : "number", "description" : "Maximum health of central tower or 0 if not present. Requires walls presence." },
|
||||
"upperTowerHealth" : { "type" : "number", "description" : "Maximum health of upper tower or 0 if not present. Requires walls presence." },
|
||||
"lowerTowerHealth" : { "type" : "number", "description" : "Maximum health of lower tower or 0 if not present. Requires walls presence." },
|
||||
"hasMoat" : { "type" : "boolean","description" : "If set to true, moat will be placed in front of the walls. Requires walls presence." }
|
||||
}
|
||||
},
|
||||
"cost" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
|
@ -157,9 +157,33 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config
|
||||
"produce" : {
|
||||
"sulfur" : 1,
|
||||
"gold" : 2000
|
||||
},
|
||||
},
|
||||
|
||||
// Optional, allows this building to add fortifications during siege
|
||||
"fortifications" : {
|
||||
// Maximum health of destructible walls. Walls are only present if their health is above zero".
|
||||
// Presence of walls is required for all other fortification types
|
||||
"wallsHealth" : 3,
|
||||
|
||||
//determine how this building can be built. Possible values are:
|
||||
// If set to true, moat will be placed in front of the walls. Requires walls presence.
|
||||
"hasMoat" : true
|
||||
|
||||
// Maximum health of central tower or 0 if not present. Requires walls presence.
|
||||
"citadelHealth" : 2,
|
||||
// Maximum health of upper tower or 0 if not present. Requires walls presence.
|
||||
"upperTowerHealth" : 2,
|
||||
// Maximum health of lower tower or 0 if not present. Requires walls presence.
|
||||
"lowerTowerHealth" : 2,
|
||||
|
||||
// Creature ID of shooter located in central keep (citadel). Used only if citadel is present.
|
||||
"citadelShooter" : "archer",
|
||||
// Creature ID of shooter located in upper tower. Used only if upper tower is present.
|
||||
"upperTowerShooter" : "archer",
|
||||
// Creature ID of shooter located in lower tower. Used only if lower tower is present.
|
||||
"lowerTowerShooter" : "archer",
|
||||
},
|
||||
|
||||
//determine how this building can be built. Possible values are:
|
||||
// normal - default value. Fulfill requirements, use resources, spend one day
|
||||
// auto - building appears when all requirements are built
|
||||
// special - building can not be built manually
|
||||
|
@ -450,6 +450,7 @@ set(lib_MAIN_HEADERS
|
||||
|
||||
entities/building/CBuilding.h
|
||||
entities/building/CBuildingHandler.h
|
||||
entities/building/TownFortifications.h
|
||||
entities/faction/CFaction.h
|
||||
entities/faction/CTown.h
|
||||
entities/faction/CTownHandler.h
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "bonuses/Updaters.h"
|
||||
#include "../CStack.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../entities/building/TownFortifications.h"
|
||||
#include "../filesystem/Filesystem.h"
|
||||
#include "../mapObjects/CGTownInstance.h"
|
||||
#include "../texts/CGeneralTextHandler.h"
|
||||
@ -202,28 +203,25 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
|
||||
}
|
||||
|
||||
//setting up siege obstacles
|
||||
if (town && town->hasFort())
|
||||
if (town && town->fortificationsLevel().wallsHealth != 0)
|
||||
{
|
||||
auto fortification = town->fortificationsLevel();
|
||||
|
||||
curB->si.gateState = EGateState::CLOSED;
|
||||
|
||||
curB->si.wallState[EWallPart::GATE] = EWallState::INTACT;
|
||||
|
||||
for(const auto wall : {EWallPart::BOTTOM_WALL, EWallPart::BELOW_GATE, EWallPart::OVER_GATE, EWallPart::UPPER_WALL})
|
||||
{
|
||||
if (town->hasBuilt(BuildingID::CASTLE))
|
||||
curB->si.wallState[wall] = EWallState::REINFORCED;
|
||||
else
|
||||
curB->si.wallState[wall] = EWallState::INTACT;
|
||||
}
|
||||
curB->si.wallState[wall] = static_cast<EWallState>(fortification.wallsHealth);
|
||||
|
||||
if (town->hasBuilt(BuildingID::CITADEL))
|
||||
curB->si.wallState[EWallPart::KEEP] = EWallState::INTACT;
|
||||
if (fortification.citadelHealth != 0)
|
||||
curB->si.wallState[EWallPart::KEEP] = static_cast<EWallState>(fortification.citadelHealth);
|
||||
|
||||
if (town->hasBuilt(BuildingID::CASTLE))
|
||||
{
|
||||
curB->si.wallState[EWallPart::UPPER_TOWER] = EWallState::INTACT;
|
||||
curB->si.wallState[EWallPart::BOTTOM_TOWER] = EWallState::INTACT;
|
||||
}
|
||||
if (fortification.upperTowerHealth != 0)
|
||||
curB->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);
|
||||
}
|
||||
|
||||
//randomize obstacles
|
||||
@ -369,7 +367,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
|
||||
handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH1, 52);
|
||||
handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH2, 18);
|
||||
handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH3, 154);
|
||||
if(town && town->hasFort())
|
||||
if(town && town->fortificationsLevel().wallsHealth > 0)
|
||||
handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH4, 120);
|
||||
}
|
||||
|
||||
@ -419,18 +417,16 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
|
||||
|
||||
}
|
||||
|
||||
if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL)
|
||||
if (curB->town)
|
||||
{
|
||||
// keep tower
|
||||
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_CENTRAL_TOWER);
|
||||
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 (curB->town->fortLevel() >= CGTownInstance::CASTLE)
|
||||
{
|
||||
// lower tower + upper 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 (curB->town->fortificationsLevel().lowerTowerHealth != 0)
|
||||
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_BOTTOM_TOWER);
|
||||
}
|
||||
|
||||
//Moat generating is done on server
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "CObstacleInstance.h"
|
||||
#include "DamageCalculator.h"
|
||||
#include "PossiblePlayerBattleAction.h"
|
||||
#include "../entities/building/TownFortifications.h"
|
||||
#include "../spells/ObstacleCasterProxy.h"
|
||||
#include "../spells/ISpellMechanics.h"
|
||||
#include "../spells/Problem.h"
|
||||
@ -237,7 +238,7 @@ bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest,
|
||||
bool CBattleInfoCallback::battleHasWallPenalty(const IBonusBearer * shooter, BattleHex shooterPosition, BattleHex destHex) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(false);
|
||||
if(!battleGetSiegeLevel())
|
||||
if(battleGetFortifications().wallsHealth == 0)
|
||||
return false;
|
||||
|
||||
const std::string cachingStrNoWallPenalty = "type_NO_WALL_PENALTY";
|
||||
@ -288,7 +289,7 @@ std::vector<PossiblePlayerBattleAction> CBattleInfoCallback::getClientActionsFor
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::MOVE_STACK);
|
||||
|
||||
const auto * siegedTown = battleGetDefendedTown();
|
||||
if(siegedTown && siegedTown->hasFort() && stack->hasBonusOfType(BonusType::CATAPULT)) //TODO: check shots
|
||||
if(siegedTown && siegedTown->fortificationsLevel().wallsHealth > 0 && stack->hasBonusOfType(BonusType::CATAPULT)) //TODO: check shots
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::CATAPULT);
|
||||
if(stack->hasBonusOfType(BonusType::HEALER))
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::HEAL);
|
||||
@ -943,7 +944,7 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
|
||||
}
|
||||
|
||||
//gate -> should be before stacks
|
||||
if(battleGetSiegeLevel() > 0)
|
||||
if(battleGetFortifications().wallsHealth > 0)
|
||||
{
|
||||
EAccessibility accessibility = EAccessibility::ACCESSIBLE;
|
||||
switch(battleGetGateState())
|
||||
@ -975,7 +976,7 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
|
||||
}
|
||||
|
||||
//walls
|
||||
if(battleGetSiegeLevel() > 0)
|
||||
if(battleGetFortifications().wallsHealth > 0)
|
||||
{
|
||||
static const int permanentlyLocked[] = {12, 45, 62, 112, 147, 165};
|
||||
for(auto hex : permanentlyLocked)
|
||||
@ -1612,7 +1613,7 @@ bool CBattleInfoCallback::isWallPartAttackable(EWallPart wallPart) const
|
||||
if(isWallPartPotentiallyAttackable(wallPart))
|
||||
{
|
||||
auto wallState = battleGetWallState(wallPart);
|
||||
return (wallState == EWallState::REINFORCED || wallState == EWallState::INTACT || wallState == EWallState::DAMAGED);
|
||||
return (wallState != EWallState::NONE && wallState != EWallState::DESTROYED);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -9,12 +9,15 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "CBattleInfoEssentials.h"
|
||||
|
||||
#include "../CStack.h"
|
||||
#include "BattleInfo.h"
|
||||
#include "CObstacleInstance.h"
|
||||
#include "../mapObjects/CGTownInstance.h"
|
||||
#include "../gameState/InfoAboutArmy.h"
|
||||
|
||||
#include "../constants/EntityIdentifiers.h"
|
||||
#include "../entities/building/TownFortifications.h"
|
||||
#include "../gameState/InfoAboutArmy.h"
|
||||
#include "../mapObjects/CGTownInstance.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -345,10 +348,10 @@ bool CBattleInfoEssentials::playerHasAccessToHeroInfo(const PlayerColor & player
|
||||
return false;
|
||||
}
|
||||
|
||||
ui8 CBattleInfoEssentials::battleGetSiegeLevel() const
|
||||
TownFortifications CBattleInfoEssentials::battleGetFortifications() const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(CGTownInstance::NONE);
|
||||
return getBattle()->getDefendedTown() ? getBattle()->getDefendedTown()->fortLevel() : CGTownInstance::NONE;
|
||||
RETURN_IF_NOT_BATTLE(TownFortifications());
|
||||
return getBattle()->getDefendedTown() ? getBattle()->getDefendedTown()->fortificationsLevel() : TownFortifications();
|
||||
}
|
||||
|
||||
bool CBattleInfoEssentials::battleCanSurrender(const PlayerColor & player) const
|
||||
@ -371,7 +374,7 @@ bool CBattleInfoEssentials::battleHasHero(BattleSide side) const
|
||||
EWallState CBattleInfoEssentials::battleGetWallState(EWallPart partOfWall) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(EWallState::NONE);
|
||||
if(battleGetSiegeLevel() == CGTownInstance::NONE)
|
||||
if(battleGetFortifications().wallsHealth == 0)
|
||||
return EWallState::NONE;
|
||||
|
||||
return getBattle()->getWallState(partOfWall);
|
||||
@ -380,7 +383,7 @@ EWallState CBattleInfoEssentials::battleGetWallState(EWallPart partOfWall) const
|
||||
EGateState CBattleInfoEssentials::battleGetGateState() const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(EGateState::NONE);
|
||||
if(battleGetSiegeLevel() == CGTownInstance::NONE)
|
||||
if(battleGetFortifications().wallsHealth == 0)
|
||||
return EGateState::NONE;
|
||||
|
||||
return getBattle()->getGateState();
|
||||
@ -389,7 +392,7 @@ EGateState CBattleInfoEssentials::battleGetGateState() const
|
||||
bool CBattleInfoEssentials::battleIsGatePassable() const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(true);
|
||||
if(battleGetSiegeLevel() == CGTownInstance::NONE)
|
||||
if(battleGetFortifications().wallsHealth == 0)
|
||||
return true;
|
||||
|
||||
return battleGetGateState() == EGateState::OPENED || battleGetGateState() == EGateState::DESTROYED;
|
||||
|
@ -18,6 +18,7 @@ class CGHeroInstance;
|
||||
class CStack;
|
||||
class IBonusBearer;
|
||||
struct InfoAboutHero;
|
||||
struct TownFortifications;
|
||||
class CArmedInstance;
|
||||
|
||||
using TStacks = std::vector<const CStack *>;
|
||||
@ -75,7 +76,7 @@ public:
|
||||
BattleSide playerToSide(const PlayerColor & player) const;
|
||||
PlayerColor sideToPlayer(BattleSide side) const;
|
||||
bool playerHasAccessToHeroInfo(const PlayerColor & player, const CGHeroInstance * h) const;
|
||||
ui8 battleGetSiegeLevel() const; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
|
||||
TownFortifications battleGetFortifications() const;
|
||||
bool battleHasHero(BattleSide side) const;
|
||||
uint32_t battleCastSpells(BattleSide side) const; //how many spells has given side cast
|
||||
const CGHeroInstance * battleGetFightingHero(BattleSide side) const; //deprecated for players callback, easy to get wrong
|
||||
|
@ -9,6 +9,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "TownFortifications.h"
|
||||
|
||||
#include "../../constants/EntityIdentifiers.h"
|
||||
#include "../../LogicalExpression.h"
|
||||
#include "../../ResourceSet.h"
|
||||
@ -35,6 +37,7 @@ public:
|
||||
TResources produce;
|
||||
TRequired requirements;
|
||||
ArtifactID warMachine;
|
||||
TownFortifications fortifications;
|
||||
std::set<EMarketMode> marketModes;
|
||||
|
||||
BuildingID bid; //structure ID
|
||||
|
49
lib/entities/building/TownFortifications.h
Normal file
49
lib/entities/building/TownFortifications.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* TownFortifications.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
|
||||
|
||||
#include "../../constants/EntityIdentifiers.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
struct TownFortifications
|
||||
{
|
||||
CreatureID citadelShooter;
|
||||
CreatureID upperTowerShooter;
|
||||
CreatureID lowerTowerShooter;
|
||||
SpellID moatSpell;
|
||||
|
||||
int8_t wallsHealth = 0;
|
||||
int8_t citadelHealth = 0;
|
||||
int8_t upperTowerHealth = 0;
|
||||
int8_t lowerTowerHealth = 0;
|
||||
bool hasMoat = false;
|
||||
|
||||
const TownFortifications & operator +=(const TownFortifications & other)
|
||||
{
|
||||
if (other.citadelShooter.hasValue())
|
||||
citadelShooter = other.citadelShooter;
|
||||
if (other.upperTowerShooter.hasValue())
|
||||
upperTowerShooter = other.upperTowerShooter;
|
||||
if (other.lowerTowerShooter.hasValue())
|
||||
lowerTowerShooter = other.lowerTowerShooter;
|
||||
if (other.moatSpell.hasValue())
|
||||
moatSpell = other.moatSpell;
|
||||
|
||||
wallsHealth = std::max(wallsHealth, other.wallsHealth);
|
||||
citadelHealth = std::max(citadelHealth, other.citadelHealth);
|
||||
upperTowerHealth = std::max(upperTowerHealth, other.upperTowerHealth);
|
||||
lowerTowerHealth = std::max(lowerTowerHealth, other.lowerTowerHealth);
|
||||
hasMoat = hasMoat || other.hasMoat;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -18,7 +18,7 @@
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
CTown::CTown()
|
||||
: faction(nullptr), mageLevel(0), primaryRes(0), moatAbility(SpellID::NONE), defaultTavernChance(0)
|
||||
: faction(nullptr), mageLevel(0), primaryRes(0), defaultTavernChance(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../building/TownFortifications.h"
|
||||
#include "../../ConstTransitivePtr.h"
|
||||
#include "../../Point.h"
|
||||
#include "../../constants/EntityIdentifiers.h"
|
||||
@ -70,7 +71,10 @@ public:
|
||||
ui32 mageLevel; //max available mage guild level
|
||||
GameResID primaryRes;
|
||||
CreatureID warMachineDeprecated;
|
||||
SpellID moatAbility;
|
||||
|
||||
/// Base state of fortifications for empty town.
|
||||
/// Used to define shooter units and moat spell ID
|
||||
TownFortifications fortifications;
|
||||
|
||||
// default chance for hero of specific class to appear in tavern, if field "tavern" was not set
|
||||
// resulting chance = sqrt(town.chance * heroClass.chance)
|
||||
@ -99,7 +103,6 @@ public:
|
||||
|
||||
std::string siegePrefix;
|
||||
std::vector<Point> siegePositions;
|
||||
CreatureID siegeShooter; // shooter creature ID
|
||||
std::string towerIconSmall;
|
||||
std::string towerIconLarge;
|
||||
|
||||
|
@ -299,6 +299,31 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
|
||||
ret->resources = TResources(source["cost"]);
|
||||
ret->produce = TResources(source["produce"]);
|
||||
|
||||
const JsonNode & fortifications = source["fortifications"];
|
||||
if (!fortifications.isNull())
|
||||
{
|
||||
VLC->identifiers()->requestIdentifierOptional("creature", fortifications["citadelShooter"], [=](si32 identifier)
|
||||
{
|
||||
ret->fortifications.citadelShooter = CreatureID(identifier);
|
||||
});
|
||||
|
||||
VLC->identifiers()->requestIdentifierOptional("creature", fortifications["upperTowerShooter"], [=](si32 identifier)
|
||||
{
|
||||
ret->fortifications.upperTowerShooter = CreatureID(identifier);
|
||||
});
|
||||
|
||||
VLC->identifiers()->requestIdentifierOptional("creature", fortifications["lowerTowerShooter"], [=](si32 identifier)
|
||||
{
|
||||
ret->fortifications.lowerTowerShooter = CreatureID(identifier);
|
||||
});
|
||||
|
||||
ret->fortifications.wallsHealth = fortifications["wallsHealth"].Integer();
|
||||
ret->fortifications.citadelHealth = fortifications["citadelHealth"].Integer();
|
||||
ret->fortifications.upperTowerHealth = fortifications["upperTowerHealth"].Integer();
|
||||
ret->fortifications.lowerTowerHealth = fortifications["lowerTowerHealth"].Integer();
|
||||
ret->fortifications.hasMoat = fortifications["hasMoat"].Bool();
|
||||
}
|
||||
|
||||
loadBuildingBonuses(source["bonuses"], ret->buildingBonuses, ret);
|
||||
|
||||
if(!source["configuration"].isNull())
|
||||
@ -477,7 +502,9 @@ void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source) const
|
||||
, town.faction->getNameTranslated()
|
||||
, (*VLC->creh)[crId]->getNameSingularTranslated());
|
||||
|
||||
town.clientInfo.siegeShooter = crId;
|
||||
town.fortifications.citadelShooter = crId;
|
||||
town.fortifications.upperTowerShooter = crId;
|
||||
town.fortifications.lowerTowerShooter = crId;
|
||||
});
|
||||
|
||||
auto & pos = town.clientInfo.siegePositions;
|
||||
@ -581,14 +608,14 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
|
||||
{
|
||||
VLC->identifiers()->requestIdentifier( "spell", source["moatAbility"], [=](si32 ability)
|
||||
{
|
||||
town->moatAbility = SpellID(ability);
|
||||
town->fortifications.moatSpell = SpellID(ability);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
VLC->identifiers()->requestIdentifier( source.getModScope(), "spell", "castleMoat", [=](si32 ability)
|
||||
{
|
||||
town->moatAbility = SpellID(ability);
|
||||
town->fortifications.moatSpell = SpellID(ability);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -248,6 +248,19 @@ bool CGTownInstance::hasCapitol() const
|
||||
return hasBuilt(BuildingID::CAPITOL);
|
||||
}
|
||||
|
||||
TownFortifications CGTownInstance::fortificationsLevel() const
|
||||
{
|
||||
auto result = town->fortifications;
|
||||
|
||||
for (auto const & buildingID : builtBuildings)
|
||||
result += town->buildings.at(buildingID)->fortifications;
|
||||
|
||||
if (result.wallsHealth == 0)
|
||||
return TownFortifications();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CGTownInstance::CGTownInstance(IGameCallback *cb):
|
||||
CGDwelling(cb),
|
||||
town(nullptr),
|
||||
@ -387,8 +400,6 @@ void CGTownInstance::initializeConfigurableBuildings(vstd::RNG & rand)
|
||||
|
||||
DamageRange CGTownInstance::getTowerDamageRange() const
|
||||
{
|
||||
assert(hasBuilt(BuildingID::CASTLE));
|
||||
|
||||
// http://heroes.thelazy.net/wiki/Arrow_tower
|
||||
// base damage, irregardless of town level
|
||||
static constexpr int baseDamage = 6;
|
||||
@ -405,8 +416,6 @@ DamageRange CGTownInstance::getTowerDamageRange() const
|
||||
|
||||
DamageRange CGTownInstance::getKeepDamageRange() const
|
||||
{
|
||||
assert(hasBuilt(BuildingID::CITADEL));
|
||||
|
||||
// http://heroes.thelazy.net/wiki/Arrow_tower
|
||||
// base damage, irregardless of town level
|
||||
static constexpr int baseDamage = 10;
|
||||
|
@ -19,6 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
class CCastleEvent;
|
||||
class CTown;
|
||||
class TownBuildingInstance;
|
||||
struct TownFortifications;
|
||||
class TownRewardableBuildingInstance;
|
||||
struct DamageRange;
|
||||
|
||||
@ -162,6 +163,7 @@ public:
|
||||
|
||||
bool needsLastStack() const override;
|
||||
CGTownInstance::EFortLevel fortLevel() const;
|
||||
TownFortifications fortificationsLevel() const;
|
||||
int hallLevel() const; // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
|
||||
int mageGuildLevel() const; // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
|
||||
int getHordeLevel(const int & HID) const; //HID - 0 or 1; returns creature level or -1 if that horde structure is not present
|
||||
|
@ -190,6 +190,12 @@ void CIdentifierStorage::requestIdentifier(const JsonNode & name, const std::fun
|
||||
requestIdentifier(ObjectCallback::fromNameWithType(name.getModScope(), name.String(), callback, false));
|
||||
}
|
||||
|
||||
void CIdentifierStorage::requestIdentifierOptional(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const
|
||||
{
|
||||
if (!name.isNull())
|
||||
requestIdentifier(type, name, callback);
|
||||
}
|
||||
|
||||
void CIdentifierStorage::tryRequestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const
|
||||
{
|
||||
requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, true));
|
||||
|
@ -84,6 +84,8 @@ public:
|
||||
void requestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const;
|
||||
void requestIdentifier(const JsonNode & name, const std::function<void(si32)> & callback) const;
|
||||
|
||||
void requestIdentifierOptional(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const;
|
||||
|
||||
/// try to request ID. If ID with such name won't be loaded, callback function will not be called
|
||||
void tryRequestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const;
|
||||
void tryRequestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const;
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "CPlayerState.h"
|
||||
#include "TerrainHandler.h"
|
||||
#include "entities/building/CBuilding.h"
|
||||
#include "entities/building/TownFortifications.h"
|
||||
#include "mapObjects/CBank.h"
|
||||
#include "mapObjects/CGCreature.h"
|
||||
#include "mapObjects/CGMarket.h"
|
||||
@ -2338,7 +2339,7 @@ void CatapultAttack::applyBattle(IBattleState * battleState)
|
||||
if(!town)
|
||||
return;
|
||||
|
||||
if(town->fortLevel() == CGTownInstance::NONE)
|
||||
if(town->fortificationsLevel().wallsHealth == 0)
|
||||
return;
|
||||
|
||||
for(const auto & part : attackedParts)
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "../../battle/CBattleInfoCallback.h"
|
||||
#include "../../battle/Unit.h"
|
||||
#include "../../mapObjects/CGTownInstance.h"
|
||||
#include "../../entities/building/TownFortifications.h"
|
||||
#include "../../networkPacks/PacksForClientBattle.h"
|
||||
#include "../../serializer/JsonSerializeFormat.h"
|
||||
|
||||
@ -39,7 +40,7 @@ bool Catapult::applicable(Problem & problem, const Mechanics * m) const
|
||||
return m->adaptProblem(ESpellCastProblem::NO_APPROPRIATE_TARGET, problem);
|
||||
}
|
||||
|
||||
if(CGTownInstance::NONE == town->fortLevel())
|
||||
if(town->fortificationsLevel().wallsHealth == 0)
|
||||
{
|
||||
return m->adaptProblem(ESpellCastProblem::NO_APPROPRIATE_TARGET, problem);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "../../bonuses/Limiters.h"
|
||||
#include "../../battle/IBattleState.h"
|
||||
#include "../../battle/CBattleInfoCallback.h"
|
||||
#include "../../entities/building/TownFortifications.h"
|
||||
#include "../../json/JsonBonus.h"
|
||||
#include "../../serializer/JsonSerializeFormat.h"
|
||||
#include "../../networkPacks/PacksForClient.h"
|
||||
@ -85,7 +86,7 @@ void Moat::convertBonus(const Mechanics * m, std::vector<Bonus> & converted) con
|
||||
//Moat battlefield effect is always permanent
|
||||
nb.duration = BonusDuration::ONE_BATTLE;
|
||||
|
||||
if(m->battle()->battleGetDefendedTown() && m->battle()->battleGetSiegeLevel() >= CGTownInstance::CITADEL)
|
||||
if(m->battle()->battleGetDefendedTown() && m->battle()->battleGetFortifications().hasMoat)
|
||||
{
|
||||
nb.sid = BonusSourceID(m->battle()->battleGetDefendedTown()->town->buildings.at(BuildingID::CITADEL)->getUniqueTypeID());
|
||||
nb.source = BonusSource::TOWN_STRUCTURE;
|
||||
@ -109,7 +110,7 @@ void Moat::apply(ServerCallback * server, const Mechanics * m, const EffectTarge
|
||||
{
|
||||
assert(m->isMassive());
|
||||
assert(m->battle()->battleGetDefendedTown());
|
||||
if(m->isMassive() && m->battle()->battleGetSiegeLevel() >= CGTownInstance::CITADEL)
|
||||
if(m->isMassive() && m->battle()->battleGetFortifications().hasMoat)
|
||||
{
|
||||
EffectTarget moat;
|
||||
placeObstacles(server, m, moat);
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "../../battle/IBattleState.h"
|
||||
#include "../../battle/CBattleInfoCallback.h"
|
||||
#include "../../entities/building/TownFortifications.h"
|
||||
#include "../../networkPacks/PacksForClientBattle.h"
|
||||
#include "../../serializer/JsonSerializeFormat.h"
|
||||
|
||||
@ -239,7 +240,7 @@ bool Obstacle::isHexAvailable(const CBattleInfoCallback * cb, const BattleHex &
|
||||
if(i->obstacleType != CObstacleInstance::MOAT)
|
||||
return false;
|
||||
|
||||
if(cb->battleGetSiegeLevel() != 0)
|
||||
if(cb->battleGetFortifications().wallsHealth != 0)
|
||||
{
|
||||
EWallPart part = cb->battleHexToWallPart(hex);
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "../../battle/IBattleState.h"
|
||||
#include "../../battle/CBattleInfoCallback.h"
|
||||
#include "../../battle/Unit.h"
|
||||
#include "../../entities/building/TownFortifications.h"
|
||||
#include "../../networkPacks/PacksForClientBattle.h"
|
||||
#include "../../serializer/JsonSerializeFormat.h"
|
||||
|
||||
@ -64,7 +65,7 @@ bool Teleport::applicable(Problem & problem, const Mechanics * m, const EffectTa
|
||||
if(!targetHex.isValid() || !m->battle()->getAccessibility(targetUnit).accessible(targetHex, targetUnit))
|
||||
return m->adaptProblem(ESpellCastProblem::WRONG_SPELL_TARGET, problem);
|
||||
|
||||
if(m->battle()->battleGetSiegeLevel() && !(isWallPassable && isMoatPassable))
|
||||
if(m->battle()->battleGetFortifications().wallsHealth > 0 && !(isWallPassable && isMoatPassable))
|
||||
{
|
||||
return !m->battle()->battleHasPenaltyOnLine(target[0].hexValue, target[1].hexValue, !isWallPassable, !isMoatPassable);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../../lib/battle/CObstacleInstance.h"
|
||||
#include "../../lib/battle/IBattleState.h"
|
||||
#include "../../lib/battle/BattleAction.h"
|
||||
#include "../../lib/entities/building/TownFortifications.h"
|
||||
#include "../../lib/gameState/CGameState.h"
|
||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||
#include "../../lib/networkPacks/SetStackEffect.h"
|
||||
@ -651,7 +652,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
|
||||
|
||||
bool canUseGate = false;
|
||||
auto dbState = battle.battleGetGateState();
|
||||
if(battle.battleGetSiegeLevel() > 0 && curStack->unitSide() == BattleSide::DEFENDER &&
|
||||
if(battle.battleGetFortifications().wallsHealth > 0 && curStack->unitSide() == BattleSide::DEFENDER &&
|
||||
dbState != EGateState::DESTROYED &&
|
||||
dbState != EGateState::BLOCKED)
|
||||
{
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "../../lib/GameSettings.h"
|
||||
#include "../../lib/battle/CBattleInfoCallback.h"
|
||||
#include "../../lib/battle/IBattleState.h"
|
||||
#include "../../lib/entities/building/TownFortifications.h"
|
||||
#include "../../lib/gameState/CGameState.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||
@ -114,13 +115,18 @@ void BattleFlowProcessor::tryPlaceMoats(const CBattleInfoCallback & battle)
|
||||
{
|
||||
const auto * town = battle.battleGetDefendedTown();
|
||||
|
||||
if (!town)
|
||||
return;
|
||||
|
||||
const auto & fortifications = town->fortificationsLevel();
|
||||
|
||||
//Moat should be initialized here, because only here we can use spellcasting
|
||||
if (town && town->fortLevel() >= CGTownInstance::CITADEL)
|
||||
if (fortifications.hasMoat)
|
||||
{
|
||||
const auto * h = battle.battleGetFightingHero(BattleSide::DEFENDER);
|
||||
const auto * actualCaster = h ? static_cast<const spells::Caster*>(h) : nullptr;
|
||||
auto moatCaster = spells::SilentCaster(battle.sideToPlayer(BattleSide::DEFENDER), actualCaster);
|
||||
auto cast = spells::BattleCast(&battle, &moatCaster, spells::Mode::PASSIVE, town->town->moatAbility.toSpell());
|
||||
auto cast = spells::BattleCast(&battle, &moatCaster, spells::Mode::PASSIVE, fortifications.moatSpell.toSpell());
|
||||
auto target = spells::Target();
|
||||
cast.cast(gameHandler->spellEnv, target);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "../../lib/battle/CBattleInfoCallback.h"
|
||||
#include "../../lib/battle/CObstacleInstance.h"
|
||||
#include "../../lib/battle/BattleInfo.h"
|
||||
#include "../../lib/entities/building/TownFortifications.h"
|
||||
#include "../../lib/gameState/CGameState.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
@ -192,7 +193,7 @@ BattleID BattleProcessor::setupBattle(int3 tile, BattleSideArray<const CArmedIns
|
||||
bool BattleProcessor::checkBattleStateChanges(const CBattleInfoCallback & battle)
|
||||
{
|
||||
//check if drawbridge state need to be changes
|
||||
if (battle.battleGetSiegeLevel() > 0)
|
||||
if (battle.battleGetFortifications().wallsHealth > 0)
|
||||
updateGateState(battle);
|
||||
|
||||
if (resultProcessor->battleIsEnding(battle))
|
||||
|
Loading…
Reference in New Issue
Block a user