diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 7a99c8251..9180e9efa 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -135,9 +135,19 @@ std::string CCreature::nodeName() const return "\"" + namePl + "\""; } -bool CCreature::isItNativeTerrain(int terrain) const +bool CCreature::isItNativeTerrain(ETerrainType::EETerrainType terrain) const { - return VLC->townh->factions[faction]->nativeTerrain == terrain; + auto native = getNativeTerrain(); + return native == terrain || native == ETerrainType::ANY_TERRAIN; +} + +ETerrainType::EETerrainType CCreature::getNativeTerrain() const +{ + //this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses + //and in the CGHeroInstance::getNativeTerrain() to setup mevement bonuses or/and penalties. + return hasBonusOfType(Bonus::NO_TERRAIN_PENALTY) ? + ETerrainType::ANY_TERRAIN + : (ETerrainType::EETerrainType)VLC->townh->factions[faction]->nativeTerrain; } void CCreature::setId(CreatureID ID) @@ -210,10 +220,11 @@ CCreatureHandler::CCreatureHandler() VLC->creh = this; allCreatures.setDescription("All creatures"); + allCreatures.setNodeType(CBonusSystemNode::ENodeTypes::ALL_CREATURES); creaturesOfLevel[0].setDescription("Creatures of unnormalized tier"); + for(int i = 1; i < ARRAY_COUNT(creaturesOfLevel); i++) creaturesOfLevel[i].setDescription("Creatures of tier " + boost::lexical_cast(i)); - loadCommanders(); } @@ -1194,6 +1205,11 @@ void CCreatureHandler::addBonusForAllCreatures(std::shared_ptr b) allCreatures.addNewBonus(b); } +void CCreatureHandler::removeBonusesFromAllCreatures() +{ + allCreatures.removeBonuses(Selector::all); +} + void CCreatureHandler::buildBonusTreeForTiers() { for(CCreature *c : creatures) diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index 21f434ee8..dd8fd3e6d 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -114,7 +114,14 @@ public: ArtifactID warMachine; - bool isItNativeTerrain(int terrain) const; + bool isItNativeTerrain(ETerrainType::EETerrainType terrain) const; + /** + Returns creature native terrain considering some terrain bonuses. + @param considerBonus is used to avoid Dead Lock when this method is called inside getAllBonuses + considerBonus = true is called from Pathfinder and fills actual nativeTerrain considering bonus(es). + considerBonus = false is called on Battle init and returns already prepared nativeTerrain without Bonus system calling. + */ + ETerrainType::EETerrainType getNativeTerrain() const; bool isDoubleWide() const; //returns true if unit is double wide on battlefield bool isFlying() const; //returns true if it is a flying unit bool isShooting() const; //returns true if unit can shoot @@ -241,6 +248,7 @@ public: CreatureID pickRandomMonster(CRandomGenerator & rand, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any void addBonusForTier(int tier, std::shared_ptr b); //tier must be <1-7> void addBonusForAllCreatures(std::shared_ptr b); + void removeBonusesFromAllCreatures(); CCreatureHandler(); ~CCreatureHandler(); diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index ec8261d2a..848fa2068 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1746,6 +1746,8 @@ void CGameState::initTowns() void CGameState::initMapObjects() { logGlobal->debug("\tObject initialization"); + VLC->creh->removeBonusesFromAllCreatures(); + // objCaller->preInit(); for(CGObjectInstance *obj : map->objects) { diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index efd2fd3ff..f2206a217 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -515,7 +515,7 @@ struct DLL_LINKAGE TurnInfo TConstBonusListPtr bonuses; mutable int maxMovePointsLand; mutable int maxMovePointsWater; - int nativeTerrain; + ETerrainType::EETerrainType nativeTerrain; TurnInfo(const CGHeroInstance * Hero, const int Turn = 0); bool isLayerAvailable(const EPathfindingLayer layer) const; diff --git a/lib/CStack.cpp b/lib/CStack.cpp index 5055f8732..716cbc777 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -29,14 +29,16 @@ CStack::CStack(const CStackInstance * Base, PlayerColor O, int I, ui8 Side, Slot owner(O), slot(S), side(Side), - initialPosition() + initialPosition(), + nativeTerrain(ETerrainType::WRONG) { health.init(); //??? } CStack::CStack() : CBonusSystemNode(STACK_BATTLE), - CUnitState() + CUnitState(), + nativeTerrain(ETerrainType::WRONG) { base = nullptr; type = nullptr; @@ -84,8 +86,8 @@ void CStack::localInit(BattleInfo * battleInfo) attachTo(army); attachTo(const_cast(type)); } - - CUnitState::localInit(this); + nativeTerrain = type->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; } @@ -323,7 +325,9 @@ bool CStack::canBeHealed() const bool CStack::isOnNativeTerrain() const { - return type->isItNativeTerrain(battle->getTerrainType()); + //this code is called from CreatureTerrainLimiter::limit on battle start + auto res = nativeTerrain == ETerrainType::ANY_TERRAIN || nativeTerrain == battle->getTerrainType(); + return res; } bool CStack::isOnTerrain(int terrain) const diff --git a/lib/CStack.h b/lib/CStack.h index ca3ba9123..2ca5244d1 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -27,6 +27,7 @@ public: ui32 ID; //unique ID of stack const CCreature * type; + ETerrainType::EETerrainType nativeTerrain; //tmp variable to save native terrain value on battle init ui32 baseAmount; PlayerColor owner; //owner - player color (255 for neutrals) diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index f4b30cf4e..526504e7e 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -172,7 +172,7 @@ std::vector CTown::defaultMoatHexes() return moatHexes; } -std::string CTown::getFactionName() const +std::string CTown::getLocalizedFactionName() const { if(faction == nullptr) return "Random"; @@ -510,7 +510,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons if(stringID == source["upgrades"].String()) { throw std::runtime_error(boost::str(boost::format("Building with ID '%s' of town '%s' can't be an upgrade of the same building.") % - stringID % ret->town->getFactionName())); + stringID % ret->town->getLocalizedFactionName())); } VLC->modh->identifiers.requestIdentifier(ret->town->getBuildingScope(), source["upgrades"], [=](si32 identifier) diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index d2bc3c1c9..1197ae383 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -224,7 +224,7 @@ public: // TODO: remove once save and mod compatability not needed static std::vector defaultMoatHexes(); - std::string getFactionName() const; + std::string getLocalizedFactionName() const; std::string getBuildingScope() const; std::set getAllBuildings() const; const CBuilding * getSpecialBuilding(BuildingSubID::EBuildingSubID subID) const; diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 8902dc83b..7a6754d17 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -860,8 +860,9 @@ class DLL_LINKAGE ETerrainType public: enum EETerrainType { + ANY_TERRAIN = -3, WRONG = -2, BORDER = -1, DIRT, SAND, GRASS, SNOW, SWAMP, - ROUGH, SUBTERRANEAN, LAVA, WATER, ROCK + ROUGH, SUBTERRANEAN, LAVA, WATER, ROCK // ROCK is also intended to be max value. }; ETerrainType(EETerrainType _num = WRONG) : num(_num) diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index df4f63623..54ad9fb10 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -1980,13 +1980,13 @@ JsonNode CreatureTerrainLimiter::toJsonNode() const return root; } -CreatureFactionLimiter::CreatureFactionLimiter(int Faction) - : faction(Faction) +CreatureFactionLimiter::CreatureFactionLimiter(TFaction creatureFaction) + : faction(creatureFaction) { } CreatureFactionLimiter::CreatureFactionLimiter() - : faction(-1) + : faction((TFaction)-1) { } diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index dff870eb0..642298245 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -520,7 +520,6 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus); - class DLL_LINKAGE BonusList { public: @@ -758,7 +757,7 @@ public: enum ENodeTypes { UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALTY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM, - TOWN_AND_VISITOR, BATTLE, COMMANDER, GLOBAL_EFFECTS + TOWN_AND_VISITOR, BATTLE, COMMANDER, GLOBAL_EFFECTS, ALL_CREATURES }; private: BonusList bonuses; //wielded bonuses (local or up-propagated here) @@ -1062,9 +1061,9 @@ public: class DLL_LINKAGE CreatureFactionLimiter : public ILimiter //applies only to creatures of given faction { public: - si8 faction; + TFaction faction; CreatureFactionLimiter(); - CreatureFactionLimiter(int TerrainType); + CreatureFactionLimiter(TFaction faction); int limit(const BonusLimitationContext &context) const override; virtual std::string toString() const override; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 8f72d045f..41294ce48 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -89,10 +89,13 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & f break; } } - else if(ti->nativeTerrain != from.terType && !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType)) + else if(ti->nativeTerrain != from.terType //the terrain is not native + && ti->nativeTerrain != ETerrainType::ANY_TERRAIN //no special creature bonus + && !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType) //no special movement bonus + ) { static const CSelector selectorPATHFINDING = Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::PATHFINDING); - static const std::string keyPATHFINDING = "type_"+std::to_string((si32)Bonus::SECONDARY_SKILL_PREMY)+"s_"+std::to_string((si32)SecondarySkill::PATHFINDING); + static const std::string keyPATHFINDING = "type_" + std::to_string((si32)Bonus::SECONDARY_SKILL_PREMY) + "s_" + std::to_string((si32)SecondarySkill::PATHFINDING); ret = VLC->heroh->terrCosts[from.terType]; ret -= valOfBonuses(selectorPATHFINDING, keyPATHFINDING); @@ -102,7 +105,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & f return ret; } -int CGHeroInstance::getNativeTerrain() const +ETerrainType::EETerrainType CGHeroInstance::getNativeTerrain() const { // NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army. // This is clearly bug in H3 however intended behaviour is not clear. @@ -110,19 +113,19 @@ int CGHeroInstance::getNativeTerrain() const // will always have best penalty without any influence from player-defined stacks order // TODO: What should we do if all hero stacks are neutral creatures? - int nativeTerrain = -1; + ETerrainType::EETerrainType nativeTerrain = ETerrainType::BORDER; + for(auto stack : stacks) { - int stackNativeTerrain = VLC->townh->factions[stack.second->type->faction]->nativeTerrain; - if(stackNativeTerrain == -1) - continue; + ETerrainType::EETerrainType stackNativeTerrain = stack.second->type->getNativeTerrain(); //consider terrain bonuses e.g. Lodestar. - if(nativeTerrain == -1) + if(stackNativeTerrain == ETerrainType::BORDER) + continue; + if(nativeTerrain == ETerrainType::BORDER) nativeTerrain = stackNativeTerrain; else if(nativeTerrain != stackNativeTerrain) - return -1; + return ETerrainType::BORDER; } - return nativeTerrain; } diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 364eb0f26..90f490af0 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -162,7 +162,7 @@ public: bool needsLastStack()const override; TFaction getFaction() const; 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 - int getNativeTerrain() const; + ETerrainType::EETerrainType getNativeTerrain() const; ui32 getLowestCreatureSpeed() const; int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation' si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 95c64a775..6c8389a31 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -1232,6 +1232,16 @@ void CGTownInstance::recreateBuildingsBonuses() addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::ATTACK); //grail addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::DEFENSE); //grail } + else if(boost::algorithm::ends_with(this->town->faction->identifier, ":cove") && hasBuilt(BuildingID::GRAIL)) + { + static TPropagatorPtr lsProp(new CPropagatorNodeType(ALL_CREATURES)); + static auto factionLimiter = std::make_shared(this->town->faction->index); + auto b = std::make_shared(Bonus::PERMANENT, Bonus::NO_TERRAIN_PENALTY, Bonus::TOWN_STRUCTURE, 0, BuildingID::GRAIL, "", -1); + + b->addLimiter(factionLimiter); + b->addPropagator(lsProp); + VLC->creh->addBonusForAllCreatures(b); + } } bool CGTownInstance::addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype) @@ -1275,6 +1285,7 @@ bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, bool CGTownInstance::addBonusImpl(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, const std::string & description, int subtype) { auto b = std::make_shared(Bonus::PERMANENT, type, Bonus::TOWN_STRUCTURE, val, building, description, subtype); + if(prop) b->addPropagator(prop); addNewBonus(b);