Przeglądaj źródła

TurnInfo: store all bonuses and use TileInfo for everything

Currently this going to break ONE_WEEK bonuses because those don't work with CWillLastDays selector.
ArseniyShestakov 10 lat temu
rodzic
commit
abc4ea272f

+ 45 - 29
lib/CPathfinder.cpp

@@ -50,11 +50,11 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan
 	}
 
 	hlp = make_unique<CPathfinderHelper>(hero);
-	if(hlp->ti->bonusFlying)
+	if(hlp->hasBonusOfType(Bonus::FLYING_MOVEMENT))
 		options.useFlying = true;
-	if(hlp->ti->bonusWaterWalking)
+	if(hlp->hasBonusOfType(Bonus::WATER_WALKING))
 		options.useWaterWalking = true;
-	if(CGWhirlpool::isProtected(hero))
+	if(hlp->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION))
 		options.useTeleportWhirlpool = true;
 
 	initializeGraph();
@@ -109,7 +109,7 @@ void CPathfinder::calculatePaths()
 		if(!movement)
 		{
 			hlp->updateTurnInfo(++turn);
-			movement = hlp->getMaxMovePoints(cp->layer, turn);
+			movement = hlp->getMaxMovePoints(cp->layer);
 		}
 
 		//add accessible neighbouring nodes to the queue
@@ -140,11 +140,11 @@ void CPathfinder::calculatePaths()
 					continue;
 
 				destAction = getDestAction();
-				int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->getTurnInfo(turn));
+				int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->getTurnInfo());
 				int remains = movement - cost;
 				if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK)
 				{
-					remains = hero->movementPointsAfterEmbark(movement, cost, destAction - 1);
+					remains = hero->movementPointsAfterEmbark(movement, cost, destAction - 1, hlp->getTurnInfo());
 					cost = movement - remains;
 				}
 				int turnAtNextTile = turn;
@@ -152,8 +152,8 @@ void CPathfinder::calculatePaths()
 				{
 					//occurs rarely, when hero with low movepoints tries to leave the road
 					hlp->updateTurnInfo(++turnAtNextTile);
-					int moveAtNextTile = hlp->getMaxMovePoints(i, turnAtNextTile);
-					cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->getTurnInfo(turnAtNextTile)); //cost must be updated, movement points changed :(
+					int moveAtNextTile = hlp->getMaxMovePoints(i);
+					cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->getTurnInfo()); //cost must be updated, movement points changed :(
 					remains = moveAtNextTile - cost;
 				}
 
@@ -285,13 +285,13 @@ bool CPathfinder::isLayerAvailable(const ELayer layer, const int turn) const
 	switch(layer)
 	{
 	case ELayer::AIR:
-		if(!hlp->ti->bonusFlying)
+		if(!hlp->hasBonusOfType(Bonus::FLYING_MOVEMENT))
 			return false;
 
 		break;
 
 	case ELayer::WATER:
-		if(!hlp->ti->bonusWaterWalking)
+		if(!hlp->hasBonusOfType(Bonus::WATER_WALKING))
 			return false;
 
 		break;
@@ -701,47 +701,63 @@ bool CPathfinder::canVisitObject() const
 	return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL;
 }
 
-TurnInfo::TurnInfo(const CGHeroInstance * h, const int Turn)
-	: turn(Turn)
+TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn)
+	: hero(Hero), maxMovePointsLand(-1), maxMovePointsWater(-1)
 {
-	maxMovePointsLand = h->maxMovePoints(true);
-	maxMovePointsWater = h->maxMovePoints(false);
-	bonusFlying = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn);
-	bonusWaterWalking = h->getBonusAtTurn(Bonus::WATER_WALKING, turn);
+	bonuses = hero->getAllBonuses(Selector::days(turn), nullptr);
+}
+
+bool TurnInfo::hasBonusOfType(Bonus::BonusType type, int subtype) const
+{
+	return bonuses->getFirst(Selector::type(type).And(Selector::subtype(subtype)));
+}
+
+int TurnInfo::valOfBonuses(Bonus::BonusType type, int subtype) const
+{
+	return bonuses->valOfBonuses(Selector::type(type).And(Selector::subtype(subtype)));
 }
 
 int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const
 {
+	if(maxMovePointsLand == -1)
+		maxMovePointsLand = hero->maxMovePoints(true, this);
+	if(maxMovePointsWater == -1)
+		maxMovePointsWater = hero->maxMovePoints(false, this);
+
 	return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
 }
 
 CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero)
