From abc4ea272fc7f75614c96528767a21fe47da892e Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 12 Nov 2015 14:04:33 +0300 Subject: [PATCH] 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. --- lib/CPathfinder.cpp | 74 +++++++++++++++++++------------ lib/CPathfinder.h | 22 ++++----- lib/mapObjects/CGHeroInstance.cpp | 23 +++++----- lib/mapObjects/CGHeroInstance.h | 5 +-- server/CGameHandler.cpp | 11 ++--- 5 files changed, 77 insertions(+), 58 deletions(-) diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index b0fa4f9cb..fc85d4118 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -50,11 +50,11 @@ CPathfinder::CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstan } hlp = make_unique(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; } } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index cdd263d28..0a7083dc8 100644 --- a/lib/CPathfinder.h +++ b/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 & vec, const boost::logic::tribool & onLand, const bool limitCoastSailing); diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index a031d080c..e6156b9bd 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/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(maxMovePoints(disembark)) / maxMovePoints(!disembark); return 0; //take all MPs otherwise diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 0c6056664..9b9b59d23 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/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 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 diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 9032cc181..4fa958575 100644 --- a/server/CGameHandler.cpp +++ b/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); }