From 0f5f4c69ec860de92ca55a103b28c5ef0b655dd5 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Wed, 5 Apr 2023 18:56:28 +0300 Subject: [PATCH] vcmi: specialize native terrain entity Specialize native terrain entity for all object that have native terrain. Allow creatures to take global bonuses into account when checking for native terrain. --- cmake_modules/VCMI_lib.cmake | 1 + include/vcmi/Entity.h | 1 + lib/BasicTypes.cpp | 20 ++++++++++++++++++++ lib/CCreatureHandler.cpp | 4 ++-- lib/CCreatureSet.cpp | 28 ++++++++++++++++++++++++++++ lib/CCreatureSet.h | 10 +++++++++- lib/CStack.cpp | 7 ++++++- lib/CStack.h | 2 ++ lib/battle/CUnitState.cpp | 5 +++++ lib/battle/CUnitState.h | 2 ++ lib/battle/Unit.cpp | 21 +++++++++++++++++++++ lib/battle/Unit.h | 8 +++++++- lib/mapObjects/CGHeroInstance.cpp | 12 +++++++++++- lib/mapObjects/CGHeroInstance.h | 9 +++++++-- 14 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 lib/BasicTypes.cpp diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index d5d415fdc..cb70110af 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -162,6 +162,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/vstd/StringUtils.cpp + ${MAIN_LIB_DIR}/BasicTypes.cpp ${MAIN_LIB_DIR}/BattleFieldHandler.cpp ${MAIN_LIB_DIR}/CAndroidVMHelper.cpp ${MAIN_LIB_DIR}/CArtHandler.cpp diff --git a/include/vcmi/Entity.h b/include/vcmi/Entity.h index edf18148e..6c90f421c 100644 --- a/include/vcmi/Entity.h +++ b/include/vcmi/Entity.h @@ -28,6 +28,7 @@ class DLL_LINKAGE WithNativeTerrain public: virtual Identifier getNativeTerrain() const = 0; virtual FactionID getFaction() const = 0; + virtual bool isItNativeTerrain(Identifier terrain) const; }; class DLL_LINKAGE Entity diff --git a/lib/BasicTypes.cpp b/lib/BasicTypes.cpp new file mode 100644 index 000000000..cb8fb65e5 --- /dev/null +++ b/lib/BasicTypes.cpp @@ -0,0 +1,20 @@ +/* + * BasicTypes.cpp, 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 + * + */ + +#include "StdInc.h" +#include "GameConstants.h" + +#include + +bool INativeTerrainProvider::isItNativeTerrain(TerrainId terrain) const +{ + auto native = getNativeTerrain(); + return native == terrain || native == ETerrainId::ANY_TERRAIN; +} \ No newline at end of file diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index a70e19f73..6d62306e4 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -348,9 +348,9 @@ TerrainId CCreature::getNativeTerrain() const //this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses //and in the CGHeroInstance::getNativeTerrain() to setup movement bonuses or/and penalties. - return hasBonus(selectorNoTerrainPenalty, cachingStringNoTerrainPenalty) + return getBonusBearer()->hasBonus(selectorNoTerrainPenalty, cachingStringNoTerrainPenalty) ? TerrainId(ETerrainId::ANY_TERRAIN) - : VLC->factions()->getByIndex(faction)->getNativeTerrain(); + : VLC->factions()->getById(getFaction())->getNativeTerrain(); } void CCreature::updateFrom(const JsonNode & data) diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index 7e39e07b0..d6938e7fa 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -25,6 +25,9 @@ #include "serializer/JsonSerializeFormat.h" #include "NetPacksBase.h" +#include +#include + VCMI_LIB_NAMESPACE_BEGIN @@ -912,6 +915,31 @@ void CStackInstance::serializeJson(JsonSerializeFormat & handler) } } +FactionID CStackInstance::getFaction() const +{ + if(type) + return type->getFaction(); + + return FactionID::NEUTRAL; +} + +const IBonusBearer* CStackInstance::getBonusBearer() const +{ + return this; +} + +TerrainId CStackInstance::getNativeTerrain() const +{ + const std::string cachingStringNoTerrainPenalty = "type_NO_TERRAIN_PENALTY_sANY"; + static const auto selectorNoTerrainPenalty = Selector::typeSubtype(Bonus::NO_TERRAIN_PENALTY, static_cast(ETerrainId::ANY_TERRAIN)); + + //this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses + //and in the CGHeroInstance::getNativeTerrain() to setup movement bonuses or/and penalties. + return getBonusBearer()->hasBonus(selectorNoTerrainPenalty, cachingStringNoTerrainPenalty) + ? TerrainId(ETerrainId::ANY_TERRAIN) + : VLC->factions()->getById(getFaction())->getNativeTerrain(); +} + CCommanderInstance::CCommanderInstance() { init(); diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index 60a83dab8..372091f6a 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -14,6 +14,8 @@ #include "CArtHandler.h" #include "CCreatureHandler.h" +#include + VCMI_LIB_NAMESPACE_BEGIN class JsonNode; @@ -61,7 +63,7 @@ public: void serializeJson(JsonSerializeFormat & handler); }; -class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet +class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet, public WithBonuses, public WithNativeTerrain { protected: const CArmedInstance *_armyObj; //stack must be part of some army, army must be part of some object @@ -92,6 +94,12 @@ public: std::string bonusToString(const std::shared_ptr& bonus, bool description) const override; // how would bonus description look for this particular type of node std::string bonusToGraphics(const std::shared_ptr & bonus) const; //file name of graphics from StackSkills , in future possibly others + //WithBonuses + const IBonusBearer* getBonusBearer() const override; + //WithNativeTerrain + FactionID getFaction() const override; + TerrainId getNativeTerrain() const override; + virtual ui64 getPower() const; CCreature::CreatureQuantityId getQuantityID() const; std::string getQuantityTXT(bool capitalized = true) const; diff --git a/lib/CStack.cpp b/lib/CStack.cpp index 45ef7636c..37aa30aac 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -78,7 +78,7 @@ void CStack::localInit(BattleInfo * battleInfo) attachTo(*army); attachTo(const_cast(*type)); } - nativeTerrain = type->getNativeTerrain(); //save nativeTerrain in the variable on the battle start to avoid dead lock + nativeTerrain = getNativeTerrain(); //save nativeTerrain in the variable on the battle start to avoid dead lock CUnitState::localInit(this); //it causes execution of the CStack::isOnNativeTerrain where nativeTerrain will be considered position = initialPosition; } @@ -338,6 +338,11 @@ int32_t CStack::unitBaseAmount() const return baseAmount; } +const IBonusBearer* CStack::getBonusBearer() const +{ + return this; +} + bool CStack::unitHasAmmoCart(const battle::Unit * unit) const { for(const CStack * st : battle->stacks) diff --git a/lib/CStack.h b/lib/CStack.h index dd52c473b..e1ba5efea 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -84,6 +84,8 @@ public: void spendMana(ServerCallback * server, const int spellCost) const override; + const IBonusBearer* getBonusBearer() const override; + PlayerColor getOwner() const override { return this->owner; diff --git a/lib/battle/CUnitState.cpp b/lib/battle/CUnitState.cpp index 6d9411ac2..a247af335 100644 --- a/lib/battle/CUnitState.cpp +++ b/lib/battle/CUnitState.cpp @@ -413,6 +413,11 @@ int32_t CUnitState::creatureIconIndex() const return unitType()->getIconIndex(); } +FactionID CUnitState::getFaction() const +{ + return unitType()->getFaction(); +} + int32_t CUnitState::getCasterUnitId() const { return static_cast(unitId()); diff --git a/lib/battle/CUnitState.h b/lib/battle/CUnitState.h index ee130edb6..3233b8b67 100644 --- a/lib/battle/CUnitState.h +++ b/lib/battle/CUnitState.h @@ -247,6 +247,8 @@ public: void localInit(const IUnitEnvironment * env_); void serializeJson(JsonSerializeFormat & handler); + FactionID getFaction() const override; + void afterAttack(bool ranged, bool counter); void afterNewRound(); diff --git a/lib/battle/Unit.cpp b/lib/battle/Unit.cpp index 8e6a82eba..b56b2a7cd 100644 --- a/lib/battle/Unit.cpp +++ b/lib/battle/Unit.cpp @@ -18,6 +18,9 @@ #include "../serializer/JsonDeserializer.h" #include "../serializer/JsonSerializer.h" +#include +#include + VCMI_LIB_NAMESPACE_BEGIN namespace battle @@ -43,6 +46,24 @@ std::string Unit::getDescription() const return fmt.str(); } +//TODO: deduplicate these functions +const IBonusBearer* Unit::getBonusBearer() const +{ + return this; +} + +TerrainId Unit::getNativeTerrain() const +{ + const std::string cachingStringNoTerrainPenalty = "type_NO_TERRAIN_PENALTY_sANY"; + static const auto selectorNoTerrainPenalty = Selector::typeSubtype(Bonus::NO_TERRAIN_PENALTY, static_cast(ETerrainId::ANY_TERRAIN)); + + //this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses + //and in the CGHeroInstance::getNativeTerrain() to setup movement bonuses or/and penalties. + return getBonusBearer()->hasBonus(selectorNoTerrainPenalty, cachingStringNoTerrainPenalty) + ? TerrainId(ETerrainId::ANY_TERRAIN) + : VLC->factions()->getById(getFaction())->getNativeTerrain(); +} + std::vector Unit::getSurroundingHexes(BattleHex assumedPosition) const { BattleHex hex = (assumedPosition != BattleHex::INVALID) ? assumedPosition : getPosition(); //use hypothetical position diff --git a/lib/battle/Unit.h b/lib/battle/Unit.h index 424e4f628..63ca6f051 100644 --- a/lib/battle/Unit.h +++ b/lib/battle/Unit.h @@ -10,6 +10,7 @@ #pragma once +#include #include #include "../HeroBonus.h" @@ -40,7 +41,7 @@ namespace BattlePhases class CUnitState; -class DLL_LINKAGE Unit : public IUnitInfo, public spells::Caster, public virtual IBonusBearer +class DLL_LINKAGE Unit : public IUnitInfo, public spells::Caster, public virtual IBonusBearer, public WithBonuses, public WithNativeTerrain { public: virtual ~Unit(); @@ -126,6 +127,11 @@ public: int getRawSurrenderCost() const; + //WithBonuses + const IBonusBearer* getBonusBearer() const override; + //WithNativeTerrain + TerrainId getNativeTerrain() const override; + //NOTE: save could possibly be const, but this requires heavy changes to Json serialization, //also this method should be called only after modifying object virtual void save(JsonNode & data) = 0; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 126521780..30a8d2a84 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -84,6 +84,16 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & f return static_cast(ret); } +FactionID CGHeroInstance::getFaction() const +{ + return FactionID(type->heroClass->faction); +} + +const IBonusBearer* CGHeroInstance::getBonusBearer() const +{ + return this; +} + TerrainId CGHeroInstance::getNativeTerrain() const { // NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army. @@ -96,7 +106,7 @@ TerrainId CGHeroInstance::getNativeTerrain() const for(const auto & stack : stacks) { - TerrainId stackNativeTerrain = stack.second->type->getNativeTerrain(); //consider terrain bonuses e.g. Lodestar. + TerrainId stackNativeTerrain = stack.second->getNativeTerrain(); //consider terrain bonuses e.g. Lodestar. if(stackNativeTerrain == ETerrainId::NONE) continue; diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 66dfd1523..c5f06c691 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -40,7 +40,7 @@ public: }; -class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster +class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster, public WithBonuses, public WithNativeTerrain { // We serialize heroes into JSON for crossover friend class CCampaignState; @@ -156,7 +156,9 @@ public: bool needsLastStack()const override; ui32 getTileCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling - TerrainId getNativeTerrain() const; + //WithNativeTerrain + FactionID getFaction() const override; + TerrainId getNativeTerrain() const override; int getLowestCreatureSpeed() const; si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day si32 getManaNewTurn() const; //calculate how much mana this hero is going to have the next day @@ -246,6 +248,9 @@ public: std::string nodeName() const override; si32 manaLimit() const override; + ///WithBonuses + const IBonusBearer* getBonusBearer() const override; + CBonusSystemNode * whereShouldBeAttachedOnSiege(const bool isBattleOutsideTown) const; CBonusSystemNode * whereShouldBeAttachedOnSiege(CGameState * gs);