Implement slope calculation and uphill/downhill step cost in terrainmovementmanager.cpp

This commit is contained in:
bart
2026-11-08 17:08:07 +01:00
parent 9c57ea3b27
commit 23d31b8258
+82 -1
View File
@@ -13,7 +13,11 @@ TerrainMovementConfig::TerrainMovementConfig()
polarSnowLineEnabled(true), polarSnowLineEnabled(true),
polarSnowLineBaseKm(2.5f), polarSnowLineBaseKm(2.5f),
polarSnowFactorStart(0.40f), polarSnowFactorStart(0.40f),
polarSnowFactorEnd(0.85f) polarSnowFactorEnd(0.85f),
uphillSlopeMultiplier(4.0f),
downhillSlopeBonusMultiplier(2.0f),
minStepCost(0.25f),
maxClimbableSlopeKm(1.2f)
{ {
movementCosts["grasland"] = 1.0f; movementCosts["grasland"] = 1.0f;
movementCosts["bos"] = 2.0f; movementCosts["bos"] = 2.0f;
@@ -74,6 +78,19 @@ bool TerrainMovementManager::loadConfig(const QString &filePath)
m_config.polarSnowFactorEnd = root["polar_snow_factor_end"].asFloat(0.85f); m_config.polarSnowFactorEnd = root["polar_snow_factor_end"].asFloat(0.85f);
} }
if (root.hasChild("uphill_slope_multiplier")) {
m_config.uphillSlopeMultiplier = root["uphill_slope_multiplier"].asFloat(4.0f);
}
if (root.hasChild("downhill_slope_bonus_multiplier")) {
m_config.downhillSlopeBonusMultiplier = root["downhill_slope_bonus_multiplier"].asFloat(2.0f);
}
if (root.hasChild("min_step_cost")) {
m_config.minStepCost = root["min_step_cost"].asFloat(0.25f);
}
if (root.hasChild("max_climbable_slope_km")) {
m_config.maxClimbableSlopeKm = root["max_climbable_slope_km"].asFloat(1.2f);
}
if (root.hasChild("movement_costs")) { if (root.hasChild("movement_costs")) {
const YamlNode &costsNode = root["movement_costs"]; const YamlNode &costsNode = root["movement_costs"];
for (auto it = costsNode.children.begin(); it != costsNode.children.end(); ++it) { for (auto it = costsNode.children.begin(); it != costsNode.children.end(); ++it) {
@@ -220,3 +237,67 @@ float TerrainMovementManager::getHexMovementCost(int tx, int ty, Matrix2D<Terrai
QString tType = getTerrainType(tx, ty, mapPoints, numPoints05); QString tType = getTerrainType(tx, ty, mapPoints, numPoints05);
return getTerrainMovementCost(tType); return getTerrainMovementCost(tType);
} }
float TerrainMovementManager::getHexElevationKm(int tx, int ty, Matrix2D<TerrainPoint> *mapPoints, int numPoints05) const
{
if (!mapPoints || numPoints05 <= 0) return 0.0f;
QVector2D w = Pathfinder::hexToWorld(tx, ty);
float scaleX = numPoints05 * 2.0f;
float scaleY = numPoints05 / LOD_0_SIZE_Y;
int mapX = (int) std::round(w.x() * scaleX);
int mapY = (int) std::round(w.y() * scaleY);
if (mapX < 0) mapX = 0;
if (mapX >= (int)mapPoints->getWidth()) mapX = (int)mapPoints->getWidth() - 1;
if (mapY < 0) mapY = 0;
if (mapY >= (int)mapPoints->getHeight()) mapY = (int)mapPoints->getHeight() - 1;
unsigned char h = mapPoints->operator()(mapX, mapY).height;
return (h / 255.0f) * 3.5f;
}
float TerrainMovementManager::calculateStepCost(
int fromX, int fromY,
int toX, int toY,
MovementDomain domain,
Matrix2D<TerrainPoint> *mapPoints,
int numPoints05,
const UnitConfig *unitConfig) const
{
// Check general hex passability of destination first
if (!isHexPassable(toX, toY, domain, mapPoints, numPoints05, unitConfig)) {
return -1.0f;
}
QString toTerrain = getTerrainType(toX, toY, mapPoints, numPoints05);
float baseCost = getTerrainMovementCost(toTerrain);
// Ships only move on flat water
if (domain == MovementDomain::Naval) {
return baseCost;
}
// For land units: compute elevation difference
float hFrom = getHexElevationKm(fromX, fromY, mapPoints, numPoints05);
float hTo = getHexElevationKm(toX, toY, mapPoints, numPoints05);
float deltaH = hTo - hFrom; // in kilometers
// Impassable cliff check
if (deltaH > m_config.maxClimbableSlopeKm) {
return -1.0f;
}
if (deltaH > 0.001f) {
// Uphill penalty: going uphill increases movement cost
float penalty = deltaH * m_config.uphillSlopeMultiplier;
return baseCost + penalty;
} else if (deltaH < -0.001f) {
// Downhill bonus: going downhill decreases movement cost (negative penalty)
float bonus = (-deltaH) * m_config.downhillSlopeBonusMultiplier;
return std::max(m_config.minStepCost, baseCost - bonus);
}
return baseCost;
}