Ver código fonte

Merge pull request #666 from ShubusCorporation/do_feature_lodestar

Lodestar Grail should work.
Alexander Shishkin 4 anos atrás
pai
commit
6bf0bea286

+ 19 - 3
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<std::string>(i));
-
 	loadCommanders();
 }
 
@@ -1194,6 +1205,11 @@ void CCreatureHandler::addBonusForAllCreatures(std::shared_ptr<Bonus> b)
 	allCreatures.addNewBonus(b);
 }
 
+void CCreatureHandler::removeBonusesFromAllCreatures()
+{
+	allCreatures.removeBonuses(Selector::all);
+}
+
 void CCreatureHandler::buildBonusTreeForTiers()
 {
 	for(CCreature *c : creatures)

+ 9 - 1
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<Bonus> b); //tier must be <1-7>
 	void addBonusForAllCreatures(std::shared_ptr<Bonus> b);
+	void removeBonusesFromAllCreatures();
 
 	CCreatureHandler();
 	~CCreatureHandler();

+ 2 - 0
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)
 	{

+ 1 - 1
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;

+ 9 - 5
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<CCreature *>(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

+ 1 - 0
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)

+ 2 - 2
lib/CTownHandler.cpp

@@ -172,7 +172,7 @@ std::vector<BattleHex> 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)

+ 1 - 1
lib/CTownHandler.h

@@ -224,7 +224,7 @@ public:
 	// TODO: remove once save and mod compatability not needed
 	static std::vector<BattleHex> defaultMoatHexes();
 
-	std::string getFactionName() const;
+	std::string getLocalizedFactionName() const;
 	std::string getBuildingScope() const;
 	std::set<si32> getAllBuildings() const;
 	const CBuilding * getSpecialBuilding(BuildingSubID::EBuildingSubID subID) const;

+ 2 - 1
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)

+ 3 - 3
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)
 {
 }
 

+ 3 - 4
lib/HeroBonus.h

@@ -520,7 +520,6 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
 
 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;

+ 13 - 10
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;
 }
 

+ 1 - 1
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

+ 11 - 0
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<CreatureFactionLimiter>(this->town->faction->index);
+		auto b = std::make_shared<Bonus>(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>(Bonus::PERMANENT, type, Bonus::TOWN_STRUCTURE, val, building, description, subtype);
+
 	if(prop)
 		b->addPropagator(prop);
 	addNewBonus(b);