diff --git a/Source/Contents/TerrainPathSearch.cpp b/Source/Contents/TerrainPathSearch.cpp index e6fe16c..d0805a5 100644 --- a/Source/Contents/TerrainPathSearch.cpp +++ b/Source/Contents/TerrainPathSearch.cpp @@ -1,10 +1,211 @@ #include "Contents/TerrainPathSearch.h" +#include "Resource/World/TerrainResource.h" + +#include +#include // OFFSET: 0x00AD7E70 // FUNCTION: fn_17860 CPathFinder::CPathFinder() : useNodePool(false) { } +// OFFSET: 0x00AD88F0 +// FUNCTION: fn_17868 +bool CPathFinder::FindPath( + int startTick, int startX, int startY, int currentFractionX, int currentFractionY, int destX, int destY, int speed, MovementRouteInterpolation& route, + char flag +) { + this->destinationX = destX; + this->destinationY = destY; + + if (startX == destX && startY == destY) { + return false; + } + + route.segment = 0; + this->Reset(); + this->nodeCount = 0; + + MovementPathNode* start = this->FindOrCreateNode(startX, startY); + start->state = 1; + start->parent = nullptr; + start->cost = 0; + start->estimatedCost = 10 * (std::abs(startX - this->destinationX) + std::abs(startY - this->destinationY)); + + std::vector& open = this->openList.Nodes(); + open.push_back(start); + std::push_heap(open.begin(), open.end(), MovementPathNodeCompare()); + + while (!open.empty()) { + MovementPathNode* current = open.front(); + std::pop_heap(open.begin(), open.end(), MovementPathNodeCompare()); + open.pop_back(); + + if (current->x == destX && current->y == destY) { + float x = static_cast(currentFractionX) * 0.0625f; + if (startX > 0) { + x = static_cast(static_cast(startX) - 0.5 + x); + } + route.startX = x; + + float y = static_cast(currentFractionY) * 0.0625f; + if (startY > 0) { + y = static_cast(static_cast(startY) - 0.5 + y); + } + route.startY = y; + + BuildMovementRoute(startTick, current, speed, route); + return true; + } + + if (this->CanMove(current->x, current->y, current->x + 1, current->y - 1, flag) && !this->AddNeighbor(current, 14, current->x + 1, current->y - 1, 5)) { + return false; + } + if (this->CanMove(current->x, current->y, current->x + 1, current->y, flag) && !this->AddNeighbor(current, 10, current->x + 1, current->y, 6)) { + return false; + } + if (this->CanMove(current->x, current->y, current->x + 1, current->y + 1, flag) && !this->AddNeighbor(current, 14, current->x + 1, current->y + 1, 7)) { + return false; + } + if (this->CanMove(current->x, current->y, current->x, current->y + 1, flag) && !this->AddNeighbor(current, 10, current->x, current->y + 1, 0)) { + return false; + } + if (this->CanMove(current->x, current->y, current->x - 1, current->y + 1, flag) && !this->AddNeighbor(current, 14, current->x - 1, current->y + 1, 1)) { + return false; + } + if (this->CanMove(current->x, current->y, current->x - 1, current->y, flag) && !this->AddNeighbor(current, 10, current->x - 1, current->y, 2)) { + return false; + } + if (this->CanMove(current->x, current->y, current->x - 1, current->y - 1, flag) && !this->AddNeighbor(current, 14, current->x - 1, current->y - 1, 3)) { + return false; + } + if (this->CanMove(current->x, current->y, current->x, current->y - 1, flag) && !this->AddNeighbor(current, 10, current->x, current->y - 1, 4)) { + return false; + } + + current->state = 2; + } + + return false; +} + +// OFFSET: 0x00AD9030 +// FUNCTION: fn_17875 +MovementPathNode* CPathFinder::FindOrCreateNode(int x, int y) { + const int width = this->attr->width; + const int key = y * width + x; + auto found = this->nodeMap.find(key); + if (found != this->nodeMap.end() && found->second) { + return found->second; + } + + MovementPathNode* node; + if (this->useNodePool) { + const int count = static_cast(this->nodePool.size()); + if (this->nodeCount >= count - 1) { + this->nodePool.resize(this->attr->height * width + 1); + } + node = &this->nodePool[this->nodeCount++]; + } else { + if (this->nodeCount >= 149) { + return nullptr; + } + node = &this->nodes[this->nodeCount++]; + } + + if (!node) { + return nullptr; + } + + node->x = x; + node->y = y; + node->state = 0; + this->nodeMap[key] = node; + return node; +} + +// OFFSET: 0x00AD9550 +// FUNCTION: fn_17879 +bool CPathFinder::CanMove(int startX, int startY, int targetX, int targetY, char flag) const { + if (targetX < 0 || targetY < 0 || targetX >= this->attr->width || targetY >= this->attr->height) { + return false; + } + + const TerrainHeightCell* cell = this->attr->Grid().CellAt(targetX, targetY); + if (!cell || cell->type == 1 || (flag != 12 && cell->type == 5)) { + return false; + } + + if (startX == targetX || startY == targetY) { + return true; + } + + const TerrainHeightCell* side = this->attr->Grid().CellAt(startX, targetY); + if (!side || side->type == 1 || side->type == 5) { + return false; + } + + side = this->attr->Grid().CellAt(targetX, startY); + if (!side || side->type == 1 || side->type == 5) { + return false; + } + + return true; +} + +// OFFSET: 0x00AD9610 +// FUNCTION: fn_17882 +bool CPathFinder::AddNeighbor(MovementPathNode* current, int cost, int nextX, int nextY, int metadata) { + const int totalCost = current->cost + cost; + MovementPathNode* next = this->FindOrCreateNode(nextX, nextY); + if (!next) { + return false; + } + + if (next->state == 0) { + next->parent = current; + next->state = 1; + next->cost = totalCost; + next->estimatedCost = totalCost + 10 * (std::abs(nextX - this->destinationX) + std::abs(nextY - this->destinationY)); + next->metadata = metadata; + std::vector& open = this->openList.Nodes(); + open.push_back(next); + std::push_heap(open.begin(), open.end(), MovementPathNodeCompare()); + return true; + } + + if (next->cost > totalCost) { + next->parent = current; + next->cost = totalCost; + next->estimatedCost = totalCost + 10 * (std::abs(nextX - this->destinationX) + std::abs(nextY - this->destinationY)); + next->metadata = metadata; + + if (next->state == 1) { + std::vector& open = this->openList.Nodes(); + for (auto it = open.begin(); it != open.end(); ++it) { + if ((*it)->x == next->x && (*it)->y == next->y) { + std::push_heap(open.begin(), it + 1, MovementPathNodeCompare()); + return true; + } + } + } else if (next->state == 2) { + next->state = 1; + } + } + + return true; +} + +// OFFSET: 0x00AD97C0 +// FUNCTION: fn_17884 +void CPathFinder::Clear() { + for (auto& [name, cached] : this->attrCache) { + delete cached; + } + + this->attrCache.clear(); +} + // OFFSET: 0x00AD9840 // FUNCTION: fn_17885 void CPathFinder::Reset() { diff --git a/Source/Contents/TerrainPathSearch.h b/Source/Contents/TerrainPathSearch.h index d9a2b07..ac758c3 100644 --- a/Source/Contents/TerrainPathSearch.h +++ b/Source/Contents/TerrainPathSearch.h @@ -2,6 +2,7 @@ #define RAGNAROK_SOURCE_CONTENTS_TERRAINPATHSEARCH_H #include "Contents/MovementRouteInterpolation.h" +#include "Resource/World/TerrainResource.h" #include #include @@ -9,8 +10,6 @@ #include #include -class C3dAttr; - struct MovementPathNodeCompare { bool operator()(const MovementPathNode* left, const MovementPathNode* right) const { return left->estimatedCost > right->estimatedCost; @@ -42,7 +41,15 @@ public: /* 1090 */ std::map nodeMap; /* 1098 */ MovementOpenList openList; + void Clear(); void Reset(); + bool CanMove(int startX, int startY, int targetX, int targetY, char flag) const; + MovementPathNode* FindOrCreateNode(int x, int y); + bool AddNeighbor(MovementPathNode* current, int cost, int nextX, int nextY, int metadata); + bool FindPath( + int startTick, int startX, int startY, int currentFractionX, int currentFractionY, int destX, int destY, int speed, MovementRouteInterpolation& route, + char flag + ); int Height() const; int Width() const; };