This commit is contained in:
ragnarok-clanker 2026-10-28 23:14:46 +01:00
commit 41b839b378
41 changed files with 2250 additions and 218 deletions

View file

@ -3,13 +3,16 @@
#include "Resource/ResourceMgr.h" #include "Resource/ResourceMgr.h"
#include "Resource/SkinPalette.h" #include "Resource/SkinPalette.h"
#include "Resource/TextureNames.h" #include "Resource/TextureNames.h"
#include "UI/Buttons/UIBitmapButton.h"
#include "UI/Buttons/UICashEmotionSprBtn.h" #include "UI/Buttons/UICashEmotionSprBtn.h"
#include "UI/CashEmotion/UICashEmotionPack.h" #include "UI/CashEmotion/UICashEmotionPack.h"
#include "UI/CashEmotion/UICashEmotionPiece.h" #include "UI/CashEmotion/UICashEmotionPiece.h"
#include "UI/Chat/ChatSuggestions.h" #include "UI/Chat/ChatSuggestions.h"
#include "UI/Edit/UIEditCtrl.h"
#include "UI/Window/WindowBorder.h" #include "UI/Window/WindowBorder.h"
#include <algorithm> #include <algorithm>
#include <array>
#include <climits> #include <climits>
#include <cstring> #include <cstring>
#include <iterator> #include <iterator>
@ -23,22 +26,34 @@ CCashEmotionMgr* g_cashEmotionMgr = nullptr;
// GLOBAL: gl_24484 // GLOBAL: gl_24484
ChatSuggestions* g_chatSuggestions = nullptr; ChatSuggestions* g_chatSuggestions = nullptr;
class ICashEmotionAction {
public:
virtual ~ICashEmotionAction();
virtual void Execute(int packId, int argument) = 0;
};
class UICashEmotionMiniWnd : public UIWindow { class UICashEmotionMiniWnd : public UIWindow {
public: public:
// OFFSET: 0x007C9200 void Slot15(int width, int height) override;
void Paint() override;
int SendMsg(UIWindow* sender, int message, std::intptr_t argument1, std::intptr_t argument2, std::intptr_t argument3, std::intptr_t argument4) override;
unsigned int Select(int actionId); unsigned int Select(int actionId);
void Select(CashEmotionCommand* command);
void Previous();
void Refresh(int first); void Refresh(int first);
void Next();
void SetIndex(int index);
protected: protected:
/* 007C */ CashEmotionCommand* m_selected; /* 007C */ CashEmotionCommand* m_selected;
/* 0080 */ UICashEmotionPiece* m_first; /* 0080 */ std::array<UICashEmotionPiece*, 3> m_pieces;
/* 0084 */ UICashEmotionPiece* m_second; /* 008C */ UIBitmapButton* m_previous;
/* 0088 */ UICashEmotionPiece* m_third; /* 0090 */ UIBitmapButton* m_next;
/* 008C */ UIButton* m_previous; /* 0094 */ ICashEmotionAction* m_action;
/* 0090 */ UIButton* m_next;
/* 0094 */ void* unknown_0094;
/* 0098 */ int m_page; /* 0098 */ int m_page;
/* 009C */ int m_selectedIndex; /* 009C */ int m_selectedIndex;
UICashEmotionPieceContext* m_controls;
}; };
// OFFSET: 0x00592290 // OFFSET: 0x00592290
@ -608,7 +623,7 @@ void CCashEmotionMgr::SetOwnedPack(int packId, unsigned int expiry) {
for (auto it = this->m_expiry.begin(); it != this->m_expiry.end(); ++it) { for (auto it = this->m_expiry.begin(); it != this->m_expiry.end(); ++it) {
if (it->first == packId) { if (it->first == packId) {
if (expiry) { if (expiry) {
it->second.seconds = expiry; it->second.Set(expiry);
} else { } else {
this->m_expiry.erase(it); this->m_expiry.erase(it);
} }
@ -617,7 +632,7 @@ void CCashEmotionMgr::SetOwnedPack(int packId, unsigned int expiry) {
} }
if (expiry > 0) { if (expiry > 0) {
this->m_expiry.emplace_back(packId, Timestamp{static_cast<__time32_t>(expiry)}); this->m_expiry.emplace_back(packId, expiry);
} }
} }
@ -648,42 +663,238 @@ bool CCashEmotionMgr::IsOwnedPack(int packId) {
return std::find(this->m_ownedPacks.begin(), this->m_ownedPacks.end(), packId) != this->m_ownedPacks.end(); return std::find(this->m_ownedPacks.begin(), this->m_ownedPacks.end(), packId) != this->m_ownedPacks.end();
} }
// OFFSET: 0x007C9200
// FUNCTION: fn_09428
void UICashEmotionMiniWnd::Select(CashEmotionCommand* command) {
this->m_selected = command;
this->m_page = 0;
this->m_selectedIndex = 0;
this->Refresh(0);
if (this->m_previous) {
this->m_previous->Enable(0);
}
if (this->m_selected && this->m_selected->Count() > 3) {
if (this->m_next) {
this->m_next->Enable(1);
}
} else if (this->m_next) {
this->m_next->Enable(0);
}
}
// OFFSET: 0x007C9290
// FUNCTION: fn_09429
void UICashEmotionMiniWnd::Previous() {
if (this->m_selectedIndex != 0) {
this->SetIndex(this->m_selectedIndex - 1);
}
}
// OFFSET: 0x007C92B0 // OFFSET: 0x007C92B0
// FUNCTION: fn_09430 // FUNCTION: fn_09430
void UICashEmotionMiniWnd::Refresh(int first) { void UICashEmotionMiniWnd::Refresh(int first) {
if (this->m_first) { if (this->m_pieces[0]) {
if (this->m_selected) { if (this->m_selected) {
std::pair<int, int> entry = this->m_selected->Get(first); std::pair<int, int> entry = this->m_selected->Get(first);
bool owned = entry.first == g_cashEmotionMgr->BasePackId() || g_cashEmotionMgr->IsOwnedPack(entry.first); bool owned = entry.first == g_cashEmotionMgr->BasePackId() || g_cashEmotionMgr->IsOwnedPack(entry.first);
this->m_first->Set(entry.first, entry.second, false, false, !owned); this->m_pieces[0]->Set(entry.first, entry.second, false, false, !owned);
} else { } else {
this->m_first->Set(-1, -1, false, false, false); this->m_pieces[0]->Set(-1, -1, false, false, false);
} }
} }
if (this->m_second) { if (this->m_pieces[1]) {
if (this->m_selected) { if (this->m_selected) {
std::pair<int, int> entry = this->m_selected->Get(first + 1); std::pair<int, int> entry = this->m_selected->Get(first + 1);
bool owned = entry.first == g_cashEmotionMgr->BasePackId() || g_cashEmotionMgr->IsOwnedPack(entry.first); bool owned = entry.first == g_cashEmotionMgr->BasePackId() || g_cashEmotionMgr->IsOwnedPack(entry.first);
this->m_second->Set(entry.first, entry.second, false, false, !owned); this->m_pieces[1]->Set(entry.first, entry.second, false, false, !owned);
} else { } else {
this->m_second->Set(-1, -1, false, false, false); this->m_pieces[1]->Set(-1, -1, false, false, false);
} }
} }
if (this->m_third) { if (this->m_pieces[2]) {
if (this->m_selected) { if (this->m_selected) {
std::pair<int, int> entry = this->m_selected->Get(first + 2); std::pair<int, int> entry = this->m_selected->Get(first + 2);
bool owned = entry.first == g_cashEmotionMgr->BasePackId() || g_cashEmotionMgr->IsOwnedPack(entry.first); bool owned = entry.first == g_cashEmotionMgr->BasePackId() || g_cashEmotionMgr->IsOwnedPack(entry.first);
this->m_third->Set(entry.first, entry.second, false, false, !owned); this->m_pieces[2]->Set(entry.first, entry.second, false, false, !owned);
} else { } else {
this->m_third->Set(-1, -1, false, false, false); this->m_pieces[2]->Set(-1, -1, false, false, false);
} }
} }
this->Invalidate(); this->Invalidate();
} }
// OFFSET: 0x007C94A0
// FUNCTION: fn_09431
void UICashEmotionMiniWnd::Slot15(int width, int height) {
int x = 24;
for (UICashEmotionPiece*& piece : this->m_pieces) {
piece = new (std::nothrow) UICashEmotionPiece(*this->m_context, *this->m_controls);
if (piece) {
piece->SetSize(42, 42);
piece->Move(x, 1);
this->AddChild(piece);
x += 49;
}
}
this->m_previous = new (std::nothrow) UIBitmapButton(*this->m_context, this->m_controls->buttons);
if (this->m_previous) {
this->m_previous->SetBitmap("유저인터페이스\\cashemotion\\btn_arrow_l_normal.bmp", 0, 0);
this->m_previous->SetBitmap("유저인터페이스\\cashemotion\\btn_arrow_l_over.bmp", 1, 0);
this->m_previous->SetBitmap("유저인터페이스\\cashemotion\\btn_arrow_l_press.bmp", 2, 0);
this->m_previous->SetSize(this->m_previous->BitmapWidth(), this->m_previous->BitmapHeight());
this->m_previous->Move(0, 0);
this->m_previous->SetId(1);
this->AddChild(this->m_previous);
}
this->m_next = new (std::nothrow) UIBitmapButton(*this->m_context, this->m_controls->buttons);
if (this->m_next) {
this->m_next->SetBitmap("유저인터페이스\\cashemotion\\bth_arrow_r_normal.bmp", 0, 0);
this->m_next->SetBitmap("유저인터페이스\\cashemotion\\bth_arrow_r_over.bmp", 1, 0);
this->m_next->SetBitmap("유저인터페이스\\cashemotion\\bth_arrow_r_press.bmp", 2, 0);
int buttonWidth = this->m_next->BitmapWidth();
this->m_next->SetSize(buttonWidth, this->m_next->BitmapHeight());
this->m_next->Move(this->geometry.width - buttonWidth, 0);
this->m_next->SetId(2);
this->AddChild(this->m_next);
}
}
// OFFSET: 0x007C96E0
// FUNCTION: fn_09432
void UICashEmotionMiniWnd::Paint() {
this->ClearSurface(0xFFFF00FF);
auto* background = static_cast<CBitmapRes*>(ResourceMgr::Instance().Load(Resolve("유저인터페이스\\cashemotion\\bg_preview.bmp")));
if (background) {
this->DrawBitmap(0, 0, &background->image, false);
}
if (this->m_selectedIndex == -1) {
return;
}
int slot = this->m_selectedIndex - this->m_page;
if (slot <= -1) {
return;
}
UICashEmotionPiece* piece = this->m_pieces.at(slot);
if (!piece) {
return;
}
auto* highlight = static_cast<CBitmapRes*>(ResourceMgr::Instance().Load(Resolve("유저인터페이스\\cashemotion\\bg_preview_select.bmp")));
if (highlight) {
this->DrawBitmap(piece->geometry.x - 3, 0, &highlight->image, false);
}
}
// OFFSET: 0x007C9790
// FUNCTION: fn_09433
void UICashEmotionMiniWnd::Next() {
if (this->m_selected && this->m_selected->Count() > this->m_selectedIndex + 1) {
this->SetIndex(this->m_selectedIndex + 1);
}
}
// OFFSET: 0x007C97C0
// FUNCTION: fn_09434
void UICashEmotionMiniWnd::SetIndex(int index) {
if (index == this->m_selectedIndex) {
return;
}
this->m_selectedIndex = index;
if (this->m_selected) {
bool enable = index > 0 && this->m_selected->Count() > 1;
if (this->m_previous) {
this->m_previous->Enable(enable);
}
enable = this->m_selectedIndex + 1 < this->m_selected->Count() && this->m_selected->Count() > 1;
if (this->m_next) {
this->m_next->Enable(enable);
}
}
if (this->m_selectedIndex >= this->m_page + 3) {
this->m_page = this->m_selectedIndex - 2;
this->Refresh(this->m_page);
} else if (this->m_selectedIndex < this->m_page) {
this->m_page = this->m_selectedIndex;
this->Refresh(this->m_page);
}
this->Invalidate();
}
// OFFSET: 0x007C98A0
// FUNCTION: fn_09435
int UICashEmotionMiniWnd::SendMsg(
UIWindow* sender, int message, std::intptr_t argument1, std::intptr_t argument2, std::intptr_t argument3, std::intptr_t argument4
) {
std::pair<int, int> entry;
switch (message) {
case 6:
if (argument1 == 1) {
this->Previous();
} else if (argument1 == 2) {
this->Next();
}
return 0;
case 60:
this->Invalidate();
return 0;
case 174:
entry.first = static_cast<int>(argument1);
entry.second = static_cast<int>(argument2);
break;
case 176:
if (argument1 == 13) {
if (!this->m_selected) {
return 0;
}
entry = this->m_selected->Get(this->m_selectedIndex);
break;
}
if (argument1 == 37) {
this->Previous();
} else if (argument1 == 39) {
this->Next();
}
return 0;
default:
return UIWindow::SendMsg(sender, message, argument1, argument2, argument3, argument4);
}
CashEmotionData* data = g_cashEmotionMgr->GetEmotionData(entry.first, entry.second);
if (!data) {
return 0;
}
int packId = data->id;
if (packId == g_cashEmotionMgr->BasePackId() || g_cashEmotionMgr->IsOwnedPack(packId)) {
if (g_chatSuggestions) {
if (g_chatSuggestions->input) {
g_chatSuggestions->input->SetText(data->name.c_str());
}
g_chatSuggestions->Show(0);
}
} else if (this->m_action) {
this->m_action->Execute(packId, 0);
}
return 0;
}
// OFFSET: 0x007C9D60 // OFFSET: 0x007C9D60
// FUNCTION: fn_09439 // FUNCTION: fn_09439
UICashEmotionPack::~UICashEmotionPack() { UICashEmotionPack::~UICashEmotionPack() {

View file

@ -26,6 +26,10 @@ public:
void Add(int packId, int emotionId); void Add(int packId, int emotionId);
std::pair<int, int> Get(int index) const; std::pair<int, int> Get(int index) const;
// INLINED
int Count() const {
return static_cast<int>(this->entries.size());
}
/* 0000 */ std::string name; /* 0000 */ std::string name;
/* 0018 */ std::vector<std::pair<int, int>> entries; /* 0018 */ std::vector<std::pair<int, int>> entries;

View file

@ -27,7 +27,7 @@ inline RagEffectState::RagEffectState()
// INLINED // INLINED
inline RagEffectState::~RagEffectState() { inline RagEffectState::~RagEffectState() {
for (EffectPrimitiveState* particle : this->related) { for (EffectPrimitiveState* particle : this->related) {
particle->parent = nullptr; particle->master = nullptr;
} }
this->related.clear(); this->related.clear();

View file

@ -1,5 +1,5 @@
#include "Contents/Effects/EffectParticle.h" #include "Contents/Effects/EffectParticle.h"
#include "Contents/Effects/RagEffectState.h" #include "Contents/Effects/AttachedEffect.h"
// OFFSET: 0x00BAD940 // OFFSET: 0x00BAD940
// FUNCTION: fn_19761 // FUNCTION: fn_19761
@ -9,13 +9,13 @@ EffectParticle::EffectParticle(ResourceMgr& resources, JobNames& names) : CRende
// OFFSET: 0x00C1DE70 // OFFSET: 0x00C1DE70
// FUNCTION: fn_20076 // FUNCTION: fn_20076
EffectParticle* CreateEffectParticle( EffectParticle* CreateEffectParticle(
RagEffectState& parent, const RenderObjectState& parentRender, int type, float x, float y, float z, ResourceMgr& resources, JobNames& names, AttachedEffect& master, const RenderObjectState& masterRender, int type, float x, float y, float z, ResourceMgr& resources, JobNames& names,
std::list<CRenderObject*>& worldObjects std::list<CRenderObject*>& worldObjects
) { ) {
auto* particle = new EffectParticle(resources, names); auto* particle = new EffectParticle(resources, names);
particle->state.Initialize(particle->render, parent, parentRender, type, x, y, z); particle->state.Initialize(particle->render, master, masterRender, type, x, y, z);
parent.related.push_back(&particle->state); master.state.related.push_back(&particle->state);
worldObjects.push_back(particle); worldObjects.push_back(particle);
return particle; return particle;

View file

@ -8,7 +8,7 @@
struct JobNames; struct JobNames;
class ResourceMgr; class ResourceMgr;
struct RagEffectState; struct AttachedEffect;
struct EffectParticle : CRenderObject { struct EffectParticle : CRenderObject {
EffectParticle(ResourceMgr& resources, JobNames& names); EffectParticle(ResourceMgr& resources, JobNames& names);
@ -22,6 +22,9 @@ struct EffectParticle : CRenderObject {
bool UpdateExit(); bool UpdateExit();
bool UpdateWarp(); bool UpdateWarp();
void PrimBottom2(); void PrimBottom2();
void PrimBottom();
void PrimBottomOut();
void PrimBubbleDrop();
bool AdvanceFrame(); bool AdvanceFrame();
void UpdateRecords(); void UpdateRecords();
@ -30,7 +33,7 @@ struct EffectParticle : CRenderObject {
}; };
EffectParticle* CreateEffectParticle( EffectParticle* CreateEffectParticle(
RagEffectState& parent, const RenderObjectState& parentRender, int type, float x, float y, float z, ResourceMgr& resources, JobNames& names, AttachedEffect& master, const RenderObjectState& masterRender, int type, float x, float y, float z, ResourceMgr& resources, JobNames& names,
std::list<CRenderObject*>& worldObjects std::list<CRenderObject*>& worldObjects
); );

View file

@ -1,5 +1,5 @@
#include "Contents/Effects/EffectPrimitiveState.h" #include "Contents/Effects/EffectPrimitiveState.h"
#include "Contents/Effects/RagEffectState.h" #include "Contents/Effects/AttachedEffect.h"
#include "Utility/Math/ScalarConversion.h" #include "Utility/Math/ScalarConversion.h"
// OFFSET: 0x00BC5E00 // OFFSET: 0x00BC5E00
@ -45,7 +45,7 @@ void EffectPrimitiveState::UpdateColor() {
} }
if (this->unknown_015C == 0) { if (this->unknown_015C == 0) {
const int parentRemaining = this->parent->lifetime - this->parent->unknown_0178; const int parentRemaining = this->master->state.lifetime - this->master->state.unknown_0178;
const int remaining = this->lifetime - current; const int remaining = this->lifetime - current;
if (remaining > parentRemaining + 1) { if (remaining > parentRemaining + 1) {

View file

@ -6,6 +6,7 @@
#include "Resource/Sprites/ActionResource.h" #include "Resource/Sprites/ActionResource.h"
#include "Contents/Effects/EffectParticle.h" #include "Contents/Effects/EffectParticle.h"
#include "Contents/Effects/EffectPrimitiveState.h" #include "Contents/Effects/EffectPrimitiveState.h"
#include "Contents/Effects/AttachedEffect.h"
#include "Contents/Effects/RagEffectState.h" #include "Contents/Effects/RagEffectState.h"
#include "Contents/Sound/SoundMgr.h" #include "Contents/Sound/SoundMgr.h"
#include "Resource/World/TerrainResource.h" #include "Resource/World/TerrainResource.h"
@ -49,6 +50,33 @@ namespace {
((matrix[5] * point.y + matrix[2] * point.x) + point.z * matrix[8]) + matrix[11] ((matrix[5] * point.y + matrix[2] * point.x) + point.z * matrix[8]) + matrix[11]
}; };
} }
// INLINED
void FaceCamera(EffectPrimitiveSlot* slots) {
const CView* view = static_cast<CGameMode*>(g_world.ActiveGameMode())->camera;
slots[0].alphaT = static_cast<std::int16_t>(360 - static_cast<int>(view->camera.yaw));
slots[1].alphaT = static_cast<std::int16_t>(360 - static_cast<int>(view->camera.pitch) + 90);
if (slots[1].alphaT >= 360) {
slots[1].alphaT -= 360;
}
}
// INLINED
__forceinline void FollowTrail(EffectPrimitiveSlot& trail, const EffectPrimitiveSlot& previous, int fade) {
trail.fullDisplayAngle = previous.fullDisplayAngle;
trail.distance = previous.distance;
trail.height[8] = previous.height[8];
trail.alphaB = previous.alphaB - fade;
if (trail.alphaB < 0) {
trail.alphaB = 0;
}
trail.vecBNow = previous.vecBNow;
trail.fullDisplayAngle = previous.fullDisplayAngle;
trail.flag1[0] = previous.flag1[0];
}
} }
// OFFSET: 0x00BB8C50 // OFFSET: 0x00BB8C50
@ -136,8 +164,8 @@ bool MoveEffectPrimitive(EffectPrimitiveState& particle, RenderObjectState& rend
} }
if ((particle.flags & 0x400u) != 0) { if ((particle.flags & 0x400u) != 0) {
const float x = particle.parent->offset[0] - render.position[0]; const float x = particle.master->state.offset[0] - render.position[0];
const float z = particle.parent->offset[2] - render.position[2]; const float z = particle.master->state.offset[2] - render.position[2];
const float distance = std::sqrt(z * z + x * x); const float distance = std::sqrt(z * z + x * x);
if (3.0f >= distance) { if (3.0f >= distance) {
@ -188,16 +216,16 @@ bool MoveEffectPrimitive(EffectPrimitiveState& particle, RenderObjectState& rend
} }
if ((particle.flags & 0x10u) != 0) { if ((particle.flags & 0x10u) != 0) {
particle.parent->offset[0] = particle.velocity.x + particle.offset.x; particle.master->state.offset[0] = particle.velocity.x + particle.offset.x;
particle.parent->offset[1] = particle.offset.y + particle.velocity.y; particle.master->state.offset[1] = particle.offset.y + particle.velocity.y;
particle.parent->offset[2] = particle.offset.z + particle.velocity.z; particle.master->state.offset[2] = particle.offset.z + particle.velocity.z;
} }
if ((particle.flags & 4u) != 0 && render.position[1] > context.ground.Height(render.position[0], render.position[2])) { if ((particle.flags & 4u) != 0 && render.position[1] > context.ground.Height(render.position[0], render.position[2])) {
particle.flags &= ~4u; particle.flags &= ~4u;
particle.unknown_01E0 = particle.lifetime - 25; particle.unknown_01E0 = particle.lifetime - 25;
particle.unknown_02BC = 0; particle.unknown_02BC = 0;
particle.parent->unknown_0170 = 1; particle.master->state.unknown_0170 = 1;
} }
const int frame = particle.unknown_01E0; const int frame = particle.unknown_01E0;
@ -257,7 +285,7 @@ bool MoveEffectPrimitive(EffectPrimitiveState& particle, RenderObjectState& rend
// OFFSET: 0x00BAE7D0 // OFFSET: 0x00BAE7D0
// FUNCTION: fn_19769 // FUNCTION: fn_19769
bool UpdateEffectPrimitive15(EffectPrimitiveState& particle, RenderObjectState& render, EffectMotionContext& context, int updateTick) { bool UpdateEffectPrimitive15(EffectPrimitiveState& particle, RenderObjectState& render, EffectMotionContext& context, int updateTick) {
if (!particle.parent) { if (!particle.master) {
return false; return false;
} }
@ -280,39 +308,39 @@ bool ShrinkEffectPrimitiveSlots(EffectPrimitiveState& particle, RenderObjectStat
for (int index = 0; index < 4; ++index) { for (int index = 0; index < 4; ++index) {
EffectPrimitiveSlot& slot = particle.slots[index]; EffectPrimitiveSlot& slot = particle.slots[index];
if (slot.active != 1) { if (slot.life != 1) {
continue; continue;
} }
float height = slot.unknown_0080 - 0.05f; float height = slot.distance - 0.05f;
++slot.frame; ++slot.process;
slot.unknown_0080 = height; slot.distance = height;
if (height <= 0.0f) { if (height <= 0.0f) {
slot.unknown_0080 = 10.0f; slot.distance = 10.0f;
slot.unknown_0002 = 0; slot.alphaB = 0;
height = 10.0f; height = 10.0f;
} }
slot.unknown_0008 = height; slot.maxHeight = height;
slot.unknown_007E = static_cast<std::int16_t>(static_cast<int>(90.0f - height * 9.0f)); slot.riseAngle = static_cast<std::int16_t>(static_cast<int>(90.0f - height * 9.0f));
if (slot.frame >= particle.lifetime - 40) { if (slot.process >= particle.lifetime - 40) {
slot.unknown_0002 -= 3; slot.alphaB -= 3;
if (slot.unknown_0002 < 0) { if (slot.alphaB < 0) {
slot.unknown_0002 = 0; slot.alphaB = 0;
} }
} else if (slot.unknown_0002 < 70) { } else if (slot.alphaB < 70) {
++slot.unknown_0002; ++slot.alphaB;
} }
for (int value = 0; value < 21; ++value) { for (int value = 0; value < 21; ++value) {
if (slot.finished[value] == 0) { if (slot.flag1[value] == 0) {
slot.values[value] = slot.unknown_0008; slot.height[value] = slot.maxHeight;
if (slot.values[value] < 0.0f) { if (slot.height[value] < 0.0f) {
slot.values[value] = 0; slot.height[value] = 0;
} }
} }
} }
@ -331,21 +359,21 @@ bool PulseEffectPrimitiveSlots(EffectPrimitiveState& particle, RenderObjectState
for (int index = 0; index < 4; ++index) { for (int index = 0; index < 4; ++index) {
EffectPrimitiveSlot& slot = particle.slots[index]; EffectPrimitiveSlot& slot = particle.slots[index];
if (slot.active != 1) { if (slot.life != 1) {
continue; continue;
} }
float phase = slot.unknown_0080 + 0.1f; float phase = slot.distance + 0.1f;
const int frame = ++slot.frame; const int frame = ++slot.process;
slot.unknown_0080 = phase; slot.distance = phase;
if (phase >= 13.0f) { if (phase >= 13.0f) {
slot.unknown_0080 = 3.0f; slot.distance = 3.0f;
slot.unknown_0002 = 0; slot.alphaB = 0;
phase = 3.0f; phase = 3.0f;
if (frame > 50) { if (frame > 50) {
slot.active = 0; slot.life = 0;
} }
} }
@ -355,23 +383,23 @@ bool PulseEffectPrimitiveSlots(EffectPrimitiveState& particle, RenderObjectState
if (phase > 5.0f) { if (phase > 5.0f) {
height = 10.0f - phase; height = 10.0f - phase;
if (slot.unknown_0002 > 0) { if (slot.alphaB > 0) {
slot.unknown_0002 -= 2; slot.alphaB -= 2;
} }
} else { } else {
slot.unknown_0002 += 2; slot.alphaB += 2;
height = phase; height = phase;
} }
slot.unknown_0008 = height * 2.0f; slot.maxHeight = height * 2.0f;
slot.unknown_007E = static_cast<std::int16_t>(static_cast<int>(90.0f - phase * 5.0f)); slot.riseAngle = static_cast<std::int16_t>(static_cast<int>(90.0f - phase * 5.0f));
for (int value = 0; value < 21; ++value) { for (int value = 0; value < 21; ++value) {
if (slot.finished[value] == 0) { if (slot.flag1[value] == 0) {
slot.values[value] = slot.unknown_0008; slot.height[value] = slot.maxHeight;
if (slot.values[value] < 0.0f) { if (slot.height[value] < 0.0f) {
slot.values[value] = 0; slot.height[value] = 0;
} }
} }
} }
@ -559,10 +587,10 @@ bool EffectParticle::AdvanceFrame() {
// OFFSET: 0x00BB5E80 // OFFSET: 0x00BB5E80
// FUNCTION: fn_19772 // FUNCTION: fn_19772
bool EffectParticle::UpdateFireWall() { bool EffectParticle::UpdateFireWall() {
const RagEffectState* parent = this->state.parent; const AttachedEffect* master = this->state.master;
this->render.position[0] = std::bit_cast<float>(parent->unknown_013C[3]); this->render.position[0] = master->render.position[0];
this->render.position[1] = std::bit_cast<float>(parent->unknown_013C[4]); this->render.position[1] = master->render.position[1];
this->render.position[2] = std::bit_cast<float>(parent->unknown_013C[5]); this->render.position[2] = master->render.position[2];
for (int index = 0; index < 4; ++index) { for (int index = 0; index < 4; ++index) {
if (this->state.scale == 3.0f && index < 3) { if (this->state.scale == 3.0f && index < 3) {
@ -571,38 +599,38 @@ bool EffectParticle::UpdateFireWall() {
EffectPrimitiveSlot& slot = this->state.slots[index]; EffectPrimitiveSlot& slot = this->state.slots[index];
if (slot.active != 1) { if (slot.life != 1) {
continue; continue;
} }
++slot.frame; ++slot.process;
if (index < 3) { if (index < 3) {
slot.angle = static_cast<std::int16_t>(slot.angle + index + 3); slot.rotStart = static_cast<std::int16_t>(slot.rotStart + index + 3);
} }
if (slot.angle >= 360) { if (slot.rotStart >= 360) {
slot.angle = static_cast<std::int16_t>(slot.angle - 360); slot.rotStart = static_cast<std::int16_t>(slot.rotStart - 360);
} }
const int limit = this->state.lifetime - 40; const int limit = this->state.lifetime - 40;
bool grown = false; bool grown = false;
if (slot.frame < limit) { if (slot.process < limit) {
if (slot.frame < 20) { if (slot.process < 20) {
if (index == 3) { if (index == 3) {
this->state.slots[3].unknown_0002 = static_cast<std::int16_t>(this->state.slots[3].unknown_0002 + 8); this->state.slots[3].alphaB = static_cast<std::int16_t>(this->state.slots[3].alphaB + 8);
if (this->state.slots[3].unknown_0002 > 100) { if (this->state.slots[3].alphaB > 100) {
this->state.slots[3].unknown_0002 = 100; this->state.slots[3].alphaB = 100;
} }
grown = true; grown = true;
} else { } else {
slot.unknown_0002 = static_cast<std::int16_t>(slot.unknown_0002 + 10); slot.alphaB = static_cast<std::int16_t>(slot.alphaB + 10);
if (slot.unknown_0002 > 180) { if (slot.alphaB > 180) {
slot.unknown_0002 = 180; slot.alphaB = 180;
} }
} }
} }
@ -612,16 +640,16 @@ bool EffectParticle::UpdateFireWall() {
} }
} else { } else {
if (index < 3) { if (index < 3) {
slot.unknown_0002 = static_cast<std::int16_t>(slot.unknown_0002 - 5); slot.alphaB = static_cast<std::int16_t>(slot.alphaB - 5);
if (slot.unknown_0002 < 0) { if (slot.alphaB < 0) {
slot.unknown_0002 = 0; slot.alphaB = 0;
} }
} else { } else {
slot.unknown_0002 = static_cast<std::int16_t>(slot.unknown_0002 - 3); slot.alphaB = static_cast<std::int16_t>(slot.alphaB - 3);
if (slot.unknown_0002 < 0) { if (slot.alphaB < 0) {
slot.unknown_0002 = 0; slot.alphaB = 0;
} }
grown = true; grown = true;
@ -630,46 +658,46 @@ bool EffectParticle::UpdateFireWall() {
if (!grown) { if (!grown) {
for (int sample = 0; sample < 21; ++sample) { for (int sample = 0; sample < 21; ++sample) {
if (slot.finished[sample] == 0) { if (slot.flag1[sample] == 0) {
if (slot.frame <= 90) { if (slot.process <= 90) {
slot.values[sample] = slot.unknown_0008 * ClientSine(sample * 9) * ClientSine(slot.frame); slot.height[sample] = slot.maxHeight * ClientSine(sample * 9) * ClientSine(slot.process);
} }
const float maximum = slot.unknown_0008 * ClientSine(sample * 9); const float maximum = slot.maxHeight * ClientSine(sample * 9);
if (slot.values[sample] > maximum) { if (slot.height[sample] > maximum) {
slot.values[sample] = maximum; slot.height[sample] = maximum;
slot.finished[sample] = 1; slot.flag1[sample] = 1;
} }
if (slot.values[sample] < 0.0f) { if (slot.height[sample] < 0.0f) {
slot.values[sample] = 0.0f; slot.height[sample] = 0.0f;
} }
} }
} }
} else { } else {
for (int sample = 0; sample < 21; ++sample) { for (int sample = 0; sample < 21; ++sample) {
if (slot.finished[sample] != 0) { if (slot.flag1[sample] != 0) {
const float base = slot.unknown_0008 * ClientSine(sample * 9) * 0.55f; const float base = slot.maxHeight * ClientSine(sample * 9) * 0.55f;
slot.values[sample] = base; slot.height[sample] = base;
slot.values[sample] = slot.unknown_0008 * ClientSine(sample * 9) * 0.45f * ClientSine(slot.frame % 360) + base; slot.height[sample] = slot.maxHeight * ClientSine(sample * 9) * 0.45f * ClientSine(slot.process % 360) + base;
} else { } else {
float value; float value;
if (slot.frame > 90) { if (slot.process > 90) {
slot.finished[sample] = 1; slot.flag1[sample] = 1;
value = slot.values[sample]; value = slot.height[sample];
} else { } else {
value = slot.unknown_0008 * ClientSine(sample * 9) * ClientSine(slot.frame); value = slot.maxHeight * ClientSine(sample * 9) * ClientSine(slot.process);
slot.values[sample] = value; slot.height[sample] = value;
} }
if (value > slot.unknown_0008 * ClientSine(sample * 9)) { if (value > slot.maxHeight * ClientSine(sample * 9)) {
slot.finished[sample] = 1; slot.flag1[sample] = 1;
} }
if (slot.values[sample] < 0.0f) { if (slot.height[sample] < 0.0f) {
slot.values[sample] = 0.0f; slot.height[sample] = 0.0f;
} }
} }
} }
@ -682,10 +710,10 @@ bool EffectParticle::UpdateFireWall() {
// OFFSET: 0x00BB63C0 // OFFSET: 0x00BB63C0
// FUNCTION: fn_19775 // FUNCTION: fn_19775
bool EffectParticle::UpdateStoneCurse() { bool EffectParticle::UpdateStoneCurse() {
const RagEffectState* parent = this->state.parent; const AttachedEffect* master = this->state.master;
this->render.position[0] = std::bit_cast<float>(parent->unknown_013C[3]); this->render.position[0] = master->render.position[0];
this->render.position[1] = std::bit_cast<float>(parent->unknown_013C[4]); this->render.position[1] = master->render.position[1];
this->render.position[2] = std::bit_cast<float>(parent->unknown_013C[5]); this->render.position[2] = master->render.position[2];
for (int index = 0; index < 4; ++index) { for (int index = 0; index < 4; ++index) {
if (this->state.scale == 3.0f && index < 3) { if (this->state.scale == 3.0f && index < 3) {
@ -694,59 +722,59 @@ bool EffectParticle::UpdateStoneCurse() {
EffectPrimitiveSlot& slot = this->state.slots[index]; EffectPrimitiveSlot& slot = this->state.slots[index];
if (slot.active != 1) { if (slot.life != 1) {
continue; continue;
} }
++slot.frame; ++slot.process;
if (index < 3) { if (index < 3) {
slot.angle = static_cast<std::int16_t>(slot.angle + index + 3); slot.rotStart = static_cast<std::int16_t>(slot.rotStart + index + 3);
} }
if (slot.angle >= 360) { if (slot.rotStart >= 360) {
slot.angle = static_cast<std::int16_t>(slot.angle - 360); slot.rotStart = static_cast<std::int16_t>(slot.rotStart - 360);
} }
if (slot.frame < this->state.lifetime - 40) { if (slot.process < this->state.lifetime - 40) {
if (slot.frame < 20 && slot.unknown_0004 != 1) { if (slot.process < 20 && slot.alphaT != 1) {
if (index == 3) { if (index == 3) {
this->state.slots[3].unknown_0002 = static_cast<std::int16_t>(this->state.slots[3].unknown_0002 + 8); this->state.slots[3].alphaB = static_cast<std::int16_t>(this->state.slots[3].alphaB + 8);
if (this->state.slots[3].unknown_0002 > 60) { if (this->state.slots[3].alphaB > 60) {
this->state.slots[3].unknown_0002 = 60; this->state.slots[3].alphaB = 60;
} }
} else { } else {
slot.unknown_0002 = static_cast<std::int16_t>(slot.unknown_0002 + 10); slot.alphaB = static_cast<std::int16_t>(slot.alphaB + 10);
if (slot.unknown_0002 > 180) { if (slot.alphaB > 180) {
slot.unknown_0002 = 180; slot.alphaB = 180;
} }
} }
} }
} else { } else {
slot.unknown_0002 = static_cast<std::int16_t>(slot.unknown_0002 - (index >= 3 ? 2 : 5)); slot.alphaB = static_cast<std::int16_t>(slot.alphaB - (index >= 3 ? 2 : 5));
if (slot.unknown_0002 < 0) { if (slot.alphaB < 0) {
slot.unknown_0002 = 0; slot.alphaB = 0;
} }
} }
for (int sample = 0; sample < 21; ++sample) { for (int sample = 0; sample < 21; ++sample) {
if (slot.finished[sample] == 0) { if (slot.flag1[sample] == 0) {
if (slot.frame <= 90) { if (slot.process <= 90) {
slot.values[sample] = slot.unknown_0008 * ClientSine(sample * 9) * ClientSine(slot.frame); slot.height[sample] = slot.maxHeight * ClientSine(sample * 9) * ClientSine(slot.process);
} }
const float maximum = slot.unknown_0008 * ClientSine(sample * 9); const float maximum = slot.maxHeight * ClientSine(sample * 9);
if (slot.values[sample] > maximum) { if (slot.height[sample] > maximum) {
slot.values[sample] = maximum; slot.height[sample] = maximum;
slot.finished[sample] = 1; slot.flag1[sample] = 1;
} }
if (slot.values[sample] < 0.0f) { if (slot.height[sample] < 0.0f) {
slot.values[sample] = 0.0f; slot.height[sample] = 0.0f;
} }
} }
} }
@ -758,10 +786,10 @@ bool EffectParticle::UpdateStoneCurse() {
// OFFSET: 0x00BB65C0 // OFFSET: 0x00BB65C0
// FUNCTION: fn_19776 // FUNCTION: fn_19776
void EffectParticle::UpdateBeginSpell() { void EffectParticle::UpdateBeginSpell() {
const RagEffectState* parent = this->state.parent; const AttachedEffect* master = this->state.master;
this->state.origin.x = std::bit_cast<float>(parent->unknown_013C[3]) + this->state.velocity.x; this->state.origin.x = master->render.position[0] + this->state.velocity.x;
this->state.origin.y = std::bit_cast<float>(parent->unknown_013C[4]) + this->state.velocity.y; this->state.origin.y = master->render.position[1] + this->state.velocity.y;
this->state.origin.z = std::bit_cast<float>(parent->unknown_013C[5]) + this->state.velocity.z; this->state.origin.z = master->render.position[2] + this->state.velocity.z;
float base; float base;
@ -815,41 +843,218 @@ void EffectParticle::UpdateBeginSpell() {
// OFFSET: 0x00BBAB10 // OFFSET: 0x00BBAB10
// FUNCTION: fn_19790 // FUNCTION: fn_19790
void EffectParticle::PrimBottom2() { void EffectParticle::PrimBottom2() {
const RagEffectState* parent = this->state.parent; const AttachedEffect* master = this->state.master;
this->render.position[0] = std::bit_cast<float>(parent->unknown_013C[3]); this->render.position[0] = master->render.position[0];
this->render.position[1] = std::bit_cast<float>(parent->unknown_013C[4]); this->render.position[1] = master->render.position[1];
this->render.position[2] = std::bit_cast<float>(parent->unknown_013C[5]); this->render.position[2] = master->render.position[2];
for (int index = 0; index < 4; ++index) { for (int index = 0; index < 4; ++index) {
EffectPrimitiveSlot& slot = this->state.slots[index]; EffectPrimitiveSlot& slot = this->state.slots[index];
if (slot.active != 1) { if (slot.life != 1) {
continue; continue;
} }
++slot.frame; ++slot.process;
if (slot.values[0] == 1.0f) { if (slot.height[0] == 1.0f) {
slot.displayAngle += 4; slot.fullDisplayAngle += 4;
} else { } else {
slot.displayAngle += 2; slot.fullDisplayAngle += 2;
} }
if (slot.displayAngle >= 360) { if (slot.fullDisplayAngle >= 360) {
slot.displayAngle -= 360; slot.fullDisplayAngle -= 360;
} }
slot.unknown_0008 = 20.0f + 10.0f * ClientSine(slot.displayAngle); slot.maxHeight = 20.0f + 10.0f * ClientSine(slot.fullDisplayAngle);
}
}
// OFFSET: 0x00BBABC0
// FUNCTION: fn_19791
void EffectParticle::PrimBottom() {
EffectPrimitiveSlot* slots = this->state.slots;
FaceCamera(slots);
const AttachedEffect* master = this->state.master;
this->render.position[0] = master->render.position[0];
this->render.position[1] = master->render.position[1];
this->render.position[2] = master->render.position[2];
EffectPrimitiveSlot& slot = slots[0];
if (slot.life != 1) {
return;
}
++slot.process;
if (slot.process % 6 == 0) {
for (int index = 3; index > 0; --index) {
EffectPrimitiveSlot& trail = slots[index];
const EffectPrimitiveSlot& previous = slots[index - 1];
if (trail.life > 0) {
trail.fullDisplayAngle = previous.fullDisplayAngle;
trail.distance = previous.distance + 0.5f;
trail.height[8] = previous.height[8];
trail.alphaB = previous.alphaB - 50;
if (trail.alphaB < 0) {
trail.alphaB = 0;
}
trail.vecBNow = previous.vecBNow;
trail.fullDisplayAngle = previous.fullDisplayAngle;
trail.flag1[0] = previous.flag1[0];
trail.flag1[2] = 2;
trail.height[7] = 0;
}
}
}
slot.height[8] += 5.0f;
if (slot.height[8] >= 360.0f) {
slot.height[8] -= 360.0f;
}
++slot.rotStart;
if (slot.rotStart >= 360) {
slot.rotStart -= 360;
}
slot.vecBNow.x = this->render.position[0];
slot.vecBNow.z = this->render.position[2];
slot.vecBPre.y = this->render.position[1] - 6.0f;
slot.vecBNow.y = slot.vecBPre.y + 3.0f * ClientSine(slot.rotStart);
if (slot.flag1[2] == 7) {
slot.fullDisplayAngle += 10;
if (slot.fullDisplayAngle >= 360) {
slot.fullDisplayAngle -= 360;
}
}
}
// OFFSET: 0x00BBAF30
// FUNCTION: fn_19792
void EffectParticle::PrimBottomOut() {
FaceCamera(this->state.slots);
const AttachedEffect* master = this->state.master;
this->render.position[0] = master->render.position[0];
this->render.position[1] = master->render.position[1];
this->render.position[2] = master->render.position[2];
for (int index = 0; index < 4; ++index) {
EffectPrimitiveSlot& slot = this->state.slots[index];
if (slot.life != 1) {
continue;
}
++slot.process;
if (slot.process <= 10) {
slot.alphaB += 25;
} else {
slot.alphaB -= 5;
if (slot.alphaB <= 0) {
slot.alphaB = 0;
slot.process = 0;
slot.vecBNow.y = slot.flag1[1] == 0 ? this->render.position[1] : this->render.position[1] - 5.0f;
}
slot.vecBNow.y -= 0.2f;
}
slot.height[8] += 5.0f;
if (slot.height[8] >= 360.0f) {
slot.height[8] -= 360.0f;
}
if (slot.flag1[1] == 1) {
slot.fullDisplayAngle += 5;
if (slot.fullDisplayAngle >= 360) {
slot.fullDisplayAngle -= 360;
}
}
}
}
// OFFSET: 0x00BBB120
// FUNCTION: fn_19793
void EffectParticle::PrimBubbleDrop() {
EffectPrimitiveSlot* slots = this->state.slots;
FaceCamera(slots);
const AttachedEffect* master = this->state.master;
this->render.position[0] = master->render.position[0];
this->render.position[1] = master->render.position[1];
this->render.position[2] = master->render.position[2];
EffectPrimitiveSlot& slot = slots[0];
if (slot.life != 1) {
return;
}
++slot.process;
if (slot.process <= 0) {
return;
}
if (slot.process > 3) {
FollowTrail(slots[3], slots[2], 25);
}
if (slot.process > 2) {
FollowTrail(slots[2], slots[1], 25);
}
if (slot.process > 1) {
FollowTrail(slots[1], slot, 100);
}
if (slot.process <= 10) {
slot.alphaB += 20;
}
if (slot.process < slot.flag1[4]) {
slot.fullDisplayAngle -= 15;
if (slot.fullDisplayAngle < 0) {
slot.fullDisplayAngle += 360;
}
slot.vecBPre.y += slot.height[1];
slot.vecBNow = {
this->render.position[0] + slot.vecBPre.x, this->render.position[1] + slot.vecBPre.y, this->render.position[2] + slot.vecBPre.z
};
} else {
slot.alphaB -= 10;
if (slot.alphaB < 0) {
slot.alphaB = 0;
}
} }
} }
// OFFSET: 0x00BB6F30 // OFFSET: 0x00BB6F30
// FUNCTION: fn_19779 // FUNCTION: fn_19779
bool EffectParticle::UpdateHit6() { bool EffectParticle::UpdateHit6() {
const RagEffectState* parent = this->state.parent; const AttachedEffect* master = this->state.master;
this->state.origin.x = std::bit_cast<float>(parent->unknown_013C[3]) + this->state.velocity.x; this->state.origin.x = master->render.position[0] + this->state.velocity.x;
this->state.origin.y = std::bit_cast<float>(parent->unknown_013C[4]) + this->state.velocity.y; this->state.origin.y = master->render.position[1] + this->state.velocity.y;
this->state.origin.z = std::bit_cast<float>(parent->unknown_013C[5]) + this->state.velocity.z; this->state.origin.z = master->render.position[2] + this->state.velocity.z;
const int frame = this->state.unknown_01E0; const int frame = this->state.unknown_01E0;
@ -930,7 +1135,7 @@ bool EffectParticle::UpdateHit6() {
this->render.position[2] = this->state.offset.z + this->state.origin.z; this->render.position[2] = this->state.offset.z + this->state.origin.z;
if ((this->state.flags & 0x100u) != 0) { if ((this->state.flags & 0x100u) != 0) {
if (3.0f >= this->HorizontalDistance(this->state.parent->offset[0], this->state.parent->offset[2])) { if (3.0f >= this->HorizontalDistance(this->state.master->state.offset[0], this->state.master->state.offset[2])) {
return false; return false;
} }
} }
@ -948,10 +1153,10 @@ bool EffectParticle::UpdateHit6() {
// FUNCTION: fn_19780 // FUNCTION: fn_19780
bool EffectParticle::UpdateCoin() { bool EffectParticle::UpdateCoin() {
CGameMode* mode = static_cast<CGameMode*>(g_world.ActiveGameMode()); CGameMode* mode = static_cast<CGameMode*>(g_world.ActiveGameMode());
const RagEffectState* parent = this->state.parent; const AttachedEffect* master = this->state.master;
this->state.origin.z = std::bit_cast<float>(parent->unknown_013C[5]) + this->state.velocity.z; this->state.origin.z = master->render.position[2] + this->state.velocity.z;
this->state.origin.y = std::bit_cast<float>(parent->unknown_013C[4]) + this->state.velocity.y; this->state.origin.y = master->render.position[1] + this->state.velocity.y;
this->state.origin.x = std::bit_cast<float>(parent->unknown_013C[3]) + this->state.velocity.x; this->state.origin.x = master->render.position[0] + this->state.velocity.x;
this->state.unknown_02EC = this->state.unknown_02F0 + this->state.unknown_02EC; this->state.unknown_02EC = this->state.unknown_02F0 + this->state.unknown_02EC;
const float speed = this->state.unknown_02C0 + this->state.unknown_02BC; const float speed = this->state.unknown_02C0 + this->state.unknown_02BC;
@ -1082,8 +1287,8 @@ bool EffectParticle::UpdateDetoxication() {
// OFFSET: 0x00BB7EC0 // OFFSET: 0x00BB7EC0
// FUNCTION: fn_19782 // FUNCTION: fn_19782
bool EffectParticle::UpdateExit() { bool EffectParticle::UpdateExit() {
const RagEffectState* parent = this->state.parent; const AttachedEffect* master = this->state.master;
this->state.origin = {std::bit_cast<float>(parent->unknown_013C[3]), std::bit_cast<float>(parent->unknown_013C[4]), std::bit_cast<float>(parent->unknown_013C[5])}; this->state.origin = {master->render.position[0], master->render.position[1], master->render.position[2]};
const float lateral = this->state.unknown_033C + this->state.unknown_0330; const float lateral = this->state.unknown_033C + this->state.unknown_0330;
float vertical = this->state.unknown_02EC + this->state.unknown_02F0; float vertical = this->state.unknown_02EC + this->state.unknown_02F0;
@ -1146,10 +1351,10 @@ bool EffectParticle::UpdateExit() {
// OFFSET: 0x00BB8180 // OFFSET: 0x00BB8180
// FUNCTION: fn_19783 // FUNCTION: fn_19783
bool EffectParticle::UpdateWarp() { bool EffectParticle::UpdateWarp() {
const RagEffectState* parent = this->state.parent; const AttachedEffect* master = this->state.master;
this->state.origin = { this->state.origin = {
std::bit_cast<float>(parent->unknown_013C[3]) + this->state.velocity.x, std::bit_cast<float>(parent->unknown_013C[4]) + this->state.velocity.y, master->render.position[0] + this->state.velocity.x, master->render.position[1] + this->state.velocity.y,
std::bit_cast<float>(parent->unknown_013C[5]) + this->state.velocity.z master->render.position[2] + this->state.velocity.z
}; };
const int angle = static_cast<int>(this->state.unknown_0314); const int angle = static_cast<int>(this->state.unknown_0314);

View file

@ -1,4 +1,5 @@
#include "Contents/Effects/EffectPrimitiveRendering.h" #include "Contents/Effects/EffectPrimitiveRendering.h"
#include "Contents/Effects/AttachedEffect.h"
#include "Contents/Effects/EffectPrimitiveState.h" #include "Contents/Effects/EffectPrimitiveState.h"
#include "Contents/Effects/EffectVisibility.h" #include "Contents/Effects/EffectVisibility.h"
#include "Contents/Actors/RenderObjectState.h" #include "Contents/Actors/RenderObjectState.h"
@ -86,7 +87,7 @@ void DrawEffectPrimitive15(EffectPrimitiveState& particle, const RenderObjectSta
void RenderEffectPrimitive15( void RenderEffectPrimitive15(
EffectPrimitiveState& particle, const RenderObjectState& render, EffectPrimitiveRenderContext& context, const EffectVisibilityContext& visibility EffectPrimitiveState& particle, const RenderObjectState& render, EffectPrimitiveRenderContext& context, const EffectVisibilityContext& visibility
) { ) {
if (particle.parent && PartyEffectVisible(*particle.parent, visibility)) { if (particle.master && PartyEffectVisible(particle.master->state, visibility)) {
DrawEffectPrimitive15(particle, render, context); DrawEffectPrimitive15(particle, render, context);
} }
} }

View file

@ -1,5 +1,5 @@
#include "Contents/Effects/EffectPrimitiveState.h" #include "Contents/Effects/EffectPrimitiveState.h"
#include "Contents/Effects/RagEffectState.h" #include "Contents/Effects/AttachedEffect.h"
#include "Contents/Actors/RenderObjectState.h" #include "Contents/Actors/RenderObjectState.h"
#include "Utility/Math/AffineMatrix.h" #include "Utility/Math/AffineMatrix.h"
@ -10,7 +10,7 @@
// FUNCTION: fn_19762 // FUNCTION: fn_19762
EffectPrimitiveSlot::EffectPrimitiveSlot() { EffectPrimitiveSlot::EffectPrimitiveSlot() {
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
this->unknown_0084[i] = {0.0f, 0.0f, 0.0f}; this->vectors[i] = {0.0f, 0.0f, 0.0f};
} }
} }
@ -29,7 +29,7 @@ EffectPrimitiveState::EffectPrimitiveState(RenderObjectState& render) {
this->unknown_0158 = 0; this->unknown_0158 = 0;
this->unknown_015C = 0; this->unknown_015C = 0;
this->unknown_0138 = 0; this->unknown_0138 = 0;
this->parent = nullptr; this->master = nullptr;
this->unknown_01D4 = 0; this->unknown_01D4 = 0;
this->unknown_01D8 = 0; this->unknown_01D8 = 0;
this->unknown_01E0 = 0; this->unknown_01E0 = 0;
@ -146,8 +146,8 @@ EffectPrimitiveState::EffectPrimitiveState(RenderObjectState& render) {
// INLINED // INLINED
EffectPrimitiveState::~EffectPrimitiveState() { EffectPrimitiveState::~EffectPrimitiveState() {
if (this->parent) { if (this->master) {
auto& related = this->parent->related; auto& related = this->master->state.related;
for (auto entry = related.begin(); entry != related.end(); ++entry) { for (auto entry = related.begin(); entry != related.end(); ++entry) {
if (*entry == this) { if (*entry == this) {
@ -165,16 +165,16 @@ EffectPrimitiveState::~EffectPrimitiveState() {
// OFFSET: 0x00BAE460 // OFFSET: 0x00BAE460
// FUNCTION: fn_19765 // FUNCTION: fn_19765
void EffectPrimitiveState::GetColorOverride(unsigned char* red, unsigned char* green, unsigned char* blue) { void EffectPrimitiveState::GetColorOverride(unsigned char* red, unsigned char* green, unsigned char* blue) {
if (this->parent->unknown_11C88[0] != -1) { if (this->master->state.unknown_11C88[0] != -1) {
*red = static_cast<unsigned char>(this->parent->unknown_11C88[0]); *red = static_cast<unsigned char>(this->master->state.unknown_11C88[0]);
} }
if (this->parent->unknown_11C88[1] != -1) { if (this->master->state.unknown_11C88[1] != -1) {
*green = static_cast<unsigned char>(this->parent->unknown_11C88[1]); *green = static_cast<unsigned char>(this->master->state.unknown_11C88[1]);
} }
if (this->parent->unknown_11C88[2] != -1) { if (this->master->state.unknown_11C88[2] != -1) {
*blue = static_cast<unsigned char>(this->parent->unknown_11C88[2]); *blue = static_cast<unsigned char>(this->master->state.unknown_11C88[2]);
} }
} }
@ -198,15 +198,15 @@ void __stdcall CalculateVectorAngle(float x, float y, std::int16_t* angle) {
// OFFSET: 0x00BAE5D0 // OFFSET: 0x00BAE5D0
// FUNCTION: fn_19767 // FUNCTION: fn_19767
void EffectPrimitiveState::Initialize( void EffectPrimitiveState::Initialize(
RenderObjectState& render, RagEffectState& parent, const RenderObjectState& parentRender, int type, float x, float y, float z RenderObjectState& render, AttachedEffect& master, const RenderObjectState& masterRender, int type, float x, float y, float z
) { ) {
const float first = x + this->offset.x; const float first = x + this->offset.x;
this->parent = &parent; this->master = &master;
this->type = type; this->type = type;
this->offset.x = first; this->offset.x = first;
this->offset.y = y + this->offset.y; this->offset.y = y + this->offset.y;
this->offset.z = z + this->offset.z; this->offset.z = z + this->offset.z;
this->origin = {parentRender.position[0], parentRender.position[1], parentRender.position[2]}; this->origin = {masterRender.position[0], masterRender.position[1], masterRender.position[2]};
render.position[0] = this->origin.x; render.position[0] = this->origin.x;
render.position[1] = this->origin.y; render.position[1] = this->origin.y;
render.position[2] = this->origin.z; render.position[2] = this->origin.z;

View file

@ -8,7 +8,7 @@
#include <vector> #include <vector>
#include "Contents/Actors/RenderObjectState.h" #include "Contents/Actors/RenderObjectState.h"
struct RagEffectState; struct AttachedEffect;
class CTexture; class CTexture;
struct EffectVector3 { struct EffectVector3 {
@ -33,34 +33,42 @@ struct EffectPrimitiveRecord {
struct EffectPrimitiveSlot { struct EffectPrimitiveSlot {
EffectPrimitiveSlot(); EffectPrimitiveSlot();
/* 0000 */ unsigned char active; /* 0000 */ unsigned char life;
/* 0001 */ unsigned char unknown_0001; /* 0001 */ unsigned char unknown_0001;
/* 0002 */ std::int16_t unknown_0002; /* 0002 */ std::int16_t alphaB;
/* 0004 */ std::int16_t unknown_0004; /* 0004 */ std::int16_t alphaT;
/* 0006 */ std::int16_t displayAngle; /* 0006 */ std::int16_t fullDisplayAngle;
/* 0008 */ float unknown_0008; /* 0008 */ float maxHeight;
/* 000C */ int frame; /* 000C */ int process;
/* 0010 */ std::int16_t angle; /* 0010 */ std::int16_t rotStart;
/* 0012 */ unsigned char unknown_0012[2]; /* 0012 */ unsigned char unknown_0012[2];
/* 0014 */ float values[21]; /* 0014 */ float height[21];
/* 0068 */ unsigned char finished[21]; /* 0068 */ unsigned char flag1[21];
/* 007D */ unsigned char unknown_007D; /* 007D */ unsigned char unknown_007D;
/* 007E */ std::int16_t unknown_007E; /* 007E */ std::int16_t riseAngle;
/* 0080 */ float unknown_0080; /* 0080 */ float distance;
/* 0084 */ EffectVector3 unknown_0084[4]; union {
/* 0084 */ EffectVector3 vectors[4];
struct {
/* 0084 */ EffectVector3 vecBNow;
/* 0090 */ EffectVector3 vecTNow;
/* 009C */ EffectVector3 vecBPre;
/* 00A8 */ EffectVector3 vecTPre;
};
};
}; };
struct EffectPrimitiveState { struct EffectPrimitiveState {
explicit EffectPrimitiveState(RenderObjectState& render); explicit EffectPrimitiveState(RenderObjectState& render);
~EffectPrimitiveState(); ~EffectPrimitiveState();
void Initialize(RenderObjectState& render, RagEffectState& parent, const RenderObjectState& parentRender, int type, float x, float y, float z); void Initialize(RenderObjectState& render, AttachedEffect& master, const RenderObjectState& masterRender, int type, float x, float y, float z);
void UpdateColor(); void UpdateColor();
void InitializeRecords(const RenderObjectState& render); void InitializeRecords(const RenderObjectState& render);
void GetColorOverride(unsigned char* red, unsigned char* green, unsigned char* blue); void GetColorOverride(unsigned char* red, unsigned char* green, unsigned char* blue);
/* 0138 */ std::uint32_t unknown_0138; /* 0138 */ std::uint32_t unknown_0138;
/* 013C */ std::uint32_t unknown_013C; /* 013C */ std::uint32_t unknown_013C;
/* 0140 */ RagEffectState* parent; /* 0140 */ AttachedEffect* master;
/* 0144 */ CTexture** textures; /* 0144 */ CTexture** textures;
/* 0148 */ std::uint32_t unknown_0148; /* 0148 */ std::uint32_t unknown_0148;
/* 014C */ EffectVector3 unknown_014C; /* 014C */ EffectVector3 unknown_014C;
@ -185,9 +193,9 @@ static_assert(offsetof(EffectVector3, z) == 0x8);
static_assert(offsetof(EffectPrimitiveRecord, corners) == 0xC); static_assert(offsetof(EffectPrimitiveRecord, corners) == 0xC);
static_assert(offsetof(EffectPrimitiveRecord, matrix) == 0x48); static_assert(offsetof(EffectPrimitiveRecord, matrix) == 0x48);
static_assert(offsetof(EffectPrimitiveRecord, color) == 0x88); static_assert(offsetof(EffectPrimitiveRecord, color) == 0x88);
static_assert(offsetof(EffectPrimitiveSlot, values) == 0x14); static_assert(offsetof(EffectPrimitiveSlot, height) == 0x14);
static_assert(offsetof(EffectPrimitiveSlot, finished) == 0x68); static_assert(offsetof(EffectPrimitiveSlot, flag1) == 0x68);
static_assert(offsetof(EffectPrimitiveSlot, unknown_0084) == 0x84); static_assert(offsetof(EffectPrimitiveSlot, vecBNow) == 0x84);
static_assert(sizeof(EffectPrimitiveState) == 0xF8E0); static_assert(sizeof(EffectPrimitiveState) == 0xF8E0);
#endif #endif

View file

@ -1,7 +1,7 @@
#include "Contents/Effects/AttachedEffect.h"
#include "Contents/Effects/EffectActor.h" #include "Contents/Effects/EffectActor.h"
#include "Contents/Effects/EffectParticle.h" #include "Contents/Effects/EffectParticle.h"
#include "Contents/Effects/PartyEffect.h" #include "Contents/Effects/PartyEffect.h"
#include "Contents/Effects/RagEffectState.h"
#include "Contents/Ground/TerrainHeight.h" #include "Contents/Ground/TerrainHeight.h"
#include "Render/Textures/TextureCache.h" #include "Render/Textures/TextureCache.h"
@ -10,9 +10,12 @@
// OFFSET: 0x00C2CF20 // OFFSET: 0x00C2CF20
// FUNCTION: fn_20088 // FUNCTION: fn_20088
bool UpdatePartyEffect( bool UpdatePartyEffect(
RagEffectState& effect, RenderObjectState& render, ResourceMgr& resources, JobNames& names, std::list<CRenderObject*>& worldObjects, TextureCache& textures, AttachedEffect& self, ResourceMgr& resources, JobNames& names, std::list<CRenderObject*>& worldObjects, TextureCache& textures, const TerrainHeight& ground,
const TerrainHeight& ground, int updateTick int updateTick
) { ) {
RagEffectState& effect = self.state;
RenderObjectState& render = self.render;
if (!effect.parent) { if (!effect.parent) {
return false; return false;
} }
@ -36,7 +39,7 @@ bool UpdatePartyEffect(
} }
if (effect.unknown_0178 == 0) { if (effect.unknown_0178 == 0) {
EffectParticle* particle = CreateEffectParticle(effect, render, 15, 0, 0, 0, resources, names, worldObjects); EffectParticle* particle = CreateEffectParticle(self, render, 15, 0, 0, 0, resources, names, worldObjects);
effect.unknown_11C84 = &particle->state; effect.unknown_11C84 = &particle->state;
EffectPrimitiveState& state = *effect.unknown_11C84; EffectPrimitiveState& state = *effect.unknown_11C84;
state.lifetime = 9999; state.lifetime = 9999;

View file

@ -3,8 +3,7 @@
#include <list> #include <list>
struct RagEffectState; struct AttachedEffect;
struct RenderObjectState;
struct EffectParticle; struct EffectParticle;
struct JobNames; struct JobNames;
struct TerrainHeight; struct TerrainHeight;
@ -13,8 +12,8 @@ class TextureCache;
class CRenderObject; class CRenderObject;
bool UpdatePartyEffect( bool UpdatePartyEffect(
RagEffectState& effect, RenderObjectState& render, ResourceMgr& resources, JobNames& names, std::list<CRenderObject*>& worldObjects, TextureCache& textures, AttachedEffect& self, ResourceMgr& resources, JobNames& names, std::list<CRenderObject*>& worldObjects, TextureCache& textures, const TerrainHeight& ground,
const TerrainHeight& ground, int updateTick int updateTick
); );
#endif #endif

View file

@ -20,6 +20,8 @@ class GroundGeometry;
struct NaviCell { struct NaviCell {
/* 0000 */ int x; /* 0000 */ int x;
/* 0004 */ int y; /* 0004 */ int y;
int GetY() const;
}; };
struct NaviLink { struct NaviLink {
@ -34,6 +36,12 @@ struct NaviDataEntry {
}; };
struct NaviRouteLine { struct NaviRouteLine {
// OFFSET: 0x00B92F10
// FUNCTION: fn_19558
~NaviRouteLine() {
this->text.clear();
}
/* 0000 */ int kind; /* 0000 */ int kind;
/* 0004 */ int x; /* 0004 */ int x;
/* 0008 */ int y; /* 0008 */ int y;
@ -79,6 +87,9 @@ public:
virtual const char* GetTitle() const; virtual const char* GetTitle() const;
virtual NaviCell* GetCell(NaviCell* out) const; virtual NaviCell* GetCell(NaviCell* out) const;
virtual CNaviInfo* GetInfo() const; virtual CNaviInfo* GetInfo() const;
bool HasId(const unsigned int& id) const;
bool IsNamed(const char* name) const;
}; };
// VTABLE: vt_01778 // VTABLE: vt_01778
@ -105,6 +116,17 @@ public:
CNaviInfo* GetInfo() const override; CNaviInfo* GetInfo() const override;
}; };
#pragma pack(push, 1)
struct NaviObjectRecord {
/* 0000 */ int type;
/* 0004 */ int index;
/* 0008 */ int cellX;
/* 000C */ int cellY;
/* 0010 */ std::int16_t nameLength;
/* 0012 */ char name[1];
};
#pragma pack(pop)
class CNaviInfo { class CNaviInfo {
public: public:
~CNaviInfo(); ~CNaviInfo();
@ -118,10 +140,12 @@ public:
std::vector<CNavi_Link*>::iterator LinksEnd(); std::vector<CNavi_Link*>::iterator LinksEnd();
int GetCellY() const; int GetCellY() const;
int GetIndex() const; int GetIndex() const;
int GetCellX() const;
CNavi_Link* FindLink(unsigned int key, bool byId); CNavi_Link* FindLink(unsigned int key, bool byId);
CNavi_Object* FindNpc(unsigned int key, bool byId); CNavi_Object* FindNpc(unsigned int key, bool byId);
CNavi_Object* FindNpcAt(int cellX, int cellY); CNavi_Object* FindNpcAt(int cellX, int cellY);
std::vector<CNavi_Object*>* Npcs(); std::vector<CNavi_Object*>* Npcs();
CNavi_Link* FindMatchingLink(CNavi_Link link);
/* 0000 */ int index; /* 0000 */ int index;
/* 0004 */ int type; /* 0004 */ int type;
@ -137,12 +161,22 @@ public:
struct NavigationTarget { struct NavigationTarget {
NavigationTarget() : type(-1), object(nullptr), x(-1), y(-1) {} NavigationTarget() : type(-1), object(nullptr), x(-1), y(-1) {}
// INLINED
NavigationTarget(int type, void* object, int x, int y, std::string name) {
this->type = type;
this->object = object;
this->x = x;
this->y = y;
this->name = name;
}
~NavigationTarget(); ~NavigationTarget();
/* 0000 */ int type; /* 0000 */ int type;
/* 0004 */ void* object; /* 0004 */ void* object;
/* 0008 */ int x; /* 0008 */ int x = 0;
/* 000C */ int y; /* 000C */ int y = 0;
/* 0010 */ std::string name; /* 0010 */ std::string name;
}; };
@ -155,6 +189,8 @@ public:
CNaviInfo* GetInfo(); CNaviInfo* GetInfo();
NaviCell* GetCell(NaviCell* out); NaviCell* GetCell(NaviCell* out);
void Clear(); void Clear();
void ClearAll();
CNavi_Link* FindLink(unsigned int key, bool byId);
int FindRoute(std::vector<CNavi_Link*>* route, bool direct, bool allowGates, bool includeObjects, bool allowWarps, int side); int FindRoute(std::vector<CNavi_Link*>* route, bool direct, bool allowGates, bool includeObjects, bool allowWarps, int side);
void ResetLinkDepths(); void ResetLinkDepths();
@ -190,6 +226,9 @@ public:
std::string DestinationTitle(); std::string DestinationTitle();
float FitToSquare(int* offsetX, int* offsetY, int width, int height, float size) const; float FitToSquare(int* offsetX, int* offsetY, int width, int height, float size) const;
void LoadSettings(); void LoadSettings();
bool BuildRoute(int startX, int startY, int destX, int destY, bool collectCells);
NaviCell* MapCellToView(NaviCell* out, const char* mapName, int cellX, int cellY, float size);
std::string RouteText();
void Reset(bool clearAll); void Reset(bool clearAll);
void ClearRouteNodes(); void ClearRouteNodes();
void ClearNodePaths(); void ClearNodePaths();
@ -197,6 +236,17 @@ public:
bool UpdateGround(); bool UpdateGround();
bool PlayerCell(int* column, int* row); bool PlayerCell(int* column, int* row);
bool LoadStates(std::vector<char> packet); bool LoadStates(std::vector<char> packet);
bool AddState(const std::vector<std::string>& tokens);
bool ParseFlags(const char* text, bool* first, bool* second, bool* third);
NaviCell ParseCell(const char* text);
std::vector<char> WriteTarget(NavigationTarget target);
std::vector<char> WriteObject(CNavi_Object* object);
void ClearSavedTarget();
void ClearCurrentObject();
void ClearStates();
void ClearNodes();
int FindNodePath(unsigned int index);
void SelectNodePath(std::string name);
/* 0000 */ CNaviData data; /* 0000 */ CNaviData data;
/* 004C */ CPathFinder pathFinder; /* 004C */ CPathFinder pathFinder;
@ -254,6 +304,7 @@ static_assert(offsetof(CNaviInfo, visited) == 0x40);
static_assert(offsetof(NaviDataEntry, cost) == 0x08); static_assert(offsetof(NaviDataEntry, cost) == 0x08);
static_assert(offsetof(CNavi_Link, linkedLinks) == 0x8C); static_assert(offsetof(CNavi_Link, linkedLinks) == 0x8C);
static_assert(sizeof(NaviCell) == 0x08); static_assert(sizeof(NaviCell) == 0x08);
static_assert(offsetof(NaviObjectRecord, name) == 0x12);
static_assert(sizeof(NaviDataEntry) == 0x0C); static_assert(sizeof(NaviDataEntry) == 0x0C);
static_assert(sizeof(NaviRouteLine) == 0x2C); static_assert(sizeof(NaviRouteLine) == 0x2C);
static_assert(offsetof(NaviRouteLine, text) == 0x0C); static_assert(offsetof(NaviRouteLine, text) == 0x0C);

View file

@ -3,6 +3,8 @@
#include "Client/Mode/CGameMode.h" #include "Client/Mode/CGameMode.h"
#include "Contents/World/WorldScene.h" #include "Contents/World/WorldScene.h"
#include <cstring>
// OFFSET: 0x00B8DF10 // OFFSET: 0x00B8DF10
// FUNCTION: fn_19461 // FUNCTION: fn_19461
std::uint16_t CNavi_Object::GetFlag() const { std::uint16_t CNavi_Object::GetFlag() const {
@ -180,3 +182,18 @@ CNavi_Object* CNaviInfo::FindNpcAt(int cellX, int cellY) {
std::vector<CNavi_Object*>* CNaviInfo::Npcs() { std::vector<CNavi_Object*>* CNaviInfo::Npcs() {
return &this->npcs; return &this->npcs;
} }
// OFFSET: 0x00B8E010
// FUNCTION: fn_19465
CNavi_Link* CNaviInfo::FindMatchingLink(CNavi_Link link) {
const CNavi_Link target = link;
for (CNavi_Link* candidate : this->links) {
if (candidate->cellX == target.cellX && candidate->cellY == target.cellY && candidate->unknown_0080 == target.unknown_0080 &&
candidate->unknown_0084 == target.unknown_0084 && candidate->info != nullptr &&
std::strcmp(candidate->info->name.c_str(), target.info->name.c_str()) == 0 && candidate->linkedInfo != nullptr &&
std::strcmp(candidate->linkedInfo->name.c_str(), target.linkedInfo->name.c_str()) == 0) {
return candidate;
}
}
return nullptr;
}

View file

@ -1,6 +1,12 @@
#include "Contents/NavigationManager.h" #include "Contents/NavigationManager.h"
#include "Contents/MessageTable.h"
#include "Utility/Text/FormatString.h"
#include "Client/Registry.h" #include "Client/Registry.h"
#include "Client/Session.h"
#include "Resource/ResourceMgr.h"
#include "UI/Window/UIWindow.h"
#include "UI/Window/UIWindowMgr.h"
#include "Utility/RegistryKey.h" #include "Utility/RegistryKey.h"
#include <cstdio> #include <cstdio>
@ -17,6 +23,170 @@ CNaviInfo* CNavigationManager::FindObject(const char* name) const {
return nullptr; return nullptr;
} }
// OFFSET: 0x00B88840
// FUNCTION: fn_19377
CNavi_Link* CNaviData::FindLink(unsigned int key, bool byId) {
if (byId) {
for (CNavi_Link* object : this->objects) {
if (object->HasId(key)) {
return object;
}
}
return nullptr;
}
if (this->objects.empty() || key >= this->objects.size()) {
return nullptr;
}
return this->objects[key];
}
// OFFSET: 0x00B88910
// FUNCTION: fn_19379
int NaviCell::GetY() const {
return this->y;
}
// OFFSET: 0x00B88930
// FUNCTION: fn_19381
void CNaviData::ClearAll() {
this->Clear();
for (CNavi_Link*& object : this->objects) {
if (object != nullptr) {
delete object;
}
object = nullptr;
}
this->objects.clear();
for (CNaviInfo*& info : this->destinations) {
if (info != nullptr) {
delete info;
}
info = nullptr;
}
this->destinations.clear();
}
// OFFSET: 0x00B8CF70
// FUNCTION: fn_19432
bool CNavi_Object::HasId(const unsigned int& id) const {
return static_cast<unsigned int>(this->id) == id;
}
// OFFSET: 0x00B8D0D0
// FUNCTION: fn_19433
bool CNavi_Object::IsNamed(const char* name) const {
if (name == nullptr) {
return false;
}
return this->name == std::string(name);
}
// OFFSET: 0x00B8E8E0
// FUNCTION: fn_19496
int CNaviInfo::GetCellX() const {
return this->cellX;
}
// OFFSET: 0x00B97750
// FUNCTION: fn_19600
bool CNavigationManager::BuildRoute(int startX, int startY, int destX, int destY, bool collectCells) {
this->movementRoute.points.clear();
const bool found = this->pathFinder.FindPath(0, startX, startY, 8, 8, destX, destY, 1, this->movementRoute, 15);
this->pathFinder.Reset();
if (!collectCells) {
return found;
}
this->cells.clear();
if (!found) {
return found;
}
CNaviInfo* info = this->FindObject(g_session.m_navigationPlayerMap);
if (info == nullptr) {
const std::string resolved = ResourceMgr::Instance().aliases.ResolveMapName(g_session.m_navigationPlayerMap);
info = this->FindObject(resolved.c_str());
if (info == nullptr) {
return found;
}
}
const int height = info->GetCellY();
const int width = info->GetCellX();
int offsetX;
int offsetY;
const float scale = this->FitToSquare(&offsetX, &offsetY, width, height, 126.0f);
for (const MovementRoutePoint& point : this->movementRoute.points) {
NaviCell cell;
cell.x = static_cast<int>(point.x * scale + offsetX);
cell.y = static_cast<int>(126.0f - (point.y * scale + offsetY));
bool exists = false;
for (const NaviCell& other : this->cells) {
if (other.x == cell.x && other.y == cell.y) {
exists = true;
break;
}
}
if (!exists) {
this->cells.push_back(cell);
}
}
if (g_windowMgr.window_4F174 != nullptr) {
g_windowMgr.window_4F174->Invalidate();
}
return found;
}
// OFFSET: 0x00B97E90
// FUNCTION: fn_19603
NaviCell* CNavigationManager::MapCellToView(NaviCell* out, const char* mapName, int cellX, int cellY, float size) {
CNaviInfo* info = this->FindObject(mapName);
if (info == nullptr) {
const std::string resolved = ResourceMgr::Instance().aliases.ResolveMapName(mapName);
info = this->FindObject(resolved.c_str());
}
out->x = -1;
out->y = -1;
if (info != nullptr) {
const int height = info->GetCellY();
const int width = info->GetCellX();
int offsetX;
int offsetY;
const float scale = this->FitToSquare(&offsetX, &offsetY, width, height, size);
out->x = static_cast<int>(cellX * scale + offsetX);
out->y = static_cast<int>(static_cast<int>(size) - (cellY * scale + offsetY));
}
return out;
}
// OFFSET: 0x00B98630
// FUNCTION: fn_19608
std::string CNavigationManager::RouteText() {
char buffer[0x32];
std::memset(buffer, 0, sizeof(buffer));
CNaviInfo* info = this->data.GetInfo();
if (info != nullptr) {
NaviCell cell;
if (this->data.GetCell(&cell)->x > 0) {
NaviCell cellY;
NaviCell cellX;
const int y = this->data.GetCell(&cellY)->y;
const int x = this->data.GetCell(&cellX)->x;
StringFormat(buffer, sizeof(buffer), static_cast<std::size_t>(-1), "[%s] -> %s %d %d", LookupMessage(3585), info->GetName(), x, y);
} else {
StringFormat(buffer, sizeof(buffer), static_cast<std::size_t>(-1), "[%s] %s", LookupMessage(3585), info->GetName());
}
}
this->routeText.assign(buffer, std::strlen(buffer));
return this->routeText;
}
// OFFSET: 0x00B8D270 // OFFSET: 0x00B8D270
// FUNCTION: fn_19434 // FUNCTION: fn_19434
bool CNaviInfo::HasName(const char* name) const { bool CNaviInfo::HasName(const char* name) const {

View file

@ -2,7 +2,9 @@
#include "Client/Session.h" #include "Client/Session.h"
#include "Resource/ResourceMgr.h" #include "Resource/ResourceMgr.h"
#include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <cstring> #include <cstring>
// GLOBAL: gl_24627 // GLOBAL: gl_24627
@ -270,3 +272,282 @@ void CNavigationManager::Reset(bool clearAll) {
this->unknown_12A4 = nullptr; this->unknown_12A4 = nullptr;
this->ClearRouteNodes(); this->ClearRouteNodes();
} }
// OFFSET: 0x00B93110
// FUNCTION: fn_19562
bool CNavigationManager::AddState(const std::vector<std::string>& tokens) {
if (tokens.size() < 2) {
return false;
}
CNaviInfo* info = this->FindObject(tokens[0].c_str());
if (info == nullptr) {
return false;
}
const NaviCell cell = this->ParseCell(tokens[1].c_str());
if (cell.x == -1 || cell.y == -1) {
return false;
}
std::string name;
for (std::size_t i = 2; i < tokens.size(); ++i) {
name.append(" ", 1);
name.append(tokens[i]);
}
NavigationTarget target(0, info, cell.x, cell.y, name);
this->states.push_back(target);
return true;
}
// OFFSET: 0x00B93670
// FUNCTION: fn_19563
bool CNavigationManager::ParseFlags(const char* text, bool* first, bool* second, bool* third) {
const char* firstSlash = std::strstr(text, "/");
if (firstSlash == nullptr) {
return false;
}
const char* rest = firstSlash + 1;
const char* secondSlash = std::strstr(rest, "/");
if (secondSlash == nullptr) {
return false;
}
std::string firstText(text, firstSlash);
if (firstText.empty()) {
return false;
}
std::string secondText(rest, secondSlash);
if (secondText.empty()) {
return false;
}
std::string thirdText(secondSlash + 1, text + std::strlen(text));
if (thirdText.empty()) {
return false;
}
const int firstValue = std::atoi(firstText.c_str());
const int secondValue = std::atoi(secondText.c_str());
const int thirdValue = std::atoi(thirdText.c_str());
if ((firstValue != 0 && firstValue != 1) || (secondValue != 0 && secondValue != 1) || (thirdValue != 0 && thirdValue != 1)) {
return false;
}
*first = firstValue == 1;
*second = secondValue == 1;
*third = thirdValue == 1;
return true;
}
// OFFSET: 0x00B938F0
// FUNCTION: fn_19564
NaviCell CNavigationManager::ParseCell(const char* text) {
NaviCell cell;
const char* slash;
if (text == nullptr || (slash = std::strstr(text, "/")) == nullptr) {
cell.x = -1;
cell.y = -1;
return cell;
}
std::string xText(text, slash);
if (xText.empty()) {
cell.x = -1;
cell.y = -1;
return cell;
}
std::string yText(slash + 1, text + std::strlen(text));
if (yText.empty()) {
cell.x = -1;
cell.y = -1;
return cell;
}
const int x = std::atoi(xText.c_str());
const int y = std::atoi(yText.c_str());
if (x >= 0 && x <= 500 && y >= 0 && y <= 500) {
cell.x = x;
cell.y = y;
} else {
cell.x = -1;
cell.y = -1;
}
return cell;
}
// OFFSET: 0x00B94890
// FUNCTION: fn_19568
std::vector<char> CNavigationManager::WriteTarget(NavigationTarget target) {
std::vector<char> buffer;
if (target.object == nullptr) {
return buffer;
}
buffer.resize(sizeof(int), '\0');
*reinterpret_cast<int*>(buffer.data()) = target.type;
if (target.type == 1 || target.type == 0) {
std::string name = static_cast<CNaviInfo*>(target.object)->GetName();
buffer.insert(buffer.end(), name.begin(), name.end());
} else if (target.type == 3) {
std::vector<char> object = this->WriteObject(static_cast<CNavi_Object*>(target.object));
buffer.insert(buffer.end(), object.begin(), object.end());
} else if (target.type == 2) {
std::vector<char> object = this->WriteObject(static_cast<CNavi_Object*>(target.object));
buffer.insert(buffer.end(), object.begin(), object.end());
}
return buffer;
}
// OFFSET: 0x00B94B50
// FUNCTION: fn_19569
std::vector<char> CNavigationManager::WriteObject(CNavi_Object* object) {
std::vector<char> buffer;
if (object != nullptr) {
const int type = object->GetType();
const int index = object->GetIndex();
NaviCell cell;
object->GetCell(&cell);
std::string name = object->GetName();
std::string title = object->GetTitle();
std::string map = object->GetInfo()->GetName();
buffer.resize(map.size() + 0x16 + title.size() + name.size(), '\0');
NaviObjectRecord* record = reinterpret_cast<NaviObjectRecord*>(buffer.data());
record->type = type;
record->index = index;
record->cellX = cell.x;
record->cellY = cell.y;
const std::size_t nameLength = static_cast<std::int16_t>(name.size());
reinterpret_cast<NaviObjectRecord*>(buffer.data())->nameLength = static_cast<std::int16_t>(name.size());
std::memcpy(buffer.data() + 0x12, name.c_str(), nameLength);
const std::size_t titleLength = static_cast<std::int16_t>(title.size());
*reinterpret_cast<std::int16_t*>(buffer.data() + nameLength + 0x12) = static_cast<std::int16_t>(title.size());
std::memcpy(buffer.data() + nameLength + 0x14, title.c_str(), titleLength);
const std::size_t offset = nameLength + 0x14 + titleLength;
*reinterpret_cast<std::int16_t*>(buffer.data() + offset) = static_cast<std::int16_t>(map.size());
std::memcpy(buffer.data() + offset + 2, map.c_str(), static_cast<std::int16_t>(map.size()));
}
return buffer;
}
// OFFSET: 0x00B950B0
// FUNCTION: fn_19571
void CNavigationManager::ClearSavedTarget() {
this->savedTarget = NavigationTarget();
this->unknown_115C = false;
this->routeResult = 0;
this->nodes.clear();
this->selectedNode = -1;
this->movementRoute.segment = 0;
this->unknown_117C = false;
this->ground = nullptr;
this->cells.clear();
this->unknown_1258 = 1;
}
// OFFSET: 0x00B95260
// FUNCTION: fn_19572
void CNavigationManager::ClearCurrentObject() {
this->currentTarget.type = 0;
this->currentTarget.object = nullptr;
}
// OFFSET: 0x00B95AD0
// FUNCTION: fn_19576
void CNavigationManager::ClearStates() {
this->states.clear();
}
// OFFSET: 0x00B95AF0
// FUNCTION: fn_19577
void CNavigationManager::ClearNodes() {
this->nodes.clear();
}
// OFFSET: 0x00B95DC0
// FUNCTION: fn_19579
int CNavigationManager::FindNodePath(unsigned int index) {
if (this->states[index].type == 1 || this->states[index].type == 0) {
return -1;
}
for (unsigned int i = 0; i < this->nodePaths.size(); ++i) {
if (this->nodePaths[i].empty()) {
return -1;
}
const unsigned int first = this->nodePaths[i][0];
if (first >= this->states.size()) {
return -1;
}
NavigationTarget& target = this->states[index];
if (target.type == 3 && target.x == -1000 && target.y == -1000) {
if (this->states[first].type != 1) {
continue;
}
CNaviInfo* info = static_cast<CNaviInfo*>(this->states[first].object);
if (info == nullptr) {
return -1;
}
CNavi_Object* object = static_cast<CNavi_Object*>(target.object);
if (object == nullptr || object->GetInfo() == nullptr) {
return -1;
}
if (std::strcmp(info->GetName(), object->GetInfo()->GetName()) == 0) {
return i;
}
}
NavigationTarget& other = this->states[first];
if (other.type == this->states[index].type) {
CNavi_Object* otherObject = static_cast<CNavi_Object*>(other.object);
if (otherObject == nullptr) {
return -1;
}
CNavi_Object* object = static_cast<CNavi_Object*>(this->states[index].object);
if (object == nullptr) {
return -1;
}
if (std::strcmp(otherObject->GetName(), object->GetName()) == 0) {
return i;
}
}
}
return -1;
}
// OFFSET: 0x00B95FB0
// FUNCTION: fn_19580
void CNavigationManager::SelectNodePath(std::string name) {
if (name.empty() || this->nodePaths.size() < 2) {
return;
}
std::vector<int> first = this->nodePaths[0];
for (unsigned int i = 0; i < this->nodePaths.size(); ++i) {
if (this->states[this->nodePaths[i][0]].name == name) {
this->nodePaths[0] = this->nodePaths[i];
this->nodePaths[i] = first;
break;
}
}
}

View file

@ -1,10 +1,82 @@
#include "Client/Mode/ActiveGameMode.h"
#include "Client/Mode/CGameMode.h"
#include "Contents/Actors/GameActor.h"
#include "Contents/Configuration/ServiceOptions.h"
#include "Contents/Effects/EffectActor.h"
#include "Contents/Effects/EffectLists.h"
#include "Contents/Effects/EffectPrimitiveState.h"
#include "Contents/MessageTable.h"
#include "Contents/Skills/SkillId.h" #include "Contents/Skills/SkillId.h"
#include "Contents/Skills/SkillInfo.h" #include "Contents/Skills/SkillInfo.h"
#include "Contents/Sound/SoundMgr.h"
#include "Contents/UserData/NormalItemSlots.h" #include "Contents/UserData/NormalItemSlots.h"
#include "Contents/World/WorldScene.h"
#include "Resource/World/TerrainResource.h"
#include "Script/LuaScript.h" #include "Script/LuaScript.h"
#include <memory> #include <memory>
class CEfstAction_ChangeSize : public CEfstAction {
public:
void Slot01(int efstId, int argument2, int argument3, int argument4, CRenderObject* actor) override;
void Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) override;
};
class CEfstAction_GenderChange : public CEfstAction {
public:
void Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) override;
};
class CEfstAction_ShowEffect : public CEfstAction {
public:
void Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) override;
};
class CSkillAction_RA_UNLIMIT : public CSkillAction {
public:
void Slot08(int level, CGameActor* source) override;
};
class CEfstAction_UNLIMIT : public CEfstAction {
public:
void Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) override;
};
class CEfstAction_SHADOW_WEAPON : public CEfstAction {
public:
EfstActionMessage Slot03() override;
EfstActionMessage Slot04() override;
};
class CEfstAction_DANCING_KNIFE : public CEfstAction {
public:
void Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) override;
};
class CEfstAction_SHADOW_EXCEED : public CEfstAction {
public:
void Slot01(int efstId, int argument2, int argument3, int argument4, CRenderObject* actor) override;
void Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) override;
EfstActionMessage Slot03() override;
EfstActionMessage Slot04() override;
};
class CSkillAction_ABC_CHASING_BREAK : public CSkillAction {
public:
bool Slot07() override;
};
class CSkillAction_ABC_CHASING_SHOT : public CSkillAction {
public:
bool Slot07() override;
};
class CSkillAction_ABC_ABYSS_FLAME_ATK : public CSkillAction {
public:
bool Slot03(int argument) override;
};
// GLOBAL: gl_24640 // GLOBAL: gl_24640
CSkillActionMgr* g_skillActionMgr; CSkillActionMgr* g_skillActionMgr;
@ -404,3 +476,202 @@ bool ShowsCastEffect(int skillId) {
return true; return true;
} }
} }
// OFFSET: 0x007747B0
// FUNCTION: fn_08327
void CEfstAction_ChangeSize::Slot01(int efstId, int argument2, int argument3, int argument4, CRenderObject* actor) {
if (actor) {
g_effectMgr->Retire(actor->render.accountId, 2374);
}
}
// OFFSET: 0x007747E0
// FUNCTION: fn_08328
void CEfstAction_ChangeSize::Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) {
this->Slot00(efstId, value, 0, 0, actor, false);
}
// OFFSET: 0x00774E20
// FUNCTION: fn_08331
void CEfstAction_GenderChange::Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) {
this->Slot00(efstId, value, 0, 0, actor, false);
}
// OFFSET: 0x00774F30
// FUNCTION: fn_08334
void CEfstAction_ShowEffect::Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) {
this->Slot00(efstId, value, 0, 0, actor, false);
}
// OFFSET: 0x00774FB0
// FUNCTION: fn_08335
void CSkillAction_RA_UNLIMIT::Slot08(int level, CGameActor* source) {
g_soundMgr->PlayWave("effect\\EF_BeginSpell.wav", 0.0f, 0.0f, 0.0f, 250, 40, 1.0f, 0);
}
// OFFSET: 0x007756D0
// FUNCTION: fn_08339
void CEfstAction_UNLIMIT::Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) {
this->Slot00(efstId, value, 0, 0, actor, false);
}
// OFFSET: 0x007764E0
// FUNCTION: fn_08346
EfstActionMessage CEfstAction_SHADOW_WEAPON::Slot03() {
return {LookupMessage(3766), 0xFFFF00};
}
// OFFSET: 0x00776510
// FUNCTION: fn_08347
EfstActionMessage CEfstAction_SHADOW_WEAPON::Slot04() {
return {LookupMessage(3767), 0xFF};
}
// OFFSET: 0x00777910
// FUNCTION: fn_08359
void CEfstAction_DANCING_KNIFE::Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) {
this->Slot00(efstId, value, 0, 0, actor, false);
}
// OFFSET: 0x00777E80
// FUNCTION: fn_08366
void CEfstAction_SHADOW_EXCEED::Slot01(int efstId, int argument2, int argument3, int argument4, CRenderObject* actor) {
if (!actor) {
return;
}
auto* effectActor = static_cast<EffectActor*>(actor);
while (effectActor->state.Attachments().HasId(1004)) {
actor->SendObjectMessage(nullptr, 109, 0, 1004, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
}
// OFFSET: 0x00777EE0
// FUNCTION: fn_08367
void CEfstAction_SHADOW_EXCEED::Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) {
this->Slot00(efstId, value, 0, 0, actor, true);
}
// OFFSET: 0x00777F00
// FUNCTION: fn_08368
EfstActionMessage CEfstAction_SHADOW_EXCEED::Slot03() {
return {LookupMessage(3762), 0xFFFF00};
}
// OFFSET: 0x00777F30
// FUNCTION: fn_08369
EfstActionMessage CEfstAction_SHADOW_EXCEED::Slot04() {
return {LookupMessage(3763), 0xFF};
}
// OFFSET: 0x00778140
// FUNCTION: fn_08371
bool CSkillAction_ABC_CHASING_BREAK::Slot07() {
return true;
}
// OFFSET: 0x00778580
// FUNCTION: fn_08375
bool CSkillAction_ABC_CHASING_SHOT::Slot07() {
return true;
}
// OFFSET: 0x007794D0
// FUNCTION: fn_08385
bool CSkillAction_ABC_ABYSS_FLAME_ATK::Slot03(int argument) {
return true;
}
// OFFSET: 0x0077ACC0
// FUNCTION: fn_08404
std::pair<bool, int> CSkillActionMgr::ActionSlot01(int skillId) {
const auto found = this->m_skillActions.find(skillId);
std::unique_ptr<CSkillAction> action(found != this->m_skillActions.end() ? found->second() : nullptr);
if (!action) {
return {false, 8};
}
return {true, action->Slot01()};
}
// OFFSET: 0x0077AD90
// FUNCTION: fn_08405
EffectVector3 CellWorldPosition(int column, int row) {
if (!g_world.ActiveGameMode() || !static_cast<CGameMode*>(g_world.ActiveGameMode())->scene ||
!static_cast<CGameMode*>(g_world.ActiveGameMode())->scene->terrain) {
return EffectVector3{0.0f, 0.0f, 0.0f};
}
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
static_cast<CGameMode*>(g_world.ActiveGameMode())->scene->terrain->Grid().CellOrigin(column, row, x, z);
y = static_cast<CGameMode*>(g_world.ActiveGameMode())->scene->terrain->Grid().Height(x, z);
return EffectVector3{x + 2.5f, y + 0.0f, z + 2.5f};
}
// OFFSET: 0x0077AEA0
// FUNCTION: fn_08406
EfstActionMessage CSkillActionMgr::EfstSlot03(int efstId) {
const auto found = this->m_efstActions.find(efstId);
std::unique_ptr<CEfstAction> action(found != this->m_efstActions.end() ? found->second() : nullptr);
if (!action) {
return {nullptr, 0xFFFF00};
}
return action->Slot03();
}
// OFFSET: 0x0077AF80
// FUNCTION: fn_08407
EfstActionMessage CSkillActionMgr::EfstSlot04(int efstId) {
const auto found = this->m_efstActions.find(efstId);
std::unique_ptr<CEfstAction> action(found != this->m_efstActions.end() ? found->second() : nullptr);
if (!action) {
return {nullptr, 0xFF};
}
return action->Slot04();
}
// OFFSET: 0x0077B060
// FUNCTION: fn_08408
int CSkillActionMgr::ActionSlot14(int skillId) {
const auto found = this->m_skillActions.find(skillId);
std::unique_ptr<CSkillAction> action(found != this->m_skillActions.end() ? found->second() : nullptr);
return action ? action->Slot14() : 0;
}
// OFFSET: 0x0077B210
// FUNCTION: fn_08409
std::pair<bool, int> CSkillActionMgr::ActionSlot02(int skillId) {
const auto found = this->m_skillActions.find(skillId);
std::unique_ptr<CSkillAction> action(found != this->m_skillActions.end() ? found->second() : nullptr);
if (!action) {
return {false, 7};
}
return {true, action->Slot02()};
}
// OFFSET: 0x0077B2E0
// FUNCTION: fn_08410
bool CSkillActionMgr::ActionSlot03(int skillId, int argument) {
const auto found = this->m_skillActions.find(skillId);
std::unique_ptr<CSkillAction> action(found != this->m_skillActions.end() ? found->second() : nullptr);
return action ? action->Slot03(argument) : false;
}
// OFFSET: 0x0077B3A0
// FUNCTION: fn_08411
bool CSkillActionMgr::ActionSlot04(int skillId) {
const auto found = this->m_skillActions.find(skillId);
std::unique_ptr<CSkillAction> action(found != this->m_skillActions.end() ? found->second() : nullptr);
return action ? action->Slot04() : false;
}
// OFFSET: 0x0077B460
// FUNCTION: fn_08412
bool CSkillActionMgr::HasEfstAction(int efstId) {
return this->m_efstActions.find(efstId) != this->m_efstActions.end();
}

View file

@ -6,16 +6,83 @@
#include <functional> #include <functional>
#include <map> #include <map>
#include <new> #include <new>
#include <utility>
#include <vector> #include <vector>
class CEfstAction; class CGameActor;
class CRenderObject;
class CSkillObjectAction; class CSkillObjectAction;
class LuaScript; class LuaScript;
struct EffectVector3;
struct NormalItemSlot; struct NormalItemSlot;
struct EfstActionMessage {
/* 0000 */ const char* text;
/* 0004 */ std::uint32_t color;
};
class CSkillAction { class CSkillAction {
public: public:
virtual bool Slot00() = 0; virtual bool Slot00() {
return false;
}
virtual int Slot01() {
return 8;
}
virtual int Slot02() {
return 7;
}
virtual bool Slot03(int argument) {
return false;
}
virtual bool Slot04() {
return false;
}
virtual bool Slot05() {
return false;
}
virtual bool Slot06() {
return false;
}
virtual bool Slot07() {
return false;
}
virtual void Slot08(int level, CGameActor* source) {
}
virtual void Slot09(int argument1, int argument2, int argument3) {
}
virtual void Slot10(int argument1, int argument2, int argument3, int argument4, int argument5) {
}
virtual void Slot11(int level, CGameActor* source, CGameActor* target) {
}
virtual void Slot12(int argument1, int argument2, int argument3, int argument4, int argument5) {
}
virtual bool Slot13() {
return false;
}
virtual int Slot14() {
return 0;
}
virtual void Slot15(int level, CGameActor* source, CGameActor* target) {
}
virtual void Slot16(int level, CGameActor* source, CGameActor* target) {
}
};
class CEfstAction {
public:
virtual void Slot00(int efstId, int value, int argument3, int argument4, CRenderObject* actor, bool argument6) {
}
virtual void Slot01(int efstId, int argument2, int argument3, int argument4, CRenderObject* actor) {
}
virtual void Slot02(int efstId, int value, int argument3, int argument4, CRenderObject* actor) {
}
virtual EfstActionMessage Slot03() {
return {nullptr, 0xFFFF00};
}
virtual EfstActionMessage Slot04() {
return {nullptr, 0xFF};
}
}; };
struct SkillInfoEntry { struct SkillInfoEntry {
@ -68,6 +135,14 @@ public:
static CSkillActionMgr* GetInstance(); static CSkillActionMgr* GetInstance();
bool IsSupportAction(int skillId); bool IsSupportAction(int skillId);
std::pair<bool, int> ActionSlot01(int skillId);
std::pair<bool, int> ActionSlot02(int skillId);
int ActionSlot14(int skillId);
bool ActionSlot03(int skillId, int argument);
bool ActionSlot04(int skillId);
EfstActionMessage EfstSlot03(int efstId);
EfstActionMessage EfstSlot04(int efstId);
bool HasEfstAction(int efstId);
}; };
extern CSkillActionMgr* g_skillActionMgr; extern CSkillActionMgr* g_skillActionMgr;
@ -85,6 +160,7 @@ bool IsOptionalSelfTargetSkill(int skillId);
bool HidesUseFailureMessage(int skillId); bool HidesUseFailureMessage(int skillId);
bool IsSupportSkill(int skillId); bool IsSupportSkill(int skillId);
bool ShowsCastEffect(int skillId); bool ShowsCastEffect(int skillId);
EffectVector3 CellWorldPosition(int column, int row);
static_assert(sizeof(SkillInfoEntry) == 8); static_assert(sizeof(SkillInfoEntry) == 8);
#if defined(_WIN32) && !defined(_WIN64) #if defined(_WIN32) && !defined(_WIN64)
static_assert(offsetof(SkillInfoEntry, unknown_0004) == 0x4); static_assert(offsetof(SkillInfoEntry, unknown_0004) == 0x4);
@ -93,6 +169,8 @@ static_assert(offsetof(CSkillActionMgr, m_efstActions) == 0x0C);
static_assert(offsetof(CSkillActionMgr, m_skillObjectActions) == 0x14); static_assert(offsetof(CSkillActionMgr, m_skillObjectActions) == 0x14);
static_assert(sizeof(CSkillActionMgr) == 0x1C); static_assert(sizeof(CSkillActionMgr) == 0x1C);
static_assert(sizeof(CSkillAction) == 4); static_assert(sizeof(CSkillAction) == 4);
static_assert(sizeof(CEfstAction) == 4);
static_assert(sizeof(EfstActionMessage) == 8);
#endif #endif
#endif #endif

View file

@ -0,0 +1,33 @@
#ifndef RAGNAROK_SOURCE_UI_BUTTONS_UIBMPRADIOBTN_H
#define RAGNAROK_SOURCE_UI_BUTTONS_UIBMPRADIOBTN_H
#include "UI/Window/UIWindow.h"
#include <string>
struct UIButtonContext;
class UIBmpRadioBtn : public UIWindow {
public:
void Slot28(int x, int y) override;
protected:
/* 007C */ unsigned char unknown_007C[0x64];
/* 00E0 */ std::string m_tooltip;
UIButtonContext* m_controls;
};
class UIBmpRadioBtn2 : public UIBmpRadioBtn {
};
class UICaptchaImage : public UIWindow {
public:
void Slot28(int x, int y) override;
protected:
/* 007C */ unsigned char unknown_007C[0x124];
/* 01A0 */ std::string m_tooltip;
UIButtonContext* m_controls;
};
#endif

View file

@ -1,4 +1,6 @@
#include "Render/DeviceContext/DeviceContext.h" #include "Render/DeviceContext/DeviceContext.h"
#include "UI/UIBarGraph.h"
#include "UI/Buttons/UIBmpRadioBtn.h"
#include "Render/Text/TextContext.h" #include "Render/Text/TextContext.h"
#include "UI/Window/UIWindowMgr.h" #include "UI/Window/UIWindowMgr.h"
#include "UI/Buttons/UIItemTagButton.h" #include "UI/Buttons/UIItemTagButton.h"
@ -6,6 +8,7 @@
#include "UI/Lists/UITreeView.h" #include "UI/Lists/UITreeView.h"
#include "UI/UIStaticItem.h" #include "UI/UIStaticItem.h"
#include <cstdio>
#include <cstring> #include <cstring>
#include <string> #include <string>
@ -200,3 +203,76 @@ void UIItemTagButton::Slot28(int x, int y) {
// FUNCTION: fn_11459 // FUNCTION: fn_11459
void UIStaticItem_Illust::Slot28(int, int) { void UIStaticItem_Illust::Slot28(int, int) {
} }
// OFFSET: 0x00891810
// FUNCTION: fn_11450
void UIBmpRadioBtn::Slot28(int, int) {
if (!this->m_tooltip.empty()) {
int screenX = 0, screenY = -20;
for (const UIWindow* window = this; window; window = window->parent) {
screenX = (screenX + window->geometry.x);
screenY = (screenY + window->geometry.y);
}
this->m_controls->services.Tooltip(this->m_tooltip.c_str(), screenX, screenY, -1, false, true);
}
}
// OFFSET: 0x00891870
// FUNCTION: fn_11451
void UICaptchaImage::Slot28(int, int) {
if (!this->m_tooltip.empty()) {
int screenX = 0, screenY = -20;
for (const UIWindow* window = this; window; window = window->parent) {
screenX = (screenX + window->geometry.x);
screenY = (screenY + window->geometry.y);
}
this->m_controls->services.Tooltip(this->m_tooltip.c_str(), screenX, screenY, -1, false, true);
}
}
// OFFSET: 0x00891240
// FUNCTION: fn_11447
void UIBarGraph4::Slot28(int, int) {
if (!this->m_current || !this->m_maximum || *this->m_maximum < *this->m_current || *this->m_maximum == 0) {
return;
}
int screenX = 0, screenY = -16;
for (const UIWindow* window = this; window; window = window->parent) {
screenX = (screenX + window->geometry.x);
screenY = (screenY + window->geometry.y);
}
char text[260];
std::memset(text, 0, sizeof(text));
const int permille = static_cast<int>(static_cast<float>(*this->m_current) / static_cast<float>(*this->m_maximum) * 1000.0f);
std::sprintf(text, "%.1f %%", static_cast<float>(permille) * 0.1f);
this->m_controls->services.Tooltip(text, screenX, screenY, -1, false, true);
}
// OFFSET: 0x008918D0
// FUNCTION: fn_11452
void UIINT64BarGraph::Slot28(int, int) {
if (this->m_maximum < this->m_current || this->m_maximum == 0 || this->m_maximum == 99999999) {
return;
}
int screenX = 0, screenY = -16;
for (const UIWindow* window = this; window; window = window->parent) {
screenX = (screenX + window->geometry.x);
screenY = (screenY + window->geometry.y);
}
char text[260];
std::memset(text, 0, sizeof(text));
const int permille = static_cast<int>(static_cast<double>(this->m_current) / static_cast<double>(this->m_maximum) * 1000.0);
std::sprintf(text, "%.1f %%", static_cast<float>(permille) * 0.1f);
this->m_controls->services.Tooltip(text, screenX, screenY, -1, false, true);
}

View file

@ -14,6 +14,7 @@ struct ItemTagRetirement;
struct ChatSuggestions { struct ChatSuggestions {
UIWindow& chat; UIWindow& chat;
UIWindow* const& window; UIWindow* const& window;
UIEditCtrl* const& input;
bool Visible() const; bool Visible() const;

View file

@ -3,16 +3,21 @@
#include "UI/Window/UIFrameWnd.h" #include "UI/Window/UIFrameWnd.h"
#include <vector>
class UINewChatWnd : public UIFrameWnd { class UINewChatWnd : public UIFrameWnd {
public: public:
void Move(int x, int y) override; void Move(int x, int y) override;
void Show(int visible) override;
private: private:
/* 00BC */ int unknown_00BC; /* 00BC */ int unknown_00BC;
/* 00C0 */ int unknown_00C0; /* 00C0 */ int unknown_00C0;
/* 00C4 */ int m_inputX; /* 00C4 */ int m_inputX;
/* 00C8 */ int m_inputY; /* 00C8 */ int m_inputY;
/* 00CC */ unsigned char unknown_00CC[0x50]; /* 00CC */ unsigned char unknown_00CC[0x28];
/* 00F4 */ std::vector<UIWindow*> m_tabs;
/* 0100 */ unsigned char unknown_0100[0x1C];
/* 011C */ int m_inputOffsetY; /* 011C */ int m_inputOffsetY;
}; };

View file

@ -0,0 +1,16 @@
#ifndef RAGNAROK_SOURCE_UI_CHAT_UISUBCHATMINIWND_H
#define RAGNAROK_SOURCE_UI_CHAT_UISUBCHATMINIWND_H
#include "UI/Window/UIFrameWnd.h"
class UISubChatMiniWnd : public UIFrameWnd {
public:
int SendMsg(UIWindow* sender, int message, std::intptr_t a, std::intptr_t b, std::intptr_t c, std::intptr_t d) override;
private:
/* 00B4 */ int unknown_00B4;
/* 00B8 */ int m_rowHeight;
/* 00BC */ int m_offsetY;
};
#endif

View file

@ -0,0 +1,14 @@
#ifndef RAGNAROK_SOURCE_UI_DIALOGS_UIMEMORIALDUNWND_H
#define RAGNAROK_SOURCE_UI_DIALOGS_UIMEMORIALDUNWND_H
#include "UI/Window/UIFrameWnd.h"
extern int g_memorialDunWndX;
extern int g_memorialDunWndY;
class UIMemorialDunWnd : public UIFrameWnd {
public:
void Move(int x, int y) override;
};
#endif

View file

@ -0,0 +1,17 @@
#ifndef RAGNAROK_SOURCE_UI_LISTS_UICHATHISBOX_H
#define RAGNAROK_SOURCE_UI_LISTS_UICHATHISBOX_H
#include "UI/Lists/UIListBox.h"
#include <string>
#include <vector>
class UIChatHisBox : public UIListBox {
public:
const char* HistoryText(unsigned int index);
/* 00DC */ unsigned char unknown_00DC[0x18];
/* 00F4 */ std::vector<std::string> history;
};
#endif

View file

@ -0,0 +1,16 @@
#ifndef RAGNAROK_SOURCE_UI_LISTS_UIIMGMENU_H
#define RAGNAROK_SOURCE_UI_LISTS_UIIMGMENU_H
#include "UI/Window/UIWindow.h"
class UIImgMenu : public UIWindow {
public:
int ItemAt(int x, int y);
/* 0080 */ int count;
/* 0084 */ int itemWidth;
/* 0088 */ int itemHeight;
/* 008C */ int horizontal;
};
#endif

View file

@ -29,6 +29,7 @@ public:
virtual void AddLine(const char* text, std::uint32_t color); virtual void AddLine(const char* text, std::uint32_t color);
virtual void Clear(); virtual void Clear();
virtual void Refresh(); virtual void Refresh();
int FindLine(const std::string& text);
/* 007C */ int backgroundRed; /* 007C */ int backgroundRed;
/* 0080 */ int backgroundGreen; /* 0080 */ int backgroundGreen;

View file

@ -0,0 +1,144 @@
#ifndef RAGNAROK_SOURCE_UI_LISTS_UILISTBOXTEMPLATE_H
#define RAGNAROK_SOURCE_UI_LISTS_UILISTBOXTEMPLATE_H
#include "UI/Lists/UIVScrollBar.h"
#include "UI/Window/UIControl.h"
#include <algorithm>
#include <cstddef>
#include <functional>
#include <list>
#include <vector>
class CUIListItem : public CUIControl {
public:
virtual void SetSelected(bool selected);
/* 0084 */ unsigned char unknown_0084[0x04];
/* 0088 */ int selected;
/* 008C */ unsigned char unknown_008C[0x04];
/* 0090 */ unsigned char frozen;
/* 0094 */ int index;
};
namespace AdventureGuide {
class CUISubTab : public CUIListItem {};
class CUITab : public CUIListItem {};
class CUIItemInfo : public CUIListItem {};
class CUIMonsterInfo : public CUIListItem {};
class CUIQuestInfo : public CUIListItem {};
class CUITabInfo : public CUIListItem {};
class CItemData {};
class CMonsterData {};
class CQuestInfo {};
}
namespace Guide_Intro {
struct Contents {};
}
template <class T> class CUIListBox : public CUIControl {
public:
// OFFSET: 0x009D3FD0
// OFFSET: 0x009D4040
// FUNCTION: fn_14037
// FUNCTION: fn_14038
void Resize(int width, int height) override {
UIWindow::Resize(width, height);
this->scrollBar->Resize(this->scrollBar->geometry.width, height);
int total = 0;
for (T* item : this->items) {
total += item->geometry.height;
}
this->scrollBar->SetTotal(total);
this->scrollBar->SetVisible(this->geometry.height);
this->scrollBar->SetPosition(this->scrollBar->position);
}
/* 0084 */ unsigned char unknown_0084[0x2C];
/* 00B0 */ CUIVScrollBar* scrollBar;
/* 00B4 */ unsigned char unknown_00B4[0x10];
/* 00C4 */ std::list<T*> items;
};
template <class T, class D> class CUIListBoxEx : public CUIControl {
public:
// OFFSET: 0x009D43D0
// OFFSET: 0x009D45D0
// OFFSET: 0x009D47D0
// OFFSET: 0x009D49D0
// FUNCTION: fn_14043
// FUNCTION: fn_14044
// FUNCTION: fn_14045
// FUNCTION: fn_14046
void OnScroll(int position) {
auto it = this->items.begin();
if (it == this->items.end()) {
return;
}
if (this->data.size() == 0) {
for (; it != this->items.end(); ++it) {
(*it)->index = -1;
(*it)->Show(0);
}
return;
}
const int first = (*it)->index;
unsigned int row = position / this->rowHeight;
if (first != -1) {
if (first < static_cast<int>(row)) {
if (row - first < static_cast<unsigned int>(this->items.size())) {
std::rotate(this->items.begin(), this->items.begin() + (row - first), this->items.end());
}
} else if (first > static_cast<int>(row)) {
if (first - row < static_cast<unsigned int>(this->items.size())) {
std::rotate(this->items.begin(), this->items.end() - (first - row), this->items.end());
}
}
}
it = this->items.begin();
for (int y = this->itemY - position % this->rowHeight; y < this->geometry.height; y += this->rowHeight, ++it, ++row) {
if (it == this->items.end() || row >= this->data.size()) {
break;
}
T* item = *it;
if (item) {
if (this->rebind || row != static_cast<unsigned int>(item->index)) {
item->index = row;
if (this->binder) {
auto node = this->data.begin();
std::advance(node, static_cast<int>(row));
this->binder(item, *node);
}
}
item->SetSelected(row == this->selected);
item->Move(this->itemX, y);
item->Show(1);
}
}
for (; it != this->items.end(); ++it) {
(*it)->index = -1;
(*it)->Show(0);
}
}
/* 0084 */ unsigned char unknown_0084[0x2C];
/* 00B0 */ CUIVScrollBar* scrollBar;
/* 00B4 */ int itemWidth;
/* 00B8 */ int rowHeight;
/* 00BC */ int itemX;
/* 00C0 */ int itemY;
/* 00C4 */ int unknown_00C4;
/* 00C8 */ std::vector<T*> items;
/* 00D4 */ std::list<D> data;
/* 00DC */ unsigned int selected;
/* 00E0 */ unsigned char unknown_00E0;
/* 00E1 */ unsigned char rebind;
/* 00E2 */ unsigned char unknown_00E2[0x2E];
/* 0110 */ std::function<void(T*, const D&)> binder;
};
#endif

View file

@ -4,6 +4,10 @@
#include "Resource/ResourceMgr.h" #include "Resource/ResourceMgr.h"
#include "UI/Window/UIWindowMgr.h" #include "UI/Window/UIWindowMgr.h"
#include "UI/Lists/UIScrollBar.h" #include "UI/Lists/UIScrollBar.h"
#include "UI/Lists/UIChatHisBox.h"
#include "UI/Lists/UIImgMenu.h"
#include "UI/Lists/UIListBoxEx.h"
#include "UI/Lists/UITabControl.h"
#include <algorithm> #include <algorithm>
@ -315,3 +319,74 @@ void UIScrollBar::Configure(int x, int y, unsigned char vertical, int length) {
this->Slot15(width, height); this->Slot15(width, height);
this->Slot22(width, height); this->Slot22(width, height);
} }
// OFFSET: 0x00880800
// FUNCTION: fn_11279
int UIListBoxEx::FindLine(const std::string& text) {
const std::string key = text;
auto it = this->lines.begin();
for (; it != this->lines.end(); ++it) {
if (key == it->second.text) {
break;
}
}
return it == this->lines.end() ? -1 : it->first;
}
// OFFSET: 0x00880920
// FUNCTION: fn_11280
int UIImgMenu::ItemAt(int x, int y) {
const int width = this->geometry.width - 3;
if (x < 1 || x >= width || y < 1) {
return -1;
}
const int height = this->geometry.height - 3;
if (y >= height) {
return -1;
}
int size;
int offset;
if (this->horizontal != 0) {
size = width / this->count;
this->itemWidth = size;
offset = x;
} else {
size = height / this->count;
this->itemHeight = size;
offset = y;
}
return std::min(offset / size, this->count - 1);
}
// OFFSET: 0x00880B20
// FUNCTION: fn_11283
int UITabControl::TabCount() {
return static_cast<int>(this->labels.size());
}
// OFFSET: 0x00880B40
// FUNCTION: fn_11284
int UITabControl::TabAt(int x, int y) {
int offset = x;
if (this->layout == 0 || this->layout == 2) {
offset = y;
}
const int count = this->TabCount();
for (int index = 0; index < count; ++index) {
offset -= this->widths[index];
if (offset < 0) {
return index;
}
}
return -1;
}
// OFFSET: 0x00880BB0
// FUNCTION: fn_11285
const char* UIChatHisBox::HistoryText(unsigned int index) {
if (index < this->history.size() && !this->history[index].empty()) {
return this->history[index].c_str();
}
return nullptr;
}

View file

@ -0,0 +1,21 @@
#ifndef RAGNAROK_SOURCE_UI_LISTS_UITABCONTROL_H
#define RAGNAROK_SOURCE_UI_LISTS_UITABCONTROL_H
#include "UI/Window/UIWindow.h"
#include <string>
#include <vector>
class UITabControl : public UIWindow {
public:
int TabCount();
int TabAt(int x, int y);
/* 0080 */ int layout;
/* 0084 */ unsigned char unknown_0084[0x30];
/* 00B4 */ std::vector<std::string> labels;
/* 00C0 */ unsigned char unknown_00C0[0xC];
/* 00CC */ std::vector<int> widths;
};
#endif

View file

@ -0,0 +1,37 @@
#ifndef RAGNAROK_SOURCE_UI_LISTS_UIVSCROLLBAR_H
#define RAGNAROK_SOURCE_UI_LISTS_UIVSCROLLBAR_H
#include "UI/Window/UIControl.h"
#include <cstddef>
#include <functional>
class CUIVScrollBar : public CUIControl {
public:
virtual void Slot54();
void SetTotal(int value);
void SetVisible(int value);
void SetPosition(int value);
/* 0084 */ UIWindow* upButton;
/* 0088 */ CUIControl* thumb;
/* 008C */ int unknown_008C;
/* 0090 */ int total;
/* 0094 */ int visible;
/* 0098 */ int unknown_0098;
/* 009C */ int position;
/* 00A0 */ int maximum;
/* 00A4 */ int trackLength;
/* 00A8 */ unsigned char dragging;
/* 00A9 */ unsigned char unknown_00A9[0x1F];
/* 00C8 */ std::function<void(int)> onChange;
};
#if defined(_WIN32) && !defined(_WIN64)
static_assert(offsetof(CUIVScrollBar, total) == 0x90);
static_assert(offsetof(CUIVScrollBar, dragging) == 0xA8);
static_assert(offsetof(CUIVScrollBar, onChange) == 0xC8);
#endif
#endif

View file

@ -1,4 +1,11 @@
#include "Client/Session.h" #include "Client/Session.h"
#include "Resource/Bitmap/BitmapResource.h"
#include "Resource/ResourceMgr.h"
#include "Resource/SkinPalette.h"
#include "Resource/TextureNames.h"
#include "UI/Buttons/UIButton.h"
#include "UI/Window/UIRebirthWnd.h"
#include "UI/Window/UISeekPartyMBWnd.h"
#include "UI/Shortcuts/ShortcutCooldowns.h" #include "UI/Shortcuts/ShortcutCooldowns.h"
#include <algorithm> #include <algorithm>
@ -204,3 +211,32 @@ void ShortcutCooldowns::Paint(
++entry; ++entry;
} }
} }
// OFFSET: 0x0092DDE0
// FUNCTION: fn_12673
void UIRebirthWnd::Paint() {
auto* background = static_cast<CBitmapRes*>(ResourceMgr::Instance().Load(Resolve("유저인터페이스\\win_msgbox1.bmp")));
this->DrawBitmap(0, 0, &background->image, false);
}
// OFFSET: 0x0092E230
// FUNCTION: fn_12676
void UISeekPartyMBWnd::Paint() {
auto& controls = *this->m_controls;
this->DrawTitleBar(controls.fonts, controls.locale, true, "", 0);
if (this->m_minimized) {
return;
}
unsigned red;
unsigned green;
unsigned blue;
SkinPaletteChannels(ResourceMgr::Instance(), 2, 2, red, green, blue);
this->FillRegion(0, 17, this->geometry.width, this->geometry.height - 38, RGB(red, green, blue));
const RECT outer = {50, 95, 310, 293};
const RECT inner = {51, 96, 309, 292};
this->FillSurface(0, &outer);
this->FillSurface(0xF0F0F0, &inner);
this->DrawButtonBar(2, 0, 1);
}

View file

@ -15,18 +15,29 @@
#include "UI/Shortcuts/ShortcutVisibility.h" #include "UI/Shortcuts/ShortcutVisibility.h"
#include "UI/Shortcuts/UIShortCutWnd.h" #include "UI/Shortcuts/UIShortCutWnd.h"
#include "UI/Chat/UINewChatWnd.h" #include "UI/Chat/UINewChatWnd.h"
#include "UI/Chat/UISubChatMiniWnd.h"
#include "UI/Dialogs/UIMemorialDunWnd.h"
#include "UI/Buttons/UIBitmapButton.h" #include "UI/Buttons/UIBitmapButton.h"
#include "UI/Buttons/UIButton.h" #include "UI/Buttons/UIButton.h"
#include "UI/Buttons/UICheckBox.h" #include "UI/Buttons/UICheckBox.h"
#include "UI/Layout/WindowSnapping.h" #include "UI/Layout/WindowSnapping.h"
#include "UI/Window/UIWindow.h" #include "UI/Window/UIWindow.h"
#include "UI/Window/UIWindowMgr.h" #include "UI/Window/UIWindowMgr.h"
#include "UI/Window/UIWindowCallBtnWnd.h"
#include "UI/Window/WindowManagerWindows.h"
#include "Render/Renderer.h"
#include <stdio.h> #include <stdio.h>
#include <cstdlib> #include <cstdlib>
namespace {} namespace {}
// GLOBAL: gl_24737
int g_memorialDunWndX;
// GLOBAL: gl_24738
int g_memorialDunWndY;
// OFFSET: 0x009122B0 // OFFSET: 0x009122B0
// FUNCTION: fn_12528 // FUNCTION: fn_12528
UIShortCutWnd::UIShortCutWnd(UIWindowContext& windows, UIFrameContext& frames, ShortcutWindowContext& shortcuts) UIShortCutWnd::UIShortCutWnd(UIWindowContext& windows, UIFrameContext& frames, ShortcutWindowContext& shortcuts)
@ -429,3 +440,62 @@ void UINewChatWnd::Move(int x, int y) {
window->SendMsg(this, 6, 197, this->geometry.x + x, this->geometry.y + y, 0); window->SendMsg(this, 6, 197, this->geometry.x + x, this->geometry.y + y, 0);
} }
} }
// OFFSET: 0x0093D090
// FUNCTION: fn_12765
void UINewChatWnd::Show(int visible) {
this->geometry.visible = visible;
if (visible != 0) {
this->Invalidate();
this->m_frameContext->snapping.Store(this);
} else if (this->m_frameContext->snapping.enabled) {
this->m_frameContext->snapping.rectangles.erase(this);
}
for (UIWindow* tab : this->m_tabs) {
tab->Show(this->geometry.visible);
}
for (unsigned int i = 0; i < g_windowMgr.retainedWindows.size(); ++i) {
g_windowMgr.retainedWindows[i]->Show(this->geometry.visible);
}
}
// OFFSET: 0x0091E960
// FUNCTION: fn_12630
void UIMemorialDunWnd::Move(int x, int y) {
const int left = x <= g_renderer->viewport.width - this->geometry.width ? x : 0;
const int top = y <= g_renderer->viewport.height - this->geometry.height ? y : 310;
this->UIFrameWnd::Move(left, top);
if (g_memorialDunWndX != left) {
g_memorialDunWndX = left;
}
if (g_memorialDunWndY != top) {
g_memorialDunWndY = top;
}
}
// OFFSET: 0x0093CA50
// FUNCTION: fn_12762
int UISubChatMiniWnd::SendMsg(UIWindow* sender, int message, std::intptr_t a, std::intptr_t b, std::intptr_t c, std::intptr_t d) {
if (message != 6) {
return this->UIFrameWnd::SendMsg(sender, message, a, b, c, d);
}
if (a == 197) {
this->Move(b, c - this->m_offsetY);
} else if (a == 202) {
this->Slot25(this->m_rowHeight * b + 1, 0);
}
return 0;
}
// OFFSET: 0x0093CAB0
// FUNCTION: fn_12763
int UIWindowCallBtnWnd::SendMsg(UIWindow*, int message, std::intptr_t a, std::intptr_t, std::intptr_t, std::intptr_t) {
if (message == 6) {
this->m_frameContext->windows.EnsureWindow(a);
}
return 0;
}

34
Source/UI/UIBarGraph.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef RAGNAROK_SOURCE_UI_UIBARGRAPH_H
#define RAGNAROK_SOURCE_UI_UIBARGRAPH_H
#include "UI/Window/UIWindow.h"
#include <cstdint>
struct UIButtonContext;
class UIBarGraph3 : public UIWindow {
};
class UIBarGraph4 : public UIBarGraph3 {
public:
void Slot28(int x, int y) override;
protected:
/* 007C */ const int* m_current;
/* 0080 */ const int* m_maximum;
UIButtonContext* m_controls;
};
class UIINT64BarGraph : public UIWindow {
public:
void Slot28(int x, int y) override;
protected:
/* 007C */ int unknown_007C;
/* 0080 */ std::int64_t m_current;
/* 0088 */ std::int64_t m_maximum;
UIButtonContext* m_controls;
};
#endif

View file

@ -81,3 +81,9 @@ void UILapineScrollBar::Paint() {
this->minimumThumb = 0; this->minimumThumb = 0;
this->parent->Invalidate(); this->parent->Invalidate();
} }
// OFFSET: 0x00600260
// FUNCTION: fn_04157
void UILapineBox_IngredientListWnd::Paint() {
this->ClearSurface(0xFFF7F7);
}

View file

@ -8,4 +8,12 @@ public:
void Paint() override; void Paint() override;
}; };
class UIScrollWnd : public UIWindow {
};
class UILapineBox_IngredientListWnd : public UIScrollWnd {
public:
void Paint() override;
};
#endif #endif

View file

@ -1,4 +1,5 @@
#include "UI/Window/UIControl.h" #include "UI/Window/UIControl.h"
#include "UI/Lists/UIListBoxTemplate.h"
#include "Resource/Bitmap/BitmapResource.h" #include "Resource/Bitmap/BitmapResource.h"
#include "Resource/ResourceMgr.h" #include "Resource/ResourceMgr.h"
#include "Resource/TextureNames.h" #include "Resource/TextureNames.h"
@ -150,3 +151,80 @@ void CUIControl::Slot52(int value) {
// FUNCTION: fn_14036 // FUNCTION: fn_14036
void CUIControl::Slot53(int) { void CUIControl::Slot53(int) {
} }
// OFFSET: 0x009F2B70
// FUNCTION: fn_14726
void CUIVScrollBar::SetTotal(int value) {
const int count = value > 1 ? value : 1;
this->total = count;
const int shown = this->visible <= count ? this->visible : count;
this->visible = shown;
this->maximum = count - shown;
this->Invalidate();
}
// OFFSET: 0x009F2E80
// FUNCTION: fn_14732
void CUIVScrollBar::SetVisible(int value) {
const int count = this->total;
const int shown = count < value ? count : value;
this->visible = shown;
this->maximum = count - shown;
this->Invalidate();
}
// OFFSET: 0x009F2530
// FUNCTION: fn_14716
void CUIVScrollBar::SetPosition(int value) {
int zero = 0;
const int* clamped = &this->maximum;
if (value <= this->maximum) {
clamped = value >= 0 ? &value : &zero;
}
if (!this->dragging) {
this->position = *clamped;
}
this->Show(this->total > this->visible);
this->Slot54();
if (this->onChange) {
this->onChange(this->position);
}
this->Invalidate();
}
// OFFSET: 0x009F25C0
// FUNCTION: fn_14717
void CUIVScrollBar::Slot54() {
CUIControl* bar = this->thumb;
float offset;
if (bar->hitEnabled) {
const int length = static_cast<int>((static_cast<float>(this->visible) / static_cast<float>(this->total)) * static_cast<float>(this->trackLength));
bar->Resize(this->geometry.width, length > 1 ? length : 1);
offset = (static_cast<float>(this->trackLength) / static_cast<float>(this->total)) * static_cast<float>(this->position);
} else {
if (this->total - this->visible <= 0) {
bar->Move(0, this->upButton->geometry.height);
return;
}
offset = static_cast<float>(this->position * (this->trackLength - bar->geometry.height)) / static_cast<float>(this->total - this->visible);
}
this->thumb->Move(0, static_cast<int>(offset + static_cast<float>(this->upButton->geometry.height)));
}
// OFFSET: 0x009F0290
// FUNCTION: fn_14661
void CUIListItem::SetSelected(bool selected) {
if (this->selected != selected) {
this->selected = selected;
if (!this->frozen) {
this->Invalidate();
}
}
}
template void CUIListBox<AdventureGuide::CUITab>::Resize(int, int);
template void CUIListBox<AdventureGuide::CUISubTab>::Resize(int, int);
template void CUIListBoxEx<AdventureGuide::CUITabInfo, Guide_Intro::Contents>::OnScroll(int);
template void CUIListBoxEx<AdventureGuide::CUIQuestInfo, AdventureGuide::CQuestInfo>::OnScroll(int);
template void CUIListBoxEx<AdventureGuide::CUIMonsterInfo, AdventureGuide::CMonsterData>::OnScroll(int);
template void CUIListBoxEx<AdventureGuide::CUIItemInfo, AdventureGuide::CItemData>::OnScroll(int);

View file

@ -0,0 +1,11 @@
#ifndef RAGNAROK_SOURCE_UI_WINDOW_UIREBIRTHWND_H
#define RAGNAROK_SOURCE_UI_WINDOW_UIREBIRTHWND_H
#include "UI/Window/UIFrameWnd.h"
class UIRebirthWnd : public UIFrameWnd {
public:
void Paint() override;
};
#endif

View file

@ -0,0 +1,18 @@
#ifndef RAGNAROK_SOURCE_UI_WINDOW_UISEEKPARTYMBWND_H
#define RAGNAROK_SOURCE_UI_WINDOW_UISEEKPARTYMBWND_H
#include "UI/Window/UIFrameWnd.h"
struct UIButtonContext;
class UISeekPartyMBWnd : public UIFrameWnd {
public:
void Paint() override;
protected:
/* 00BC */ unsigned char unknown_00BC[0xAC];
/* 0168 */ unsigned char m_minimized;
UIButtonContext* m_controls;
};
#endif

View file

@ -0,0 +1,11 @@
#ifndef RAGNAROK_SOURCE_UI_WINDOW_UIWINDOWCALLBTNWND_H
#define RAGNAROK_SOURCE_UI_WINDOW_UIWINDOWCALLBTNWND_H
#include "UI/Window/UIFrameWnd.h"
class UIWindowCallBtnWnd : public UIFrameWnd {
public:
int SendMsg(UIWindow* sender, int message, std::intptr_t a, std::intptr_t b, std::intptr_t c, std::intptr_t d) override;
};
#endif

View file

@ -7,7 +7,9 @@
class Timestamp { class Timestamp {
public: public:
Timestamp(__time32_t value);
~Timestamp(); ~Timestamp();
void Set(__time32_t value);
TIMESTAMP_STRUCT ToSqlTimestamp(); TIMESTAMP_STRUCT ToSqlTimestamp();
int IsBefore(__time32_t other) const; int IsBefore(__time32_t other) const;
int IsBeforeNow() const; int IsBeforeNow() const;