From 23d31b8258a8d756e1f3873620203efbf639d382 Mon Sep 17 00:00:00 2001 From: bart Date: Sun, 8 Nov 2026 17:08:07 +0100 Subject: [PATCH] Implement slope calculation and uphill/downhill step cost in terrainmovementmanager.cpp --- terrainmovementmanager.cpp | 83 +++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/terrainmovementmanager.cpp b/terrainmovementmanager.cpp index f5a64d1..508ab0c 100644 --- a/terrainmovementmanager.cpp +++ b/terrainmovementmanager.cpp @@ -13,7 +13,11 @@ TerrainMovementConfig::TerrainMovementConfig() polarSnowLineEnabled(true), polarSnowLineBaseKm(2.5f), 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["bos"] = 2.0f; @@ -74,6 +78,19 @@ bool TerrainMovementManager::loadConfig(const QString &filePath) 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")) { const YamlNode &costsNode = root["movement_costs"]; for (auto it = costsNode.children.begin(); it != costsNode.children.end(); ++it) { @@ -220,3 +237,67 @@ float TerrainMovementManager::getHexMovementCost(int tx, int ty, Matrix2D *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 *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; +}