-	: ti(nullptr), hero(Hero)
+	: turn(0), hero(Hero)
 {
 	turnsInfo.reserve(16);
 	updateTurnInfo();
 }
 
-void CPathfinderHelper::updateTurnInfo(const int turn)
+void CPathfinderHelper::updateTurnInfo(const int Turn)
 {
-	if(!ti || ti->turn != turn)
+	if(turn != Turn)
 	{
-		if(turn < turnsInfo.size())
-			ti = turnsInfo[turn];
-		else
+		turn = Turn;
+		if(turn >= turnsInfo.size())
 		{
-			ti = new TurnInfo(hero, turn);
+			auto ti = new TurnInfo(hero, turn);
 			turnsInfo.push_back(ti);
 		}
 	}
 }
 
-const TurnInfo * CPathfinderHelper::getTurnInfo(const int turn) const
+const TurnInfo * CPathfinderHelper::getTurnInfo() const
 {
 	return turnsInfo[turn];
 }
 
-int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer, const int turn) const
+bool CPathfinderHelper::hasBonusOfType(const Bonus::BonusType type, const int subtype) const
+{
+	return turnsInfo[turn]->hasBonusOfType(type, subtype);
+}
+
+int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer layer) const
 {
 	return turnsInfo[turn]->getMaxMovePoints(layer);
 }
@@ -796,17 +812,17 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 & sr
 	auto s = h->cb->getTile(src), d = h->cb->getTile(dst);
 	int ret = h->getTileCost(*d, *s, ti);
 
-	if(d->blocked && ti->bonusFlying)
+	if(d->blocked && ti->hasBonusOfType(Bonus::FLYING_MOVEMENT))
 	{
-		ret *= (100.0 + ti->bonusFlying->val) / 100.0;
+		ret *= (100.0 + ti->valOfBonuses(Bonus::FLYING_MOVEMENT)) / 100.0;
 	}
 	else if(d->terType == ETerrainType::WATER)
 	{
 		if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds
 			ret *= 0.666;
-		else if(!h->boat && ti->bonusWaterWalking)
+		else if(!h->boat && ti->hasBonusOfType(Bonus::WATER_WALKING))
 		{
-			ret *= (100.0 + ti->bonusWaterWalking->val) / 100.0;
+			ret *= (100.0 + ti->valOfBonuses(Bonus::WATER_WALKING)) / 100.0;
 		}
 	}
 

+ 12 - 10
lib/CPathfinder.h

@@ -192,28 +192,30 @@ private:
 
 };
 
-struct TurnInfo
+struct DLL_LINKAGE TurnInfo
 {
-	int turn;
-	int maxMovePointsLand;
-	int maxMovePointsWater;
-	const Bonus * bonusFlying;
-	const Bonus * bonusWaterWalking;
+	const CGHeroInstance * hero;
+	TBonusListPtr bonuses;
+	mutable int maxMovePointsLand;
+	mutable int maxMovePointsWater;
 
-	TurnInfo(const CGHeroInstance * h, const int Turn = 0);
+	TurnInfo(const CGHeroInstance * Hero, const int Turn = 0);
+	bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
+	int valOfBonuses(const Bonus::BonusType type, const int subtype = -1) const;
 	int getMaxMovePoints(const EPathfindingLayer layer) const;
 };
 
 class DLL_LINKAGE CPathfinderHelper
 {
 public:
-	TurnInfo * ti;
+	int turn;
 	const CGHeroInstance * hero;
 
 	CPathfinderHelper(const CGHeroInstance * Hero);
 	void updateTurnInfo(const int turn = 0);
-	const TurnInfo * getTurnInfo(const int turn) const;
-	int getMaxMovePoints(const EPathfindingLayer layer, const int turn) const;
+	const TurnInfo * getTurnInfo() const;
+	bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
+	int getMaxMovePoints(const EPathfindingLayer layer) const;
 
 	static void getNeighbours(CGameState * gs, const TerrainTile & srct, const int3 & tile, std::vector<int3> & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing);
 

+ 12 - 11
lib/mapObjects/CGHeroInstance.cpp

@@ -80,7 +80,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro
 			break;
 		}
 	}
