#include "pathfinder.h" #include "terrainmovementmanager.h" #include "unitconfig.h" #include #include #include #include QVector2D Pathfinder::hexToWorld(int x, int y) { float wx = x * (LOD_0_SIZE * 0.75f); float wy = y * (LOD_0_SIZE_Y * 2.0f) + ((x % 2 == 0) ? 0.0f : LOD_0_SIZE_Y); return QVector2D(wx, wy); } void Pathfinder::worldToHex(float wx, float wy, int &outX, int &outY) { float stepX = LOD_0_SIZE * 0.75f; float stepY = LOD_0_SIZE_Y * 2.0f; int approxX = (int) std::round(wx / stepX); int approxY = (int) std::round(wy / stepY); float minDistSq = 1e9f; int bestX = 0; int bestY = 0; for (int dx = -2; dx <= 2; ++dx) { for (int dy = -2; dy <= 2; ++dy) { int cx = approxX + dx; int cy = approxY + dy; if (cx >= 0 && cx < MAP_WIDTH && cy >= 0 && cy < MAP_HEIGHT) { QVector2D center = hexToWorld(cx, cy); float distSq = (wx - center.x()) * (wx - center.x()) + (wy - center.y()) * (wy - center.y()); if (distSq < minDistSq) { minDistSq = distSq; bestX = cx; bestY = cy; } } } } outX = bestX; outY = bestY; } int Pathfinder::hexDistance(int x1, int y1, int x2, int y2) { int q1 = x1; int r1 = y1 - (x1 - (x1 & 1)) / 2; int s1 = -q1 - r1; int q2 = x2; int r2 = y2 - (x2 - (x2 & 1)) / 2; int s2 = -q2 - r2; return (std::abs(q1 - q2) + std::abs(r1 - r2) + std::abs(s1 - s2)) / 2; } namespace { struct Node { int x, y; float g, f; bool operator>(const Node &other) const { return f > other.f; } }; inline int packCoord(int x, int y) { return (y << 16) | (x & 0xFFFF); } } std::vector> Pathfinder::findPath( int startX, int startY, int goalX, int goalY, Matrix2D *mapPoints, int numPoints05, MovementDomain domain, const UnitConfig *unitConfig) { std::vector> path; if (startX == goalX && startY == goalY) { path.emplace_back(startX, startY); return path; } if (goalX < 0 || goalX >= MAP_WIDTH || goalY < 0 || goalY >= MAP_HEIGHT) { return path; } // Goal hex must be passable for this unit/domain if (!TerrainMovementManager::instance().isHexPassable(goalX, goalY, domain, mapPoints, numPoints05, unitConfig)) { return path; } std::priority_queue, std::greater> openSet; std::unordered_map gScore; std::unordered_map cameFrom; int startPacked = packCoord(startX, startY); gScore[startPacked] = 0.0f; openSet.push({startX, startY, 0.0f, (float)hexDistance(startX, startY, goalX, goalY)}); int maxIterations = 5000; int iterations = 0; bool found = false; while (!openSet.empty() && iterations++ < maxIterations) { Node current = openSet.top(); openSet.pop(); if (current.x == goalX && current.y == goalY) { found = true; break; } int currPacked = packCoord(current.x, current.y); if (current.g > gScore[currPacked]) continue; for (int edge = 0; edge < 6; ++edge) { int nx, ny; getHexNeighbor(current.x, current.y, edge, nx, ny); if (nx < 0 || nx >= MAP_WIDTH || ny < 0 || ny >= MAP_HEIGHT) continue; // Check if neighbor hex is passable for this unit's domain if (!TerrainMovementManager::instance().isHexPassable(nx, ny, domain, mapPoints, numPoints05, unitConfig)) { continue; } // Step cost from configurable terrain movement manager float stepCost = TerrainMovementManager::instance().getHexMovementCost(nx, ny, mapPoints, numPoints05); float tentativeG = current.g + stepCost; int nPacked = packCoord(nx, ny); if (!gScore.count(nPacked) || tentativeG < gScore[nPacked]) { gScore[nPacked] = tentativeG; cameFrom[nPacked] = currPacked; float h = (float) hexDistance(nx, ny, goalX, goalY); openSet.push({nx, ny, tentativeG, tentativeG + h}); } } } if (found) { int curr = packCoord(goalX, goalY); while (curr != startPacked) { int cx = curr & 0xFFFF; int cy = curr >> 16; path.emplace_back(cx, cy); curr = cameFrom[curr]; } path.emplace_back(startX, startY); std::reverse(path.begin(), path.end()); } return path; }