#include "Contents/Effects/EffectLists.h" #include "Contents/Effects/EffectTypes.h" #include "Contents/Effects/CharacterParticleBundle.h" #include "Contents/Effects/GravityControlEffect.h" #include "Contents/Effects/SkillEffects.h" #include "Client/Mode/CGameMode.h" #include "Client/Session.h" #include "Client/Mode/ModeMgr.h" #include "Contents/Actors/RenderObject.h" #include "Contents/Camera/Camera.h" #include "Render/Renderer.h" #include "Utility/Cipher/XorBytes.h" #include "Utility/Math/AffineMatrix.h" #include #include #include #include // GLOBAL: gl_24787 int g_levelEffectId1; // GLOBAL: gl_24788 int g_levelEffectId2; void CRenderObject::ClearLevelEffects() { reinterpret_cast(this)[0x2B8] = 0; this->SendMsg(this, 0x6D, 0, 0xC8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, 0x3D0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, 0x3D1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, 0x3D2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, 0x3D3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, 0x3FE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, 0x3FF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, 0x4CA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, 0x4CB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0x2F5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0x370, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0xC8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0x18D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0x18E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0x16A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0x16A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0xC9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0xCA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, 0x6D, 0, 0xCA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, g_levelEffectId1, g_levelEffectId1 >> 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); this->SendMsg(this, 0x6D, 0, g_levelEffectId2, g_levelEffectId2 >> 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } // OFFSET: 0x00B25750 // FUNCTION: fn_18783 CEffect* EffectLists::Find(int actor, int id) const { const auto found = this->active.find(actor); if (found != this->active.end()) { for (CEffect* effect : found->second) { if (effect->id == id) { return effect; } } } return nullptr; } // OFFSET: 0x00B3B040 // FUNCTION: fn_18794 void EffectLists::Render() { if (!g_session.IsEffectRenderEnabled()) { return; } for (auto& entry : this->active) { for (CEffect* effect : entry.second) { effect->UpdateVisibility(); } } } // OFFSET: 0x00B3BE50 // FUNCTION: fn_18803 float DirectionAngle(float x, float z) { const float length = SquareRoot(x * x + z * z); const float angle = static_cast(std::acos(static_cast(1.0f / length * x))) / 3.141592f * 180.0f; if (z < 0.0f) { return 360.0f - angle; } return angle; } // OFFSET: 0x00B3BED0 // FUNCTION: fn_18804 double ScreenAngle(vector3d from, vector3d to) { float fromReciprocalDepth = 0.0f; float toReciprocalDepth = 0.0f; float fromX; float fromY; float toX; float toY; g_renderer->ProjectViewport(&from.x, static_cast(g_modeMgr.GetGameMode())->camera->frame.view.values, &fromX, &fromY, &fromReciprocalDepth); g_renderer->ProjectViewport(&to.x, static_cast(g_modeMgr.GetGameMode())->camera->frame.view.values, &toX, &toY, &toReciprocalDepth); const float dx = toX - fromX; const float dy = toY - fromY; double angle = static_cast(std::acos(static_cast(dx / SquareRoot(dy * dy + dx * dx)))) / 3.141592f * 180.0f; if (fromY > toY) { angle = 360.0 - angle; } return angle; } // OFFSET: 0x00B3BFD0 // FUNCTION: fn_18805 std::string CurrentMapScriptName() { std::string name = reinterpret_cast(g_modeMgr.GetGameMode()->SendMsg(9, 0, 0, 0, 0)); const std::size_t at = name.rfind('@'); if (at != std::string::npos && at > 3) { name.erase(at - 4, 3); } return name.substr(0, name.find(".")); } // OFFSET: 0x00B3C1C0 // FUNCTION: fn_18806 std::string EffectFunctionName(std::string name) { std::string result = "_" + name + "_Effect"; const std::size_t at = result.find("@"); if (at != std::string::npos) { result.erase(at, 1); } return result; } // OFFSET: 0x00B257C0 // FUNCTION: fn_18784 CEffect* EffectLists::Find(int actor, int instance, int id) const { const auto found = this->active.find(actor); if (found != this->active.end()) { for (CEffect* effect : found->second) { if (effect->id == id && effect->unknown_00C4 == instance) { return effect; } } } return nullptr; } // OFFSET: 0x00B253A0 // FUNCTION: fn_18779 void EffectLists::Retire(CEffect* effect, unsigned char removeActive) { if (effect) { this->retired.push_back(effect); if (removeActive == 1 && this->active.find(static_cast(effect->actor)) != this->active.end()) { this->active[static_cast(effect->actor)].remove(effect); } } } // OFFSET: 0x00B25120 // FUNCTION: fn_18777 void EffectLists::Retire(int actor, int id) { if (CEffect* effect = this->Find(actor, id)) { this->retired.push_back(effect); if (this->active.find(static_cast(effect->actor)) != this->active.end()) { this->active[static_cast(effect->actor)].remove(effect); } } } // OFFSET: 0x00B25240 // FUNCTION: fn_18778 void EffectLists::Retire(int actor, int instance, int id) { if (actor > 0 && instance > 0) { if (CEffect* effect = this->Find(actor, instance, id)) { this->retired.push_back(effect); if (this->active.find(static_cast(effect->actor)) != this->active.end()) { this->active[static_cast(effect->actor)].remove(effect); } } } } // OFFSET: 0x00B24320 // FUNCTION: fn_18772 void EffectLists::RetireActor(int actor) { const auto found = this->active.find(actor); if (found == this->active.end()) { return; } for (auto effect = found->second.begin(); effect != found->second.end(); ++effect) { this->retired.push_back(*effect); } found->second.clear(); } // OFFSET: 0x00B25830 // FUNCTION: fn_18785 CEffect* EffectLists::At(int id, unsigned index) const { const auto found = this->indexed.find(id); if (found == this->indexed.end() || index >= found->second.size()) { return nullptr; } return found->second[index]; } // OFFSET: 0x00B24190 // FUNCTION: fn_18771 void EffectLists::Add(int actor, CEffect* effect) { this->active[actor].push_back(effect); switch (effect->id) { case EF_EMITTER: case EF_MAGIC_FLOOR: case EF_EVILS_PAW: case EF_ANIMATED_EMITTER: case EF_NEW_RANGEDATTACK: case EF_UNK_2343: this->indexed[effect->id].push_back(effect); break; } } // OFFSET: 0x00B24A50 // FUNCTION: fn_18774 CEffect* EffectLists::Spawn(int actor, int id, float x, float y, float z, std::uint32_t sourceActor, int frame, unsigned char reuse, int instance) { CEffect* effect = nullptr; if (id < 0 || id > 60000) { switch (id) { case EF_READYPORTAL: case EF_PORTAL: case EF_LOCKON: case EF_BOTTOM: case EF_BOTTOM2: case EF_FIREPILLARON: case EF_REFINEOK: case EF_REFINEFAIL: case EF_TOPRANK: case EF_PARTY: case EF_STATUS_STATE: case EF_TALK_FROSTJOKE: case EF_TALK_SCREAM: case EF_BLIND: case EF_ANGEL2: case EF_MINI_TETRIS: case EF_ANGEL: case EF_REFLECTBODY: case EF_ASURABODY: case EF_QUAKEBODY: case EF_JUMPBODY: case EF_LANDBODY: case EF_TAE_READY: case EF_DEVIL1: case EF_DEVIL2: case EF_DEVIL3: case EF_DEVIL4: case EF_DEVIL5: case EF_DEVIL6: case EF_DEVIL7: case EF_DEVIL8: case EF_DEVIL9: case EF_DEVIL10: case EF_BLUEBODY: case EF_REDLIGHTBODY: case EF_RED_HIT: case EF_BLUE_HIT: case EF_ANGEL3: case EF_UNDEADBODY: case EF_UNDEADBODY_DEL: case EF_FIREPILLARON2: case EF_JUMPBODY1: case EF_UNK_1398: break; default: return nullptr; } } else if (id == EF_LEVEL150 || id == EF_LEVEL150_SUB) { if (this->Find(actor, EF_LEVEL99)) { return nullptr; } } if (actor) { effect = this->Find(actor, instance, id); if (effect) { if (instance > 0) { return nullptr; } if (effect->retained || reuse) { return effect; } } } const auto allocator = this->allocators.find(id); if (allocator == this->allocators.end()) { return nullptr; } effect = allocator->second->Create(); if (effect) { effect->SetActor(actor); effect->SetSourceActor(sourceActor); effect->position[0] = x; effect->position[1] = y; effect->position[2] = z; effect->Slot29(id); if (frame != -1) { effect->unknown_00B0 = frame; } if (instance > 0) { effect->unknown_00C4 = instance; effect->retained = 1; } effect->Slot01(); effect->Slot12(); this->Add(actor, effect); } return effect; } // OFFSET: 0x00B254C0 // FUNCTION: fn_18780 void EffectLists::Clear() { for (auto entry = this->active.begin(); entry != this->active.end(); ++entry) { this->RetireActor(entry->first); } this->active.clear(); this->RetireActor(0); this->indexed.clear(); for (auto effect = this->retired.begin(); effect != this->retired.end(); ++effect) { (*effect)->Close(); delete *effect; } this->retired.clear(); } // OFFSET: 0x00B255B0 // FUNCTION: fn_18781 void EffectLists::Release() { for (auto entry = this->allocators.begin(); entry != this->allocators.end(); ++entry) { delete entry->second; } this->allocators.clear(); this->Clear(); } // OFFSET: 0x00B1D530 // FUNCTION: fn_45453 EffectLists::~EffectLists() { this->Release(); } // OFFSET: 0x00B25730 // FUNCTION: fn_18782 void DestroyEffectMgr() { if (g_effectMgr) { delete g_effectMgr; g_effectMgr = nullptr; } } namespace { template class EncryptedName { public: explicit EncryptedName(const std::uint8_t (&source)[Length]) : m_encrypted(true) { for (std::uint64_t index = 0; index < Length; ++index) { this->m_text[index] = source[index]; } } ~EncryptedName() { for (std::uint64_t index = 0; index < Length; ++index) { this->m_text[index] = 0; } } const char* Text() { if (this->m_encrypted) { utility::XorBytes(this->m_text, Length, Key); this->m_encrypted = false; } return reinterpret_cast(this->m_text); } private: std::uint8_t m_text[Length]; bool m_encrypted; }; class CBezierArcThrowEffect : public CEffect { public: // INLINED explicit CBezierArcThrowEffect(const std::string& name) : CEffect(-1), name(name), unknown_00F0(0), unknown_00F4(0), unknown_00F8(0) {} /* 00CC */ int unknown_00CC; /* 00D0 */ int unknown_00D0; /* 00D4 */ int unknown_00D4; /* 00D8 */ std::string name; /* 00F0 */ int unknown_00F0; /* 00F4 */ int unknown_00F4; /* 00F8 */ int unknown_00F8; }; class CThunderingOrb : public CBezierArcThrowEffect { public: // INLINED explicit CThunderingOrb(const std::string& name) : CBezierArcThrowEffect(name) {} }; class CWindBomb : public CBezierArcThrowEffect { public: // INLINED explicit CWindBomb(const std::string& name) : CBezierArcThrowEffect(name) {} }; #if defined(_WIN32) && !defined(_WIN64) static_assert(sizeof(CBezierArcThrowEffect) == 0xFC); static_assert(sizeof(CThunderingOrb) == 0xFC); static_assert(sizeof(CWindBomb) == 0xFC); #endif template struct CEffectAllocator : CAllocator { CEffect* Create() override; }; } // OFFSET: 0x00B1D9E0 // FUNCTION: fn_18561 const char* ThunderingOrbName() { const std::uint8_t source[] = { 0x3B, 0x47, 0xD8, 0x58, 0x73, 0x53, 0xC0, 0x7D, 0x20, 0x41, 0xC5, 0x44, 0x79, 0x4B, 0xCE, 0x7D, 0x36, 0x5B, 0xCA, 0x4E, 0x78, 0x5D, 0xC9, 0x73, 0x0B, 0x7D, 0xF8, 0x7F, 0x53, 0x6A, 0xF9, 0x46, 0x11, 0x72, 0xD2, 0x7E, 0x45, 0x6D, 0xD4, 0x40, 0x0D, 0x77, 0xD1, 0x65, 0x5F, 0x7A, 0xE5, 0x4B, 0x1A, 0x67, 0xE4, 0x7F, 0x50, 0x50, 0xE4, 0x5D, 0x1D, 0x4A, 0xFE, 0x7C, 0x56, 0x63, 0xE7, 0x01, 0x0C, 0x61, 0xFF, 0x11}; thread_local EncryptedName name(source); return name.Text(); } // OFFSET: 0x00B24FA0 // FUNCTION: fn_18776 bool CreateEffectMgr() { if (g_effectMgr) { return false; } EffectLists* mgr = new (std::nothrow) EffectLists(); if (mgr) { mgr->initialized = false; mgr->initialized = true; } g_effectMgr = mgr; if (!g_effectMgr) { return false; } if (!g_effectMgr->initialized) { delete g_effectMgr; g_effectMgr = nullptr; return false; } return true; } // OFFSET: 0x00B24DE0 // FUNCTION: fn_18775 void EffectLists::SpawnLevel99(CRenderObject* actor, unsigned char flag, int extra) { if (!actor || extra != 0) { return; } actor->ClearLevelEffects(); if (!flag) { return; } const int aid = static_cast(actor->render.accountId); const float x = actor->render.position.x; const float y = actor->render.position.y; const float z = actor->render.position.z; CEffect* effect = nullptr; if (aid) { effect = this->Find(aid, 0, EF_LEVEL99); if (effect && effect->retained) { goto applyLevel99; } } { const auto allocator = this->allocators.find(EF_LEVEL99); if (allocator == this->allocators.end()) { return; } effect = allocator->second->Create(); if (!effect) { return; } effect->SetActor(static_cast(aid)); effect->SetSourceActor(0); effect->position[0] = x; effect->position[1] = y; effect->position[2] = z; effect->Slot29(EF_LEVEL99); effect->Slot01(); effect->Slot12(); this->Add(aid, effect); } applyLevel99: { const char path[] = {'e', 'f', 'f', 'e', 'c', 't', '\\', '\xb9', '\xf8', '\xb0', '\xb3', '3', '.', 'b', 'm', 'p'}; effect->Slot11(std::string(path, sizeof(path))); effect->Slot08(0x50, 0x50, 0xFF); } } namespace { // OFFSET: 0x00B20B20 // FUNCTION: fn_18655 template <> CEffect* CEffectAllocator::Create() { return new (std::nothrow) CThunderingOrb(ThunderingOrbName()); } // OFFSET: 0x00B20E70 // FUNCTION: fn_18660 template <> CEffect* CEffectAllocator::Create() { const std::uint8_t source[] = { 0xCB, 0xCB, 0xBE, 0x44, 0xF1, 0x23, 0xBB, 0x6B, 0xD0, 0xCE, 0xA2, 0x43, 0xF1, 0x20, 0xBD, 0x76, 0xC2, 0xDB, 0xB7, 0x7A, 0xDC, 0x11, 0x9B, 0x66, 0xED, 0xF6, 0x86, 0x6F, 0xEA, 0x0B, 0x90, 0x4B, 0xE1, 0xF8, 0x8F, 0x62, 0xE9, 0x08, 0x96, 0x57, 0xEB, 0xC6, 0x89, 0x62, 0xD8, 0x1D, 0xA0, 0x4D, 0xE0, 0xEB, 0x85, 0x6C, 0xD1, 0x10, 0xD1, 0x4A, 0xFB, 0xEB, 0xEB}; thread_local EncryptedName name(source); return new (std::nothrow) CWindBomb(name.Text()); } // OFFSET: 0x00B1F010 // FUNCTION: fn_18604 template <> CEffect* CEffectAllocator::Create() { return new (std::nothrow) CGravityControlEffect(); } // OFFSET: 0x00B1F740 // FUNCTION: fn_18618 template <> CEffect* CEffectAllocator::Create() { return new (std::nothrow) CMeteorEffect(); } // OFFSET: 0x00B20110 // FUNCTION: fn_18637 template <> CEffect* CEffectAllocator::Create() { return new (std::nothrow) CShadowExceedNew(); } // OFFSET: 0x00B20400 // FUNCTION: fn_18642 template <> CEffect* CEffectAllocator::Create() { return new (std::nothrow) CSprArrowEffect(); } // OFFSET: 0x00B1E1D0 // FUNCTION: fn_18576 template <> CEffect* CEffectAllocator::Create() { return new (std::nothrow) CChopChopClawMarks(); } template struct CEffectAllocator; template struct CEffectAllocator; template struct CEffectAllocator; template struct CEffectAllocator; template struct CEffectAllocator; template struct CEffectAllocator; template struct CEffectAllocator; } CGravityControlEffect::~CGravityControlEffect() { } CMeteorEffect::~CMeteorEffect() { } CShadowExceedNew::~CShadowExceedNew() { } CSprArrowEffect::~CSprArrowEffect() { } CChopChopClawMarks::~CChopChopClawMarks() { }