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
This commit is contained in:
ragnarok-clanker 2026-10-30 06:43:59 +00:00
commit 05be22bdcf
26 changed files with 852 additions and 4 deletions

View file

@ -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();

View file

@ -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 <cstring>
// 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<int>(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<CGameMode*>(g_modeMgr.GetGameMode());
this->m_angle = mode->scene->player->render.angle;
}
// OFFSET: 0x00E4C2E0
// FUNCTION: fn_24283
bool CGraffiSkill::OnProcess() {
return true;
}

View file

@ -0,0 +1,34 @@
#ifndef RAGNAROK_SOURCE_CONTENTS_ACTORS_GRAFFISKILL_H
#define RAGNAROK_SOURCE_CONTENTS_ACTORS_GRAFFISKILL_H
#include "Contents/Actors/Skill.h"
#include <cstddef>
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

View file

@ -1,5 +1,7 @@
#include "Contents/Actors/GroundScope.h"
#include "Contents/Skills/SkillInfo.h"
#include <windows.h>
#include <mmsystem.h>
@ -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);
}
}

View file

@ -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;
};

View file

@ -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<UIWindow*>(this->unknown_0264[0]));
this->unknown_0264[0] = 0;
}
this->ReleaseModel();
for (std::uint64_t target : this->m_targets) {
CGameMode* mode = static_cast<CGameMode*>(g_modeMgr.GetGameMode());
reinterpret_cast<WorldObjects*>(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<CGameMode*>(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

View file

@ -5,20 +5,37 @@
#include <cstddef>
#include <cstdint>
#include <vector>
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<std::uint64_t> 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

View file

@ -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);
}
}
}

View file

@ -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<unsigned char>& output, const std::vector<unsigned char>& bytes) {
output.insert(output.end(), bytes.begin(), bytes.end());
}

View file

@ -1,10 +1,13 @@
#ifndef RAGNAROK_SOURCE_CONTENTS_CLIENTSTATE_H
#define RAGNAROK_SOURCE_CONTENTS_CLIENTSTATE_H
#include <vector>
extern bool g_homunculusAutoFeed;
extern bool g_petAutoFeed;
void RequestBasicInfoRefresh();
void __stdcall sub_E489C0(bool enabled);
void AppendBytes(std::vector<unsigned char>& output, const std::vector<unsigned char>& bytes);
#endif

View file

@ -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 <windows.h>
#include <timeapi.h>
// 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<float>(static_cast<double>(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<int>(this->GetActRes()->FrameCount(this->render.previousAnimation))) {
this->render.curMotion = static_cast<int>(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;
}

View file

@ -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 <cstddef>
#include <cstdint>
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

View file

@ -0,0 +1,11 @@
#include "Contents/Effects/DummyEffectLauncher.h"
#include <windows.h>
#include <timeapi.h>
// OFFSET: 0x00E4C1C0
// FUNCTION: fn_24281
bool CDummyEffectLauncher::OnProcess() {
float frame = static_cast<float>(static_cast<double>(timeGetTime() - this->render.unknown_008C) / 24.0);
return frame <= static_cast<float>(this->lifeFrames);
}

View file

@ -0,0 +1,21 @@
#ifndef RAGNAROK_SOURCE_CONTENTS_EFFECTS_DUMMYEFFECTLAUNCHER_H
#define RAGNAROK_SOURCE_CONTENTS_EFFECTS_DUMMYEFFECTLAUNCHER_H
#include "Contents/Effects/EffectActor.h"
#include <cstddef>
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

View file

@ -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<CGameMode*>(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<int>(&this->unknown_0164), 0, 0, 0, 0);
}
}
++this->unknown_0158;
return true;
}

View file

@ -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;

View file

