mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-19 21:10:12 +02:00
Rework and optimize turnInfo used by pathfinder
This commit is contained in:
parent
579b64cd39
commit
dea1eba20b
@ -47,11 +47,10 @@ ChainActor::ChainActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t
|
|||||||
initialTurn = 0;
|
initialTurn = 0;
|
||||||
armyValue = getHeroArmyStrengthWithCommander(hero, hero);
|
armyValue = getHeroArmyStrengthWithCommander(hero, hero);
|
||||||
heroFightingStrength = hero->getHeroStrength();
|
heroFightingStrength = hero->getHeroStrength();
|
||||||
tiCache.reset(new TurnInfo(hero));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy)
|
ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy)
|
||||||
:hero(carrier->hero), tiCache(carrier->tiCache), heroRole(carrier->heroRole), isMovable(true), creatureSet(heroArmy), chainMask(carrier->chainMask | other->chainMask),
|
:hero(carrier->hero), heroRole(carrier->heroRole), isMovable(true), creatureSet(heroArmy), chainMask(carrier->chainMask | other->chainMask),
|
||||||
baseActor(this), carrierParent(carrier), otherParent(other), heroFightingStrength(carrier->heroFightingStrength),
|
baseActor(this), carrierParent(carrier), otherParent(other), heroFightingStrength(carrier->heroFightingStrength),
|
||||||
actorExchangeCount(carrier->actorExchangeCount + other->actorExchangeCount), armyCost(carrier->armyCost + other->armyCost), actorAction()
|
actorExchangeCount(carrier->actorExchangeCount + other->actorExchangeCount), armyCost(carrier->armyCost + other->armyCost), actorAction()
|
||||||
{
|
{
|
||||||
@ -75,7 +74,7 @@ int ChainActor::maxMovePoints(CGPathNode::ELayer layer)
|
|||||||
throw std::logic_error("Asking movement points for static actor");
|
throw std::logic_error("Asking movement points for static actor");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return hero->movementPointsLimitCached(layer, tiCache.get());
|
return hero->movementPointsLimit(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ChainActor::toString() const
|
std::string ChainActor::toString() const
|
||||||
@ -133,7 +132,6 @@ void ChainActor::setBaseActor(HeroActor * base)
|
|||||||
heroFightingStrength = base->heroFightingStrength;
|
heroFightingStrength = base->heroFightingStrength;
|
||||||
armyCost = base->armyCost;
|
armyCost = base->armyCost;
|
||||||
actorAction = base->actorAction;
|
actorAction = base->actorAction;
|
||||||
tiCache = base->tiCache;
|
|
||||||
actorExchangeCount = base->actorExchangeCount;
|
actorExchangeCount = base->actorExchangeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,6 @@ public:
|
|||||||
float heroFightingStrength;
|
float heroFightingStrength;
|
||||||
uint8_t actorExchangeCount;
|
uint8_t actorExchangeCount;
|
||||||
TResources armyCost;
|
TResources armyCost;
|
||||||
std::shared_ptr<TurnInfo> tiCache;
|
|
||||||
|
|
||||||
ChainActor() = default;
|
ChainActor() = default;
|
||||||
virtual ~ChainActor() = default;
|
virtual ~ChainActor() = default;
|
||||||
|
@ -306,6 +306,10 @@
|
|||||||
"tavernInvite" : false,
|
"tavernInvite" : false,
|
||||||
// minimal primary skills for heroes
|
// minimal primary skills for heroes
|
||||||
"minimalPrimarySkills": [ 0, 0, 1, 1]
|
"minimalPrimarySkills": [ 0, 0, 1, 1]
|
||||||
|
/// movement points hero can get on start of the turn when on land, depending on speed of slowest creature (0-based list)
|
||||||
|
"movementPointsLand" : [ 1500, 1500, 1500, 1500, 1560, 1630, 1700, 1760, 1830, 1900, 1960, 2000 ],
|
||||||
|
/// movement points hero can get on start of the turn when on sea, depending on speed of slowest creature (0-based list)
|
||||||
|
"movementPointsSea" : [ 1500 ],
|
||||||
},
|
},
|
||||||
|
|
||||||
"towns":
|
"towns":
|
||||||
@ -560,29 +564,6 @@
|
|||||||
"type" : "MANA_PER_KNOWLEDGE_PERCENTAGE", //1000% mana per knowledge
|
"type" : "MANA_PER_KNOWLEDGE_PERCENTAGE", //1000% mana per knowledge
|
||||||
"val" : 1000,
|
"val" : 1000,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
},
|
|
||||||
"landMovement" :
|
|
||||||
{
|
|
||||||
"type" : "MOVEMENT", //Basic land movement
|
|
||||||
"subtype" : "heroMovementLand",
|
|
||||||
"val" : 1300,
|
|
||||||
"valueType" : "BASE_NUMBER",
|
|
||||||
"updater" : {
|
|
||||||
"type" : "ARMY_MOVEMENT", //Enable army movement bonus
|
|
||||||
"parameters" : [
|
|
||||||
20, // Movement points for lowest speed numerator
|
|
||||||
3, // Movement points for lowest speed denominator
|
|
||||||
10, // Resulting value, rounded down, will be multiplied by this number
|
|
||||||
700 // All army movement bonus cannot be higher than this value (so, max movement will be 1300 + 700 for this settings)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"seaMovement" :
|
|
||||||
{
|
|
||||||
"type" : "MOVEMENT", //Basic sea movement
|
|
||||||
"subtype" : "heroMovementSea",
|
|
||||||
"val" : 1500,
|
|
||||||
"valueType" : "BASE_NUMBER"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -76,6 +76,8 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
|
|||||||
{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, "heroes", "retreatOnWinWithoutTroops" },
|
{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, "heroes", "retreatOnWinWithoutTroops" },
|
||||||
{EGameSettings::HEROES_STARTING_STACKS_CHANCES, "heroes", "startingStackChances" },
|
{EGameSettings::HEROES_STARTING_STACKS_CHANCES, "heroes", "startingStackChances" },
|
||||||
{EGameSettings::HEROES_TAVERN_INVITE, "heroes", "tavernInvite" },
|
{EGameSettings::HEROES_TAVERN_INVITE, "heroes", "tavernInvite" },
|
||||||
|
{EGameSettings::HEROES_MOVEMENT_POINTS_LAND, "heroes", "movementPointsLand" },
|
||||||
|
{EGameSettings::HEROES_MOVEMENT_POINTS_SEA, "heroes", "movementPointsSea" },
|
||||||
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
|
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
|
||||||
{EGameSettings::MAP_FORMAT_CHRONICLES, "mapFormat", "chronicles" },
|
{EGameSettings::MAP_FORMAT_CHRONICLES, "mapFormat", "chronicles" },
|
||||||
{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS, "mapFormat", "hornOfTheAbyss" },
|
{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS, "mapFormat", "hornOfTheAbyss" },
|
||||||
|
@ -49,6 +49,8 @@ enum class EGameSettings
|
|||||||
HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS,
|
HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS,
|
||||||
HEROES_STARTING_STACKS_CHANCES,
|
HEROES_STARTING_STACKS_CHANCES,
|
||||||
HEROES_TAVERN_INVITE,
|
HEROES_TAVERN_INVITE,
|
||||||
|
HEROES_MOVEMENT_POINTS_LAND,
|
||||||
|
HEROES_MOVEMENT_POINTS_SEA,
|
||||||
MAP_FORMAT_ARMAGEDDONS_BLADE,
|
MAP_FORMAT_ARMAGEDDONS_BLADE,
|
||||||
MAP_FORMAT_CHRONICLES,
|
MAP_FORMAT_CHRONICLES,
|
||||||
MAP_FORMAT_HORN_OF_THE_ABYSS,
|
MAP_FORMAT_HORN_OF_THE_ABYSS,
|
||||||
|
@ -83,10 +83,10 @@ void BonusList::stackBonuses()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int BonusList::totalValue() const
|
int BonusList::totalValue(int baseValue) const
|
||||||
{
|
{
|
||||||
if (bonuses.empty())
|
if (bonuses.empty())
|
||||||
return 0;
|
return baseValue;
|
||||||
|
|
||||||
struct BonusCollection
|
struct BonusCollection
|
||||||
{
|
{
|
||||||
@ -104,6 +104,7 @@ int BonusList::totalValue() const
|
|||||||
};
|
};
|
||||||
|
|
||||||
BonusCollection accumulated;
|
BonusCollection accumulated;
|
||||||
|
accumulated.base = baseValue;
|
||||||
int indexMaxCount = 0;
|
int indexMaxCount = 0;
|
||||||
int indexMinCount = 0;
|
int indexMinCount = 0;
|
||||||
|
|
||||||
@ -208,12 +209,12 @@ void BonusList::getAllBonuses(BonusList &out) const
|
|||||||
out.push_back(b);
|
out.push_back(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
int BonusList::valOfBonuses(const CSelector &select) const
|
int BonusList::valOfBonuses(const CSelector &select, int baseValue) const
|
||||||
{
|
{
|
||||||
BonusList ret;
|
BonusList ret;
|
||||||
CSelector limit = nullptr;
|
CSelector limit = nullptr;
|
||||||
getBonuses(ret, select, limit);
|
getBonuses(ret, select, limit);
|
||||||
return ret.totalValue();
|
return ret.totalValue(baseValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode BonusList::toJsonNode() const
|
JsonNode BonusList::toJsonNode() const
|
||||||
|
@ -58,14 +58,14 @@ public:
|
|||||||
|
|
||||||
// BonusList functions
|
// BonusList functions
|
||||||
void stackBonuses();
|
void stackBonuses();
|
||||||
int totalValue() const;
|
int totalValue(int baseValue = 0) const;
|
||||||
void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit = nullptr) const;
|
void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit = nullptr) const;
|
||||||
void getAllBonuses(BonusList &out) const;
|
void getAllBonuses(BonusList &out) const;
|
||||||
|
|
||||||
//special find functions
|
//special find functions
|
||||||
std::shared_ptr<Bonus> getFirst(const CSelector &select);
|
std::shared_ptr<Bonus> getFirst(const CSelector &select);
|
||||||
std::shared_ptr<const Bonus> getFirst(const CSelector &select) const;
|
std::shared_ptr<const Bonus> getFirst(const CSelector &select) const;
|
||||||
int valOfBonuses(const CSelector &select) const;
|
int valOfBonuses(const CSelector &select, int baseValue = 0) const;
|
||||||
|
|
||||||
// conversion / output
|
// conversion / output
|
||||||
JsonNode toJsonNode() const;
|
JsonNode toJsonNode() const;
|
||||||
|
@ -111,18 +111,6 @@ ArmyMovementUpdater::ArmyMovementUpdater(int base, int divider, int multiplier,
|
|||||||
|
|
||||||
std::shared_ptr<Bonus> ArmyMovementUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
std::shared_ptr<Bonus> ArmyMovementUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
||||||
{
|
{
|
||||||
if(b->type == BonusType::MOVEMENT && context.getNodeType() == CBonusSystemNode::HERO)
|
|
||||||
{
|
|
||||||
auto speed = static_cast<const CGHeroInstance &>(context).getLowestCreatureSpeed();
|
|
||||||
si32 armySpeed = speed * base / divider;
|
|
||||||
auto counted = armySpeed * multiplier;
|
|
||||||
auto newBonus = std::make_shared<Bonus>(*b);
|
|
||||||
newBonus->source = BonusSource::ARMY;
|
|
||||||
newBonus->val += vstd::amin(counted, max);
|
|
||||||
return newBonus;
|
|
||||||
}
|
|
||||||
if(b->type != BonusType::MOVEMENT)
|
|
||||||
logGlobal->error("ArmyMovementUpdater should only be used for MOVEMENT bonus!");
|
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,31 +73,6 @@ void CGHeroPlaceholder::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
handler.serializeInt("powerRank", powerRank.value());
|
handler.serializeInt("powerRank", powerRank.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lowestSpeed(const CGHeroInstance * chi)
|
|
||||||
{
|
|
||||||
static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED);
|
|
||||||
static const std::string keySTACKS_SPEED = "type_" + std::to_string(static_cast<si32>(BonusType::STACKS_SPEED));
|
|
||||||
|
|
||||||
if(!chi->stacksCount())
|
|
||||||
{
|
|
||||||
if(chi->commander && chi->commander->alive)
|
|
||||||
{
|
|
||||||
return chi->commander->valOfBonuses(selectorSTACKS_SPEED, keySTACKS_SPEED);
|
|
||||||
}
|
|
||||||
|
|
||||||
logGlobal->error("Hero %d (%s) has no army!", chi->id.getNum(), chi->getNameTranslated());
|
|
||||||
return 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto i = chi->Slots().begin();
|
|
||||||
//TODO? should speed modifiers (eg from artifacts) affect hero movement?
|
|
||||||
|
|
||||||
int ret = (i++)->second->valOfBonuses(selectorSTACKS_SPEED, keySTACKS_SPEED);
|
|
||||||
for(; i != chi->Slots().end(); i++)
|
|
||||||
ret = std::min(ret, i->second->valOfBonuses(selectorSTACKS_SPEED, keySTACKS_SPEED));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui32 CGHeroInstance::getTileMovementCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const
|
ui32 CGHeroInstance::getTileMovementCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const
|
||||||
{
|
{
|
||||||
int64_t ret = GameConstants::BASE_MOVEMENT_COST;
|
int64_t ret = GameConstants::BASE_MOVEMENT_COST;
|
||||||
@ -107,13 +82,10 @@ ui32 CGHeroInstance::getTileMovementCost(const TerrainTile & dest, const Terrain
|
|||||||
{
|
{
|
||||||
ret = from.getRoad()->movementCost;
|
ret = from.getRoad()->movementCost;
|
||||||
}
|
}
|
||||||
else if(ti->nativeTerrain != from.getTerrainID() &&//the terrain is not native
|
else if(ti->hasNoTerrainPenalty(from.getTerrainID())) //no special movement bonus
|
||||||
ti->nativeTerrain != ETerrainId::ANY_TERRAIN && //no special creature bonus
|
|
||||||
!ti->hasBonusOfType(BonusType::NO_TERRAIN_PENALTY, BonusSubtypeID(from.getTerrainID()))) //no special movement bonus
|
|
||||||
{
|
{
|
||||||
|
|
||||||
ret = VLC->terrainTypeHandler->getById(from.getTerrainID())->moveCost;
|
ret = VLC->terrainTypeHandler->getById(from.getTerrainID())->moveCost;
|
||||||
ret -= ti->valOfBonuses(BonusType::ROUGH_TERRAIN_DISCOUNT);
|
ret -= ti->getRoughTerrainDiscountValue();
|
||||||
if(ret < GameConstants::BASE_MOVEMENT_COST)
|
if(ret < GameConstants::BASE_MOVEMENT_COST)
|
||||||
ret = GameConstants::BASE_MOVEMENT_COST;
|
ret = GameConstants::BASE_MOVEMENT_COST;
|
||||||
}
|
}
|
||||||
@ -257,30 +229,44 @@ void CGHeroInstance::setMovementPoints(int points)
|
|||||||
|
|
||||||
int CGHeroInstance::movementPointsLimit(bool onLand) const
|
int CGHeroInstance::movementPointsLimit(bool onLand) const
|
||||||
{
|
{
|
||||||
return valOfBonuses(BonusType::MOVEMENT, onLand ? BonusCustomSubtype::heroMovementLand : BonusCustomSubtype::heroMovementSea);
|
auto ti = getTurnInfo(0);
|
||||||
|
return onLand ? ti->getMovePointsLimitLand() : ti->getMovePointsLimitWater();
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGHeroInstance::getLowestCreatureSpeed() const
|
int CGHeroInstance::getLowestCreatureSpeed() const
|
||||||
{
|
{
|
||||||
return lowestCreatureSpeed;
|
static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED);
|
||||||
|
static const std::string cachingStr = "type_" + std::to_string(static_cast<si32>(BonusType::STACKS_SPEED));
|
||||||
|
|
||||||
|
if(stacksCount() != 0)
|
||||||
|
{
|
||||||
|
int minimalSpeed = std::numeric_limits<int>::max();
|
||||||
|
//TODO? should speed modifiers (eg from artifacts) affect hero movement?
|
||||||
|
for(const auto & slot : Slots())
|
||||||
|
minimalSpeed = std::min(minimalSpeed, slot.second->valOfBonuses(selectorSTACKS_SPEED, cachingStr));
|
||||||
|
|
||||||
|
return minimalSpeed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(commander && commander->alive)
|
||||||
|
return commander->valOfBonuses(selectorSTACKS_SPEED, cachingStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGHeroInstance::updateArmyMovementBonus(bool onLand, const TurnInfo * ti) const
|
return 10;
|
||||||
{
|
|
||||||
auto realLowestSpeed = lowestSpeed(this);
|
|
||||||
if(lowestCreatureSpeed != realLowestSpeed)
|
|
||||||
{
|
|
||||||
lowestCreatureSpeed = realLowestSpeed;
|
|
||||||
//Let updaters run again
|
|
||||||
treeHasChanged();
|
|
||||||
ti->updateHeroBonuses(BonusType::MOVEMENT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<TurnInfo> CGHeroInstance::getTurnInfo(int days) const
|
||||||
|
{
|
||||||
|
return std::make_unique<TurnInfo>(turnInfoCache.get(), this, days);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGHeroInstance::movementPointsLimitCached(bool onLand, const TurnInfo * ti) const
|
int CGHeroInstance::movementPointsLimitCached(bool onLand, const TurnInfo * ti) const
|
||||||
{
|
{
|
||||||
updateArmyMovementBonus(onLand, ti);
|
if (onLand)
|
||||||
return ti->valOfBonuses(BonusType::MOVEMENT, onLand ? BonusCustomSubtype::heroMovementLand : BonusCustomSubtype::heroMovementSea);
|
return ti->getMovePointsLimitLand();
|
||||||
|
else
|
||||||
|
return ti->getMovePointsLimitWater();
|
||||||
}
|
}
|
||||||
|
|
||||||
CGHeroInstance::CGHeroInstance(IGameCallback * cb)
|
CGHeroInstance::CGHeroInstance(IGameCallback * cb)
|
||||||
@ -295,8 +281,8 @@ CGHeroInstance::CGHeroInstance(IGameCallback * cb)
|
|||||||
gender(EHeroGender::DEFAULT),
|
gender(EHeroGender::DEFAULT),
|
||||||
primarySkills(this),
|
primarySkills(this),
|
||||||
magicSchoolMastery(this),
|
magicSchoolMastery(this),
|
||||||
manaPerKnowledgeCached(this, Selector::type()(BonusType::MANA_PER_KNOWLEDGE_PERCENTAGE)),
|
turnInfoCache(std::make_unique<TurnInfoCache>(this)),
|
||||||
lowestCreatureSpeed(0)
|
manaPerKnowledgeCached(this, Selector::type()(BonusType::MANA_PER_KNOWLEDGE_PERCENTAGE))
|
||||||
{
|
{
|
||||||
setNodeType(HERO);
|
setNodeType(HERO);
|
||||||
ID = Obj::HERO;
|
ID = Obj::HERO;
|
||||||
@ -1365,11 +1351,11 @@ int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool
|
|||||||
std::unique_ptr<TurnInfo> turnInfoLocal;
|
std::unique_ptr<TurnInfo> turnInfoLocal;
|
||||||
if(!ti)
|
if(!ti)
|
||||||
{
|
{
|
||||||
turnInfoLocal = std::make_unique<TurnInfo>(this);
|
turnInfoLocal = getTurnInfo(0);
|
||||||
ti = turnInfoLocal.get();
|
ti = turnInfoLocal.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ti->hasBonusOfType(BonusType::FREE_SHIP_BOARDING))
|
if(!ti->hasFreeShipBoarding())
|
||||||
return 0; // take all MPs by default
|
return 0; // take all MPs by default
|
||||||
|
|
||||||
auto boatLayer = boat ? boat->layer : EPathfindingLayer::SAIL;
|
auto boatLayer = boat ? boat->layer : EPathfindingLayer::SAIL;
|
||||||
|
@ -25,8 +25,10 @@ class CGBoat;
|
|||||||
class CGTownInstance;
|
class CGTownInstance;
|
||||||
class CMap;
|
class CMap;
|
||||||
class UpgradeInfo;
|
class UpgradeInfo;
|
||||||
|
class TurnInfo;
|
||||||
|
|
||||||
struct TerrainTile;
|
struct TerrainTile;
|
||||||
struct TurnInfo;
|
struct TurnInfoCache;
|
||||||
|
|
||||||
class DLL_LINKAGE CGHeroPlaceholder : public CGObjectInstance
|
class DLL_LINKAGE CGHeroPlaceholder : public CGObjectInstance
|
||||||
{
|
{
|
||||||
@ -62,9 +64,9 @@ private:
|
|||||||
PrimarySkillsCache primarySkills;
|
PrimarySkillsCache primarySkills;
|
||||||
MagicSchoolMasteryCache magicSchoolMastery;
|
MagicSchoolMasteryCache magicSchoolMastery;
|
||||||
BonusValueCache manaPerKnowledgeCached;
|
BonusValueCache manaPerKnowledgeCached;
|
||||||
|
std::unique_ptr<TurnInfoCache> turnInfoCache;
|
||||||
|
|
||||||
std::set<SpellID> spells; //known spells (spell IDs)
|
std::set<SpellID> spells; //known spells (spell IDs)
|
||||||
mutable int lowestCreatureSpeed;
|
|
||||||
ui32 movement; //remaining movement points
|
ui32 movement; //remaining movement points
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -224,11 +226,11 @@ public:
|
|||||||
int movementPointsLimit(bool onLand) const;
|
int movementPointsLimit(bool onLand) const;
|
||||||
//cached version is much faster, TurnInfo construction is costly
|
//cached version is much faster, TurnInfo construction is costly
|
||||||
int movementPointsLimitCached(bool onLand, const TurnInfo * ti) const;
|
int movementPointsLimitCached(bool onLand, const TurnInfo * ti) const;
|
||||||
//update army movement bonus
|
|
||||||
void updateArmyMovementBonus(bool onLand, const TurnInfo * ti) const;
|
|
||||||
|
|
||||||
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;
|
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;
|
||||||
|
|
||||||
|
std::unique_ptr<TurnInfo> getTurnInfo(int days) const;
|
||||||
|
|
||||||
double getFightingStrength() const; // takes attack / defense skill into account
|
double getFightingStrength() const; // takes attack / defense skill into account
|
||||||
double getMagicStrength() const; // takes knowledge / spell power skill but also current mana, whether the hero owns a spell-book and whether that books contains anything into account
|
double getMagicStrength() const; // takes knowledge / spell power skill but also current mana, whether the hero owns a spell-book and whether that books contains anything into account
|
||||||
double getHeroStrength() const; // includes fighting and magic strength
|
double getHeroStrength() const; // includes fighting and magic strength
|
||||||
|
@ -514,11 +514,7 @@ CPathfinderHelper::CPathfinderHelper(CGameState * gs, const CGHeroInstance * Her
|
|||||||
canCastWaterWalk = Hero->canCastThisSpell(waterWalk.toSpell());
|
canCastWaterWalk = Hero->canCastThisSpell(waterWalk.toSpell());
|
||||||
}
|
}
|
||||||
|
|
||||||
CPathfinderHelper::~CPathfinderHelper()
|
CPathfinderHelper::~CPathfinderHelper() = default;
|
||||||
{
|
|
||||||
for(auto * ti : turnsInfo)
|
|
||||||
delete ti;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPathfinderHelper::updateTurnInfo(const int Turn)
|
void CPathfinderHelper::updateTurnInfo(const int Turn)
|
||||||
{
|
{
|
||||||
@ -526,10 +522,7 @@ void CPathfinderHelper::updateTurnInfo(const int Turn)
|
|||||||
{
|
{
|
||||||
turn = Turn;
|
turn = Turn;
|
||||||
if(turn >= turnsInfo.size())
|
if(turn >= turnsInfo.size())
|
||||||
{
|
turnsInfo.push_back(hero->getTurnInfo(turn));
|
||||||
auto * ti = new TurnInfo(hero, turn);
|
|
||||||
turnsInfo.push_back(ti);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,12 +554,7 @@ bool CPathfinderHelper::isLayerAvailable(const EPathfindingLayer & layer) const
|
|||||||
|
|
||||||
const TurnInfo * CPathfinderHelper::getTurnInfo() const
|
const TurnInfo * CPathfinderHelper::getTurnInfo() const
|
||||||
{
|
{
|
||||||
return turnsInfo[turn];
|
return turnsInfo[turn].get();
|
||||||
}
|
|
||||||
|
|
||||||
bool CPathfinderHelper::hasBonusOfType(const BonusType type) const
|
|
||||||
{
|
|
||||||
return turnsInfo[turn]->hasBonusOfType(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer & layer) const
|
int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer & layer) const
|
||||||
@ -575,15 +563,16 @@ int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer & layer) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPathfinderHelper::getNeighbours(
|
void CPathfinderHelper::getNeighbours(
|
||||||
const TerrainTile & srcTile,
|
const TerrainTile & sourceTile,
|
||||||
const int3 & srcCoord,
|
const int3 & srcCoord,
|
||||||
NeighbourTilesVector & vec,
|
NeighbourTilesVector & vec,
|
||||||
const boost::logic::tribool & onLand,
|
const boost::logic::tribool & onLand,
|
||||||
const bool limitCoastSailing) const
|
const bool limitCoastSailing) const
|
||||||
{
|
{
|
||||||
CMap * map = gs->map;
|
CMap * map = gs->map;
|
||||||
|
const TerrainType * sourceTerrain = sourceTile.getTerrain();
|
||||||
|
|
||||||
static const int3 dirs[] = {
|
constexpr std::array dirs = {
|
||||||
int3(-1, +1, +0), int3(0, +1, +0), int3(+1, +1, +0),
|
int3(-1, +1, +0), int3(0, +1, +0), int3(+1, +1, +0),
|
||||||
int3(-1, +0, +0), /* source pos */ int3(+1, +0, +0),
|
int3(-1, +0, +0), /* source pos */ int3(+1, +0, +0),
|
||||||
int3(-1, -1, +0), int3(0, -1, +0), int3(+1, -1, +0)
|
int3(-1, -1, +0), int3(0, -1, +0), int3(+1, -1, +0)
|
||||||
@ -596,12 +585,12 @@ void CPathfinderHelper::getNeighbours(
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
const TerrainTile & destTile = map->getTile(destCoord);
|
const TerrainTile & destTile = map->getTile(destCoord);
|
||||||
const TerrainType* terrain = destTile.getTerrain();
|
const TerrainType * destTerrain = destTile.getTerrain();
|
||||||
if(!terrain->isPassable())
|
if(!destTerrain->isPassable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/// Following condition let us avoid diagonal movement over coast when sailing
|
/// Following condition let us avoid diagonal movement over coast when sailing
|
||||||
if(srcTile.isWater() && limitCoastSailing && terrain->isWater() && dir.x && dir.y) //diagonal move through water
|
if(sourceTerrain->isWater() && limitCoastSailing && destTerrain->isWater() && dir.x && dir.y) //diagonal move through water
|
||||||
{
|
{
|
||||||
const int3 horizontalNeighbour = srcCoord + int3{dir.x, 0, 0};
|
const int3 horizontalNeighbour = srcCoord + int3{dir.x, 0, 0};
|
||||||
const int3 verticalNeighbour = srcCoord + int3{0, dir.y, 0};
|
const int3 verticalNeighbour = srcCoord + int3{0, dir.y, 0};
|
||||||
@ -609,7 +598,7 @@ void CPathfinderHelper::getNeighbours(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(indeterminate(onLand) || onLand == terrain->isLand())
|
if(indeterminate(onLand) || onLand == destTerrain->isLand())
|
||||||
{
|
{
|
||||||
vec.push_back(destCoord);
|
vec.push_back(destCoord);
|
||||||
}
|
}
|
||||||
@ -663,54 +652,59 @@ int CPathfinderHelper::getMovementCost(
|
|||||||
|
|
||||||
bool isWaterLayer;
|
bool isWaterLayer;
|
||||||
if(indeterminate(isDstWaterLayer))
|
if(indeterminate(isDstWaterLayer))
|
||||||
isWaterLayer = ((hero->boat && hero->boat->layer == EPathfindingLayer::WATER) || ti->hasBonusOfType(BonusType::WATER_WALKING)) && dt->isWater();
|
isWaterLayer = ((hero->boat && hero->boat->layer == EPathfindingLayer::WATER) || ti->hasWaterWalking()) && dt->isWater();
|
||||||
else
|
else
|
||||||
isWaterLayer = static_cast<bool>(isDstWaterLayer);
|
isWaterLayer = static_cast<bool>(isDstWaterLayer);
|
||||||
|
|
||||||
bool isAirLayer = (hero->boat && hero->boat->layer == EPathfindingLayer::AIR) || ti->hasBonusOfType(BonusType::FLYING_MOVEMENT);
|
bool isAirLayer = (hero->boat && hero->boat->layer == EPathfindingLayer::AIR) || ti->hasFlyingMovement();
|
||||||
|
|
||||||
int ret = hero->getTileMovementCost(*dt, *ct, ti);
|
int movementCost = hero->getTileMovementCost(*dt, *ct, ti);
|
||||||
if(isSailLayer)
|
if(isSailLayer)
|
||||||
{
|
{
|
||||||
if(ct->hasFavorableWinds())
|
if(ct->hasFavorableWinds())
|
||||||
ret = static_cast<int>(ret * 2.0 / 3);
|
movementCost = static_cast<int>(movementCost * 2.0 / 3);
|
||||||
}
|
}
|
||||||
else if(isAirLayer)
|
else if(isAirLayer)
|
||||||
vstd::amin(ret, GameConstants::BASE_MOVEMENT_COST + ti->valOfBonuses(BonusType::FLYING_MOVEMENT));
|
vstd::amin(movementCost, GameConstants::BASE_MOVEMENT_COST + ti->getFlyingMovementValue());
|
||||||
else if(isWaterLayer && ti->hasBonusOfType(BonusType::WATER_WALKING))
|
else if(isWaterLayer && ti->hasWaterWalking())
|
||||||
ret = static_cast<int>(ret * (100.0 + ti->valOfBonuses(BonusType::WATER_WALKING)) / 100.0);
|
movementCost = static_cast<int>(movementCost * (100.0 + ti->getWaterWalkingValue()) / 100.0);
|
||||||
|
|
||||||
if(src.x != dst.x && src.y != dst.y) //it's diagonal move
|
if(src.x != dst.x && src.y != dst.y) //it's diagonal move
|
||||||
{
|
{
|
||||||
int old = ret;
|
int old = movementCost;
|
||||||
ret = static_cast<int>(ret * M_SQRT2);
|
movementCost = static_cast<int>(movementCost * M_SQRT2);
|
||||||
//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
|
//diagonal move costs too much but normal move is possible - allow diagonal move for remaining move points
|
||||||
// https://heroes.thelazy.net/index.php/Movement#Diagonal_move_exception
|
// https://heroes.thelazy.net/index.php/Movement#Diagonal_move_exception
|
||||||
if(ret > remainingMovePoints && remainingMovePoints >= old)
|
if(movementCost > remainingMovePoints && remainingMovePoints >= old)
|
||||||
{
|
{
|
||||||
return remainingMovePoints;
|
return remainingMovePoints;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int left = remainingMovePoints - ret;
|
//it might be the last tile - if no further move possible we take all move points
|
||||||
constexpr auto maxCostOfOneStep = static_cast<int>(175 * M_SQRT2); // diagonal move on Swamp - 247 MP
|
const int pointsLeft = remainingMovePoints - movementCost;
|
||||||
if(checkLast && left > 0 && left <= maxCostOfOneStep) //it might be the last tile - if no further move possible we take all move points
|
if(checkLast && pointsLeft > 0)
|
||||||
{
|
{
|
||||||
NeighbourTilesVector vec;
|
int minimalNextMoveCost = hero->getTileMovementCost(*dt, *ct, ti);
|
||||||
|
|
||||||
getNeighbours(*dt, dst, vec, ct->isLand(), true);
|
if (pointsLeft < minimalNextMoveCost)
|
||||||
for(const auto & elem : vec)
|
return remainingMovePoints;
|
||||||
{
|
|
||||||
int fcost = getMovementCost(dst, elem, nullptr, nullptr, left, false);
|
// NeighbourTilesVector vec;
|
||||||
if(fcost <= left)
|
//
|
||||||
{
|
// getNeighbours(*dt, dst, vec, ct->isLand(), true);
|
||||||
return ret;
|
// for(const auto & elem : vec)
|
||||||
}
|
// {
|
||||||
}
|
// int fcost = getMovementCost(dst, elem, nullptr, nullptr, pointsLeft, false);
|
||||||
ret = remainingMovePoints;
|
// if(fcost <= pointsLeft)
|
||||||
|
// {
|
||||||
|
// return movementCost;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// movementCost = remainingMovePoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return movementCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class CGWhirlpool;
|
class CGWhirlpool;
|
||||||
struct TurnInfo;
|
class TurnInfo;
|
||||||
struct PathfinderOptions;
|
struct PathfinderOptions;
|
||||||
|
|
||||||
// Optimized storage - tile can have 0-8 neighbour tiles
|
// Optimized storage - tile can have 0-8 neighbour tiles
|
||||||
@ -78,7 +78,7 @@ public:
|
|||||||
int turn;
|
int turn;
|
||||||
PlayerColor owner;
|
PlayerColor owner;
|
||||||
const CGHeroInstance * hero;
|
const CGHeroInstance * hero;
|
||||||
std::vector<TurnInfo *> turnsInfo;
|
std::vector<std::unique_ptr<TurnInfo>> turnsInfo;
|
||||||
const PathfinderOptions & options;
|
const PathfinderOptions & options;
|
||||||
bool canCastFly;
|
bool canCastFly;
|
||||||
bool canCastWaterWalk;
|
bool canCastWaterWalk;
|
||||||
@ -93,7 +93,7 @@ public:
|
|||||||
void updateTurnInfo(const int turn = 0);
|
void updateTurnInfo(const int turn = 0);
|
||||||
bool isLayerAvailable(const EPathfindingLayer & layer) const;
|
bool isLayerAvailable(const EPathfindingLayer & layer) const;
|
||||||
const TurnInfo * getTurnInfo() const;
|
const TurnInfo * getTurnInfo() const;
|
||||||
bool hasBonusOfType(BonusType type) const;
|
//bool hasBonusOfType(BonusType type) const;
|
||||||
int getMaxMovePoints(const EPathfindingLayer & layer) const;
|
int getMaxMovePoints(const EPathfindingLayer & layer) const;
|
||||||
|
|
||||||
TeleporterTilesVector getCastleGates(const PathNodeInfo & source) const;
|
TeleporterTilesVector getCastleGates(const PathNodeInfo & source) const;
|
||||||
|
@ -10,40 +10,154 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "TurnInfo.h"
|
#include "TurnInfo.h"
|
||||||
|
|
||||||
|
#include "../IGameCallback.h"
|
||||||
|
#include "../IGameSettings.h"
|
||||||
#include "../TerrainHandler.h"
|
#include "../TerrainHandler.h"
|
||||||
#include "../VCMI_Lib.h"
|
#include "../VCMI_Lib.h"
|
||||||
#include "../bonuses/BonusList.h"
|
#include "../bonuses/BonusList.h"
|
||||||
|
#include "../json/JsonNode.h"
|
||||||
#include "../mapObjects/CGHeroInstance.h"
|
#include "../mapObjects/CGHeroInstance.h"
|
||||||
#include "../mapObjects/MiscObjects.h"
|
#include "../mapObjects/MiscObjects.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
TurnInfo::BonusCache::BonusCache(const TConstBonusListPtr & bl)
|
TConstBonusListPtr TurnInfoBonusList::getBonusList(const CGHeroInstance * target, const CSelector & bonusSelector)
|
||||||
{
|
{
|
||||||
for(const auto & terrain : VLC->terrainTypeHandler->objects)
|
std::lock_guard guard(bonusListMutex);
|
||||||
{
|
|
||||||
auto selector = Selector::typeSubtype(BonusType::NO_TERRAIN_PENALTY, BonusSubtypeID(terrain->getId()));
|
if (target->getTreeVersion() == bonusListVersion)
|
||||||
if (bl->getFirst(selector))
|
return bonusList;
|
||||||
noTerrainPenalty.insert(terrain->getId());
|
|
||||||
|
bonusList = target->getBonuses(bonusSelector);
|
||||||
|
bonusListVersion = target->getTreeVersion();
|
||||||
|
|
||||||
|
return bonusList;
|
||||||
}
|
}
|
||||||
|
|
||||||
freeShipBoarding = static_cast<bool>(bl->getFirst(Selector::type()(BonusType::FREE_SHIP_BOARDING)));
|
int TurnInfo::hasWaterWalking() const
|
||||||
flyingMovement = static_cast<bool>(bl->getFirst(Selector::type()(BonusType::FLYING_MOVEMENT)));
|
{
|
||||||
flyingMovementVal = bl->valOfBonuses(Selector::type()(BonusType::FLYING_MOVEMENT));
|
return waterWalkingTest;
|
||||||
waterWalking = static_cast<bool>(bl->getFirst(Selector::type()(BonusType::WATER_WALKING)));
|
|
||||||
waterWalkingVal = bl->valOfBonuses(Selector::type()(BonusType::WATER_WALKING));
|
|
||||||
pathfindingVal = bl->valOfBonuses(Selector::type()(BonusType::ROUGH_TERRAIN_DISCOUNT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn):
|
int TurnInfo::hasFlyingMovement() const
|
||||||
hero(Hero),
|
|
||||||
maxMovePointsLand(-1),
|
|
||||||
maxMovePointsWater(-1),
|
|
||||||
turn(turn)
|
|
||||||
{
|
{
|
||||||
bonuses = hero->getAllBonuses(Selector::days(turn), Selector::all, "all_days" + std::to_string(turn));
|
return flyingMovementTest;
|
||||||
bonusCache = std::make_unique<BonusCache>(bonuses);
|
}
|
||||||
nativeTerrain = hero->getNativeTerrain();
|
|
||||||
|
int TurnInfo::hasNoTerrainPenalty(const TerrainId &terrain) const
|
||||||
|
{
|
||||||
|
return noterrainPenalty[terrain.num];
|
||||||
|
}
|
||||||
|
|
||||||
|
int TurnInfo::hasFreeShipBoarding() const
|
||||||
|
{
|
||||||
|
return freeShipBoardingTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TurnInfo::getFlyingMovementValue() const
|
||||||
|
{
|
||||||
|
return flyingMovementValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TurnInfo::getWaterWalkingValue() const
|
||||||
|
{
|
||||||
|
return waterWalkingValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TurnInfo::getRoughTerrainDiscountValue() const
|
||||||
|
{
|
||||||
|
return roughTerrainDiscountValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TurnInfo::getMovePointsLimitLand() const
|
||||||
|
{
|
||||||
|
return movePointsLimitLand;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TurnInfo::getMovePointsLimitWater() const
|
||||||
|
{
|
||||||
|
return movePointsLimitWater;
|
||||||
|
}
|
||||||
|
|
||||||
|
TurnInfo::TurnInfo(TurnInfoCache * sharedCache, const CGHeroInstance * target, int Turn)
|
||||||
|
: noterrainPenalty(VLC->terrainTypeHandler->size())
|
||||||
|
, target(target)
|
||||||
|
{
|
||||||
|
CSelector daySelector = Selector::days(Turn);
|
||||||
|
|
||||||
|
int lowestSpeed;
|
||||||
|
if (target->getTreeVersion() == sharedCache->heroLowestSpeedVersion)
|
||||||
|
{
|
||||||
|
lowestSpeed = sharedCache->heroLowestSpeedValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lowestSpeed = target->getLowestCreatureSpeed();
|
||||||
|
sharedCache->heroLowestSpeedValue = lowestSpeed;
|
||||||
|
sharedCache->heroLowestSpeedVersion = target->getTreeVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static const CSelector selector = Selector::type()(BonusType::WATER_WALKING);
|
||||||
|
const auto & bonuses = sharedCache->waterWalking.getBonusList(target, selector);
|
||||||
|
waterWalkingTest = bonuses->getFirst(selector) != nullptr;
|
||||||
|
waterWalkingValue = bonuses->valOfBonuses(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static const CSelector selector = Selector::type()(BonusType::FLYING_MOVEMENT);
|
||||||
|
const auto & bonuses = sharedCache->flyingMovement.getBonusList(target, selector);
|
||||||
|
flyingMovementTest = bonuses->getFirst(selector) != nullptr;
|
||||||
|
flyingMovementValue = bonuses->valOfBonuses(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static const CSelector selector = Selector::type()(BonusType::FREE_SHIP_BOARDING);
|
||||||
|
const auto & bonuses = sharedCache->flyingMovement.getBonusList(target, selector);
|
||||||
|
freeShipBoardingTest = bonuses->getFirst(selector) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static const CSelector selector = Selector::type()(BonusType::ROUGH_TERRAIN_DISCOUNT);
|
||||||
|
const auto & bonuses = sharedCache->flyingMovement.getBonusList(target, selector);
|
||||||
|
roughTerrainDiscountValue = bonuses->getFirst(selector) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static const CSelector selector = Selector::typeSubtype(BonusType::MOVEMENT, BonusCustomSubtype::heroMovementSea);
|
||||||
|
const auto & vectorSea = target->cb->getSettings().getValue(EGameSettings::HEROES_MOVEMENT_POINTS_SEA).Vector();
|
||||||
|
const auto & bonuses = sharedCache->flyingMovement.getBonusList(target, selector);
|
||||||
|
int baseMovementPointsSea;
|
||||||
|
if (lowestSpeed < vectorSea.size())
|
||||||
|
baseMovementPointsSea = vectorSea[lowestSpeed].Integer();
|
||||||
|
else
|
||||||
|
baseMovementPointsSea = vectorSea.back().Integer();
|
||||||
|
|
||||||
|
movePointsLimitWater = bonuses->valOfBonuses(selector, baseMovementPointsSea);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static const CSelector selector = Selector::typeSubtype(BonusType::MOVEMENT, BonusCustomSubtype::heroMovementSea);
|
||||||
|
const auto & vectorLand = target->cb->getSettings().getValue(EGameSettings::HEROES_MOVEMENT_POINTS_LAND).Vector();
|
||||||
|
const auto & bonuses = sharedCache->flyingMovement.getBonusList(target, selector);
|
||||||
|
int baseMovementPointsLand;
|
||||||
|
if (lowestSpeed < vectorLand.size())
|
||||||
|
baseMovementPointsLand = vectorLand[lowestSpeed].Integer();
|
||||||
|
else
|
||||||
|
baseMovementPointsLand = vectorLand.back().Integer();
|
||||||
|
|
||||||
|
movePointsLimitLand = bonuses->valOfBonuses(selector, baseMovementPointsLand);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static const CSelector selector = Selector::type()(BonusType::NO_TERRAIN_PENALTY);
|
||||||
|
const auto & bonuses = sharedCache->flyingMovement.getBonusList(target, selector);
|
||||||
|
for (const auto & bonus : *bonuses)
|
||||||
|
{
|
||||||
|
TerrainId affectedTerrain = bonus->subtype.as<TerrainId>();
|
||||||
|
noterrainPenalty.at(affectedTerrain.num) = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const
|
bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const
|
||||||
@ -51,19 +165,19 @@ bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const
|
|||||||
switch(layer.toEnum())
|
switch(layer.toEnum())
|
||||||
{
|
{
|
||||||
case EPathfindingLayer::AIR:
|
case EPathfindingLayer::AIR:
|
||||||
if(hero && hero->boat && hero->boat->layer == EPathfindingLayer::AIR)
|
if(target && target->boat && target->boat->layer == EPathfindingLayer::AIR)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(!hasBonusOfType(BonusType::FLYING_MOVEMENT))
|
if(!hasFlyingMovement())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EPathfindingLayer::WATER:
|
case EPathfindingLayer::WATER:
|
||||||
if(hero && hero->boat && hero->boat->layer == EPathfindingLayer::WATER)
|
if(target && target->boat && target->boat->layer == EPathfindingLayer::WATER)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(!hasBonusOfType(BonusType::WATER_WALKING))
|
if(!hasWaterWalking())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -72,80 +186,9 @@ bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TurnInfo::hasBonusOfType(BonusType type) const
|
|
||||||
{
|
|
||||||
return hasBonusOfType(type, BonusSubtypeID());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TurnInfo::hasBonusOfType(BonusType type, BonusSubtypeID subtype) const
|
|
||||||
{
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case BonusType::FREE_SHIP_BOARDING:
|
|
||||||
return bonusCache->freeShipBoarding;
|
|
||||||
case BonusType::FLYING_MOVEMENT:
|
|
||||||
return bonusCache->flyingMovement;
|
|
||||||
case BonusType::WATER_WALKING:
|
|
||||||
return bonusCache->waterWalking;
|
|
||||||
case BonusType::NO_TERRAIN_PENALTY:
|
|
||||||
return bonusCache->noTerrainPenalty.count(subtype.as<TerrainId>());
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<bool>(
|
|
||||||
bonuses->getFirst(Selector::type()(type).And(Selector::subtype()(subtype))));
|
|
||||||
}
|
|
||||||
|
|
||||||
int TurnInfo::valOfBonuses(BonusType type) const
|
|
||||||
{
|
|
||||||
return valOfBonuses(type, BonusSubtypeID());
|
|
||||||
}
|
|
||||||
|
|
||||||
int TurnInfo::valOfBonuses(BonusType type, BonusSubtypeID subtype) const
|
|
||||||
{
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case BonusType::FLYING_MOVEMENT:
|
|
||||||
return bonusCache->flyingMovementVal;
|
|
||||||
case BonusType::WATER_WALKING:
|
|
||||||
return bonusCache->waterWalkingVal;
|
|
||||||
case BonusType::ROUGH_TERRAIN_DISCOUNT:
|
|
||||||
return bonusCache->pathfindingVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bonuses->valOfBonuses(Selector::type()(type).And(Selector::subtype()(subtype)));
|
|
||||||
}
|
|
||||||
|
|
||||||
int TurnInfo::getMaxMovePoints(const EPathfindingLayer & layer) const
|
int TurnInfo::getMaxMovePoints(const EPathfindingLayer & layer) const
|
||||||
{
|
{
|
||||||
if(maxMovePointsLand == -1)
|
return layer == EPathfindingLayer::SAIL ? getMovePointsLimitWater() : getMovePointsLimitLand();
|
||||||
maxMovePointsLand = hero->movementPointsLimitCached(true, this);
|
|
||||||
if(maxMovePointsWater == -1)
|
|
||||||
maxMovePointsWater = hero->movementPointsLimitCached(false, this);
|
|
||||||
|
|
||||||
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TurnInfo::updateHeroBonuses(BonusType type) const
|
|
||||||
{
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case BonusType::FREE_SHIP_BOARDING:
|
|
||||||
bonusCache->freeShipBoarding = static_cast<bool>(bonuses->getFirst(Selector::type()(BonusType::FREE_SHIP_BOARDING)));
|
|
||||||
break;
|
|
||||||
case BonusType::FLYING_MOVEMENT:
|
|
||||||
bonusCache->flyingMovement = static_cast<bool>(bonuses->getFirst(Selector::type()(BonusType::FLYING_MOVEMENT)));
|
|
||||||
bonusCache->flyingMovementVal = bonuses->valOfBonuses(Selector::type()(BonusType::FLYING_MOVEMENT));
|
|
||||||
break;
|
|
||||||
case BonusType::WATER_WALKING:
|
|
||||||
bonusCache->waterWalking = static_cast<bool>(bonuses->getFirst(Selector::type()(BonusType::WATER_WALKING)));
|
|
||||||
bonusCache->waterWalkingVal = bonuses->valOfBonuses(Selector::type()(BonusType::WATER_WALKING));
|
|
||||||
break;
|
|
||||||
case BonusType::ROUGH_TERRAIN_DISCOUNT:
|
|
||||||
bonusCache->pathfindingVal = bonuses->valOfBonuses(Selector::type()(BonusType::ROUGH_TERRAIN_DISCOUNT));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
bonuses = hero->getAllBonuses(Selector::days(turn), Selector::all, "all_days" + std::to_string(turn));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -10,43 +10,74 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../bonuses/Bonus.h"
|
#include "../bonuses/Bonus.h"
|
||||||
#include "../GameConstants.h"
|
#include "../bonuses/BonusSelector.h"
|
||||||
|
#include "../bonuses/BonusCache.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
|
|
||||||
struct DLL_LINKAGE TurnInfo
|
class TurnInfoBonusList
|
||||||
{
|
{
|
||||||
/// This is certainly not the best design ever and certainly can be improved
|
TConstBonusListPtr bonusList;
|
||||||
/// Unfortunately for pathfinder that do hundreds of thousands calls onus system add too big overhead
|
std::mutex bonusListMutex;
|
||||||
struct BonusCache {
|
std::atomic<int64_t> bonusListVersion = 0;
|
||||||
std::set<TerrainId> noTerrainPenalty;
|
public:
|
||||||
bool freeShipBoarding;
|
TConstBonusListPtr getBonusList(const CGHeroInstance * target, const CSelector & bonusSelector);
|
||||||
bool flyingMovement;
|
|
||||||
int flyingMovementVal;
|
|
||||||
bool waterWalking;
|
|
||||||
int waterWalkingVal;
|
|
||||||
int pathfindingVal;
|
|
||||||
|
|
||||||
BonusCache(const TConstBonusListPtr & bonusList);
|
|
||||||
};
|
};
|
||||||
std::unique_ptr<BonusCache> bonusCache;
|
|
||||||
|
|
||||||
const CGHeroInstance * hero;
|
struct TurnInfoCache
|
||||||
mutable TConstBonusListPtr bonuses;
|
{
|
||||||
mutable int maxMovePointsLand;
|
TurnInfoBonusList waterWalking;
|
||||||
mutable int maxMovePointsWater;
|
TurnInfoBonusList flyingMovement;
|
||||||
TerrainId nativeTerrain;
|
TurnInfoBonusList noTerrainPenalty;
|
||||||
int turn;
|
TurnInfoBonusList freeShipBoarding;
|
||||||
|
TurnInfoBonusList roughTerrainDiscount;
|
||||||
|
TurnInfoBonusList movementPointsLimitLand;
|
||||||
|
TurnInfoBonusList movementPointsLimitWater;
|
||||||
|
|
||||||
TurnInfo(const CGHeroInstance * Hero, const int Turn = 0);
|
const CGHeroInstance * target;
|
||||||
|
|
||||||
|
mutable std::atomic<int64_t> heroLowestSpeedVersion = 0;
|
||||||
|
mutable std::atomic<int64_t> heroLowestSpeedValue = 0;
|
||||||
|
|
||||||
|
explicit TurnInfoCache(const CGHeroInstance * target):
|
||||||
|
target(target)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DLL_LINKAGE TurnInfo
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const CGHeroInstance * target;
|
||||||
|
|
||||||
|
// stores cached values per each terrain
|
||||||
|
std::vector<bool> noterrainPenalty;
|
||||||
|
|
||||||
|
int flyingMovementValue;
|
||||||
|
int waterWalkingValue;
|
||||||
|
int roughTerrainDiscountValue;
|
||||||
|
int movePointsLimitLand;
|
||||||
|
int movePointsLimitWater;
|
||||||
|
|
||||||
|
bool waterWalkingTest;
|
||||||
|
bool flyingMovementTest;
|
||||||
|
bool freeShipBoardingTest;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int hasWaterWalking() const;
|
||||||
|
int hasFlyingMovement() const;
|
||||||
|
int hasNoTerrainPenalty(const TerrainId & terrain) const;
|
||||||
|
int hasFreeShipBoarding() const;
|
||||||
|
|
||||||
|
int getFlyingMovementValue() const;
|
||||||
|
int getWaterWalkingValue() const;
|
||||||
|
int getRoughTerrainDiscountValue() const;
|
||||||
|
int getMovePointsLimitLand() const;
|
||||||
|
int getMovePointsLimitWater() const;
|
||||||
|
|
||||||
|
TurnInfo(TurnInfoCache * sharedCache, const CGHeroInstance * target, int Turn);
|
||||||
bool isLayerAvailable(const EPathfindingLayer & layer) const;
|
bool isLayerAvailable(const EPathfindingLayer & layer) const;
|
||||||
bool hasBonusOfType(const BonusType type) const;
|
|
||||||
bool hasBonusOfType(const BonusType type, const BonusSubtypeID subtype) const;
|
|
||||||
int valOfBonuses(const BonusType type) const;
|
|
||||||
int valOfBonuses(const BonusType type, const BonusSubtypeID subtype) const;
|
|
||||||
void updateHeroBonuses(BonusType type) const;
|
|
||||||
int getMaxMovePoints(const EPathfindingLayer & layer) const;
|
int getMaxMovePoints(const EPathfindingLayer & layer) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -846,8 +846,8 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
|
|||||||
auto pathfinderHelper = std::make_unique<CPathfinderHelper>(gs, h, PathfinderOptions(this));
|
auto pathfinderHelper = std::make_unique<CPathfinderHelper>(gs, h, PathfinderOptions(this));
|
||||||
auto ti = pathfinderHelper->getTurnInfo();
|
auto ti = pathfinderHelper->getTurnInfo();
|
||||||
|
|
||||||
const bool canFly = pathfinderHelper->hasBonusOfType(BonusType::FLYING_MOVEMENT) || (h->boat && h->boat->layer == EPathfindingLayer::AIR);
|
const bool canFly = ti->hasFlyingMovement() || (h->boat && h->boat->layer == EPathfindingLayer::AIR);
|
||||||
const bool canWalkOnSea = pathfinderHelper->hasBonusOfType(BonusType::WATER_WALKING) || (h->boat && h->boat->layer == EPathfindingLayer::WATER);
|
const bool canWalkOnSea = ti->hasWaterWalking() || (h->boat && h->boat->layer == EPathfindingLayer::WATER);
|
||||||
const int cost = pathfinderHelper->getMovementCost(h->visitablePos(), hmpos, nullptr, nullptr, h->movementPointsRemaining());
|
const int cost = pathfinderHelper->getMovementCost(h->visitablePos(), hmpos, nullptr, nullptr, h->movementPointsRemaining());
|
||||||
|
|
||||||
const bool movingOntoObstacle = t.blocked() && !t.visitable();
|
const bool movingOntoObstacle = t.blocked() && !t.visitable();
|
||||||
|
@ -584,7 +584,7 @@ std::vector<SetMovePoints> NewTurnProcessor::updateHeroesMovementPoints()
|
|||||||
{
|
{
|
||||||
for (CGHeroInstance *h : elem.second.getHeroes())
|
for (CGHeroInstance *h : elem.second.getHeroes())
|
||||||
{
|
{
|
||||||
auto ti = std::make_unique<TurnInfo>(h, 1);
|
auto ti = h->getTurnInfo(1);
|
||||||
// NOTE: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356
|
// NOTE: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356
|
||||||
int32_t newMovementPoints = h->movementPointsLimitCached(gameHandler->gameState()->map->getTile(h->visitablePos()).isLand(), ti.get());
|
int32_t newMovementPoints = h->movementPointsLimitCached(gameHandler->gameState()->map->getTile(h->visitablePos()).isLand(), ti.get());
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user