From c890e4b2e81817e8357cab1192dafb2f67e3d644 Mon Sep 17 00:00:00 2001 From: ragnarok-clanker Date: Sun, 1 Nov 2026 07:42:28 +0000 Subject: [PATCH] Decompile fn_21580 (CGameMode::ReceiveSkillList) Batch #9303 by secret: 0 byte-identical, 1 confirmed by peer review. - fn_21580 CGameMode::ReceiveSkillList: logic confirmed by peer review Peer-reviewed-by: gordaoprogramas, KWDev, Bielhp Accepted without human review: peers confirmed the logic ("same" ahead by at least 3, with 70% of the votes). --- Source/Client/Mode/CGameMode.h | 15 +++++++ Source/Client/Mode/GameMode.cpp | 78 ++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/Source/Client/Mode/CGameMode.h b/Source/Client/Mode/CGameMode.h index 7ce60793c..28dfa98e4 100644 --- a/Source/Client/Mode/CGameMode.h +++ b/Source/Client/Mode/CGameMode.h @@ -59,6 +59,20 @@ struct PartyRosterEntry { static_assert(sizeof(PartyRosterEntry) == 0x36); +#pragma pack(push, 1) +struct SkillListEntry { + /* 0000 */ std::uint16_t id; + /* 0002 */ int type; + /* 0006 */ std::int16_t level; + /* 0008 */ std::int16_t spCost; + /* 000A */ std::int16_t range; + /* 000C */ unsigned char upgradable; + /* 000D */ std::int16_t unknown_000D; +}; +#pragma pack(pop) + +static_assert(sizeof(SkillListEntry) == 0xF); + struct GameModeLoopSound { /* 0000 */ std::string name; /* 0018 */ std::uint32_t npcId; @@ -335,6 +349,7 @@ public: void SpawnMandraAttack(std::uint32_t aid, int target, char flag); void OpenActorMenu(const ActorPickInfo* pick, int handled); int HandleActorPick(ActorPickInfo* pick, int result); + void ReceiveSkillList(int packetType, const std::vector& skills); std::uint32_t FindUnknown01AC(std::uint32_t key); std::string ActorName(std::uint32_t id); std::uint32_t ActorNameColor(int job, std::uint32_t aid); diff --git a/Source/Client/Mode/GameMode.cpp b/Source/Client/Mode/GameMode.cpp index 1f9430b42..d616fe200 100644 --- a/Source/Client/Mode/GameMode.cpp +++ b/Source/Client/Mode/GameMode.cpp @@ -1470,7 +1470,7 @@ void CGameMode::InitializeCamera() { #include "Contents/Schedule/Schedule.h" #include "UI/Text/UINameBalloonText.h" #include "UI/Text/UITransBalloonText.h" -#include "Contents/Actors/Pc.h" +#include "Contents/Messenger/ClientMessenger.h" #include "ActorPickNode.h" #include "Client/Registry.h" #include "Contents/Schedule/CheckGDISchedule.h" @@ -3829,4 +3829,80 @@ struct ContactNpcPacket { }; #pragma pack(pop) +namespace EquipmentProperties { +class CEquipmentPropertiesMgr { +public: + virtual ~CEquipmentPropertiesMgr(); + /* 0004 */ unsigned char unknown_0004[0x1F8]; + + CEquipmentPropertiesMgr(); + void RefreshPretend(); +}; +} + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(EquipmentProperties::CEquipmentPropertiesMgr) == 0x1FC); +#endif + +// OFFSET: 0x00D07030 +// FUNCTION: fn_21580 +void CGameMode::ReceiveSkillList(int packetType, const std::vector& skills) { + if (packetType == 0x10F || packetType == 0xB32) { + g_session.m_skillBundle.skills.clear(); + } else if (packetType == 0x235) { + g_session.m_homunSkills.clear(); + } else if (packetType == 0x29D) { + g_session.m_mercSkills.clear(); + } + + SkillWindows windows{g_windowMgr.window_02C4, g_windowMgr.window_01E8, *reinterpret_cast(&g_windowMgr)}; + for (const SkillListEntry& entry : skills) { + CSkillInfo skill; + if ((entry.id != 0 && entry.id < 7999) || g_session.IsHomunculusSkill(entry.id) || g_session.IsMercenarySkill(entry.id)) { + skill.id = entry.id; + skill.unknown_000C = entry.type; + skill.level = entry.level; + skill.unknown_0014 = entry.spCost; + skill.unknown_0018 = entry.upgradable; + skill.unknown_001C = entry.range; + skill.name = skill.IdName(*g_session.m_hotkeyScript); + skill.unknown_0030 = entry.unknown_000D; + skill.unknown_0034 = skill.UseApAmount(*g_session.m_hotkeyScript, entry.level); + g_session.m_skillBundle.ApplyLevelChange(skill); + + if (packetType == 0x10F || packetType == 0xB32) { + ClientSkills client{ + g_session.m_skillBundle, + g_session.m_homunSkills, + g_session.m_mercSkills, + g_session.m_additionalSkills, + reinterpret_cast(g_session.m_jobFlags), + g_activeSkillEffects + }; + const SkillUseLevels levels{ + g_session.m_skillBundle.unknown_0064, *reinterpret_cast*>(g_session.unknown_0710), g_session.m_homunSkillLevels + }; + client.Update(skill, 0, 0, *g_session.m_hotkeyScript, levels, windows); + } else if (packetType == 0x235) { + g_session.UpdateHomunSkill(skill, 0); + } else if (packetType == 0x29D) { + g_session.UpdateMercSkill(skill, 0); + } + } + } + + if (packetType == 0x10F || packetType == 0xB32) { + windows.Notify(); + } else if (packetType == 0x235) { + g_session.NotifyHomunSkillWindows(); + } else if (packetType == 0x29D) { + g_session.NotifyMercSkillWindows(); + } + + if (g_equipmentPropertiesMgr == nullptr) { + g_equipmentPropertiesMgr = new (std::nothrow) EquipmentProperties::CEquipmentPropertiesMgr; + } + g_equipmentPropertiesMgr->RefreshPretend(); +} + // OFFSET: 0x00CE2880