From 05be22bdcf318a637029acc8a261a062b9bbcda4 Mon Sep 17 00:00:00 2001 From: ragnarok-clanker Date: Fri, 30 Oct 2026 06:43:59 +0000 Subject: [PATCH] Decompile 22 functions in Source/Client/Session.h, Source/Contents/Actors/GroundScope.cpp, ... Batch #4990 by youko: 3 byte-identical, 19 confirmed by peer review. - fn_24228 ReadMessengerMembers: logic confirmed by peer review - fn_24229 ReadItemList: logic confirmed by peer review - fn_24230 ReadItemArray: logic confirmed by peer review - fn_24232 CSession::SetUnknown5BC0: logic confirmed by peer review - fn_24236 MessengerMember::Deserialize: logic confirmed by peer review - fn_24247 ClientItemLists::SetItems1750: logic confirmed by peer review - fn_24249 AppendBytes: logic confirmed by peer review - fn_24258 CMsgEffect::~CMsgEffect: logic confirmed by peer review - fn_24259 CSkill::~CSkill: logic confirmed by peer review - fn_24267 CGroundScope::SkillScopeType: logic confirmed by peer review - fn_24268 CSkill::GetTrapModelName: logic confirmed by peer review - fn_24269 CSkill::ReleaseModel: logic confirmed by peer review - fn_24270 CMsgEffect::UsesMsgEffect: logic confirmed by peer review - fn_24275 CGraffiSkill::ReleaseTexture: byte-identical - fn_24277 CArrowEffect::Reset: logic confirmed by peer review - fn_24278 CEffectMaker::Reset: byte-identical - fn_24279 CGraffiSkill::BuildTexture: logic confirmed by peer review - fn_24280 CArrowEffect::OnProcess: logic confirmed by peer review - fn_24281 CDummyEffectLauncher::OnProcess: logic confirmed by peer review - fn_24282 CEffectMaker::OnProcess: logic confirmed by peer review - fn_24283 CGraffiSkill::OnProcess: byte-identical - fn_24285 CMandraAttackEffect::OnProcess: logic confirmed by peer review Peer-reviewed-by: eAthena, willerthal, KWDev 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/Actors/GraffiSkill.cpp, Source/Contents/Effects/ArrowEffect.cpp, Source/Contents/Effects/DummyEffectLauncher.cpp, Source/Contents/Effects/MandraAttackEffect.cpp, Source/Contents/Effects/MsgEffect.cpp --- Source/Client/Session.h | 7 +- Source/Contents/Actors/GraffiSkill.cpp | 97 +++++++++++++++ Source/Contents/Actors/GraffiSkill.h | 34 ++++++ Source/Contents/Actors/GroundScope.cpp | 86 ++++++++++++++ Source/Contents/Actors/GroundScope.h | 2 + Source/Contents/Actors/Skill.cpp | 112 ++++++++++++++++++ Source/Contents/Actors/Skill.h | 19 ++- Source/Contents/ClientItemLists.cpp | 13 ++ Source/Contents/ClientState.cpp | 16 +++ Source/Contents/ClientState.h | 3 + Source/Contents/Effects/ArrowEffect.cpp | 90 ++++++++++++++ Source/Contents/Effects/ArrowEffect.h | 33 ++++++ .../Contents/Effects/DummyEffectLauncher.cpp | 11 ++ Source/Contents/Effects/DummyEffectLauncher.h | 21 ++++ Source/Contents/Effects/EffectMaker.cpp | 35 ++++++ Source/Contents/Effects/EffectMaker.h | 7 +- .../Contents/Effects/MandraAttackEffect.cpp | 30 +++++ Source/Contents/Effects/MandraAttackEffect.h | 21 ++++ Source/Contents/Effects/MsgEffect.cpp | 23 ++++ Source/Contents/Effects/MsgEffect.h | 24 ++++ Source/Contents/Items/ItemInfo.cpp | 51 ++++++++ Source/Contents/Items/ItemInfo.h | 2 + Source/Contents/Messenger/MessengerMember.cpp | 104 ++++++++++++++++ Source/Contents/Messenger/MessengerMember.h | 5 + Source/Network/ClientOptionPacket.cpp | 5 + data/compile.json | 5 + 26 files changed, 852 insertions(+), 4 deletions(-) create mode 100644 Source/Contents/Actors/GraffiSkill.cpp create mode 100644 Source/Contents/Actors/GraffiSkill.h create mode 100644 Source/Contents/Effects/ArrowEffect.cpp create mode 100644 Source/Contents/Effects/ArrowEffect.h create mode 100644 Source/Contents/Effects/DummyEffectLauncher.cpp create mode 100644 Source/Contents/Effects/DummyEffectLauncher.h create mode 100644 Source/Contents/Effects/MandraAttackEffect.cpp create mode 100644 Source/Contents/Effects/MandraAttackEffect.h create mode 100644 Source/Contents/Effects/MsgEffect.cpp create mode 100644 Source/Contents/Effects/MsgEffect.h diff --git a/Source/Client/Session.h b/Source/Client/Session.h index fdf6cef3..99b66f6d 100644 --- a/Source/Client/Session.h +++ b/Source/Client/Session.h @@ -266,7 +266,9 @@ public: /* 59D0 */ int unknown_59D0; /* 59D4 */ int unknown_59D4; /* 59D8 */ LuaScript* m_hotkeyScript; - /* 59DC */ unsigned char unknown_59DC[0x218]; + /* 59DC */ unsigned char unknown_59DC[0x1E4]; + /* 5BC0 */ int unknown_5BC0; + /* 5BC4 */ unsigned char unknown_5BC4[0x30]; /* 5BF4 */ int m_pairValues[3]; /* 5C00 */ unsigned char unknown_5C00[0x1C]; /* 5C1C */ int unknown_5C1C; @@ -340,6 +342,7 @@ public: void SetShopItem(int shopId, unsigned int index, int itemId, int first, int second); bool AddShopName(std::string name, int shopId); int SetUnknown8134(int value); + void SetUnknown5BC0(int value); void ClearUnknown1710(int id); void ClearUnknown1740(int id); void ClearUnknown1738(int id); @@ -519,6 +522,8 @@ public: void CollectCardItems(); void SetHomunculusAutoFeed(bool enabled); void SetPetAutoFeed(bool enabled); + void SendHomunculusAutoFeedOption(unsigned char enabled); + void SendPetAutoFeedOption(unsigned char enabled); int GetRobeAlpha(int robeId); bool IsTopLayerRobe(int robe); int IsExchangeItemListFull(); diff --git a/Source/Contents/Actors/GraffiSkill.cpp b/Source/Contents/Actors/GraffiSkill.cpp new file mode 100644 index 00000000..0a44e6a4 --- /dev/null +++ b/Source/Contents/Actors/GraffiSkill.cpp @@ -0,0 +1,97 @@ +#include "Contents/Actors/GraffiSkill.h" + +#include "Client/Mode/CGameMode.h" +#include "Client/Mode/ModeMgr.h" +#include "Contents/Actors/Pc.h" +#include "Contents/World/World.h" +#include "Render/Textures/Texture.h" +#include "Render/Textures/TextureCache.h" + +#include + +// INLINED +CGraffiSkill::~CGraffiSkill() { + delete this->m_canvas; +} + +// OFFSET: 0x00E4B930 +// FUNCTION: fn_24275 +void CGraffiSkill::ReleaseTexture() { + delete this->m_canvas; +} + +// OFFSET: 0x00E4BAD0 +// FUNCTION: fn_24279 +void CGraffiSkill::BuildTexture() { + this->m_penX = 0; + this->m_penY = 0; + this->m_font = g_texMgr.Load("effect\\alpabet.bmp", 0, false, true, false); + this->m_canvas = CreateTexture(0x180, 0x180, 0, false, false); + + int length = static_cast(std::strlen(this->m_text)); + this->m_penY += (0x90 - length) / 24 * 36; + + int width; + for (int i = 0; i < length; ++i) { + char letter = this->m_text[i]; + unsigned char glyph; + int x = this->m_penX; + if (letter >= 'a' && letter <= 'z') { + glyph = letter - 'a' + 26; + width = 32; + } else if (letter >= 'A' && letter <= 'Z') { + glyph = letter - 'A'; + width = 32; + } else if (letter >= '0' && letter <= '9') { + glyph = letter - '0' + 54; + width = 32; + } else if (letter == ' ') { + for (const char* next = &this->m_text[i + 1]; *next != ' ' && *next != '\0'; ++next) { + x += 32; + } + + if (x >= 0x160) { + this->m_penY += 36; + x = 0; + this->m_penX = 0; + } else { + x = this->m_penX; + } + + glyph = 99; + } else { + if (letter == '?') { + glyph = 53; + } else if (letter == '!') { + glyph = 54; + } else { + glyph = 99; + } + + width = 16; + } + + if (width + x >= 0x160) { + this->m_penY += 36; + x = 0; + this->m_penX = 0; + } + + if (glyph != 99) { + this->m_canvas->CopyTexture(this->m_font, (glyph & 7) * 32, (glyph >> 3) * 32, 32, 32, x, this->m_penY, width, 32); + x = this->m_penX; + } + + x += width; + this->m_penX = x; + } + + CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); + this->m_angle = mode->scene->player->render.angle; +} + +// OFFSET: 0x00E4C2E0 +// FUNCTION: fn_24283 +bool CGraffiSkill::OnProcess() { + return true; +} diff --git a/Source/Contents/Actors/GraffiSkill.h b/Source/Contents/Actors/GraffiSkill.h new file mode 100644 index 00000000..94c377ac --- /dev/null +++ b/Source/Contents/Actors/GraffiSkill.h @@ -0,0 +1,34 @@ +#ifndef RAGNAROK_SOURCE_CONTENTS_ACTORS_GRAFFISKILL_H +#define RAGNAROK_SOURCE_CONTENTS_ACTORS_GRAFFISKILL_H + +#include "Contents/Actors/Skill.h" + +#include + +class CTexture; + +class CGraffiSkill : public CSkill { +public: + ~CGraffiSkill() override; + bool OnProcess() override; + + virtual void BuildTexture(); + virtual void ReleaseTexture(); + + /* 0440 */ unsigned char unknown_0440[0x0C]; + /* 044C */ CTexture* m_canvas; + /* 0450 */ CTexture* m_font; + /* 0454 */ char m_text[0x100]; + /* 0554 */ int m_penX; + /* 0558 */ int m_penY; + /* 055C */ float m_angle; +}; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(CGraffiSkill) == 0x560); +static_assert(offsetof(CGraffiSkill, m_canvas) == 0x44C); +static_assert(offsetof(CGraffiSkill, m_text) == 0x454); +static_assert(offsetof(CGraffiSkill, m_angle) == 0x55C); +#endif + +#endif diff --git a/Source/Contents/Actors/GroundScope.cpp b/Source/Contents/Actors/GroundScope.cpp index 9098cc5e..3dbe590f 100644 --- a/Source/Contents/Actors/GroundScope.cpp +++ b/Source/Contents/Actors/GroundScope.cpp @@ -1,5 +1,7 @@ #include "Contents/Actors/GroundScope.h" +#include "Contents/Skills/SkillInfo.h" + #include #include @@ -12,3 +14,87 @@ bool CGroundScope::OnProcess() { return true; } + +// OFFSET: 0x00E4AE10 +// FUNCTION: fn_24267 +int CGroundScope::SkillScopeType(int skillId) { + switch (skillId) { + case 0x0B: + case 0x19: + case 0x2F: + case 0x45: + case 0x50: + case 0x56: + case 0x58: + case 0x6E: + case 0x81: + case 0x82: + case 0x89: + case 0x8C: + case 0x17D: + case 0x1E3: + case 0x8D5: + return 3; + case 0x11: + case 0x15: + case 0x46: + case 0x5B: + case 0x5C: + case 0x1E4: + case 0x302: + case 0x7D5: + case 0x7F0: + case 0x7FC: + return 5; + case 0x4F: + case 0x1DE: + case 0x2DD: + case 0x2E3: + case 0x8BD: + case 0x992: + case 0x994: + case 0x995: + case 0x996: + case 0x138E: + case 0x1395: + case 0x13A4: + case 0x13B2: + case 0x146B: + case 0x146D: + case 0x146E: + case 0x149F: + return 7; + case 0x53: + case 0x59: + case 0x2D3: + case 0x2DB: + case 0x301: + case 0x7D8: + case 0x8D4: + case 0x98E: + case 0x138C: + case 0x1462: + case 0x1466: + case 0x14C7: + case 0x14F9: + case 0x14FA: + case 0x14FB: + case 0x14FC: + case 0x14FD: + return 9; + case 0x55: + case 0x1461: + return 11; + case 0x2C4: + case 0x8A5: + return 18; + case 0x149E: + return 19; + case 0x2E0: + case 0x98F: + case 0x991: + return -2; + default: + return CSkillActionMgr::GetInstance()->ActionSlot14(skillId); + } +} diff --git a/Source/Contents/Actors/GroundScope.h b/Source/Contents/Actors/GroundScope.h index 4c701917..fca0a93e 100644 --- a/Source/Contents/Actors/GroundScope.h +++ b/Source/Contents/Actors/GroundScope.h @@ -9,6 +9,8 @@ class CGroundScope : public EffectActor { public: bool OnProcess() override; + int SkillScopeType(int skillId); + /* 0158 */ unsigned char unknown_0158[0xC]; /* 0164 */ unsigned long m_lastingTime; }; diff --git a/Source/Contents/Actors/Skill.cpp b/Source/Contents/Actors/Skill.cpp index c37dbb54..c3709e4e 100644 --- a/Source/Contents/Actors/Skill.cpp +++ b/Source/Contents/Actors/Skill.cpp @@ -1,6 +1,118 @@ #include "Contents/Actors/Skill.h" + +#include "Client/Mode/CGameMode.h" +#include "Client/Mode/ModeMgr.h" +#include "Contents/Effects/Effect.h" +#include "Contents/Effects/EffectLists.h" +#include "Contents/Effects/RagEffect.h" +#include "Contents/Models/ModelActor.h" +#include "Contents/World/World.h" +#include "Contents/World/WorldObjects.h" #include "Render/Renderer.h" #include "Render/SpriteGeometry.h" +#include "UI/Window/UIWindowMgr.h" + +// OFFSET: 0x00E4AA30 +// FUNCTION: fn_24259 +CSkill::~CSkill() { + if (this->unknown_0264[0] != 0) { + g_windowMgr.Schedule(reinterpret_cast(this->unknown_0264[0])); + this->unknown_0264[0] = 0; + } + + this->ReleaseModel(); + for (std::uint64_t target : this->m_targets) { + CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); + reinterpret_cast(mode->scene)->SendObjectMessage(this, target, 24, 0, 0, 0, 0); + } + + this->m_targets.clear(); +} + +// OFFSET: 0x00E4B400 +// FUNCTION: fn_24268 +const char* CSkill::GetTrapModelName() { + switch (this->GetEffectiveJob(-1)) { + case 0x8F: + case 0xDA: + case 0xDE: + case 0xE6: + return "외부소품\\트랩03_3.rsm"; + case 0x90: + return "외부소품\\트랩02.rsm"; + case 0x91: + case 0xCB: + return "외부소품\\트랩01.rsm"; + case 0x93: + return "외부소품\\트랩03.rsm"; + case 0x94: + return "외부소품\\트랩03_6.rsm"; + case 0x95: + return "외부소품\\트랩03_4.rsm"; + case 0x96: + return "외부소품\\트랩03_5.rsm"; + case 0x97: + return "외부소품\\트랩03_2.rsm"; + case 0x98: + return "외부소품\\트랩04.rsm"; + case 0x99: + return "외부소품\\트랩05.rsm"; + case 0xD2: + return "event\\3차트랩_변화01.rsm"; + case 0xD3: + return "event\\3차트랩_변수01.rsm"; + case 0xD4: + return "event\\3차트랩_변지01.rsm"; + case 0xD5: + return "event\\3차트랩_변풍01.rsm"; + case 0xD6: + return "event\\3차트랩_화01.rsm"; + case 0xD7: + return "event\\3차트랩_수01.rsm"; + case 0xD8: + return "event\\3차트랩_풍01.rsm"; + case 0xD9: + return "event\\3차트랩_지01.rsm"; + case 0xE5: + return "event\\3차트랩_가시01.rsm"; + case 0x5174: + return "effect\\deepblindtrap\\deep_trap.rsm2"; + case 0x5175: + return "effect\\solidtrap\\sol_trap.rsm2"; + case 0x5176: + return "effect\\swifttrap\\swi_trap.rsm2"; + case 0x5177: + return "effect\\flametrap\\fla_trap.rsm2"; + default: + return nullptr; + } +} + +// OFFSET: 0x00E4B580 +// FUNCTION: fn_24269 +void CSkill::ReleaseModel() { + if (this->m_model == nullptr) { + return; + } + + CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); + WorldSpatialTree* spatial = &mode->scene->spatial; + if (spatial != nullptr) { + spatial->RemoveModel(this->m_model, 0); + } + + delete this->m_model; + this->m_model = nullptr; + if (this->m_effect != nullptr) { + this->m_effect->SendMsg(nullptr, 24, 0, 0, 0, 0, 0, 0); + } + + this->m_effect = nullptr; + if (this->m_hasEffect) { + g_effectMgr->Find(this->skillAid, this->m_effectId)->RetireIfInactive(); + this->m_hasEffect = false; + } +} // OFFSET: 0x00E503F0 // FUNCTION: fn_24294 diff --git a/Source/Contents/Actors/Skill.h b/Source/Contents/Actors/Skill.h index 7ce60401..92c32076 100644 --- a/Source/Contents/Actors/Skill.h +++ b/Source/Contents/Actors/Skill.h @@ -5,20 +5,37 @@ #include #include +#include + +class C3dActor; +struct CRagEffect; class CSkill : public CGameActor { public: + ~CSkill() override; + + const char* GetTrapModelName(); + void ReleaseModel(); void Render(int argument) override; /* 0410 */ unsigned char unknown_0410[0x08]; /* 0418 */ std::uint32_t skillAid; - /* 041C */ unsigned char unknown_041C[0x10]; + /* 041C */ C3dActor* m_model; + /* 0420 */ CRagEffect* m_effect; + /* 0424 */ int m_effectId; + /* 0428 */ bool m_hasEffect; + /* 0429 */ unsigned char unknown_0429[0x03]; /* 042C */ int range; + /* 0430 */ std::vector m_targets; + /* 043C */ int unknown_043C; }; #if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(CSkill) == 0x440); static_assert(offsetof(CSkill, skillAid) == 0x418); +static_assert(offsetof(CSkill, m_hasEffect) == 0x428); static_assert(offsetof(CSkill, range) == 0x42C); +static_assert(offsetof(CSkill, m_targets) == 0x430); #endif #endif diff --git a/Source/Contents/ClientItemLists.cpp b/Source/Contents/ClientItemLists.cpp index 34f6a37e..7e7f15dc 100644 --- a/Source/Contents/ClientItemLists.cpp +++ b/Source/Contents/ClientItemLists.cpp @@ -200,6 +200,7 @@ struct ClientItemLists { void ClearItems1768(); void ClearItems1740(); void ClearItems1750(); + void SetItems1750(int count, const short* ids); void ClearItems1758(); void ClearItems1760(); void ClearItems1748(); @@ -1146,3 +1147,15 @@ ITEM_INFO CSession::FindItemByIdentity(std::uint32_t identity, int location) { return ITEM_INFO(0); } + +// OFFSET: 0x00E49F40 +// FUNCTION: fn_24247 +void ClientItemLists::SetItems1750(int count, const short* ids) { + this->ClearItems1750(); + for (int i = 0; i < count; ++i) { + const ITEM_INFO item = LookupClientItemByIdentity(g_session.m_inventoryItems, ids[i]); + if (item.unknown_0000[1] > 0) { + this->items_1750.push_back(item); + } + } +} diff --git a/Source/Contents/ClientState.cpp b/Source/Contents/ClientState.cpp index 7771d218..73480ed5 100644 --- a/Source/Contents/ClientState.cpp +++ b/Source/Contents/ClientState.cpp @@ -80,3 +80,19 @@ void RequestBasicInfoRefresh() { g_windowMgr.unknown_01E0->SendMsg(nullptr, 106, 575, 0, 0, 0); } } + +// OFFSET: 0x00E49290 +// FUNCTION: fn_24232 +void CSession::SetUnknown5BC0(int value) { + this->unknown_5BC0 = value; + UIWindow* window = g_windowMgr.LookupWindow(0x272E); + if (window != nullptr) { + window->SendMsg(nullptr, 60, 0, 0, 0, 0); + } +} + +// OFFSET: 0x00E4A180 +// FUNCTION: fn_24249 +void AppendBytes(std::vector& output, const std::vector& bytes) { + output.insert(output.end(), bytes.begin(), bytes.end()); +} diff --git a/Source/Contents/ClientState.h b/Source/Contents/ClientState.h index da37c136..fc504b4e 100644 --- a/Source/Contents/ClientState.h +++ b/Source/Contents/ClientState.h @@ -1,10 +1,13 @@ #ifndef RAGNAROK_SOURCE_CONTENTS_CLIENTSTATE_H #define RAGNAROK_SOURCE_CONTENTS_CLIENTSTATE_H +#include + extern bool g_homunculusAutoFeed; extern bool g_petAutoFeed; void RequestBasicInfoRefresh(); void __stdcall sub_E489C0(bool enabled); +void AppendBytes(std::vector& output, const std::vector& bytes); #endif diff --git a/Source/Contents/Effects/ArrowEffect.cpp b/Source/Contents/Effects/ArrowEffect.cpp new file mode 100644 index 00000000..20bf3df1 --- /dev/null +++ b/Source/Contents/Effects/ArrowEffect.cpp @@ -0,0 +1,90 @@ +#include "Contents/Effects/ArrowEffect.h" + +#include "Client/Mode/ModeMgr.h" +#include "Resource/Sprites/ActionResource.h" +#include "Utility/Math/AffineMatrix.h" + +#include +#include + +// OFFSET: 0x00E4BA20 +// FUNCTION: fn_24277 +void CArrowEffect::Reset() { + this->render.animation = 0; + this->startPos = this->render.position; + this->startFrame = 0.0f; + this->velocity.x = 0.0f; + this->velocity.y = 0.0f; + this->velocity.z = 0.0f; + this->render.curMotion = 0; + this->frameCount = 0; +} + +// OFFSET: 0x00E4BE60 +// FUNCTION: fn_24280 +bool CArrowEffect::OnProcess() { + float frame = static_cast(static_cast(timeGetTime() - this->render.unknown_008C) / 24.0); + if (this->startFrame > frame) { + return true; + } + + if (this->render.visible == 0) { + this->render.visible = 1; + + vector3d target; + target.x = 0.0f; + target.y = 0.0f; + target.z = 0.0f; + if (this->targetAid == 0 || !this->GetTargetPos(this->targetAid, target)) { + target.z = this->targetPos.z; + target.x = this->targetPos.x; + target.y = this->targetPos.y; + } + + target.y -= 10.0f; + this->velocity.z = target.z - this->render.position.z; + this->velocity.y = target.y - this->render.position.y; + this->velocity.x = (target.x - this->render.position.x) * 0.125f; + this->velocity.y *= 0.125f; + this->velocity.z *= 0.125f; + + float distance = this->CalcDist(target.x, target.y, target.z); + if (distance < 40.0f) { + this->speedScale = 40.0f / distance; + this->velocity.x = this->speedScale * this->velocity.x; + this->velocity.y = this->speedScale * this->velocity.y; + this->velocity.z = this->speedScale * this->velocity.z; + } + + this->Face(target.x, target.z); + + float dy = target.y - this->render.position.y; + float dx = target.x - this->render.position.x; + float dz = target.z - this->render.position.z; + float inverse = 1.0f / SquareRoot(dy * dy + dx * dx + dz * dz); + this->render.position.x += inverse * dx * 2.5f; + this->render.position.y += inverse * dy * 2.5f; + this->render.position.z += inverse * dz * 2.5f; + } + + frame -= this->startFrame; + ++this->frameCount; + this->render.previousAnimation = this->render.animation; + if (this->frameCount > 8) { + ++this->render.curMotion; + if (this->render.curMotion >= static_cast(this->GetActRes()->FrameCount(this->render.previousAnimation))) { + this->render.curMotion = static_cast(this->GetActRes()->FrameCount(this->render.previousAnimation)) - 1; + } + } + + this->render.unknown_007C = this->GetDir(this->render.angle); + g_modeMgr.GetGameMode(); + this->render.position.x = frame * this->velocity.x + this->startPos.x; + this->render.position.y = frame * this->velocity.y + this->startPos.y; + this->render.position.z = frame * this->velocity.z + this->startPos.z; + if (frame > 8.0f / this->speedScale) { + return false; + } + + return true; +} diff --git a/Source/Contents/Effects/ArrowEffect.h b/Source/Contents/Effects/ArrowEffect.h new file mode 100644 index 00000000..37fb0aad --- /dev/null +++ b/Source/Contents/Effects/ArrowEffect.h @@ -0,0 +1,33 @@ +#ifndef RAGNAROK_SOURCE_CONTENTS_EFFECTS_ARROWEFFECT_H +#define RAGNAROK_SOURCE_CONTENTS_EFFECTS_ARROWEFFECT_H + +#include "Contents/Actors/RenderObject.h" +#include "Utility/Math/Vector3d.h" + +#include +#include + +class CArrowEffect : public CRenderObject { +public: + bool OnProcess() override; + + void Reset(); + + /* 0138 */ vector3d startPos; + /* 0144 */ vector3d velocity; + /* 0150 */ vector3d targetPos; + /* 015C */ float speedScale; + /* 0160 */ float startFrame; + /* 0164 */ std::uint32_t targetAid; + /* 0168 */ int frameCount; + /* 016C */ int unknown_016C; +}; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(CArrowEffect) == 0x170); +static_assert(offsetof(CArrowEffect, startPos) == 0x138); +static_assert(offsetof(CArrowEffect, speedScale) == 0x15C); +static_assert(offsetof(CArrowEffect, frameCount) == 0x168); +#endif + +#endif diff --git a/Source/Contents/Effects/DummyEffectLauncher.cpp b/Source/Contents/Effects/DummyEffectLauncher.cpp new file mode 100644 index 00000000..1fcb05a7 --- /dev/null +++ b/Source/Contents/Effects/DummyEffectLauncher.cpp @@ -0,0 +1,11 @@ +#include "Contents/Effects/DummyEffectLauncher.h" + +#include +#include + +// OFFSET: 0x00E4C1C0 +// FUNCTION: fn_24281 +bool CDummyEffectLauncher::OnProcess() { + float frame = static_cast(static_cast(timeGetTime() - this->render.unknown_008C) / 24.0); + return frame <= static_cast(this->lifeFrames); +} diff --git a/Source/Contents/Effects/DummyEffectLauncher.h b/Source/Contents/Effects/DummyEffectLauncher.h new file mode 100644 index 00000000..50b17096 --- /dev/null +++ b/Source/Contents/Effects/DummyEffectLauncher.h @@ -0,0 +1,21 @@ +#ifndef RAGNAROK_SOURCE_CONTENTS_EFFECTS_DUMMYEFFECTLAUNCHER_H +#define RAGNAROK_SOURCE_CONTENTS_EFFECTS_DUMMYEFFECTLAUNCHER_H + +#include "Contents/Effects/EffectActor.h" + +#include + +class CDummyEffectLauncher : public EffectActor { +public: + bool OnProcess() override; + + /* 0158 */ int lifeFrames; + /* 015C */ int unknown_015C; +}; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(CDummyEffectLauncher) == 0x160); +static_assert(offsetof(CDummyEffectLauncher, lifeFrames) == 0x158); +#endif + +#endif diff --git a/Source/Contents/Effects/EffectMaker.cpp b/Source/Contents/Effects/EffectMaker.cpp index 35cb2903..a18be91e 100644 --- a/Source/Contents/Effects/EffectMaker.cpp +++ b/Source/Contents/Effects/EffectMaker.cpp @@ -1,5 +1,11 @@ #include "Contents/Effects/EffectMaker.h" +#include "Client/Mode/CGameMode.h" +#include "Client/Mode/ModeMgr.h" +#include "Contents/Actors/Pc.h" +#include "Contents/Effects/RagEffect.h" +#include "Contents/World/World.h" + // OFFSET: 0x00E4A2B0 // FUNCTION: fn_24251 CEffectMaker::CEffectMaker() { @@ -11,3 +17,32 @@ CEffectMaker::CEffectMaker() { this->unknown_016C = 0; this->unknown_0170 = 0; } + +// OFFSET: 0x00E4BA80 +// FUNCTION: fn_24278 +void CEffectMaker::Reset() { + this->unknown_0160 = -1; + this->unknown_015C = 360; + this->unknown_0158 = 360; + this->unknown_0164 = 0; + this->unknown_0168 = 0; + this->unknown_016C = 0; + this->unknown_0170 = 0; +} + +// OFFSET: 0x00E4C210 +// FUNCTION: fn_24282 +bool CEffectMaker::OnProcess() { + CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); + CPlayer* player = mode->scene->player; + if (this->CalcDist(player->render.position.x, player->render.position.z) < 130.0f && this->unknown_0158 >= this->unknown_015C) { + this->unknown_0158 = 0; + CRagEffect* effect = this->LaunchEffect(this->unknown_0160, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); + if (effect != nullptr) { + effect->SendMsg(nullptr, 46, 0, reinterpret_cast(&this->unknown_0164), 0, 0, 0, 0); + } + } + + ++this->unknown_0158; + return true; +} diff --git a/Source/Contents/Effects/EffectMaker.h b/Source/Contents/Effects/EffectMaker.h index 728a4568..5e79f586 100644 --- a/Source/Contents/Effects/EffectMaker.h +++ b/Source/Contents/Effects/EffectMaker.h @@ -7,9 +7,12 @@ struct CEffectMaker : EffectActor { CEffectMaker(); + bool OnProcess() override; - /* 0158 */ int unknown_0158; - /* 015C */ int unknown_015C; + void Reset(); + + /* 0158 */ unsigned unknown_0158; + /* 015C */ unsigned unknown_015C; /* 0160 */ int unknown_0160; /* 0164 */ int unknown_0164; /* 0168 */ int unknown_0168; diff --git a/Source/Contents/Effects/MandraAttackEffect.cpp b/Source/Contents/Effects/MandraAttackEffect.cpp new file mode 100644 index 00000000..9237a635 --- /dev/null +++ b/Source/Contents/Effects/MandraAttackEffect.cpp @@ -0,0 +1,30 @@ +#include "Contents/Effects/MandraAttackEffect.h" + +#include "Resource/Sprites/ActionResource.h" + +#include +#include + +// OFFSET: 0x00E4C310 +// FUNCTION: fn_24285 +bool CMandraAttackEffect::OnProcess() { + float frame = static_cast(static_cast(timeGetTime() - this->render.unknown_008C) / 24.0); + float startFrame = this->startFrame; + if (startFrame > frame) { + this->render.visible = 0; + return true; + } + + this->render.visible = 1; + if (!this->render.isMotionFinished) { + int frameCount = static_cast(this->GetActRes()->FrameCount(this->render.previousAnimation)); + float elapsed = (frame - startFrame) / this->render.speed; + this->render.curMotion = static_cast(elapsed) % frameCount; + if (elapsed / frameCount >= 1.0f) { + this->render.isMotionFinished = true; + this->render.curMotion = frameCount - 1; + } + } + + return !this->render.isMotionFinished; +} diff --git a/Source/Contents/Effects/MandraAttackEffect.h b/Source/Contents/Effects/MandraAttackEffect.h new file mode 100644 index 00000000..186c5f4a --- /dev/null +++ b/Source/Contents/Effects/MandraAttackEffect.h @@ -0,0 +1,21 @@ +#ifndef RAGNAROK_SOURCE_CONTENTS_EFFECTS_MANDRAATTACKEFFECT_H +#define RAGNAROK_SOURCE_CONTENTS_EFFECTS_MANDRAATTACKEFFECT_H + +#include "Contents/Actors/RenderObject.h" + +#include + +class CMandraAttackEffect : public CRenderObject { +public: + bool OnProcess() override; + + /* 0138 */ float startFrame; + /* 013C */ int unknown_013C; +}; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(CMandraAttackEffect) == 0x140); +static_assert(offsetof(CMandraAttackEffect, startFrame) == 0x138); +#endif + +#endif diff --git a/Source/Contents/Effects/MsgEffect.cpp b/Source/Contents/Effects/MsgEffect.cpp new file mode 100644 index 00000000..98b42394 --- /dev/null +++ b/Source/Contents/Effects/MsgEffect.cpp @@ -0,0 +1,23 @@ +#include "Contents/Effects/MsgEffect.h" + +// OFFSET: 0x00E4A9B0 +// FUNCTION: fn_24258 +CMsgEffect::~CMsgEffect() { + if (this->m_owner != nullptr) { + this->m_owner->SendMsg(this, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } +} + +// OFFSET: 0x00E4B630 +// FUNCTION: fn_24270 +bool CMsgEffect::UsesMsgEffect(int type) { + switch (type) { + case 3: + case 18: + case 27: + case 31: + return true; + default: + return false; + } +} diff --git a/Source/Contents/Effects/MsgEffect.h b/Source/Contents/Effects/MsgEffect.h new file mode 100644 index 00000000..f49a7d57 --- /dev/null +++ b/Source/Contents/Effects/MsgEffect.h @@ -0,0 +1,24 @@ +#ifndef RAGNAROK_SOURCE_CONTENTS_EFFECTS_MSGEFFECT_H +#define RAGNAROK_SOURCE_CONTENTS_EFFECTS_MSGEFFECT_H + +#include "Contents/Effects/EffectActor.h" + +#include + +class CMsgEffect : public EffectActor { +public: + static bool UsesMsgEffect(int type); + + ~CMsgEffect() override; + + /* 0158 */ int unknown_0158; + /* 015C */ CGameObject* m_owner; + /* 0160 */ unsigned char unknown_0160[0x58]; +}; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(sizeof(CMsgEffect) == 0x1B8); +static_assert(offsetof(CMsgEffect, m_owner) == 0x15C); +#endif + +#endif diff --git a/Source/Contents/Items/ItemInfo.cpp b/Source/Contents/Items/ItemInfo.cpp index bf3017f7..4c2d88e8 100644 --- a/Source/Contents/Items/ItemInfo.cpp +++ b/Source/Contents/Items/ItemInfo.cpp @@ -534,3 +534,54 @@ void ITEM_INFO::SetCard(const int& card, const int& index) { std::memcpy(this->unknown_001C + index * 4, &card, sizeof(card)); } } + +// OFFSET: 0x00E48E90 +// FUNCTION: fn_24229 +int ReadItemList(std::list& items, int offset, int size, const std::vector& data) { + if (size <= 0) { + return offset; + } + + std::vector buffer(size, 0); + std::memcpy(buffer.data(), data.data() + offset, size); + int end = offset + size; + std::uint32_t consumed = 0; + std::vector remaining(buffer); + while (consumed < buffer.size()) { + ITEM_INFO item; + consumed += item.Deserialize(remaining); + items.push_back(item); + int left = static_cast(buffer.size()) - static_cast(consumed); + if (left > 0) { + std::memcpy(remaining.data(), buffer.data() + consumed, left); + } + } + + return end; +} + +// OFFSET: 0x00E49090 +// FUNCTION: fn_24230 +int ReadItemArray(ITEM_INFO* items, int offset, int size, const std::vector& data) { + if (size <= 0) { + return offset; + } + + std::vector buffer(size, 0); + std::memcpy(buffer.data(), data.data() + offset, size); + int end = offset + size; + std::uint32_t consumed = 0; + ITEM_INFO item; + std::vector remaining(buffer); + for (int i = 0; i < 10; ++i) { + consumed += item.Deserialize(remaining); + items[i] = item; + if (consumed >= buffer.size()) { + break; + } + + std::memcpy(remaining.data(), buffer.data() + consumed, buffer.size() - consumed); + } + + return end; +} diff --git a/Source/Contents/Items/ItemInfo.h b/Source/Contents/Items/ItemInfo.h index a524c934..53d72bd9 100644 --- a/Source/Contents/Items/ItemInfo.h +++ b/Source/Contents/Items/ItemInfo.h @@ -113,6 +113,8 @@ ITEM_INFO LookupClientItemByIdentity(const std::list& values, std::ui bool IsNumericText(const char* text); bool MatchItemName(std::string name, std::string query, unsigned char partial, std::list* result, int id); std::list SearchItemIds(std::string query, unsigned char cardsOnly, unsigned char partial); +int ReadItemList(std::list& items, int offset, int size, const std::vector& data); +int ReadItemArray(ITEM_INFO* items, int offset, int size, const std::vector& data); #if defined(_WIN32) && !defined(_WIN64) diff --git a/Source/Contents/Messenger/MessengerMember.cpp b/Source/Contents/Messenger/MessengerMember.cpp index 796315f1..5c3eea75 100644 --- a/Source/Contents/Messenger/MessengerMember.cpp +++ b/Source/Contents/Messenger/MessengerMember.cpp @@ -23,6 +23,13 @@ #include namespace { +#pragma pack(push, 1) + struct MessengerRecordHeader { + /* 0000 */ std::int16_t id; + /* 0002 */ std::uint32_t bytes; + }; +#pragma pack(pop) + struct MiniPartyIconPosition { /* 0000 */ int x; /* 0004 */ int y; @@ -99,6 +106,7 @@ public: #if defined(_WIN32) && !defined(_WIN64) static_assert(sizeof(MiniPartyIconPosition) == 12); +static_assert(sizeof(MessengerRecordHeader) == 6); #endif // OFFSET: 0x00D03DC0 @@ -140,6 +148,102 @@ MessengerMember& MessengerMember::operator=(const MessengerMember& other) { // FUNCTION: fn_07666 MessengerMember::~MessengerMember() = default; +// OFFSET: 0x00E48C00 +// FUNCTION: fn_24228 +int ReadMessengerMembers(std::list& members, int offset, int size, const std::vector& data) { + if (size <= 0) { + return offset; + } + + std::vector buffer(size, 0); + std::memcpy(buffer.data(), data.data() + offset, size); + int end = offset + size; + std::uint32_t consumed = 0; + std::vector remaining(buffer); + while (consumed < buffer.size()) { + MessengerMember member; + consumed += member.Deserialize(remaining); + members.push_back(member); + int left = static_cast(buffer.size()) - static_cast(consumed); + if (left > 0) { + std::memcpy(remaining.data(), buffer.data() + consumed, left); + } + } + + return end; +} + +// OFFSET: 0x00E494F0 +// FUNCTION: fn_24236 +std::uint32_t MessengerMember::Deserialize(const std::vector& data) { + if (data.begin() == data.end()) { + return 0xFFFFFFFF; + } + + std::uint32_t offset = 6; + MessengerRecordHeader header{}; + std::memcpy(&header, data.data(), sizeof(header)); + if (header.id != 251) { + return 0xFFFFFFFF; + } + + while (offset < data.size()) { + header = {}; + std::memcpy(&header, data.data() + offset, sizeof(header)); + offset += 6; + const unsigned char* source = data.data() + offset; + switch (header.id) { + case 252: + return offset; + case 253: + std::memcpy(&this->valid, source, header.bytes); + offset += header.bytes; + break; + case 254: + std::memcpy(&this->accountId, source, header.bytes); + offset += header.bytes; + break; + case 255: + std::memcpy(&this->characterId, source, header.bytes); + offset += header.bytes; + break; + case 256: { + char buffer[260]; + std::memset(buffer, 0, sizeof(buffer)); + std::memcpy(buffer, source, header.bytes); + offset += header.bytes; + this->name = buffer; + break; + } + case 257: { + char buffer[260]; + std::memset(buffer, 0, sizeof(buffer)); + std::memcpy(buffer, source, header.bytes); + offset += header.bytes; + this->map = buffer; + break; + } + case 258: + std::memcpy(&this->leader, source, header.bytes); + offset += header.bytes; + break; + case 259: + std::memcpy(&this->offline, source, header.bytes); + offset += header.bytes; + break; + case 260: + std::memcpy(&this->color, source, header.bytes); + offset += header.bytes; + break; + default: + offset += header.bytes; + break; + } + } + + return offset; +} + // OFFSET: 0x00738590 // FUNCTION: fn_07667 UIDragMiniPartyWnd::~UIDragMiniPartyWnd() = default; diff --git a/Source/Contents/Messenger/MessengerMember.h b/Source/Contents/Messenger/MessengerMember.h index b4232406..b55af248 100644 --- a/Source/Contents/Messenger/MessengerMember.h +++ b/Source/Contents/Messenger/MessengerMember.h @@ -2,7 +2,9 @@ #define RAGNAROK_SOURCE_CONTENTS_MESSENGER_MESSENGERMEMBER_H #include +#include #include +#include struct MessengerMember { MessengerMember(); @@ -10,6 +12,7 @@ struct MessengerMember { MessengerMember(const MessengerMember& other); MessengerMember& operator=(const MessengerMember& other); ~MessengerMember(); + std::uint32_t Deserialize(const std::vector& data); /* 0000 */ std::int32_t valid; /* 0004 */ std::uint32_t accountId; @@ -24,6 +27,8 @@ struct MessengerMember { /* 004C */ std::int32_t dead; }; +int ReadMessengerMembers(std::list& members, int offset, int size, const std::vector& data); + #if defined(_WIN32) && !defined(_WIN64) static_assert(sizeof(MessengerMember) == 80); #endif diff --git a/Source/Network/ClientOptionPacket.cpp b/Source/Network/ClientOptionPacket.cpp index 4d5a3f43..0b29f834 100644 --- a/Source/Network/ClientOptionPacket.cpp +++ b/Source/Network/ClientOptionPacket.cpp @@ -1,4 +1,5 @@ #include "Network/ClientOptionPacket.h" +#include "Client/Session.h" #include "Network/Connection/Connection.h" #include "Network/Packets/PacketLengths.h" @@ -24,3 +25,7 @@ void SendClientOption(unsigned char value, CConnection& connection, const Packet packet.value = value; connection.Send(lengths.FixedLength(packet.id), &packet); } + +// OFFSET: 0x00E48BA0 + +// OFFSET: 0x00E49380 diff --git a/data/compile.json b/data/compile.json index 57010a0d..07f3ca4c 100644 --- a/data/compile.json +++ b/data/compile.json @@ -295,6 +295,7 @@ "Source/Contents/Actors/CharacterRender.cpp": 4, "Source/Contents/Actors/GameActor.cpp": 4, "Source/Contents/Actors/GameObject.cpp": 4, + "Source/Contents/Actors/GraffiSkill.cpp": 4, "Source/Contents/Actors/GroundScope.cpp": 4, "Source/Contents/Actors/Item.cpp": 4, "Source/Contents/Actors/MercenaryAI.cpp": 4, @@ -329,6 +330,7 @@ "Source/Contents/Effects/AMachineEffect.cpp": 4, "Source/Contents/Effects/AcidifiedThrow.cpp": 4, "Source/Contents/Effects/AnimatedEmitterEffect.cpp": 4, + "Source/Contents/Effects/ArrowEffect.cpp": 4, "Source/Contents/Effects/AstralStrikeEffect.cpp": 4, "Source/Contents/Effects/AxeBoomerang.cpp": 4, "Source/Contents/Effects/Bash.cpp": 4, @@ -348,6 +350,7 @@ "Source/Contents/Effects/CommonFloorEffect.cpp": 4, "Source/Contents/Effects/Competentia.cpp": 4, "Source/Contents/Effects/CresciveBolt.cpp": 4, + "Source/Contents/Effects/DummyEffectLauncher.cpp": 4, "Source/Contents/Effects/EZ2STREffectEx.cpp": 4, "Source/Contents/Effects/Effect.cpp": 0, "Source/Contents/Effects/EffectActor.cpp": 4, @@ -387,11 +390,13 @@ "Source/Contents/Effects/LightSphereEffect.cpp": 4, "Source/Contents/Effects/MagicFloorEffect.cpp": 4, "Source/Contents/Effects/MagnumBreak.cpp": 4, + "Source/Contents/Effects/MandraAttackEffect.cpp": 4, "Source/Contents/Effects/MapChainEffect.cpp": 4, "Source/Contents/Effects/MapMagicZoneEffect.cpp": 4, "Source/Contents/Effects/MappillarEffect.cpp": 4, "Source/Contents/Effects/MeteorEffect.cpp": 4, "Source/Contents/Effects/MonsterSkillEffects.cpp": 4, + "Source/Contents/Effects/MsgEffect.cpp": 4, "Source/Contents/Effects/NyanggrassEffect.cpp": 4, "Source/Contents/Effects/Particle.cpp": 4, "Source/Contents/Effects/ParticlePool.cpp": 4,