From 4cf34135244d4ac4908ca560c283b4d3b01f52eb Mon Sep 17 00:00:00 2001 From: ragnarok-clanker Date: Wed, 28 Oct 2026 18:48:31 +0000 Subject: [PATCH] Decompile 4 functions in Source/Client/Mode/CGameMode.h, Source/Contents/Ground/TerrainCoordinates.cpp, ... Batch #1556 by ragnarok-clanker: 2 byte-identical, 2 confirmed by peer review. - fn_19489 CNavi_Link::GetLinkedInfo: byte-identical - fn_19481 CNaviInfo::IsVisited: byte-identical - fn_45449 CNaviData::Clear: logic confirmed by peer review - fn_19371 CNaviData::~CNaviData: logic confirmed by peer review Accepted without human review: peers confirmed the logic ("same" ahead by at least 3, with 70% of the votes). Adds to the build: Source/Contents/NavigationDestinations.cpp, Source/Contents/NavigationGraph.cpp, Source/Contents/WorldNavigation.cpp --- Source/Client/Mode/CGameMode.h | 2 + Source/Contents/Ground/TerrainCoordinates.cpp | 11 +- Source/Contents/Ground/TerrainHeight.h | 1 - Source/Contents/NavigationDestinations.cpp | 48 +++++ Source/Contents/NavigationGraph.cpp | 187 ++++++++++++++++++ Source/Contents/NavigationLink.cpp | 21 ++ Source/Contents/NavigationManager.h | 44 +++-- Source/Contents/NavigationRequest.cpp | 23 +++ Source/Contents/NavigationRoute.cpp | 14 +- Source/Contents/NavigationSource.cpp | 2 +- Source/Contents/WorldNavigation.cpp | 21 ++ data/compile.json | 3 + 12 files changed, 350 insertions(+), 27 deletions(-) create mode 100644 Source/Contents/NavigationDestinations.cpp create mode 100644 Source/Contents/NavigationGraph.cpp create mode 100644 Source/Contents/WorldNavigation.cpp diff --git a/Source/Client/Mode/CGameMode.h b/Source/Client/Mode/CGameMode.h index d9334f1..c56d11a 100644 --- a/Source/Client/Mode/CGameMode.h +++ b/Source/Client/Mode/CGameMode.h @@ -25,6 +25,8 @@ public: /* 03C8 */ unsigned char unknown_03C8[0x2A8]; std::string MapName(); + void WorldCell(float x, float z, int& column, int& row) const; + void PlayerCell(int* column, int* row) const; }; #if defined(_WIN32) && !defined(_WIN64) diff --git a/Source/Contents/Ground/TerrainCoordinates.cpp b/Source/Contents/Ground/TerrainCoordinates.cpp index 69ee303..f17c3c8 100644 --- a/Source/Contents/Ground/TerrainCoordinates.cpp +++ b/Source/Contents/Ground/TerrainCoordinates.cpp @@ -1,4 +1,7 @@ +#include "Client/Mode/CGameMode.h" #include "Contents/Ground/TerrainHeight.h" +#include "Contents/World/WorldScene.h" +#include "Resource/World/TerrainResource.h" #include "Utility/Math/ScalarConversion.h" #include @@ -21,12 +24,12 @@ void TerrainHeight::CellCenter(int column, int row, float& x, float& z) const { // OFFSET: 0x00CD5230 // FUNCTION: fn_21278 -void TerrainHeight::WorldCell(float x, float z, int& column, int& row) const { - const float size = static_cast(this->cellSize); - const float first = static_cast(this->width / 2) + x / size; +void CGameMode::WorldCell(float x, float z, int& column, int& row) const { + const float size = static_cast(this->scene->terrain->cellSize); + const float first = static_cast(this->scene->terrain->width / 2) + x / size; const float flooredX = static_cast(std::floor(static_cast(first))); column = TruncateToInt(flooredX); - const float second = static_cast(this->height / 2) + z / size; + const float second = static_cast(this->scene->terrain->height / 2) + z / size; const float flooredZ = static_cast(std::floor(static_cast(second))); row = TruncateToInt(flooredZ); } diff --git a/Source/Contents/Ground/TerrainHeight.h b/Source/Contents/Ground/TerrainHeight.h index d60debc..d140d7e 100644 --- a/Source/Contents/Ground/TerrainHeight.h +++ b/Source/Contents/Ground/TerrainHeight.h @@ -21,7 +21,6 @@ struct TerrainHeight { bool Walkable(int column, int row) const; void CellOrigin(int column, int row, float& x, float& z) const; void CellCenter(int column, int row, float& x, float& z) const; - void WorldCell(float x, float z, int& column, int& row) const; void WorldSubcell(float x, float z, int& column, int& row, int& subColumn, int& subRow) const; void SpatialCell(float x, float z, int& column, int& row) const; diff --git a/Source/Contents/NavigationDestinations.cpp b/Source/Contents/NavigationDestinations.cpp new file mode 100644 index 0000000..4348975 --- /dev/null +++ b/Source/Contents/NavigationDestinations.cpp @@ -0,0 +1,48 @@ +#include "Contents/NavigationManager.h" + +#include + +#pragma pack(push, 1) +struct NavigationStateRecord { + /* 0000 */ int type; + /* 0004 */ char map[0x14]; + /* 0018 */ int x; + /* 001C */ int y; + /* 0020 */ int nameLength; +}; +#pragma pack(pop) + +// OFFSET: 0x00B974D0 +bool CNavigationManager::LoadStates(std::vector packet) { + if (packet.begin() == packet.end()) { + return false; + } + + const int count = *reinterpret_cast(packet.data()); + this->states.clear(); + + std::size_t offset = sizeof(int); + for (int i = 0; i < count; ++i) { + NavigationTarget target; + NavigationStateRecord record; + std::memcpy(&record, packet.data() + offset, sizeof(record)); + target.type = record.type; + target.x = record.x; + target.y = record.y; + offset += sizeof(record); + + if (record.nameLength > 0) { + target.name.resize(record.nameLength, '\0'); + std::memcpy(&target.name[0], packet.data() + offset, record.nameLength); + offset += record.nameLength; + } + + CNaviInfo* info = this->FindObject(record.map); + if (info != nullptr) { + target.object = info; + this->states.push_back(target); + } + } + + return true; +} diff --git a/Source/Contents/NavigationGraph.cpp b/Source/Contents/NavigationGraph.cpp new file mode 100644 index 0000000..05909c7 --- /dev/null +++ b/Source/Contents/NavigationGraph.cpp @@ -0,0 +1,187 @@ +#include "Contents/NavigationManager.h" + +// OFFSET: 0x00B89990 +int CNaviData::FindRoute(std::vector* route, bool direct, bool allowGates, bool includeObjects, bool allowWarps, int side) { + for (NaviDataEntry& entry : this->entries) { + entry.previous = -1; + entry.link = nullptr; + entry.cost = 0; + } + this->entries.clear(); + + for (CNaviInfo* destination : this->destinations) { + destination->SetVisited(false); + } + + if (includeObjects) { + for (CNavi_Link* object : this->objects) { + object->SetDepth(0); + NaviDataEntry entry = {-1, object, 1}; + this->entries.push_back(entry); + } + } + + for (NaviLink& start : this->startLinks) { + CNavi_Link* link = start.link; + const int type = link->GetType(); + bool accept; + if (type == 204) { + if (!allowGates) { + continue; + } + accept = !link->GetLinkedInfo()->IsVisited(); + } else if (type == 205) { + if (!allowWarps) { + continue; + } + accept = !link->GetLinkedInfo()->IsVisited(); + } else if (type == 202 || type == 203) { + if (allowGates != true) { + continue; + } + accept = type == 202 ? side == 2 : side == 1; + } else { + accept = !link->GetLinkedInfo()->IsVisited(); + } + if (accept) { + start.link->SetDepth(0); + NaviDataEntry entry = {-1, start.link, start.cost}; + this->entries.push_back(entry); + } + } + + int first = 0; + int depth = 1; + int count = static_cast(this->entries.size()); + int best = -1; + int result; + + for (;;) { + for (int i = first; i < count; ++i) { + NaviDataEntry& entry = this->entries[i]; + if (this->info != entry.link->GetLinkedInfo()) { + continue; + } + + int cost; + if (direct) { + cost = entry.cost; + } else { + bool found = false; + for (NaviLink& end : this->endLinks) { + if (entry.link == end.link) { + cost = end.cost + entry.cost; + found = true; + break; + } + } + if (!found) { + continue; + } + } + + if (this->range > cost) { + this->range = cost; + best = i; + } + + result = this->range; + std::vector path; + for (int index = best; index != -1; index = this->entries[index].previous) { + path.push_back(this->entries[index].link); + } + route->clear(); + for (int index = static_cast(path.size()) - 1; index >= 0; --index) { + route->push_back(path[index]); + } + goto done; + } + + for (int i = first; i < count; ++i) { + CNavi_Link* current = this->entries[i].link; + for (std::vector::iterator it = current->LinkedBegin(); it != current->LinkedEnd(); ++it) { + CNavi_Link* next = it->link; + if (next == nullptr) { + continue; + } + + if (depth == next->depth) { + const int cost = it->cost + this->entries[i].cost; + for (std::size_t j = count; j < this->entries.size(); ++j) { + if (next == this->entries[j].link) { + if (this->entries[j].cost > cost) { + this->entries[j].previous = i; + this->entries[j].link = next; + this->entries[j].cost = cost; + } + break; + } + } + continue; + } + + if (depth < next->depth || next->depth != -1) { + continue; + } + + const int type = next->GetType(); + bool accept; + if (type == 204) { + if (!allowGates) { + continue; + } + accept = !next->GetLinkedInfo()->IsVisited(); + } else if (type == 205) { + if (!allowWarps) { + continue; + } + accept = !next->GetLinkedInfo()->IsVisited(); + } else if (type == 202 || type == 203) { + if (allowGates != true) { + continue; + } + accept = type == 202 ? side == 2 : side == 1; + } else { + accept = !next->GetLinkedInfo()->IsVisited(); + } + if (accept) { + next->SetDepth(depth); + NaviDataEntry entry = {i, next, it->cost + this->entries[i].cost}; + this->entries.push_back(entry); + } + } + } + + first = count; + count = static_cast(this->entries.size()); + if (first == count) { + break; + } + ++depth; + if (depth >= 100) { + break; + } + } + result = 0; + +done: + this->range = 50000; + for (NaviDataEntry& entry : this->entries) { + entry.previous = -1; + entry.link = nullptr; + entry.cost = 0; + } + this->entries.clear(); + this->ResetLinkDepths(); + return result; +} + +// OFFSET: 0x00B8A720 +void CNaviData::ResetLinkDepths() { + for (CNaviInfo* destination : this->destinations) { + std::vector::iterator end = destination->LinksEnd(); + for (std::vector::iterator it = destination->LinksBegin(); it != end; ++it) { + (*it)->SetDepth(-1); + } + } +} diff --git a/Source/Contents/NavigationLink.cpp b/Source/Contents/NavigationLink.cpp index 51d6420..19e7b64 100644 --- a/Source/Contents/NavigationLink.cpp +++ b/Source/Contents/NavigationLink.cpp @@ -54,3 +54,24 @@ CNaviInfo* CNavi_Link::GetInfo() const { } return this->info; } + +// OFFSET: 0x00B8DB20 +std::vector::iterator CNavi_Link::LinkedBegin() { + return this->linkedLinks.begin(); +} + +// OFFSET: 0x00B8DE60 +std::vector::iterator CNavi_Link::LinkedEnd() { + return this->linkedLinks.end(); +} + +// OFFSET: 0x00B8E860 +// FUNCTION: fn_19489 +CNaviInfo* CNavi_Link::GetLinkedInfo() const { + return this->linkedInfo; +} + +// OFFSET: 0x00B90400 +void CNavi_Link::SetDepth(int depth) { + this->depth = depth; +} diff --git a/Source/Contents/NavigationManager.h b/Source/Contents/NavigationManager.h index 971117d..8d156bc 100644 --- a/Source/Contents/NavigationManager.h +++ b/Source/Contents/NavigationManager.h @@ -23,14 +23,14 @@ struct NaviCell { }; struct NaviLink { - /* 0000 */ CNavi_Object* object; - /* 0004 */ int value; + /* 0000 */ CNavi_Link* link; + /* 0004 */ int cost; }; struct NaviDataEntry { - /* 0000 */ int unknown_0000; - /* 0004 */ int unknown_0004; - /* 0008 */ int unknown_0008; + /* 0000 */ int previous; + /* 0004 */ CNavi_Link* link; + /* 0008 */ int cost; }; struct NaviRouteLine { @@ -87,9 +87,14 @@ public: /* 007C */ CNaviInfo* linkedInfo; /* 0080 */ int unknown_0080; /* 0084 */ int unknown_0084; - /* 0088 */ int unknown_0088; + /* 0088 */ int depth; /* 008C */ std::vector linkedLinks; + CNaviInfo* GetLinkedInfo() const; + void SetDepth(int depth); + std::vector::iterator LinkedBegin(); + std::vector::iterator LinkedEnd(); + ~CNavi_Link() override; int GetId() const override; int GetType() const override; @@ -107,6 +112,10 @@ public: bool HasName(const char* name) const; const char* GetName() const; const char* GetTitle() const; + bool IsVisited() const; + void SetVisited(bool visited); + std::vector::iterator LinksBegin(); + std::vector::iterator LinksEnd(); /* 0000 */ int index; /* 0004 */ int type; @@ -114,7 +123,7 @@ public: /* 0020 */ std::string title; /* 0038 */ int cellX; /* 003C */ int cellY; - /* 0040 */ bool unknown_0040; + /* 0040 */ bool visited; /* 0044 */ std::vector npcs; /* 0050 */ std::vector mobs; /* 005C */ std::vector links; @@ -140,16 +149,18 @@ public: CNaviInfo* GetInfo(); NaviCell* GetCell(NaviCell* out); void Clear(); + int FindRoute(std::vector* route, bool direct, bool allowGates, bool includeObjects, bool allowWarps, int side); + void ResetLinkDepths(); /* 0000 */ std::vector destinations; - /* 000C */ std::vector objects; + /* 000C */ std::vector objects; /* 0018 */ CNaviInfo* info; /* 001C */ int cellX; /* 0020 */ int cellY; /* 0024 */ int range; - /* 0028 */ std::vector unknown_0028; - /* 0034 */ std::vector unknown_0034; - /* 0040 */ std::vector unknown_0040; + /* 0028 */ std::vector entries; + /* 0034 */ std::vector startLinks; + /* 0040 */ std::vector endLinks; }; class CNavigationManager { @@ -178,6 +189,8 @@ public: void ClearNodePaths(); CNaviInfo* GetLinkedPlayerInfo(); bool UpdateGround(); + bool PlayerCell(int* column, int* row); + bool LoadStates(std::vector packet); /* 0000 */ CNaviData data; /* 004C */ CPathFinder pathFinder; @@ -230,6 +243,9 @@ extern CNavigationManager g_navigationManager; #if defined(_WIN32) && !defined(_WIN64) static_assert(sizeof(CNavi_Link) == 0x98); static_assert(offsetof(CNavi_Link, linkedInfo) == 0x7C); +static_assert(offsetof(CNavi_Link, depth) == 0x88); +static_assert(offsetof(CNaviInfo, visited) == 0x40); +static_assert(offsetof(NaviDataEntry, cost) == 0x08); static_assert(offsetof(CNavi_Link, linkedLinks) == 0x8C); static_assert(sizeof(NaviCell) == 0x08); static_assert(sizeof(NaviDataEntry) == 0x0C); @@ -255,9 +271,9 @@ static_assert(sizeof(CNaviData) == 0x4C); static_assert(offsetof(CNaviData, info) == 0x18); static_assert(offsetof(CNaviData, cellX) == 0x1C); static_assert(offsetof(CNaviData, range) == 0x24); -static_assert(offsetof(CNaviData, unknown_0028) == 0x28); -static_assert(offsetof(CNaviData, unknown_0034) == 0x34); -static_assert(offsetof(CNaviData, unknown_0040) == 0x40); +static_assert(offsetof(CNaviData, entries) == 0x28); +static_assert(offsetof(CNaviData, startLinks) == 0x34); +static_assert(offsetof(CNaviData, endLinks) == 0x40); static_assert(offsetof(CNavigationManager, pathFinder) == 0x004C); static_assert(offsetof(CNavigationManager, states) == 0x10F0); static_assert(offsetof(CNavigationManager, currentTarget) == 0x10FC); diff --git a/Source/Contents/NavigationRequest.cpp b/Source/Contents/NavigationRequest.cpp index df40627..61a1b3e 100644 --- a/Source/Contents/NavigationRequest.cpp +++ b/Source/Contents/NavigationRequest.cpp @@ -91,3 +91,26 @@ bool CNavigationManager::UpdateGround() { this->pathFinder.attr = mode->scene->terrain; return true; } + +// OFFSET: 0x00B8E350 +std::vector::iterator CNaviInfo::LinksBegin() { + return this->links.begin(); +} + +// OFFSET: 0x00B8E360 +std::vector::iterator CNaviInfo::LinksEnd() { + return this->links.end(); +} + +// OFFSET: 0x00B8E760 +// FUNCTION: fn_19481 +bool CNaviInfo::IsVisited() const { + return this->visited; +} + +// OFFSET: 0x00B90410 +void CNaviInfo::SetVisited(bool visited) { + if (this->type == 5001) { + this->visited = visited; + } +} diff --git a/Source/Contents/NavigationRoute.cpp b/Source/Contents/NavigationRoute.cpp index 01fb1f1..a3f6ab7 100644 --- a/Source/Contents/NavigationRoute.cpp +++ b/Source/Contents/NavigationRoute.cpp @@ -194,19 +194,19 @@ CNaviInfo* CNaviData::GetInfo() { // OFFSET: 0x00B88A00 // FUNCTION: fn_45449 void CNaviData::Clear() { - this->unknown_0034.clear(); + this->startLinks.clear(); this->info = nullptr; this->cellX = 0; this->cellY = 0; this->range = 50000; - this->unknown_0040.clear(); + this->endLinks.clear(); - for (NaviDataEntry& entry : this->unknown_0028) { - entry.unknown_0000 = -1; - entry.unknown_0004 = 0; - entry.unknown_0008 = 0; + for (NaviDataEntry& entry : this->entries) { + entry.previous = -1; + entry.link = nullptr; + entry.cost = 0; } - this->unknown_0028.clear(); + this->entries.clear(); } // OFFSET: 0x00B8CD60 diff --git a/Source/Contents/NavigationSource.cpp b/Source/Contents/NavigationSource.cpp index 2ddb4d3..0a8490c 100644 --- a/Source/Contents/NavigationSource.cpp +++ b/Source/Contents/NavigationSource.cpp @@ -118,7 +118,7 @@ CNaviData::CNaviData() : info(nullptr), cellX(0), cellY(0), range(50000) { CNaviData::~CNaviData() { this->Clear(); - for (CNavi_Object*& object : this->objects) { + for (CNavi_Link*& object : this->objects) { if (object != nullptr) { delete object; } diff --git a/Source/Contents/WorldNavigation.cpp b/Source/Contents/WorldNavigation.cpp new file mode 100644 index 0000000..9ca26b7 --- /dev/null +++ b/Source/Contents/WorldNavigation.cpp @@ -0,0 +1,21 @@ +#include "Client/Mode/ActiveGameMode.h" +#include "Client/Mode/CGameMode.h" +#include "Contents/Actors/RenderObject.h" +#include "Contents/NavigationManager.h" +#include "Contents/World/WorldScene.h" + +// OFFSET: 0x00B961E0 +bool CNavigationManager::PlayerCell(int* column, int* row) { + CGameMode* mode = static_cast(g_world.ActiveGameMode()); + if (mode == nullptr) { + return false; + } + mode->PlayerCell(column, row); + return true; +} + +// OFFSET: 0x00CD5510 +void CGameMode::PlayerCell(int* column, int* row) const { + const auto* player = reinterpret_cast(this->scene->player); + this->WorldCell(player->render.position[0], player->render.position[2], *column, *row); +} diff --git a/data/compile.json b/data/compile.json index 40b2100..1234e34 100644 --- a/data/compile.json +++ b/data/compile.json @@ -368,6 +368,8 @@ "Source/Contents/MovementRouteInterpolation.cpp": 4, "Source/Contents/Movies/BinkMovie.cpp": 3, "Source/Contents/Movies/BinkMovies.cpp": 3, + "Source/Contents/NavigationDestinations.cpp": 4, + "Source/Contents/NavigationGraph.cpp": 4, "Source/Contents/NavigationLink.cpp": 4, "Source/Contents/NavigationRequest.cpp": 4, "Source/Contents/NavigationRoute.cpp": 4, @@ -431,6 +433,7 @@ "Source/Contents/World/WorldSpatialTree.cpp": 3, "Source/Contents/World/WorldTerrainPicking.cpp": 4, "Source/Contents/World/WorldViewSpatial.cpp": 3, + "Source/Contents/WorldNavigation.cpp": 4, "Source/File/CommonObject.cpp": 1, "Source/File/DirectoryRemoval.cpp": 1, "Source/File/File.cpp": 1,