From e5b9803cb7f828f90e3ce45e22c8a5a5666f662d Mon Sep 17 00:00:00 2001 From: ragnarok-clanker Date: Sat, 31 Oct 2026 18:22:19 +0000 Subject: [PATCH] Decompile fn_21351 (CGameMode::WriteReplaySkillState) Batch #8495 by youko: 0 byte-identical, 1 confirmed by peer review. - fn_21351 CGameMode::WriteReplaySkillState: logic confirmed by peer review Peer-reviewed-by: Yommy, alisonrag, zindokar Accepted without human review: peers confirmed the logic ("same" ahead by at least 3, with 70% of the votes). --- Source/Client/Mode/CGameMode.h | 1 + Source/Client/Mode/GameMode.cpp | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/Source/Client/Mode/CGameMode.h b/Source/Client/Mode/CGameMode.h index 838ac5e79..e190721c6 100644 --- a/Source/Client/Mode/CGameMode.h +++ b/Source/Client/Mode/CGameMode.h @@ -547,6 +547,7 @@ public: bool RecordReplayMessage(int message, int value, int first, int second); void UpdateReplayActionSpeeds(); struct ReplayTime StopReplayRecording(); + std::vector WriteReplaySkillState(); void ReceivePacket0AB8(const unsigned char* packet); void ReceivePacket0A29(int value); void ReceiveCashShopBanner(int value1, int value2, int value3); diff --git a/Source/Client/Mode/GameMode.cpp b/Source/Client/Mode/GameMode.cpp index a30ef02ac..0bd995541 100644 --- a/Source/Client/Mode/GameMode.cpp +++ b/Source/Client/Mode/GameMode.cpp @@ -1641,3 +1641,54 @@ void CGameMode::TargetActorByAid(int aid) { *reinterpret_cast(&this->unknown_04F4[0xBC]) = 0; } } + +#include "Contents/Actors/Skill.h" + +#pragma pack(push, 1) +struct SkillEntryPacket { + /* 0000 */ std::int16_t packetType; + /* 0002 */ std::uint32_t aid; + /* 0006 */ std::uint32_t creatorAid; + /* 000A */ std::int16_t x; + /* 000C */ std::int16_t y; + /* 000E */ unsigned char job; + /* 000F */ unsigned char isVisible; +}; +#pragma pack(pop) + +static_assert(sizeof(SkillEntryPacket) == 16); + +// OFFSET: 0x00CEB4C0 +// FUNCTION: fn_21351 +std::vector CGameMode::WriteReplaySkillState() { + std::vector output; + { + const auto record = BuildReplayTaggedRecord(17000, nullptr, 0); + output.insert(output.end(), record.begin(), record.end()); + } + + for (CRenderObject* object : this->scene->unknown_0020) { + CSkill* skill = static_cast(object); + SkillEntryPacket packet; + packet.packetType = 0x11F; + packet.aid = skill->skillAid; + packet.creatorAid = static_cast(skill->unknown_0410); + packet.job = static_cast(skill->GetEffectiveJob(-1)); + packet.isVisible = 1; + + const C3dAttr* terrain = this->scene->terrain; + const float column = static_cast(std::floor(static_cast(terrain->width / 2) + skill->render.position.x / static_cast(terrain->cellSize))); + const float row = static_cast(std::floor(static_cast(terrain->height / 2) + skill->render.position.z / static_cast(terrain->cellSize))); + packet.x = static_cast(static_cast(column)); + packet.y = static_cast(static_cast(row)); + + const auto record = BuildReplayTaggedRecord(0xCD, &packet, sizeof(packet)); + output.insert(output.end(), record.begin(), record.end()); + } + + { + const auto record = BuildReplayTaggedRecord(17001, nullptr, 0); + output.insert(output.end(), record.begin(), record.end()); + } + return output; +}