@ -0,0 +1,30 @@
#include "Contents/Effects/MandraAttackEffect.h"
#include "Resource/Sprites/ActionResource.h"
#include <windows.h>
#include <timeapi.h>
// OFFSET: 0x00E4C310
// FUNCTION: fn_24285
bool CMandraAttackEffect::OnProcess() {
float frame = static_cast<float>(static_cast<double>(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<int>(this->GetActRes()->FrameCount(this->render.previousAnimation));
float elapsed = (frame - startFrame) / this->render.speed;
this->render.curMotion = static_cast<int>(elapsed) % frameCount;
if (elapsed / frameCount >= 1.0f) {
this->render.isMotionFinished = true;
this->render.curMotion = frameCount - 1;
}
}
return !this->render.isMotionFinished;
}

View file

@ -0,0 +1,21 @@
#ifndef RAGNAROK_SOURCE_CONTENTS_EFFECTS_MANDRAATTACKEFFECT_H
#define RAGNAROK_SOURCE_CONTENTS_EFFECTS_MANDRAATTACKEFFECT_H
#include "Contents/Actors/RenderObject.h"
#include <cstddef>
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

View file

@ -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;
}
}

View file

@ -0,0 +1,24 @@
#ifndef RAGNAROK_SOURCE_CONTENTS_EFFECTS_MSGEFFECT_H
#define RAGNAROK_SOURCE_CONTENTS_EFFECTS_MSGEFFECT_H
#include "Contents/Effects/EffectActor.h"
#include <cstddef>
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

View file

@ -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<ITEM_INFO>& items, int offset, int size, const std::vector<unsigned char>& data) {
if (size <= 0) {
return offset;
}
std::vector<unsigned char> buffer(size, 0);
std::memcpy(buffer.data(), data.data() + offset, size);
int end = offset + size;
std::uint32_t consumed = 0;
std::vector<unsigned char> remaining(buffer);
while (consumed < buffer.size()) {
ITEM_INFO item;
consumed += item.Deserialize(remaining);
items.push_back(item);
int left = static_cast<int>(buffer.size()) - static_cast<int>(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<unsigned char>& data) {
if (size <= 0) {
return offset;
}
std::vector<unsigned char> 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<unsigned char> 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;
}

View file

@ -113,6 +113,8 @@ ITEM_INFO LookupClientItemByIdentity(const std::list<ITEM_INFO>& values, std::ui
bool IsNumericText(const char* text);
bool MatchItemName(std::string name, std::string query, unsigned char partial, std::list<int>* result, int id);
std::list<int> SearchItemIds(std::string query, unsigned char cardsOnly, unsigned char partial);
int ReadItemList(std::list<ITEM_INFO>& items, int offset, int size, const std::vector<unsigned char>& data);
int ReadItemArray(ITEM_INFO* items, int offset, int size, const std::vector<unsigned char>& data);
#if defined(_WIN32) && !defined(_WIN64)

View file

@ -23,6 +23,13 @@
#include <vector>
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<MessengerMember>& members, int offset, int size, const std::vector<unsigned char>& data) {
if (size <= 0) {
return offset;
}
std::vector<unsigned char> buffer(size, 0);
std::memcpy(buffer.data(), data.data() + offset, size);
int end = offset + size;
std::uint32_t consumed = 0;
std::vector<unsigned char> remaining(buffer);
while (consumed < buffer.size()) {
MessengerMember member;
consumed += member.Deserialize(remaining);
members.push_back(member);
int left = static_cast<int>(buffer.size()) - static_cast<int>(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<unsigned char>& 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;

View file

@ -2,7 +2,9 @@
#define RAGNAROK_SOURCE_CONTENTS_MESSENGER_MESSENGERMEMBER_H
#include <cstdint>
#include <list>
#include <string>
#include <vector>
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<unsigned char>& 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<MessengerMember>& members, int offset, int size, const std::vector<unsigned char>& data);
#if defined(_WIN32) && !defined(_WIN64)
static_assert(sizeof(MessengerMember) == 80);
#endif

View file

@ -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

View file

@ -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,