diff --git a/Source/Contents/Actors/GameActor.h b/Source/Contents/Actors/GameActor.h index 85159fa1..a8227132 100644 --- a/Source/Contents/Actors/GameActor.h +++ b/Source/Contents/Actors/GameActor.h @@ -1,7 +1,7 @@ #ifndef RAGNAROK_SOURCE_CONTENTS_ACTORS_GAMEACTOR_H #define RAGNAROK_SOURCE_CONTENTS_ACTORS_GAMEACTOR_H -#include "Contents/Actors/RenderObject.h" +#include "Contents/Effects/EffectActor.h" #include "Contents/MovementRouteInterpolation.h" #include @@ -17,11 +17,11 @@ enum { BODYSTATE_STONECURSE_ING, }; -class CGameActor : public CRenderObject { +class CGameActor : public EffectActor { public: ~CGameActor() override; - /* 0138 */ unsigned char unknown_0138[0xE8]; + /* 0158 */ unsigned char unknown_0158[0xC8]; /* 0220 */ MovementRouteInterpolation route; /* 0238 */ unsigned char unknown_0238; /* 0239 */ unsigned char unknown_0239[0x1B]; diff --git a/Source/Contents/Effects/EffectActor.h b/Source/Contents/Effects/EffectActor.h index af074643..9128bb0b 100644 --- a/Source/Contents/Effects/EffectActor.h +++ b/Source/Contents/Effects/EffectActor.h @@ -3,9 +3,12 @@ #include "Contents/Effects/EffectAttachments.h" #include "Contents/Actors/RenderObject.h" +#include "Utility/Math/Vector3d.h" #include +struct CRagEffect; + struct EffectActorState { EffectActorState(); ~EffectActorState(); @@ -22,6 +25,7 @@ struct EffectActorState { struct EffectActor : CRenderObject { EffectActor(); bool EffectVisible() const; + CRagEffect* LaunchEffect(int effectId, vector3d deltaPos, float rotation); /* 0138 */ EffectActorState state; }; diff --git a/Source/Contents/Effects/EffectPrim.h b/Source/Contents/Effects/EffectPrim.h index 7c2138fa..3f7bccce 100644 --- a/Source/Contents/Effects/EffectPrim.h +++ b/Source/Contents/Effects/EffectPrim.h @@ -20,6 +20,7 @@ struct CEffectPrim : CRenderObject { bool Prim3DParticleNoMaster(); bool Prim3DParticleOrbit(); bool Prim3DParticleSpline(); + bool Prim3DTextureNoMaster(); void PrimBottom2(); void PrimBottom(); void PrimBottomOut(); @@ -37,6 +38,7 @@ struct CEffectPrim : CRenderObject { void PrimSMA(); void PrimShieldBoomerang3(); bool PrimSuperAngel(); + void PrimTanji2(); void PrimTeiHit2(); bool PrimSakura(); bool PrimKouenka(); diff --git a/Source/Contents/Effects/EffectPrimitiveMotion.cpp b/Source/Contents/Effects/EffectPrimitiveMotion.cpp index d2adee68..23b6cd26 100644 --- a/Source/Contents/Effects/EffectPrimitiveMotion.cpp +++ b/Source/Contents/Effects/EffectPrimitiveMotion.cpp @@ -3,6 +3,7 @@ #include "Client/Mode/ModeMgr.h" #include "Client/Session.h" #include "Contents/Camera/Camera.h" +#include "Contents/Actors/Pc.h" #include "Contents/World/World.h" #include "Resource/Sprites/ActionResource.h" #include "Contents/Effects/EffectPrim.h" @@ -1996,6 +1997,81 @@ bool CEffectPrim::PrimSuperAngel() { return true; } +// OFFSET: 0x00BC3FA0 +// FUNCTION: fn_19841 +void CEffectPrim::PrimTanji2() { + EffectPrimitiveSlot* slots = this->state.slots; + FaceCamera(slots); + + const CRagEffect* master = this->state.master; + this->render.position.x = master->render.position.x; + this->render.position.y = master->render.position.y; + this->render.position.z = master->render.position.z; + + EffectPrimitiveSlot& slot = slots[0]; + + if (slot.life != 1) { + return; + } + + ++slot.process; + + if (slot.process <= 0) { + return; + } + + if (slot.flag1[2] == 1) { + if (slot.process <= 10) { + slot.alphaB += 25; + } + + if (slot.alphaB >= 255) { + slot.alphaB = 255; + } + } + + const int rate = slot.flag1[2] == 1 ? 1 : 3; + + if (slot.process % rate == 0) { + 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, 150); + } + } + + slot.fullDisplayAngle -= 15; + + if (slot.fullDisplayAngle < 0) { + slot.fullDisplayAngle += 360; + } + + if (slot.process == 1) { + const float rx = this->state.offset.x - slot.vecBNow.x; + const float rz = this->state.offset.z - slot.vecBNow.z; + Get_Angle(rx, rz, &slot.rotStart); + } + + slot.vecBNow.x += slot.height[1] * g_cosTable[slot.rotStart]; + slot.vecBNow.z += slot.height[1] * g_sinTable[slot.rotStart]; + + if (slot.process % 4 == 0) { + CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); + CRagEffect* effect = gameMode->scene->player->LaunchEffect(416, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); + + if (effect) { + vector3d position = slot.vecBNow; + effect->SendMsg(nullptr, 14, 0, reinterpret_cast(&position), 0, 0, 0, 0); + } + } +} + // OFFSET: 0x00BBE350 // FUNCTION: fn_19811 bool CEffectPrim::PrimGhost() { @@ -2428,6 +2504,215 @@ bool CEffectPrim::Prim3DParticleGravity() { return this->AnimMotion(); } +// OFFSET: 0x00BB9610 +// FUNCTION: fn_19786 +bool CEffectPrim::Prim3DTextureNoMaster() { + EffectPrimitiveState& particle = this->state; + const vector3d origin{ + particle.origin.x + particle.velocity.x, particle.origin.y + particle.velocity.y, particle.origin.z + particle.velocity.z + }; + + if (particle.unknown_0200 == particle.unknown_01E0) { + constexpr float sentinel = 0.0001f; + + if (particle.unknown_02C8 != sentinel) { + particle.unknown_0358 = particle.unknown_02C8; + } + + if (particle.unknown_02CC != sentinel) { + particle.unknown_0364 = particle.unknown_02CC; + } + + if (particle.unknown_029C != sentinel) { + particle.unknown_02BC = particle.unknown_029C; + } + + if (particle.unknown_02C4 != sentinel) { + particle.unknown_035C = particle.unknown_02C4; + } + + if (particle.unknown_02D0 != sentinel) { + particle.unknown_0368 = particle.unknown_02D0; + } + + if (particle.unknown_02A0 != sentinel) { + particle.unknown_02C0 = particle.unknown_02A0; + } + + if (particle.unknown_02A4.x != sentinel) { + particle.unknown_0370.x = particle.unknown_02A4.x; + } + + if (particle.unknown_02A4.y != sentinel) { + particle.unknown_0370.y = particle.unknown_02A4.y; + } + + if (particle.unknown_02A4.z != sentinel) { + particle.unknown_0370.z = particle.unknown_02A4.z; + } + + if (particle.unknown_02B0.x != sentinel) { + particle.unknown_03AC.x = particle.unknown_02B0.x; + } + + if (particle.unknown_02B0.y != sentinel) { + particle.unknown_03AC.y = particle.unknown_02B0.y; + } + + if (particle.unknown_02B0.z != sentinel) { + particle.unknown_03AC.z = particle.unknown_02B0.z; + } + } + + particle.unknown_0370.x = particle.unknown_03AC.x + particle.unknown_0370.x; + particle.unknown_0370.y = particle.unknown_03AC.y + particle.unknown_0370.y; + particle.unknown_0370.z = particle.unknown_03AC.z + particle.unknown_0370.z; + particle.unknown_02BC = particle.unknown_02C0 + particle.unknown_02BC; + particle.unknown_0358 = particle.unknown_035C + particle.unknown_0358; + particle.unknown_0364 = particle.unknown_0364 + particle.unknown_0368; + particle.unknown_0330 = particle.unknown_033C + particle.unknown_0330; + particle.unknown_0334 = particle.unknown_0340 + particle.unknown_0334; + + if ((particle.flags & 0x400u) != 0) { + particle.unknown_0370 = {}; + particle.unknown_0370 = Forward(particle.unknown_02BC, particle.matrix1); + particle.offset.x = particle.unknown_0370.x + particle.offset.x; + particle.offset.y = particle.unknown_0370.y + particle.offset.y; + particle.offset.z = particle.unknown_0370.z + particle.offset.z; + } else if ((particle.flags & 0x100u) != 0) { + particle.offset.x = particle.unknown_0370.x + particle.offset.x; + particle.offset.y = particle.unknown_0370.y + particle.offset.y; + particle.offset.z = particle.unknown_0370.z + particle.offset.z; + } else { + particle.unknown_0370 = Forward(particle.unknown_02BC, particle.matrix1); + particle.offset.x = particle.unknown_0370.x + particle.offset.x; + particle.offset.y = particle.unknown_0370.y + particle.offset.y; + particle.offset.z = particle.unknown_0370.z + particle.offset.z; + } + + if ((particle.flags & 0x400u) != 0) { + if (3.0f >= this->CalcDist(particle.master->state.offset[0], particle.master->state.offset[2])) { + particle.unknown_014C.x = std::bit_cast(1u); + return false; + } + + particle.unknown_030C = this->render.angle; + } + + this->render.position.x = origin.x + particle.offset.x; + this->render.position.y = origin.y + particle.offset.y; + this->render.position.z = origin.z + particle.offset.z; + particle.unknown_0354 = particle.unknown_0354 + particle.unknown_0358; + particle.unknown_0360 = particle.unknown_0360 + particle.unknown_0364; + particle.unknown_030C = particle.unknown_0330 + particle.unknown_030C; + particle.unknown_0310 = particle.unknown_0334 + particle.unknown_0310; + + if (!(particle.unknown_0360 <= particle.unknown_036C)) { + particle.unknown_0368 = 0; + particle.unknown_0364 = 0; + } + + auto& rotation = *reinterpret_cast(particle.matrix2); + float angle; + + if ((particle.flags & 0x200u) != 0) { + const CView* view = static_cast(g_modeMgr.GetGameMode())->camera; + rotation.MakeZRotation(view->camera.pitch); + angle = this->render.angle; + } else { + if ((particle.flags & 0x40u) != 0) { + const CView* view = static_cast(g_modeMgr.GetGameMode())->camera; + particle.unknown_030C = view->camera.yaw + 90.0f; + particle.unknown_0314 = view->camera.pitch; + rotation.MakeXRotation(GetRadian(static_cast(particle.unknown_0310))); + rotation.AppendZRotation(GetRadian(static_cast(particle.unknown_0314))); + } else { + rotation.MakeZRotation(GetRadian(static_cast(particle.unknown_0314))); + rotation.AppendXRotation(GetRadian(static_cast(particle.unknown_0310))); + } + + angle = particle.unknown_030C; + } + + rotation.AppendYRotation(GetRadian(static_cast(angle))); + + if ((particle.flags & 2u) != 0) { + const auto index = particle.unknown_01D4; + particle.unknown_01D4 = index + 1u == 10u ? 0u : index + 1u; + const float offset = static_cast(static_cast(index)) / 10.0f; + particle.unknown_02E4 = offset; + particle.unknown_02E8 = offset + 1.0f; + } + + if ((particle.flags & 0x10u) != 0) { + particle.master->state.offset[0] = particle.velocity.x + particle.offset.x; + particle.master->state.offset[1] = particle.offset.y + particle.velocity.y; + particle.master->state.offset[2] = particle.offset.z + particle.velocity.z; + } + + if ((particle.flags & 4u) != 0) { + CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); + + if (this->render.position.y > mode->scene->terrain->Grid().Height(this->render.position.x, this->render.position.z)) { + particle.flags &= ~4u; + particle.unknown_01E0 = particle.lifetime - 25; + particle.unknown_02BC = 0; + particle.master->state.unknown_0170 = 1; + } + } + + if ((particle.flags & 0x20u) != 0 && particle.unknown_01E0 % static_cast(particle.unknown_01CC) == 0) { + particle.unknown_0364 *= -1.0f; + } + + const bool wrap254 = (particle.flags & 0x800u) != 0; + const bool gated = wrap254 && (particle.red == 254 || particle.blue == 254 || particle.green == 254); + bool advance = true; + + if (gated) { + if (particle.unknown_0234 != 0) { + ++particle.unknown_01FC; + } + + advance = particle.unknown_0234 == particle.unknown_01FC; + } + + if (advance) { + particle.red += particle.unknown_0248; + particle.green += particle.unknown_024C; + particle.blue += particle.unknown_0250; + + if (gated) { + particle.unknown_01FC = 0; + } + } + + if (wrap254) { + particle.red = particle.red > 254 ? 0 : particle.red < 0 ? 254 : particle.red; + particle.green = particle.green > 254 ? 0 : particle.green < 0 ? 254 : particle.green; + particle.blue = particle.blue > 254 ? 0 : particle.blue < 0 ? 254 : particle.blue; + } else { + particle.red = particle.red > 255 ? 0 : particle.red < 0 ? 255 : particle.red; + particle.green = particle.green > 255 ? 255 : particle.green < 0 ? 0 : particle.green; + particle.blue = particle.blue > 255 ? 255 : particle.blue < 0 ? 0 : particle.blue; + } + + particle.ProcessAlpha(); + + if (particle.unknown_01E0 % particle.unknown_01E8 == 0) { + const auto next = this->render.curMotion + 1u; + this->render.curMotion = next; + + if (particle.unknown_0254 != 0) { + this->render.curMotion = static_cast(next) % static_cast(particle.textureCount); + } else if (next == particle.textureCount) { + return false; + } + } + + return true; +} + // OFFSET: 0x00BB78A0 // FUNCTION: fn_19781 bool CEffectPrim::Prim3DParticleNoMaster() { diff --git a/Source/Contents/Effects/PartyMemberEffects.cpp b/Source/Contents/Effects/PartyMemberEffects.cpp index ceadd8f7..081125aa 100644 --- a/Source/Contents/Effects/PartyMemberEffects.cpp +++ b/Source/Contents/Effects/PartyMemberEffects.cpp @@ -1,32 +1,98 @@ #include "Contents/Effects/RagEffect.h" #include "Contents/Effects/EffectActor.h" #include "Contents/Effects/PartyMemberEffects.h" -#include "Contents/Effects/RagEffectInitialization.h" +#include "Client/Application/LaunchInfo.h" +#include "Client/Mode/CGameMode.h" +#include "Client/Mode/ModeMgr.h" +#include "Client/Session.h" +#include "Contents/World/World.h" #include "Script/LuaScript.h" #include "Utility/Math/AffineMatrix.h" +#include + namespace { - // OFFSET: 0x00CADD60 - // FUNCTION: fn_21069 - CRagEffect* CreatePartyEffect(EffectActor& actor, PartyEffectMode& mode) { - mode.lastEffectId = 160; - auto* effect = new CRagEffect(); - bool force = false; - mode.effectScript.Call("IsForceRenderEffect", "d>b", 160, &force); - effect->state.forceRender = force; + struct CRollingCutterRagEffect : CRagEffect { + CRollingCutterRagEffect() : duration(60), elapsed(0) { + } - const float sine = ClientSine(0); - const float cosine = ClientCosine(0); - const float x = 0.0f * cosine - 0.0f * sine; - const float z = 0.0f * cosine + 0.0f * sine; - InitializeRagEffect(effect->state, effect->render, &actor, actor.render, 160, x, 0, z, mode.options, mode.resources); - mode.objects.push_back(effect); - actor.state.effects.push_back(&effect->state); + /* 11CA8 */ int duration; + /* 11CAC */ int elapsed; + }; - return effect; + struct CWhirlwindEffect : CRagEffect { + CWhirlwindEffect() : duration(250), first(0), second(0) { + } + + /* 11CA8 */ int duration; + /* 11CAC */ int unknown_11CAC; + /* 11CB0 */ int first; + /* 11CB4 */ int second; + }; + + void RecordLaunch(int effectId) { + std::uint32_t recent[8]; + recent[0] = effectId; + std::memcpy(recent + 1, g_launchInfo, sizeof(recent) - sizeof(recent[0])); + std::memcpy(g_launchInfo, recent, sizeof(recent)); } } +// OFFSET: 0x00CADD60 +// FUNCTION: fn_21069 +CRagEffect* EffectActor::LaunchEffect(int effectId, vector3d deltaPos, float rotation) { + CRagEffect* effect; + + if (effectId == 127) { + this->state.unknown_0140 = 1; + RecordLaunch(effectId); + effect = new CRagEffect(); + } else { + if (effectId < 0 || effectId > 60000) { + return nullptr; + } + + RecordLaunch(effectId); + + if (effectId == 837) { + effect = new CRollingCutterRagEffect(); + } else if (effectId == 2444) { + effect = new CWhirlwindEffect(); + } else { + effect = new CRagEffect(); + } + } + + if (!effect) { + return nullptr; + } + + bool force = false; + g_session.m_hotkeyScript->Call("IsForceRenderEffect", "d>b", effectId, &force); + effect->state.forceRender = force; + + const int degrees = static_cast(rotation); + const float sine = ClientSine(degrees); + const float cosine = ClientCosine(degrees); + const float x = deltaPos.x * cosine - deltaPos.z * sine; + const float z = deltaPos.z * cosine + deltaPos.x * sine; + deltaPos.x = x; + deltaPos.z = z; + + if (effectId == 166 && this->render.unknown_00A8 == 0) { + return nullptr; + } + + if (!this) { + return nullptr; + } + + effect->Init(this, effectId, deltaPos); + static_cast(g_modeMgr.GetGameMode())->scene->objects.push_back(effect); + this->state.effects.push_back(&effect->state); + return effect; +} + // INLINED bool PartyMemberEffects::Enabled() const { const auto* mode = this->activeMode; @@ -40,7 +106,7 @@ void PartyMemberEffects::EnsurePartyEffect(std::uint32_t accountId) { for (EffectActor* actor : mode.actors) { if (actor->render.accountId == accountId) { if (!actor->state.Attachments().HasId(160)) { - CreatePartyEffect(*actor, mode); + actor->LaunchEffect(160, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } return; diff --git a/Source/Contents/Effects/RagEffect.h b/Source/Contents/Effects/RagEffect.h index 7f2e5e63..9b778d79 100644 --- a/Source/Contents/Effects/RagEffect.h +++ b/Source/Contents/Effects/RagEffect.h @@ -3,13 +3,29 @@ #include "Contents/Effects/RagEffectState.h" #include "Contents/Actors/RenderObject.h" +#include "Utility/Math/Vector3d.h" #include +#include + +struct EffectActor; struct CRagEffect : CRenderObject { CRagEffect(); ~CRagEffect() override; + virtual void Slot08(); + virtual void Slot09(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth, int ninth, int tenth, int eleventh, int twelfth, int thirteenth, int fourteenth, int fifteenth, int sixteenth, int seventeenth); + virtual void Slot10(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth, int ninth); + virtual void Slot11(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth, int ninth, int tenth); + virtual void Slot12(int first, int second, int third, int fourth, int fifth); + virtual void Slot13(); + virtual void Slot14(); + virtual void Slot15(); + virtual void SendMsg(CGameObject* sender, int message, int first, std::int64_t second, int third, int fourth, int fifth, int sixth); + virtual void Init(EffectActor* parent, int id, vector3d deltaPos); + virtual void Slot18(); + /* 0138 */ RagEffectState state; };