mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-08 22:26:51 +02:00
Support for configuring minimal cost for moving between tiles
- Added `movementCostBase` parameter to game config that defines minimal amount of movement points that will be spent when moving from one tile on another while offroad (and cost of Fly / Town Portal spells) - Added `BASE_TILE_MOVEMENT_COST` bonus type that allows modifying `movementCostBase` on per-hero basis Example usage for hota-like pathfinding skill ```json "tileCostReduction" : { "type" : "BASE_TILE_MOVEMENT_COST", "val" : -15 } ```
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "../../../lib/pathfinder/CPathfinder.h"
|
||||
#include "../../../lib/pathfinder/PathfinderUtil.h"
|
||||
#include "../../../lib/pathfinder/PathfinderOptions.h"
|
||||
#include "../../../lib/IGameSettings.h"
|
||||
#include "../../../lib/CPlayerState.h"
|
||||
|
||||
namespace NKAI
|
||||
@@ -1097,7 +1098,9 @@ struct TownPortalFinder
|
||||
|
||||
// TODO: Copy/Paste from TownPortalMechanics
|
||||
townPortalSkillLevel = MasteryLevel::Type(hero->getSpellSchoolLevel(townPortal));
|
||||
movementNeeded = GameConstants::BASE_MOVEMENT_COST * (townPortalSkillLevel >= MasteryLevel::EXPERT ? 2 : 3);
|
||||
|
||||
int baseCost = hero->cb->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
||||
movementNeeded = baseCost * (townPortalSkillLevel >= MasteryLevel::EXPERT ? 2 : 3);
|
||||
}
|
||||
|
||||
bool actorCanCastTownPortal()
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include "../../../lib/pathfinder/CPathfinder.h"
|
||||
#include "../../../lib/pathfinder/PathfinderOptions.h"
|
||||
#include "../../../lib/pathfinder/PathfinderUtil.h"
|
||||
#include "../../../lib/IGameSettings.h"
|
||||
#include "../../../lib/CPlayerState.h"
|
||||
|
||||
AINodeStorage::AINodeStorage(const int3 & Sizes)
|
||||
@@ -246,7 +247,8 @@ void AINodeStorage::calculateTownPortalTeleportations(
|
||||
|
||||
// TODO: Copy/Paste from TownPortalMechanics
|
||||
auto skillLevel = hero->getSpellSchoolLevel(townPortal);
|
||||
auto movementCost = GameConstants::BASE_MOVEMENT_COST * (skillLevel >= 3 ? 2 : 3);
|
||||
int baseCost = hero->cb->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
||||
auto movementCost = baseCost * (skillLevel >= 3 ? 2 : 3);
|
||||
|
||||
if(hero->movementPointsRemaining() < movementCost)
|
||||
{
|
||||
|
@@ -306,6 +306,10 @@
|
||||
"tavernInvite" : false,
|
||||
// minimal primary skills for heroes
|
||||
"minimalPrimarySkills": [ 0, 0, 1, 1],
|
||||
|
||||
/// minimal movement cost from one tile to another while offroad. Also see BASE_TILE_MOVEMENT_COST bonus type
|
||||
/// Also affects cost of Town Portal spell and movement cost when using Fly spell
|
||||
"movementCostBase" : 100,
|
||||
/// 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)
|
||||
|
@@ -45,6 +45,7 @@
|
||||
"backpackSize" : { "type" : "number" },
|
||||
"tavernInvite" : { "type" : "boolean" },
|
||||
"minimalPrimarySkills" : { "type" : "array" },
|
||||
"movementCostBase" : { "type" : "number" },
|
||||
"movementPointsLand" : { "type" : "array" },
|
||||
"movementPointsSea" : { "type" : "array" }
|
||||
}
|
||||
|
@@ -156,10 +156,16 @@ Allows affected heroes to learn spells from each other during hero exchange
|
||||
|
||||
### ROUGH_TERRAIN_DISCOUNT
|
||||
|
||||
Reduces movement points penalty when moving on terrains with movement cost over 100 points. Can not reduce movement cost below 100 points
|
||||
Reduces movement point penalty when moving on terrain with movement cost higher than base movement cost Cannot reduce movement cost lower than base movement cost. See the `movementCostBase` parameter in the game config and the `BASE_TILE_MOVEMENT_COST` bonus type. Used for the Pathfinding skill
|
||||
|
||||
- val: penalty reduction, in movement points per tile.
|
||||
|
||||
### BASE_TILE_MOVEMENT_COST
|
||||
|
||||
Change the minimum cost required to move from one tile to another while off-road by the specified value. Has no effect on road movement. Used for pathfinding in HotA
|
||||
|
||||
- val: positive value increases the minimum cost, negative value decreases it.
|
||||
|
||||
### WANDERING_CREATURES_JOIN_BONUS
|
||||
|
||||
Increases probability for wandering monsters to join army of affected heroes
|
||||
|
@@ -78,6 +78,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
|
||||
{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, "heroes", "retreatOnWinWithoutTroops" },
|
||||
{EGameSettings::HEROES_STARTING_STACKS_CHANCES, "heroes", "startingStackChances" },
|
||||
{EGameSettings::HEROES_TAVERN_INVITE, "heroes", "tavernInvite" },
|
||||
{EGameSettings::HEROES_MOVEMENT_COST_BASE, "heroes", "movementCostBase" },
|
||||
{EGameSettings::HEROES_MOVEMENT_POINTS_LAND, "heroes", "movementPointsLand" },
|
||||
{EGameSettings::HEROES_MOVEMENT_POINTS_SEA, "heroes", "movementPointsSea" },
|
||||
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
|
||||
|
@@ -51,6 +51,7 @@ enum class EGameSettings
|
||||
HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS,
|
||||
HEROES_STARTING_STACKS_CHANCES,
|
||||
HEROES_TAVERN_INVITE,
|
||||
HEROES_MOVEMENT_COST_BASE,
|
||||
HEROES_MOVEMENT_POINTS_LAND,
|
||||
HEROES_MOVEMENT_POINTS_SEA,
|
||||
MAP_FORMAT_ARMAGEDDONS_BLADE,
|
||||
|
@@ -84,6 +84,6 @@ RoadType::RoadType():
|
||||
id(Road::NO_ROAD),
|
||||
identifier("empty"),
|
||||
modScope("core"),
|
||||
movementCost(GameConstants::BASE_MOVEMENT_COST)
|
||||
movementCost(0)
|
||||
{}
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@@ -182,6 +182,7 @@ class JsonNode;
|
||||
BONUS_NAME(INVINCIBLE) /* cannot be target of attacks or spells */ \
|
||||
BONUS_NAME(MECHANICAL) /*eg. factory creatures, cannot be rised or healed, only neutral morale, repairable by engineer */ \
|
||||
BONUS_NAME(PRISM_HEX_ATTACK_BREATH) /*eg. dragons*/ \
|
||||
BONUS_NAME(BASE_TILE_MOVEMENT_COST) /*minimal cost for moving offroad*/ \
|
||||
/* end of list */
|
||||
|
||||
|
||||
|
@@ -49,7 +49,6 @@ namespace GameConstants
|
||||
constexpr int SPELLS_QUANTITY=70;
|
||||
constexpr int CREATURES_COUNT = 197;
|
||||
|
||||
constexpr ui32 BASE_MOVEMENT_COST = 100; //default cost for non-diagonal movement
|
||||
constexpr int64_t PLAYER_RESOURCES_CAP = 1000 * 1000 * 1000;
|
||||
constexpr int ALTAR_ARTIFACTS_SLOTS = 22;
|
||||
constexpr int TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD = 144*144*2; //map tiles count threshold for 2 dimension door casts with tournament rules
|
||||
|
@@ -73,25 +73,6 @@ void CGHeroPlaceholder::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
handler.serializeInt("powerRank", powerRank.value());
|
||||
}
|
||||
|
||||
ui32 CGHeroInstance::getTileMovementCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const
|
||||
{
|
||||
int64_t ret = GameConstants::BASE_MOVEMENT_COST;
|
||||
|
||||
//if there is road both on dest and src tiles - use src road movement cost
|
||||
if(dest.hasRoad() && from.hasRoad())
|
||||
{
|
||||
ret = from.getRoad()->movementCost;
|
||||
}
|
||||
else if(!ti->hasNoTerrainPenalty(from.getTerrainID())) //no special movement bonus
|
||||
{
|
||||
ret = VLC->terrainTypeHandler->getById(from.getTerrainID())->moveCost;
|
||||
ret -= ti->getRoughTerrainDiscountValue();
|
||||
if(ret < GameConstants::BASE_MOVEMENT_COST)
|
||||
ret = GameConstants::BASE_MOVEMENT_COST;
|
||||
}
|
||||
return static_cast<ui32>(ret);
|
||||
}
|
||||
|
||||
FactionID CGHeroInstance::getFactionID() const
|
||||
{
|
||||
return getHeroClass()->faction;
|
||||
|
@@ -218,9 +218,6 @@ public:
|
||||
void setSecSkillLevel(const SecondarySkill & which, int val, bool abs); // abs == 0 - changes by value; 1 - sets to value
|
||||
void levelUp(const std::vector<SecondarySkill> & skills);
|
||||
|
||||
/// returns base movement cost for movement between specific tiles. Does not accounts for diagonal movement or last tile exception
|
||||
ui32 getTileMovementCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const;
|
||||
|
||||
void setMovementPoints(int points);
|
||||
int movementPointsRemaining() const;
|
||||
int movementPointsLimit(bool onLand) const;
|
||||
|
@@ -16,8 +16,10 @@
|
||||
#include "TurnInfo.h"
|
||||
|
||||
#include "../gameState/CGameState.h"
|
||||
#include "../IGameSettings.h"
|
||||
#include "../CPlayerState.h"
|
||||
#include "../TerrainHandler.h"
|
||||
#include "../RoadHandler.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../mapObjects/CGTownInstance.h"
|
||||
#include "../mapObjects/MiscObjects.h"
|
||||
@@ -658,14 +660,17 @@ int CPathfinderHelper::getMovementCost(
|
||||
|
||||
bool isAirLayer = (hero->boat && hero->boat->layer == EPathfindingLayer::AIR) || ti->hasFlyingMovement();
|
||||
|
||||
int movementCost = hero->getTileMovementCost(*dt, *ct, ti);
|
||||
int movementCost = getTileMovementCost(*dt, *ct, ti);
|
||||
if(isSailLayer)
|
||||
{
|
||||
if(ct->hasFavorableWinds())
|
||||
movementCost = static_cast<int>(movementCost * 2.0 / 3);
|
||||
}
|
||||
else if(isAirLayer)
|
||||
vstd::amin(movementCost, GameConstants::BASE_MOVEMENT_COST + ti->getFlyingMovementValue());
|
||||
{
|
||||
int baseCost = getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
||||
vstd::amin(movementCost, baseCost + ti->getFlyingMovementValue());
|
||||
}
|
||||
else if(isWaterLayer && ti->hasWaterWalking())
|
||||
movementCost = static_cast<int>(movementCost * (100.0 + ti->getWaterWalkingValue()) / 100.0);
|
||||
|
||||
@@ -685,7 +690,7 @@ int CPathfinderHelper::getMovementCost(
|
||||
const int pointsLeft = remainingMovePoints - movementCost;
|
||||
if(checkLast && pointsLeft > 0)
|
||||
{
|
||||
int minimalNextMoveCost = hero->getTileMovementCost(*dt, *ct, ti);
|
||||
int minimalNextMoveCost = getTileMovementCost(*dt, *ct, ti);
|
||||
|
||||
if (pointsLeft < minimalNextMoveCost)
|
||||
return remainingMovePoints;
|
||||
@@ -694,4 +699,26 @@ int CPathfinderHelper::getMovementCost(
|
||||
return movementCost;
|
||||
}
|
||||
|
||||
ui32 CPathfinderHelper::getTileMovementCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const
|
||||
{
|
||||
//if there is road both on dest and src tiles - use src road movement cost
|
||||
if(dest.hasRoad() && from.hasRoad())
|
||||
return from.getRoad()->movementCost;
|
||||
|
||||
int baseMovementCost = ti->getMovementCostBase();
|
||||
int terrainMoveCost = from.getTerrain()->moveCost;
|
||||
int terrainDiscout = ti->getRoughTerrainDiscountValue();
|
||||
|
||||
int costWithPathfinding = std::max(baseMovementCost, terrainMoveCost - terrainDiscout);
|
||||
|
||||
//if hero can move without penalty - either all-native army, or creatures like Nomads in army
|
||||
if(ti->hasNoTerrainPenalty(from.getTerrainID()))
|
||||
{
|
||||
int baseCost = getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
||||
return std::min(baseCost, costWithPathfinding);
|
||||
}
|
||||
|
||||
return costWithPathfinding;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@@ -66,6 +66,9 @@ private:
|
||||
|
||||
class DLL_LINKAGE CPathfinderHelper : private CGameInfoCallback
|
||||
{
|
||||
/// returns base movement cost for movement between specific tiles. Does not accounts for diagonal movement or last tile exception
|
||||
ui32 getTileMovementCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const;
|
||||
|
||||
public:
|
||||
enum EPatrolState
|
||||
{
|
||||
|
@@ -69,6 +69,11 @@ int TurnInfo::getRoughTerrainDiscountValue() const
|
||||
return roughTerrainDiscountValue;
|
||||
}
|
||||
|
||||
int TurnInfo::getMovementCostBase() const
|
||||
{
|
||||
return moveCostBaseValue;
|
||||
}
|
||||
|
||||
int TurnInfo::getMovePointsLimitLand() const
|
||||
{
|
||||
return movePointsLimitLand;
|
||||
@@ -123,6 +128,13 @@ TurnInfo::TurnInfo(TurnInfoCache * sharedCache, const CGHeroInstance * target, i
|
||||
roughTerrainDiscountValue = bonuses->valOfBonuses(daySelector);
|
||||
}
|
||||
|
||||
{
|
||||
static const CSelector selector = Selector::type()(BonusType::BASE_TILE_MOVEMENT_COST);
|
||||
const auto & bonuses = sharedCache->baseTileMovementCost.getBonusList(target, selector);
|
||||
int baseMovementCost = target->cb->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
||||
moveCostBaseValue = bonuses->valOfBonuses(daySelector, baseMovementCost);
|
||||
}
|
||||
|
||||
{
|
||||
static const CSelector selector = Selector::typeSubtype(BonusType::MOVEMENT, BonusCustomSubtype::heroMovementSea);
|
||||
const auto & vectorSea = target->cb->getSettings().getValue(EGameSettings::HEROES_MOVEMENT_POINTS_SEA).Vector();
|
||||
|
@@ -33,6 +33,7 @@ struct TurnInfoCache
|
||||
TurnInfoBonusList noTerrainPenalty;
|
||||
TurnInfoBonusList freeShipBoarding;
|
||||
TurnInfoBonusList roughTerrainDiscount;
|
||||
TurnInfoBonusList baseTileMovementCost;
|
||||
TurnInfoBonusList movementPointsLimitLand;
|
||||
TurnInfoBonusList movementPointsLimitWater;
|
||||
|
||||
@@ -57,6 +58,7 @@ private:
|
||||
int flyingMovementValue;
|
||||
int waterWalkingValue;
|
||||
int roughTerrainDiscountValue;
|
||||
int moveCostBaseValue;
|
||||
int movePointsLimitLand;
|
||||
int movePointsLimitWater;
|
||||
|
||||
@@ -73,6 +75,7 @@ public:
|
||||
int getFlyingMovementValue() const;
|
||||
int getWaterWalkingValue() const;
|
||||
int getRoughTerrainDiscountValue() const;
|
||||
int getMovementCostBase() const;
|
||||
int getMovePointsLimitLand() const;
|
||||
int getMovePointsLimitWater() const;
|
||||
|
||||
|
@@ -384,7 +384,8 @@ bool DimensionDoorMechanics::canBeCastAtImpl(spells::Problem & problem, const CG
|
||||
ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
|
||||
{
|
||||
const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||
const int movementCost = GameConstants::BASE_MOVEMENT_COST * ((schoolLevel >= 3) ? 2 : 3);
|
||||
const int baseCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
||||
const int movementCost = baseCost * ((schoolLevel >= 3) ? 2 : 3);
|
||||
|
||||
int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter();
|
||||
const TerrainTile * dest = env->getCb()->getTile(parameters.pos);
|
||||
@@ -447,7 +448,7 @@ TownPortalMechanics::TownPortalMechanics(const CSpell * s):
|
||||
ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
|
||||
{
|
||||
const CGTownInstance * destination = nullptr;
|
||||
const int moveCost = movementCost(parameters);
|
||||
const int moveCost = movementCost(env, parameters);
|
||||
|
||||
if(!parameters.caster->getHeroCaster())
|
||||
{
|
||||
@@ -548,7 +549,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
|
||||
void TownPortalMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
|
||||
{
|
||||
const int moveCost = movementCost(parameters);
|
||||
const int moveCost = movementCost(env, parameters);
|
||||
const CGTownInstance * destination = nullptr;
|
||||
|
||||
if(parameters.caster->getSpellSchoolLevel(owner) < 2)
|
||||
@@ -591,7 +592,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
|
||||
const int moveCost = movementCost(parameters);
|
||||
const int moveCost = movementCost(env, parameters);
|
||||
|
||||
if(static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost)
|
||||
{
|
||||
@@ -700,12 +701,13 @@ std::vector <const CGTownInstance*> TownPortalMechanics::getPossibleTowns(SpellC
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t TownPortalMechanics::movementCost(const AdventureSpellCastParameters & parameters) const
|
||||
int32_t TownPortalMechanics::movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
|
||||
{
|
||||
if(parameters.caster != parameters.caster->getHeroCaster()) //if caster is not hero
|
||||
return 0;
|
||||
|
||||
return GameConstants::BASE_MOVEMENT_COST * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3);
|
||||
int baseMovementCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
|
||||
return baseMovementCost * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3);
|
||||
}
|
||||
|
||||
///ViewMechanics
|
||||
|
@@ -86,7 +86,7 @@ protected:
|
||||
void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override;
|
||||
private:
|
||||
const CGTownInstance * findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector <const CGTownInstance*> & pool) const;
|
||||
int32_t movementCost(const AdventureSpellCastParameters & parameters) const;
|
||||
int32_t movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
|
||||
std::vector <const CGTownInstance*> getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user