-	else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, ti->turn, from.terType))
+	else if(!ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
 	{
 		// 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.
@@ -129,11 +129,6 @@ int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position o
 	}
 }
 
-const Bonus * CGHeroInstance::getBonusAtTurn(const Bonus::BonusType &type, const int &turn, const TBonusSubtype &subType) const
-{
-	return getBonus(Selector::type(type).And(Selector::days(turn)).And(Selector::subtype(subType)));
-}
-
 ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const
 {
 	for(auto & elem : secSkills)
@@ -176,8 +171,11 @@ bool CGHeroInstance::canLearnSkill() const
 	return secSkills.size() < GameConstants::SKILL_PER_HERO;
 }
 
-int CGHeroInstance::maxMovePoints(bool onLand) const
+int CGHeroInstance::maxMovePoints(bool onLand, const TurnInfo * ti) const
 {
+	if(!ti)
+		ti = new TurnInfo(this);
+
 	int base;
 
 	if(onLand)
@@ -196,10 +194,10 @@ int CGHeroInstance::maxMovePoints(bool onLand) const
 	}
 
 	const Bonus::BonusType bt = onLand ? Bonus::LAND_MOVEMENT : Bonus::SEA_MOVEMENT;
-	const int bonus = valOfBonuses(Bonus::MOVEMENT) + valOfBonuses(bt);
+	const int bonus = ti->valOfBonuses(Bonus::MOVEMENT) + ti->valOfBonuses(bt);
 
 	const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
-	const double modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
+	const double modifier = ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
 
 	return int(base* (1+modifier)) + bonus;
 }
@@ -1166,9 +1164,12 @@ CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
 		return CArmedInstance::whereShouldBeAttached(gs);
 }
 
-int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/) const
+int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/, const TurnInfo * ti) const
 {
-	if(hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
+	if(!ti)
+		ti = new TurnInfo(this);
+
+	if(ti->hasBonusOfType(Bonus::FREE_SHIP_BOARDING))
 		return (MPsBefore - basicCost) * static_cast<double>(maxMovePoints(disembark)) / maxMovePoints(!disembark);
 
 	return 0; //take all MPs otherwise

+ 2 - 3
lib/mapObjects/CGHeroInstance.h

@@ -134,7 +134,6 @@ public:
 	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
-	const Bonus * getBonusAtTurn(const Bonus::BonusType &type, const int &turn = 0, const TBonusSubtype &subType = -1) const;
 	int getCurrentLuck(int stack=-1, bool town=false) const;
 	int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
 
@@ -161,8 +160,8 @@ public:
 	void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
 	void levelUp(std::vector<SecondarySkill> skills);
 
-	int maxMovePoints(bool onLand) const;
-	int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
+	int maxMovePoints(bool onLand, const TurnInfo * ti = nullptr) const;
+	int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;
 
 	static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
 	double getFightingStrength() const; // takes attack / defense skill into account

+ 6 - 5
server/CGameHandler.cpp

@@ -1778,9 +1778,10 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
 	tmh.movePoints = h->movement;
 
 	//check if destination tile is available
-	const bool canFly = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT);
-	const bool canWalkOnSea = h->getBonusAtTurn(Bonus::WATER_WALKING);
-	const int cost = CPathfinderHelper::getMovementCost(h, h->getPosition(), hmpos, h->movement);
+	auto ti = new TurnInfo(h);
+	const bool canFly = ti->hasBonusOfType(Bonus::FLYING_MOVEMENT);
+	const bool canWalkOnSea = ti->hasBonusOfType(Bonus::WATER_WALKING);
+	const int cost = CPathfinderHelper::getMovementCost(h, h->getPosition(), hmpos, h->movement, ti);
 
 	//it's a rock or blocked and not visitable tile
 	//OR hero is on land and dest is water and (there is not present only one object - boat)
@@ -1872,14 +1873,14 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
 
 	if(!transit && embarking)
 	{
-		tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false);
+		tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false, ti);
 		return doMove(TryMoveHero::EMBARK, IGNORE_GUARDS, DONT_VISIT_DEST, LEAVING_TILE);
 		//attack guards on embarking? In H3 creatures on water had no zone of control at all
 	}
 
 	if(disembarking)
 	{
-		tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true);
+		tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true, ti);
 		return doMove(TryMoveHero::DISEMBARK, CHECK_FOR_GUARDS, VISIT_DEST, LEAVING_TILE);
 	}