#include "Contents/Effects/MonsterSkillEffects.h" #include "Client/Mode/CGameMode.h" #include "Client/Mode/ModeMgr.h" #include "Client/Session.h" #include "Contents/Camera/Camera.h" #include "Contents/Configuration/ServiceOptions.h" #include "Contents/Effects/EffectActor.h" #include "Contents/Effects/EffectMemoryMgr.h" #include "Contents/Effects/GravityControlEffect.h" #include "Contents/Effects/Particle.h" #include "Contents/Effects/SkillEffects.h" #include "Contents/Effects/SpriteParticle.h" #include "Contents/World/World.h" #include "Render/Renderer.h" #include "Render/TextureQuad.h" #include "Render/Textures/Texture.h" #include "Resource/Sprites/ActionResource.h" #include "Utility/Math/AffineMatrix.h" #include "Utility/Text/FormatString.h" #include "Contents/Effects/EffectTypes.h" #include "Contents/Effects/EZ2STREffectEx.h" #include "Resource/World/TerrainResource.h" #include "Script/LuaScript.h" #include "Utility/Text/StringUtil.h" #include #include #include #include #include // INLINED static void SetScreenVertex(RenderVertex& vertex, float x, float y, std::uint32_t color, float u, float v) { vertex.x = x; vertex.y = y; vertex.z = 0.000001f; vertex.rhw = 0.999999f; vertex.diffuse = color; vertex.specular = 0xFF000000; vertex.u = u; vertex.v = v; } // INLINED static CEZ2STRParticle* AsParticle(EZ2STREmitter* emitter) { return reinterpret_cast(emitter); } // INLINED static CSpriteParticle* AsSprite(EZ2STREmitter* emitter) { return reinterpret_cast(emitter); } void __stdcall fn_07334(CParticle* particle); void __stdcall fn_07337(CParticle* particle); void __stdcall fn_07338(CParticle* particle); void __stdcall fn_07339(CParticle* particle); // OFFSET: 0x0070C070 // FUNCTION: fn_07123 bool CCrimsonArrowEffect::Slot06() { EZ2STREmitter* emitter = this->emitter; if (!emitter) { return false; } if (emitter->lifetime == 0.0f) { return true; } return emitter->lifetime * 60.0f > static_cast(emitter->elapsed); } // OFFSET: 0x0070C150 // FUNCTION: fn_07126 CRootTwistEffect::CRootTwistEffect() : CEffect(1110) { } // OFFSET: 0x0070C0B0 // FUNCTION: fn_07124 void CCrimsonArrowEffect::Slot03() { if (!this->emitter) { return; } CEZ2STRParticle* particle = AsParticle(this->emitter); particle->position.x = this->position[0] + this->emitter->offset.x; particle->position.y = this->position[1] + this->emitter->offset.y; particle->position.z = this->position[2] + this->emitter->offset.z; particle->Process(0.0f, 0.0f, 0.0f); } // OFFSET: 0x0070C120 // FUNCTION: fn_07125 void CCrimsonArrowEffect::Slot04() { if (this->emitter) { AsParticle(this->emitter)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } // OFFSET: 0x0070C3B0 // FUNCTION: fn_07130 void CRootTwistEffect::Slot12() { if (this->id == 1110) { this->AddEvent(0, "effect\\su_roottwist.wav", 0, 0); } } // OFFSET: 0x0070C3D0 // FUNCTION: fn_07131 bool CRootTwistEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x0070C3E0 // FUNCTION: fn_07132 void CRootTwistEffect::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; ++emitter->elapsed; emitter->position.x = this->position[0] + 0.0f; emitter->position.y = this->position[1] + 3.0f; emitter->position.z = this->position[2] + 0.0f; } } // OFFSET: 0x0070C440 // FUNCTION: fn_07133 void CRootTwistEffect::Slot04() { for (auto emitter = this->emitters.begin(); emitter != this->emitters.end(); ++emitter) { AsSprite(*emitter)->sub_B40F30(1); } } // OFFSET: 0x0070C7F0 // FUNCTION: fn_07137 bool CSprArrowEffect::Slot06() { return this->unknown_00FD; } // OFFSET: 0x0070C800 // FUNCTION: fn_07138 void CSprArrowEffect::Slot03() { const float ticks = static_cast(static_cast(timeGetTime() - static_cast(this->unknown_00D0)) / 24.0); if (this->unknown_00F4 > ticks) { return; } CSpriteParticle* particle = reinterpret_cast(this->unknown_00CC); if (!particle || !particle->action) { return; } if (!this->unknown_00FC) { this->unknown_00FC = 1; this->SetSourceActor(this->sourceActor); const float dx = this->sourcePosition[0] - particle->position.x; const float dy = this->sourcePosition[1] - particle->position.y; const float dz = this->sourcePosition[2] - particle->position.z; this->unknown_00E0 = dx * 0.125f; this->unknown_00E4 = dy * 0.125f; this->unknown_00E8 = dz * 0.125f; const float lengthSquared = dy * dy + dx * dx + dz * dz; const float length = SquareRoot(lengthSquared); if (length < 40.0f) { this->unknown_00EC = 40.0f / length; this->unknown_00E0 *= this->unknown_00EC; this->unknown_00E4 *= this->unknown_00EC; this->unknown_00E8 *= this->unknown_00EC; } float angle = 0.0f; if (dx != 0.0f || dz != 0.0f) { angle = static_cast(std::atan2(static_cast(-dx), static_cast(-dz))) / 3.141592f * 180.0f; if (angle < 0.0f) { angle += 360.0f; } } this->unknown_00F0 = angle; const float inverse = 1.0f / SquareRoot(lengthSquared); particle->position.x += inverse * dx * 2.5f; particle->position.y += inverse * dy * 2.5f; particle->position.z += inverse * dz * 2.5f; } ++particle->unknown_00E8; const float time = ticks - this->unknown_00F4; if (this->frame > 8) { const int last = static_cast(particle->action->FrameCount(0)) - 1; this->unknown_00F8 = std::min(this->unknown_00F8 + 1, last); } reinterpret_cast(particle->unknown_00C4) = this->unknown_00F0 - static_cast(g_modeMgr.GetGameMode())->camera->camera.yaw; particle->position.x = time * this->unknown_00E0 + this->unknown_00D4 + particle->velocity.x; particle->position.y = this->unknown_00E4 * time + this->unknown_00D8 + particle->velocity.y; particle->position.z = this->unknown_00E8 * time + this->unknown_00DC + particle->velocity.z; if (time > 8.0f / this->unknown_00EC) { this->unknown_00FD = false; } } // OFFSET: 0x0070CB80 // FUNCTION: fn_07139 void CSprArrowEffect::Slot04() { if (this->unknown_00CC != 0 && this->unknown_00FC) { AsSprite(reinterpret_cast(this->unknown_00CC))->sub_B40F30(1); } } // OFFSET: 0x0070CBA0 // FUNCTION: fn_07140 void CSprArrowEffect::SetBulletSprite(int job) { CSpriteParticle* particle = reinterpret_cast(this->unknown_00CC); if (!particle) { return; } const char* name; switch (job) { case 20936: case 21052: case 22216: case 22218: case 22219: case 22232: case 22234: case 22235: name = "canon_bullet"; break; case 22175: case 22176: name = "RIGEL_BULLET"; break; case 22204: name = "phylla_bullet"; break; case 22197: case 22198: case 22199: name = "mg_corruption_root_h_bullet"; break; case 22214: case 22215: case 22226: case 22227: name = "mg_corruption_root_bullet"; break; case 22245: name = "VR_MD_DREGG1_bullet"; break; case 22246: name = "VR_MD_DREGG2_bullet"; break; case 22322: name = "EP21_YORKER_Bullet"; break; case 22326: name = "EP21_YOSTER_D_bullet"; break; case 22327: name = "EP21_YOSTER_E_bullet"; break; case 22354: case 22418: name = "EP21_YORMI_bullet"; break; case 22355: case 22366: case 22416: name = "EP21_YORDOS_Bullet"; break; case 22359: case 22420: name = "EP21_YOSCOPUS_Bullet"; break; case 22383: case 22384: name = "violen_bullet"; break; case 22464: name = "CH1_SHADOW_JAILER_bullet"; break; case 22467: name = "CH1_SCABIOSA_bullet"; break; case 22468: name = "CH1_LOBELIA_bullet"; break; default: name = "skel_archer_arrow"; break; } if (std::strlen(name) == 0) { this->unknown_00FD = false; return; } particle->SetSprite(std::format("몬스터\\{}.spr", name).c_str()); reinterpret_cast(this->unknown_00CC)->SetAction(std::format("몬스터\\{}.act", name).c_str()); } // OFFSET: 0x0070CEA0 // FUNCTION: fn_07141 CSpriteMableEffect::CSpriteMableEffect() : CEffect(1096) { } // OFFSET: 0x0070D040 // FUNCTION: fn_07144 void CSpriteMableEffect::Slot01() { for (int i = 0; i < 3; ++i) { CSpriteParticle* particle = static_cast(CreateSpriteParticle(reinterpret_cast(this->position), 1)); if (!particle) { continue; } particle->unknown_0004 = this->id; particle->unknown_000C = i; particle->unknown_0010 = 0; particle->unknown_0008 = 1; particle->scale = 0.3f; particle->alpha = 200; particle->unknown_0054 = static_cast(i) * 2.0943947f; reinterpret_cast(particle->unknown_0058) = 0.0f; particle->loopMode = 3; switch (i) { case 0: particle->SetSprite("이팩트\\su_spritemable\\su_spritemable_green.spr"); particle->SetAction("이팩트\\su_spritemable\\su_spritemable_green.act"); break; case 1: particle->SetSprite("이팩트\\su_spritemable\\su_spritemable_red.spr"); particle->SetAction("이팩트\\su_spritemable\\su_spritemable_red.act"); break; case 2: particle->SetSprite("이팩트\\su_spritemable\\su_spritemable_blue.spr"); particle->SetAction("이팩트\\su_spritemable\\su_spritemable_blue.act"); break; } this->unknown_002C = 9999; this->emitters.push_back(reinterpret_cast(particle)); } } // OFFSET: 0x0070D150 // FUNCTION: fn_07145 bool CSpriteMableEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x0070D2C0 // FUNCTION: fn_07147 void CSpriteMableEffect::Slot04() { if (!g_serviceOptions.Get(240)) { return; } for (auto emitter = this->emitters.begin(); emitter != this->emitters.end(); ++emitter) { AsSprite(*emitter)->sub_B40F30(1); } } // OFFSET: 0x0070D300 // FUNCTION: fn_07148 CStemSpearEffect::CStemSpearEffect() : CEffect(1100) { } // OFFSET: 0x0070D4A0 // FUNCTION: fn_07151 void CStemSpearEffect::Slot01() { for (int i = 0; i < 4; ++i) { CSpriteParticle* particle = static_cast(CreateSpriteParticle(reinterpret_cast(this->position), 0x101)); if (!particle) { continue; } particle->unknown_0004 = this->id; particle->unknown_000C = i; particle->unknown_0010 = 0; particle->unknown_0008 = 1; particle->SetSprite("이팩트\\sv_stemspear\\sv_stemspear.spr"); particle->SetAction("이팩트\\sv_stemspear\\sv_stemspear.act"); EZ2STREmitter* emitter = reinterpret_cast(particle); emitter->velocity.x = 0.0f; emitter->velocity.y = 0.0f; emitter->velocity.z = 0.0f; this->unknown_002C = 16; const float dx = this->sourcePosition[0] - this->position[0]; const float dz = this->sourcePosition[2] - this->position[2]; float angle = static_cast(std::atan2(static_cast(dx), static_cast(-dz))) / 3.141592f * 180.0f; if (angle < 0.0f) { angle += 360.0f; } float direction = 180.0f - angle; if (direction < 0.0f) { direction += 360.0f; } emitter->baseAngle = static_cast(direction); emitter->speed = SquareRoot(dx * dx + dz * dz) / static_cast(this->unknown_002C); emitter->elapsed = -(this->unknown_002C / 4) * i; this->emitters.push_back(emitter); } } // OFFSET: 0x0070D650 // FUNCTION: fn_07152 void __stdcall Nullsub226(int) { } // OFFSET: 0x0070D660 // FUNCTION: fn_07153 bool CStemSpearEffect::Slot06() { for (auto emitter = this->emitters.begin(); emitter != this->emitters.end(); ++emitter) { if ((*emitter)->elapsed < this->unknown_002C) { return true; } } return false; } // OFFSET: 0x0070D850 // FUNCTION: fn_07156 CSvgSpiritEffect::CSvgSpiritEffect() : CEffect(1128), emitter(nullptr) { } // OFFSET: 0x0070D690 // FUNCTION: fn_07154 void CStemSpearEffect::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (emitter->elapsed >= 0) { this->SetSourceActor(this->sourceActor); emitter->angle = static_cast(emitter->baseAngle) - static_cast(g_modeMgr.GetGameMode())->camera->camera.yaw; const float dx = this->sourcePosition[0] - this->position[0]; const float dy = this->sourcePosition[1] - this->position[1]; const float dz = this->sourcePosition[2] - this->position[2]; const float inverse = 1.0f / SquareRoot(dx * dx + dy * dy + dz * dz); emitter->velocity.x += dx * inverse * emitter->speed; emitter->velocity.y += dy * inverse * emitter->speed; emitter->velocity.z += dz * inverse * emitter->speed; emitter->position.x = this->position[0] + emitter->velocity.x; emitter->position.y = this->position[1] - 10.0f; emitter->position.z = this->position[2] + emitter->velocity.z; if (this->frame >= 10) { emitter->alpha -= 40; } } ++emitter->elapsed; } } // OFFSET: 0x0070D810 // FUNCTION: fn_07155 void CStemSpearEffect::Slot04() { for (auto emitter = this->emitters.begin(); emitter != this->emitters.end(); ++emitter) { if ((*emitter)->elapsed > 0 && (*emitter)->elapsed < this->unknown_002C) { AsSprite(*emitter)->sub_B40F30(1); } } } // OFFSET: 0x0070DB30 // FUNCTION: fn_07160 bool CSvgSpiritEffect::Slot06() { return this->emitter->elapsed < this->unknown_002C; } // OFFSET: 0x0070DB50 // FUNCTION: fn_07161 void CSvgSpiritEffect::Slot03() { EZ2STREmitter* emitter = this->emitter; if (emitter->elapsed >= 0) { this->SetSourceActor(this->sourceActor); emitter->angle = static_cast(emitter->baseAngle) - static_cast(g_modeMgr.GetGameMode())->camera->camera.yaw; const float dx = this->sourcePosition[0] - this->position[0]; const float dy = this->sourcePosition[1] - this->position[1]; const float dz = this->sourcePosition[2] - this->position[2]; const float inverse = 1.0f / SquareRoot(dx * dx + dy * dy + dz * dz); emitter->velocity.x += dx * inverse * emitter->speed; emitter->velocity.y += dy * inverse * emitter->speed; emitter->velocity.z += dz * inverse * emitter->speed; emitter->position.x = this->position[0] + emitter->velocity.x; emitter->position.y = this->position[1] - 10.0f; emitter->position.z = this->position[2] + emitter->velocity.z; if (this->frame >= 10) { emitter->alpha -= 40; } } ++emitter->elapsed; } // OFFSET: 0x0070DCD0 // FUNCTION: fn_07162 void CSvgSpiritEffect::Slot04() { EZ2STREmitter* emitter = this->emitter; if (emitter->elapsed > 0 && emitter->elapsed < this->unknown_002C) { AsSprite(emitter)->sub_B40F30(1); } } // OFFSET: 0x0070DCF0 // FUNCTION: fn_07163 CTunaPartyEffect::CTunaPartyEffect() : CEffect(1097) { } // OFFSET: 0x0070DF30 // FUNCTION: fn_07167 bool CTunaPartyEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x0070E090 // FUNCTION: fn_07169 void CTunaPartyEffect::Slot04() { for (auto emitter = this->emitters.begin(); emitter != this->emitters.end(); ++emitter) { AsSprite(*emitter)->sub_B40F30(1); } } // OFFSET: 0x0070E0C0 // FUNCTION: fn_07170 CCurseOfCubeEffect::CCurseOfCubeEffect() : CEffect(-1), unknown_00D8(0), unknown_00DC(0), unknown_00E0(-10.0f), unknown_00E4(0) { } // OFFSET: 0x0070EB10 // FUNCTION: fn_07176 void CCurseOfCubeEffect::Restart() { this->Close(); this->SpawnShock(); this->unknown_00D8 = 3; } // OFFSET: 0x0070EB30 // FUNCTION: fn_07177 void CCurseOfCubeEffect::Slot01() { this->retained = 1; this->SpawnStart(); if (!this->emitters.empty()) { this->unknown_00D8 = 1; } } // OFFSET: 0x0070EB60 // FUNCTION: fn_07178 bool CCurseOfCubeEffect::Slot06() { if (!this->ActorExists() || this->emitters.empty()) { return false; } return true; } // OFFSET: 0x0070F080 // FUNCTION: fn_07180 void CCurseOfCubeEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { AsParticle(*it)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } // OFFSET: 0x0070F0D0 // FUNCTION: fn_07181 CWarningPlaneEffect::CWarningPlaneEffect() : CEffect(1114), fadingIn(0), expireTime(0) { } // OFFSET: 0x0070F240 // FUNCTION: fn_07183 void CWarningPlaneEffect::AddPlane(vector3d position) { CParticle* particle = CreateParticle(reinterpret_cast(this->position), 5); if (particle) { particle->SetTexture("warning_plane.tga"); particle->unknown_0004 = this->id; particle->unknown_000C = 0; particle->unknown_0010 = 0; particle->unknown_0008 = 1; particle->scale = 2.5f; particle->alpha = 150; particle->SetColor(255, 0, 0); particle->position = position; InitParticleQuad(particle->vertices); this->emitters.push_back(reinterpret_cast(particle)); } } // OFFSET: 0x0070F350 // FUNCTION: fn_07185 void CWarningPlaneEffect::Slot01() { } // OFFSET: 0x0070F360 // FUNCTION: fn_07186 bool CWarningPlaneEffect::Slot06() { if (this->expireTime != 0 && this->expireTime < timeGetTime()) { return false; } return this->ActorExists(); } // OFFSET: 0x0070FC60 // FUNCTION: fn_07194 bool CCylinderEffect::Slot06() { return this->startTime + this->unknown_002C >= timeGetTime(); } // OFFSET: 0x0070F720 // FUNCTION: fn_07189 CCylinderEffect::CCylinderEffect() : CEffect(-1) { } // OFFSET: 0x0070FB50 // FUNCTION: fn_07193 void CCylinderEffect::Slot01() { this->startTime = timeGetTime(); for (long long i = 0; i < 21; ++i) { CParticle* particle = CreateParticle(reinterpret_cast(this->position), 5); if (!particle) { continue; } particle->unknown_0004 = this->id; particle->unknown_000C = static_cast(i); particle->unknown_0010 = 0; particle->unknown_0008 = 1; InitParticleQuad(particle->vertices); particle->unknown_00C4 = 0; if (this->id == 2077) { particle->SetTexture("effect\\nipelheim\\nipelheim_cast\\nip_cast.tga"); particle->scale = 4.5f; particle->unknown_004C = 70; particle->unknown_0054 = 25.0f; particle->unknown_00C0 = 0.052359872f; particle->sourceBlend = 5; particle->destinationBlend = 6; } this->emitters.push_back(reinterpret_cast(particle)); } } // OFFSET: 0x0070F390 // FUNCTION: fn_07187 void CWarningPlaneEffect::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (this->expireTime != 0 && emitter) { CEZ2STRParticle* particle = AsParticle(emitter); matrix rotation; rotation.Identity(); rotation.MakeXRotation(1.5707963f); rotation.AppendYRotation(static_cast(g_modeMgr.GetGameMode())->camera->camera.yaw / -180.0f * 3.141592f); matrix scale; scale.Identity(); scale.SetScale(particle->scale, particle->scale, 1.0f); matrix transform; transform.Compose(scale, rotation); particle->transform = transform; if (this->fadingIn == 1) { particle->alpha += 2; if (particle->alpha >= 250) { this->fadingIn = 0; } } else { particle->alpha -= 2; if (particle->alpha <= 50) { this->fadingIn = 1; } } } ++emitter->elapsed; } } // OFFSET: 0x0070F670 // FUNCTION: fn_07188 void CWarningPlaneEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { if (this->expireTime != 0 && *it) { AsParticle(*it)->Render(false, 0.0f, 1.0f, 0.0f, 1.0f, false, true); } } } // OFFSET: 0x0070FFA0 // FUNCTION: fn_07196 void CCylinderEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); if (!particle) { continue; } const int texPart = particle->unknown_000C; const ParticleQuad quad = {false, 0.0f, 1.0f, static_cast(texPart / 21.0), static_cast((texPart + 1) / 21.0), false, true}; particle->Render(quad); } } // OFFSET: 0x007100D0 // FUNCTION: fn_07197 CDancingKnifeEffect::CDancingKnifeEffect() : CEffect(1829) { } // OFFSET: 0x00710830 // FUNCTION: fn_07203 bool CDancingKnifeEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x00710A20 // FUNCTION: fn_07205 void CDancingKnifeEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { if (*it) { AsParticle(*it)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } for (auto it = this->unknown_00D8.begin(); it != this->unknown_00D8.end(); ++it) { if (*it) { AsParticle(*it)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x00710AE0 // FUNCTION: fn_07206 CEternalSlashAttackEffect::CEternalSlashAttackEffect() : CEffect(-1) { } // OFFSET: 0x00710C80 // FUNCTION: fn_07209 void CEternalSlashAttackEffect::Slot01() { CEZ2STRParticle* bottom = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); if (bottom) { bottom->Load(std::string("eternal_slash\\eternal_slash_bottom\\eternal_slash_bottom.str")); EZ2STREmitter* emitter = reinterpret_cast(bottom); emitter->offset.x = 0.0f; emitter->offset.y = -13.0f; emitter->offset.z = 0.0f; this->emitters.push_back(emitter); } CEZ2STRParticle* slash = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (slash) { slash->Load(std::string("eternal_slash\\eternal_slash\\eternal_slash.str")); EZ2STREmitter* emitter = reinterpret_cast(slash); emitter->offset.x = 0.0f; emitter->offset.y = -13.0f; emitter->offset.z = 0.0f; this->emitters.push_back(emitter); } } // OFFSET: 0x00710F50 // FUNCTION: fn_07210 bool CEternalSlashAttackEffect::Slot06() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (emitter && (emitter->lifetime == 0.0f || static_cast(emitter->elapsed) < emitter->lifetime * 60.0f)) { return true; } } return false; } // OFFSET: 0x00710FB0 // FUNCTION: fn_07211 void CEternalSlashAttackEffect::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (!emitter) { continue; } CEZ2STRParticle* particle = AsParticle(emitter); particle->position.x = this->position[0] + emitter->offset.x; particle->position.y = this->position[1] + emitter->offset.y; particle->position.z = this->position[2] + emitter->offset.z; particle->Process(0.0f, 0.0f, 0.0f); } } // OFFSET: 0x007110A0 // FUNCTION: fn_07212 void CEternalSlashAttackEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { if (*it) { AsParticle(*it)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x00711120 // FUNCTION: fn_07213 CEternalSlashCountEffect::CEternalSlashCountEffect() : CEffect(1913), emitter(nullptr) { } // OFFSET: 0x00711280 // FUNCTION: fn_07216 void CEternalSlashCountEffect::Slot01() { if (static_cast(this->unknown_00B0) - 1 > 4) { return; } CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (!particle) { return; } particle->Load(FormatOwnedString("eternal_slash\\eternal_slash_count\\eternal_slash_count_%02d.str", this->unknown_00B0)); particle->unknown_00D0 = 0.0f; particle->unknown_00D4 = -25.0f; particle->unknown_00D8 = 0.0f; this->emitter = reinterpret_cast(particle); } // OFFSET: 0x00711340 // FUNCTION: fn_07217 bool CEternalSlashCountEffect::Slot06() { EZ2STREmitter* emitter = this->emitter; if (!emitter) { return false; } if (emitter->lifetime == 0.0f) { return true; } return static_cast(emitter->elapsed) < emitter->lifetime * 60.0f; } // OFFSET: 0x00711380 // FUNCTION: fn_07218 void CEternalSlashCountEffect::Slot03() { if (!this->emitter) { return; } CEZ2STRParticle* particle = AsParticle(this->emitter); particle->position.x = this->position[0] + this->emitter->offset.x; particle->position.y = this->position[1] + this->emitter->offset.y; particle->position.z = this->position[2] + this->emitter->offset.z; particle->Process(0.0f, 0.0f, 0.0f); } // OFFSET: 0x007113F0 // FUNCTION: fn_07219 void CEternalSlashCountEffect::Slot04() { if (this->emitter) { AsParticle(this->emitter)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } // OFFSET: 0x00711420 // FUNCTION: fn_07220 CFallenAngelEffect::CFallenAngelEffect() : CEffect(1140), emitter(nullptr), unknown_00D0(0), unknown_00D4(0), unknown_00D8(0) { } // OFFSET: 0x00711580 // FUNCTION: fn_07223 void CFallenAngelEffect::Slot01() { if (!this->emitter) { CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); this->emitter = reinterpret_cast(particle); particle->Open("RL_fallen_angel\\Penne.str"); } else { AsParticle(this->emitter)->Restart(); } InitParticleQuad(AsParticle(this->emitter)->vertices); this->unknown_00D0 = this->position[0]; this->unknown_00D4 = this->position[1]; this->unknown_00D8 = this->position[2]; } // OFFSET: 0x00711690 // FUNCTION: fn_07224 bool CFallenAngelEffect::Slot06() { if (this->emitter->lifetime == 0.0f) { return true; } return static_cast(this->emitter->elapsed) < this->emitter->lifetime * 60.0f; } // OFFSET: 0x007116D0 // FUNCTION: fn_07225 void CFallenAngelEffect::Slot03() { EZ2STREmitter* emitter = this->emitter; const vector3d start = emitter->position; const float dx = this->unknown_00D0 - this->position[0]; const float dz = this->unknown_00D8 - this->position[2]; const float speed = 1.0f / SquareRoot(dx * dx + 0.0f + dz * dz); emitter->position.x = start.x + dx * speed * 0.09f; emitter->position.y = start.y + speed * 0.0f; emitter->position.z = start.z + dz * speed * 0.09f; AsParticle(emitter)->Process(0.0f, -80.0f, 0.0f); emitter->elapsed++; } // OFFSET: 0x007117F0 // FUNCTION: fn_07226 void CFallenAngelEffect::Slot04() { if (this->emitter) { AsParticle(this->emitter)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } // OFFSET: 0x00711850 // FUNCTION: fn_07227 void CFallenAngelEffect::SetPosition(vector3d position) { vector3d target = position; target.y += (g_cameraDistance - 230.0f) * 0.0625f + 10.0f; this->position[0] = target.x; this->position[1] = target.y; this->position[2] = target.z; this->emitter->position = target; } // OFFSET: 0x007118C0 // FUNCTION: fn_07228 CFloralFlareRoad::CFloralFlareRoad() : CEffect(1804) { } // OFFSET: 0x00711A70 // FUNCTION: fn_07231 void CFloralFlareRoad::Slot01() { CEZ2STRParticle* bottom = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); this->unknown_00CC = reinterpret_cast(bottom); bottom->Load(std::string("froral_flareroad\\froral_flareroad_bottom\\froral_flareroad_bottom.str")); for (int delay = 0; delay < 4500; delay += 300) { vector3d position = {this->position[0], this->position[1], this->position[2]}; const int distanceX = std::max(rand() % 20, 0); const int angleX = std::max(rand() % 360, 0); position.x = ClientSine(angleX) * static_cast(distanceX) + position.x; const int distanceZ = std::max(rand() % 20, 0); const int angleZ = std::max(rand() % 360, 0); position.z = ClientCosine(angleZ) * static_cast(distanceZ) + position.z; CEZ2STRParticle* flare = static_cast(CreateEZ2STRParticle(&position, 5)); flare->Load(std::string("froral_flareroad\\froral_flareroad\\froral_flareroad.str")); flare->m_delay = delay; this->emitters.push_back(reinterpret_cast(flare)); this->AddEvent(0, "effect\\AG_ALL_BLOOM.wav", delay / 16, 0); } } // OFFSET: 0x00711DA0 // FUNCTION: fn_07232 bool CFloralFlareRoad::Slot06() { return this->ActorExists(); } // OFFSET: 0x00711DB0 // FUNCTION: fn_07233 void CFloralFlareRoad::Slot03() { if (this->unknown_00CC) { AsParticle(this->unknown_00CC)->Process(0.0f, -80.0f, 0.0f); } for (auto it = this->emitters.begin(); it != this->emitters.end();) { EZ2STREmitter* emitter = *it; if (emitter->lifetime == 0.0f || emitter->lifetime * 60.0f > static_cast(emitter->elapsed)) { if (emitter) { AsParticle(emitter)->Process(0.0f, -80.0f, 0.0f); } ++it; } else { FreeParticle(AsParticle(emitter)); it = this->emitters.erase(it); } } } // OFFSET: 0x00711ED0 // FUNCTION: fn_07234 void CFloralFlareRoad::Slot04() { if (this->unknown_00CC) { AsParticle(this->unknown_00CC) ->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { if (*it) { AsParticle(*it)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x00711F80 // FUNCTION: fn_07235 CFootprintEffect::CFootprintEffect() : CEffect(2411), stepDistance(50.0), unknown_0110(1), lastPosition{}, unknown_0120(-1000.0f) { this->lastPosition.x = this->position[0]; this->lastPosition.y = this->position[1]; this->lastPosition.z = this->position[2]; } // OFFSET: 0x007123C0 // FUNCTION: fn_07238 bool CFootprintEffect::Slot33() { EffectActor* actor = static_cast(FindRenderObject(this->actor)); if (!actor || actor->render.unknown_00A8 == 0) { return true; } if (g_serviceOptions.Get(269) && this->actor != g_session.m_aid) { return false; } return actor->EffectVisible(); } // OFFSET: 0x00712410 // FUNCTION: fn_07239 void CFootprintEffect::Slot01() { char* leftFile = nullptr; char* rightFile = nullptr; g_luaScript->Call("GetFootprintPngFile", "d>ss", this->unknown_00C4, &leftFile, &rightFile); if (!leftFile || std::strlen(leftFile) == 0 || !rightFile || std::strlen(rightFile) == 0) { return; } this->unknown_00D4 = leftFile; this->unknown_00EC = rightFile; double scale = 5.0; g_luaScript->Call("GetFootprintPngScale", "d>l", this->unknown_00C4, &scale); *reinterpret_cast(&this->unknown_0048) = static_cast(scale); int alpha = 250; g_luaScript->Call("GetFootprintPngAplha", "d>d", this->unknown_00C4, &alpha); *reinterpret_cast(this->unknown_0054) = alpha; int duration = 10; g_luaScript->Call("GetFootprintPngDuration", "d>d", this->unknown_00C4, &duration); this->unknown_002C = duration; g_luaScript->Call("GetFootprintStride", "d>l", this->unknown_00C4, &this->stepDistance); this->unknown_0120 = 0.0f; } // OFFSET: 0x007127A0 // FUNCTION: fn_07240 bool CFootprintEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x007127B0 // FUNCTION: fn_07241 CParticle* CFootprintEffect::SpawnFootprint() { const std::string& texture = this->unknown_0110 ? this->unknown_00D4 : this->unknown_00EC; if (texture.size() == 0) { return nullptr; } CParticle* particle = CreateParticle(reinterpret_cast(this->position), 1); if (!particle) { return particle; } particle->alpha = *reinterpret_cast(this->unknown_0054); particle->life = static_cast(this->unknown_002C); particle->sourceBlend = 5; particle->destinationBlend = 6; particle->unknown_00C0 = static_cast(*reinterpret_cast(this->unknown_0054)) / static_cast(this->unknown_002C * 60); particle->SetTexture(texture); InitParticleQuad(particle->vertices); const float angle = ScreenAngle(&this->lastPosition.x, this->position, static_cast(g_modeMgr.GetGameMode())->camera->frame.view.values) + 90.0f; const float scale = *reinterpret_cast(&this->unknown_0048); matrix world; world.SetScale(scale, scale, 0.0f); world.AppendXRotation(1.5707964f); world.AppendYRotation((angle + static_cast(g_modeMgr.GetGameMode())->camera->camera.yaw) / 180.0f * 3.141592f); world.AppendTranslate(this->position); for (int i = 0; i < 4; ++i) { RenderVertex& vertex = particle->vertices[i]; const float x = vertex.x * world.values[0] + vertex.y * world.values[3] + 0.0f * world.values[6] + world.values[9]; const float z = vertex.x * world.values[2] + vertex.y * world.values[5] + 0.0f * world.values[8] + world.values[11]; CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); float y = mode->scene->terrain->Grid().PlaneHeight(x, z) - 2.0f; if (this->unknown_0120 != -1000.0f && y - this->unknown_0120 > 5.0f) { y = this->unknown_0120; } this->unknown_0120 = y; vertex.x = x; vertex.y = y; vertex.z = z; } this->unknown_0110 = this->unknown_0110 == 0; this->lastPosition.x = this->position[0]; this->lastPosition.y = this->position[1]; this->lastPosition.z = this->position[2]; return particle; } // OFFSET: 0x00712A90 // FUNCTION: fn_07242 void CFootprintEffect::Slot03() { const float dx = this->position[0] - this->lastPosition.x; const float dy = this->position[1] - this->lastPosition.y; const float dz = this->position[2] - this->lastPosition.z; if (static_cast(dx * dx + dy * dy + dz * dz) > this->stepDistance) { CParticle* particle = this->SpawnFootprint(); if (particle) { this->emitters.push_back(reinterpret_cast(particle)); } } for (auto it = this->emitters.begin(); it != this->emitters.end();) { CParticle* particle = AsParticle(*it); if (particle->life == 0.0f || particle->life * 60.0f > static_cast(particle->unknown_00E8)) { particle->alpha = static_cast(std::max(0.0f, static_cast(particle->alpha) - particle->unknown_00C0)); ++particle->unknown_00E8; ++it; } else { FreeParticle(particle); it = this->emitters.erase(it); } } } // OFFSET: 0x00712C50 // FUNCTION: fn_07244 void CFootprintEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { AsParticle(*it)->Render(true, 0.0f, 1.0f, 0.0f, 1.0f, false, true); } } // OFFSET: 0x00712CA0 // FUNCTION: fn_07245 CFootprintStrEffect::CFootprintStrEffect() : CEffect(2412), unknown_0140(1.0), unknown_0148(1.0), unknown_0150(0.0), unknown_0158(50.0), unknown_0160(2.0), unknown_0168(0), unknown_0169(1), unknown_016C{} { } // OFFSET: 0x007130E0 // FUNCTION: fn_07247 void CFootprintStrEffect::Close() { for (EZ2STREmitter* emitter : this->emitters) { FreeParticle(AsParticle(emitter)); } this->emitters.clear(); for (EZ2STREmitter* emitter : this->unknown_00D4) { FreeParticle(AsParticle(emitter)); } this->unknown_00D4.clear(); } // OFFSET: 0x00713370 // FUNCTION: fn_07248 bool CFootprintStrEffect::Slot33() { EffectActor* actor = static_cast(FindRenderObject(this->actor)); if (!actor || actor->render.unknown_00A8 == 0) { return true; } if (g_serviceOptions.Get(269) && this->actor != g_session.m_aid) { return false; } return actor->EffectVisible(); } // OFFSET: 0x007133C0 // FUNCTION: fn_07249 void CFootprintStrEffect::Slot01() { char* leftFile = nullptr; char* rightFile = nullptr; g_luaScript->Call("GetFootprintStrFileBottom", "d>ss", this->unknown_00C4, &leftFile, &rightFile); if (!IsNullOrEmpty(leftFile) && !IsNullOrEmpty(rightFile)) { this->unknown_00DC = leftFile; this->unknown_00F4 = rightFile; } g_luaScript->Call("GetFootprintStrFileTop", "d>ss", this->unknown_00C4, &leftFile, &rightFile); if (!IsNullOrEmpty(leftFile) && !IsNullOrEmpty(rightFile)) { this->unknown_010C = leftFile; this->unknown_0124 = rightFile; } if (this->unknown_00DC.size() == 0 && this->unknown_00F4.size() == 0 && this->unknown_010C.size() == 0 && this->unknown_0124.size() == 0) { return; } g_luaScript->Call("GetFootprintStrScale", "d>ll", this->unknown_00C4, &this->unknown_0140, &this->unknown_0148); g_luaScript->Call("GetFootprintStrTopHeight", "d>l", this->unknown_00C4, &this->unknown_0150); g_luaScript->Call("GetFootprintStride", "d>l", this->unknown_00C4, &this->unknown_0158); g_luaScript->Call("GetFootprintStrGap", "d>l", this->unknown_00C4, &this->unknown_0160); g_luaScript->Call("IsFootprintStrAdjustAngle", "d>b", this->unknown_00C4, &this->unknown_0168); } // OFFSET: 0x00713830 // FUNCTION: fn_07250 bool CFootprintStrEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x00713960 // FUNCTION: fn_07251 void CFootprintStrEffect::SpawnFootprint() { vector3d direction; direction.x = this->position[0] - this->unknown_016C.x; direction.y = 0.0f; direction.z = this->position[2] - this->unknown_016C.z; const float inverse = 1.0f / SquareRoot(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z); direction.x = inverse * direction.x; direction.y = inverse * direction.y; direction.z = inverse * direction.z; matrix rotation; rotation.MakeYRotation(1.5707964f); const float side = static_cast((this->unknown_0169 != 0) * 2 - 1); const float gap = static_cast(this->unknown_0160); vector3d point; point.x = (direction.x * rotation.values[0] + direction.y * rotation.values[3] + direction.z * rotation.values[6] + rotation.values[9]) * gap * side + this->position[0]; point.z = (direction.x * rotation.values[2] + direction.y * rotation.values[5] + direction.z * rotation.values[8] + rotation.values[11]) * gap * side + this->position[2]; point.y = static_cast(g_modeMgr.GetGameMode())->scene->terrain->Grid().Height(point.x, point.z); vector3d spawn = point; const std::string& bottom = this->unknown_0169 ? this->unknown_00DC : this->unknown_00F4; if (bottom.size() != 0) { CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(&spawn, 5)); if (particle) { float angle; if (this->unknown_0168) { angle = ScreenAngle(&this->unknown_016C.x, &spawn.x, static_cast(g_modeMgr.GetGameMode())->camera->frame.view.values) + 90.0f; } else { angle = 0.0f; } particle->Open(bottom); particle->m_scale = static_cast(this->unknown_0140); particle->m_angle = angle; this->emitters.push_back(reinterpret_cast(particle)); } } const std::string& top = this->unknown_0169 ? this->unknown_010C : this->unknown_0124; if (top.size() != 0) { spawn.x = point.x + 0.0f; spawn.y = point.y + static_cast(-this->unknown_0150); spawn.z = point.z + 0.0f; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(&spawn, 5)); if (particle) { particle->Open(top); particle->depthOffset = 0.000035f; particle->m_scale = static_cast(this->unknown_0148); this->unknown_00D4.push_back(reinterpret_cast(particle)); } } this->unknown_0169 = this->unknown_0169 == 0; this->unknown_016C.x = this->position[0]; this->unknown_016C.y = this->position[1]; this->unknown_016C.z = this->position[2]; } // OFFSET: 0x00713E00 // FUNCTION: fn_07252 void CFootprintStrEffect::Slot03() { const float dx = this->position[0] - this->unknown_016C.x; const float dy = this->position[1] - this->unknown_016C.y; const float dz = this->position[2] - this->unknown_016C.z; if (static_cast(dx * dx + dy * dy + dz * dz) > this->unknown_0158) { this->SpawnFootprint(); } for (auto it = this->emitters.begin(); it != this->emitters.end();) { CEZ2STRParticle* particle = AsParticle(*it); if (particle->Process(0.0f, 0.0f, 0.0f)) { ++it; } else { FreeParticle(particle); it = this->emitters.erase(it); } } for (auto it = this->unknown_00D4.begin(); it != this->unknown_00D4.end();) { CEZ2STRParticle* particle = AsParticle(*it); if (particle->Process(0.0f, 0.0f, 0.0f)) { ++it; } else { FreeParticle(particle); it = this->unknown_00D4.erase(it); } } } // OFFSET: 0x00713F70 // FUNCTION: fn_07253 void CFootprintStrEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { AsParticle(*it)->DrawGround(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } for (auto it = this->unknown_00D4.begin(); it != this->unknown_00D4.end(); ++it) { AsParticle(*it)->DrawBillboards(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } // OFFSET: 0x00714250 // FUNCTION: fn_07259 CFromTheAbyssEffect::CFromTheAbyssEffect() : CEffect(-1), maxLevel(5), level(0) { this->unknown_00E0[0] = AbyssWave{0.8f, {-4.8f, -9.1f, 0.0f}}; this->unknown_00E0[1] = AbyssWave{0.6f, {-3.4f, -12.8f, 0.0f}}; this->unknown_00E0[2] = AbyssWave{0.5f, {-0.7f, -14.0f, 0.0f}}; this->unknown_00E0[3] = AbyssWave{0.7f, {2.7f, -14.0f, 0.0f}}; this->unknown_00E0[4] = AbyssWave{0.4f, {4.3f, -10.0f, 0.0f}}; } // OFFSET: 0x00715160 // FUNCTION: fn_07265 void CFromTheAbyssEffect::Close() { for (auto it = this->unknown_00E8.begin(); it != this->unknown_00E8.end(); ++it) { if (it->second.emitter) { FreeParticle(AsParticle(it->second.emitter)); } } this->unknown_00E8.clear(); } // OFFSET: 0x00715400 // FUNCTION: fn_07266 void CFromTheAbyssEffect::Slot01() { this->retained = 1; } // OFFSET: 0x00715410 // FUNCTION: fn_07267 bool CFromTheAbyssEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x00715420 // FUNCTION: fn_07268 void CFromTheAbyssEffect::Slot03() { for (auto it = this->unknown_00E8.begin(); it != this->unknown_00E8.end(); ++it) { this->UpdateParticle(it->first, it->second); } } // OFFSET: 0x007154A0 // FUNCTION: fn_07269 void CFromTheAbyssEffect::UpdateParticle(long long key, AbyssParticle particle) { EZ2STREmitter* emitter = particle.emitter; if (!emitter) { return; } switch (particle.kind) { case 1: if (emitter->lifetime != 0.0f && !(emitter->lifetime * 60.0f > static_cast(emitter->elapsed))) { const double scale = AsParticle(emitter)->m_scale; const vector3d offset = emitter->offset; FreeParticle(AsParticle(emitter)); EZ2STREmitter* dying = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (dying) { AsParticle(dying)->Load("from_the_abyss\\from_the_abyss_shadowball\\from_the_abyss_shadowball.str"); dying->looping = 1; AsParticle(dying)->m_scale = static_cast(scale); dying->offset = offset; } AbyssParticle& entry = this->unknown_00E8[key]; entry.kind = 2; entry.emitter = dying; return; } break; case 2: break; case 3: if (emitter->lifetime != 0.0f && !(emitter->lifetime * 60.0f > static_cast(emitter->elapsed))) { FreeParticle(AsParticle(emitter)); AbyssParticle& entry = this->unknown_00E8[key]; entry.kind = 0; entry.emitter = nullptr; return; } break; default: return; } const vector3d origin = *reinterpret_cast(this->position); const matrix view = static_cast(g_modeMgr.GetGameMode())->camera->frame.view; const matrix& inverse = static_cast(g_modeMgr.GetGameMode())->camera->frame.inverse; const float viewX = origin.x * view.values[0] + origin.y * view.values[3] + origin.z * view.values[6] + view.values[9] + emitter->offset.x; const float viewY = origin.x * view.values[1] + origin.y * view.values[4] + origin.z * view.values[7] + view.values[10] + emitter->offset.y; const float viewZ = origin.x * view.values[2] + origin.y * view.values[5] + origin.z * view.values[8] + view.values[11] + emitter->offset.z; emitter->position.x = viewX * inverse.values[0] + viewY * inverse.values[3] + viewZ * inverse.values[6] + inverse.values[9]; emitter->position.y = viewX * inverse.values[1] + viewY * inverse.values[4] + viewZ * inverse.values[7] + inverse.values[10]; emitter->position.z = viewX * inverse.values[2] + viewY * inverse.values[5] + viewZ * inverse.values[8] + inverse.values[11]; AsParticle(emitter)->Process(0.0f, 0.0f, 0.0f); } // OFFSET: 0x00715C80 // FUNCTION: fn_07270 void CFromTheAbyssEffect::Slot04() { for (auto it = this->unknown_00E8.begin(); it != this->unknown_00E8.end(); ++it) { EZ2STREmitter* emitter = it->second.emitter; if (emitter) { AsParticle(emitter)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x00715F30 // FUNCTION: fn_07271 void CFromTheAbyssEffect::SetLevel(long long level) { if (level < 0 || level > this->maxLevel) { return; } if (level > this->level) { for (long long i = this->level; i < level; ++i) { auto it = this->unknown_00E8.find(i); if (it != this->unknown_00E8.end() && it->second.emitter) { FreeParticle(AsParticle(it->second.emitter)); } EZ2STREmitter* emitter = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (emitter) { AsParticle(emitter)->Load("from_the_abyss\\from_the_abyss_shadowball_create\\from_the_abyss_shadowball_create.str"); AsParticle(emitter)->m_scale = this->unknown_00E0[i].scale; emitter->offset = this->unknown_00E0[i].offset; AbyssParticle& entry = this->unknown_00E8[i]; entry.emitter = emitter; entry.kind = 1; } } } else if (level < this->level) { for (long long i = this->level - 1; i > level - 1; --i) { auto it = this->unknown_00E8.find(i); if (it != this->unknown_00E8.end() && it->second.emitter) { EZ2STREmitter* previous = it->second.emitter; const double scale = AsParticle(previous)->m_scale; const vector3d offset = previous->offset; FreeParticle(AsParticle(previous)); EZ2STREmitter* emitter = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (emitter) { AsParticle(emitter)->Load("from_the_abyss\\from_the_abyss_shadowball_die\\from_the_abyss_shadowball_die.str"); AsParticle(emitter)->m_scale = static_cast(scale); emitter->offset = offset; } AbyssParticle& entry = this->unknown_00E8[i]; entry.kind = 3; entry.emitter = emitter; } } } this->level = level; } // OFFSET: 0x00716800 // FUNCTION: fn_07274 CFullScreenEffect::CFullScreenEffect() : CEffect(-1), loaded(1) { } // OFFSET: 0x007169A0 // FUNCTION: fn_07277 void CFullScreenEffect::Slot01() { std::string texture; if (this->id == 2272) { texture = "effect\\state_effect\\frostbite\\frostbite_screen\\frostbite_screen.tga"; this->retained = 1; for (long long i = 0; i < 4; ++i) { CParticle* particle = CreateParticle(reinterpret_cast(this->position), 0x201); if (!particle) { this->loaded = 0; return; } particle->unknown_0004 = this->id; particle->unknown_000C = static_cast(i); particle->unknown_0010 = 0; particle->unknown_0008 = 1; particle->SetTexture(texture.c_str()); particle->sourceBlend = 5; particle->destinationBlend = 6; particle->alpha = 0xFF; this->particles.push_back(particle); } return; } this->loaded = 0; } // OFFSET: 0x00716BD0 // FUNCTION: fn_07278 bool CFullScreenEffect::Slot06() { if (this->loaded == 1 && this->ActorExists()) { return true; } return false; } // OFFSET: 0x00716BF0 // FUNCTION: fn_07279 void CFullScreenEffect::Slot04() { for (auto it = this->particles.begin(); it != this->particles.end(); ++it) { CParticle* particle = *it; if (!particle) { continue; } RenderPrimitive& primitive = particle->primitive; primitive.texture = particle->texture; const float left = g_textureOffsetX / primitive.texture->TextureWidth(); const float right = (primitive.texture->Width() + g_textureOffsetX) / primitive.texture->TextureWidth(); const float top = g_textureOffsetY / primitive.texture->TextureHeight(); const float bottom = (primitive.texture->Height() + g_textureOffsetY) / primitive.texture->TextureHeight(); const std::uint32_t color = ((particle->alpha << 8 | particle->red) << 8 | particle->green) << 8 | particle->blue; RenderVertex* vertices = primitive.quad; switch (particle->unknown_000C) { case 0: SetScreenVertex(vertices[0], 0.0f, 0.0f, color, right, top); SetScreenVertex(vertices[1], static_cast(g_renderer->viewport.centerX), 0.0f, color, left, top); SetScreenVertex(vertices[2], 0.0f, static_cast(g_renderer->viewport.centerY), color, right, bottom); SetScreenVertex(vertices[3], static_cast(g_renderer->viewport.centerX), static_cast(g_renderer->viewport.centerY), color, left, bottom); break; case 1: SetScreenVertex(vertices[0], static_cast(g_renderer->viewport.centerX), 0.0f, color, left, top); SetScreenVertex(vertices[1], static_cast(g_renderer->viewport.width), 0.0f, color, right, top); SetScreenVertex(vertices[2], static_cast(g_renderer->viewport.centerX), static_cast(g_renderer->viewport.centerY), color, left, bottom); SetScreenVertex(vertices[3], static_cast(g_renderer->viewport.width), static_cast(g_renderer->viewport.centerY), color, right, bottom); break; case 2: SetScreenVertex(vertices[0], 0.0f, static_cast(g_renderer->viewport.centerY), color, right, bottom); SetScreenVertex(vertices[1], static_cast(g_renderer->viewport.centerX), static_cast(g_renderer->viewport.centerY), color, left, bottom); SetScreenVertex(vertices[2], 0.0f, static_cast(g_renderer->viewport.height), color, right, top); SetScreenVertex(vertices[3], static_cast(g_renderer->viewport.centerX), static_cast(g_renderer->viewport.height), color, left, top); break; case 3: SetScreenVertex(vertices[0], static_cast(g_renderer->viewport.centerX), static_cast(g_renderer->viewport.centerY), color, left, bottom); SetScreenVertex(vertices[1], static_cast(g_renderer->viewport.width), static_cast(g_renderer->viewport.centerY), color, right, bottom); SetScreenVertex(vertices[2], static_cast(g_renderer->viewport.centerX), static_cast(g_renderer->viewport.height), color, left, top); SetScreenVertex(vertices[3], static_cast(g_renderer->viewport.width), static_cast(g_renderer->viewport.height), color, right, top); break; } primitive.unknown_0018 = particle->sourceBlend; primitive.unknown_001C = particle->destinationBlend; g_renderer->queues.Submit(&primitive, static_cast(particle->flags), g_renderer->fog.supported != 0, g_depthStart, g_depthEnd); } } // OFFSET: 0x007176E0 // FUNCTION: fn_07280 CGaleStorm::CGaleStorm() : CEffect(1973), unknown_00D8(5), unknown_00DC(0) { } // OFFSET: 0x00717DE0 // FUNCTION: fn_07285 void CGaleStorm::Slot01() { struct LevelData { int count; int unknown; }; LevelData table[10] = {{5}, {5}, {5}, {5}, {7}, {7}, {7}, {9}, {9}, {11}}; unsigned int index = this->unknown_00B0; if (index > 10) { index = 10; } this->unknown_00D8 = table[index - 1].count; this->unknown_00DC = table[index - 1].unknown; } // OFFSET: 0x00717EB0 // FUNCTION: fn_07286 bool CGaleStorm::Slot06() { if (this->frame > this->unknown_002C && this->emitters.empty()) { return false; } return this->ActorExists(); } // OFFSET: 0x00718040 // FUNCTION: fn_07288 void CGaleStorm::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { if (*it) { AsParticle(*it)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x007183F0 // FUNCTION: fn_07292 void CGravityControlEffect::Slot04() { if (!this->target) { return; } AsParticle(reinterpret_cast(this->target)) ->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } // OFFSET: 0x007184D0 CHawkVumerangEffect::CHawkVumerangEffect() : CEffect(1968), unknown_00E0(10), unknown_00E4(0), unknown_00E8(6.283185307179586), unknown_00F0(0), unknown_00F4(-17.0f), unknown_00F8(0), unknown_0100{}, unknown_010C{}, unknown_0118{} { } // OFFSET: 0x007187F0 // FUNCTION: fn_07297 void CHawkVumerangEffect::Close() { for (auto it = this->unknown_00D4.begin(); it != this->unknown_00D4.end(); ++it) { FreeParticle(AsParticle(*it)); } this->unknown_00D4.clear(); for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x00718B90 // FUNCTION: fn_07299 bool CHawkVumerangEffect::Slot06() { return this->emitters.size() != 0; } // OFFSET: 0x00718BA0 // FUNCTION: fn_07300 void CHawkVumerangEffect::Slot03() { if (this->emitters.size() == 0) { return; } EZ2STREmitter* emitter = this->emitters.back(); this->emitters.pop_back(); const DWORD elapsed = timeGetTime() - this->unknown_00FC; double angle = static_cast(elapsed) / static_cast(this->unknown_002C * 2) * this->unknown_00E8; if (angle < this->unknown_00E8) { angle += this->unknown_0130; if (angle > 6.283184f) { angle -= 6.283184f; } AsParticle(emitter)->m_angle = 360.0f - static_cast(angle) / 3.141592f * 180.0f; const float sine = static_cast(std::sin(static_cast(angle)) * this->unknown_0128); const float cosine = static_cast(std::cos(static_cast(angle)) * this->unknown_0128); emitter->position.z = this->unknown_0118.z + sine + this->unknown_00F8; emitter->position.x = cosine + this->unknown_0118.x + this->unknown_00F0; emitter->position.y = this->unknown_0118.y + 0.0f + this->unknown_00F4; this->emitters.emplace_front(emitter); } else { this->unknown_00D4.push_back(emitter); } long long index = this->count - this->emitters.size(); for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it, ++index) { EZ2STREmitter* current = *it; if (current) { const double alpha = 1.0f - 1.0f / static_cast(this->count) * static_cast(index); AsParticle(current)->m_scale = static_cast(alpha); AsParticle(current)->m_alpha = static_cast(alpha); AsParticle(current)->Process(0.0f, 0.0f, 0.0f); } } } // OFFSET: 0x00718E70 // FUNCTION: fn_07301 void CHawkVumerangEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (emitter) { AsParticle(emitter)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x00719030 CDropItemEffect::CDropItemEffect() : CEffect(-1), emitterCount(2), soundPath("wav\\effect\\"), position{} { this->emitters.resize(this->emitterCount); } // OFFSET: 0x00719400 // FUNCTION: fn_07307 void CDropItemEffect::Slot01() { if (!static_cast(g_modeMgr.GetGameMode())->scene) { return; } for (int i = 0; i < this->emitterCount; ++i) { EZ2STREmitter* emitter = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->CEffect::position), 5)); if (!emitter) { return; } InitParticleQuad(AsParticle(emitter)->vertices); this->emitters.push_back(emitter); } this->OpenEmitters(); this->Slot12(); static_cast(g_modeMgr.GetGameMode())->scene->GetItemByAID(this->actor); this->position.x = this->CEffect::position[0]; this->position.y = this->CEffect::position[1]; this->position.z = this->CEffect::position[2]; for (std::size_t i = 0; i < this->emitters.size(); ++i) { EZ2STREmitter* emitter = this->emitters[i]; if (emitter) { emitter->position = this->position; } } } // OFFSET: 0x00719520 // FUNCTION: fn_07308 void CDropItemEffect::Slot12() { for (std::size_t i = 0; i < this->emitters.size(); ++i) { if (this->emitters[i]) { this->emitters[i]->looping = 1; AsParticle(this->emitters[i])->flags = 13; } } this->AddEvent(0, (this->soundPath + this->soundName).c_str(), 0, 0); } // OFFSET: 0x00719660 // FUNCTION: fn_07309 bool CDropItemEffect::Slot06() { if (this->actor == 0) { return false; } if (!static_cast(g_modeMgr.GetGameMode())->scene->GetItemByAID(this->actor)) { return false; } return true; } // OFFSET: 0x00719690 // FUNCTION: fn_07310 void CDropItemEffect::OpenEmitters() { switch (this->id) { case 1190: AsParticle(this->emitters[0])->Open("new_dropitem\\dropitem_purple\\dropitem_purple\\dropitem_purple.str"); AsParticle(this->emitters[1])->Open("new_dropitem\\dropitem_purple\\dropitem_purple_bottom\\dropitem_purple_bottom.str"); this->soundName = "drop_purple.wav"; break; case 1189: AsParticle(this->emitters[0])->Open("new_dropitem\\dropitem_yellow\\dropitem_yellow\\dropitem_yellow.str"); AsParticle(this->emitters[1])->Open("new_dropitem\\dropitem_yellow\\dropitem_yellow_bottom\\dropitem_yellow_bottom.str"); this->soundName = "drop_yellow.wav"; break; case 1869: AsParticle(this->emitters[0])->Open("new_dropitem\\dropitem_blue\\dropitem_blue\\dropitem_blue.str"); AsParticle(this->emitters[1])->Open("new_dropitem\\dropitem_blue\\dropitem_blue_bottom\\dropitem_blue_bottom.str"); this->soundName = "drop_blue.wav"; break; case 1870: AsParticle(this->emitters[0])->Open("new_dropitem\\dropitem_green\\dropitem_green\\dropitem_green.str"); AsParticle(this->emitters[1])->Open("new_dropitem\\dropitem_green\\dropitem_green_bottom\\dropitem_green_bottom.str"); this->soundName = "drop_green.wav"; break; case 1871: AsParticle(this->emitters[0])->Open("new_dropitem\\dropitem_red\\dropitem_red\\dropitem_red.str"); AsParticle(this->emitters[1])->Open("new_dropitem\\dropitem_red\\dropitem_red_bottom\\dropitem_red_bottom.str"); this->soundName = "drop_red.wav"; break; default: AsParticle(this->emitters[0])->Open("new_dropitem\\dropitem_pink\\dropitem_pink\\dropitem_pink.str"); AsParticle(this->emitters[1])->Open("new_dropitem\\dropitem_pink\\dropitem_pink_bottom\\dropitem_pink_bottom.str"); this->soundName = "drop_pink.wav"; break; } } // OFFSET: 0x00719C30 // FUNCTION: fn_07311 void CDropItemEffect::Slot03() { for (std::size_t i = 0; i < this->emitters.size(); ++i) { if (this->emitters[i]) { AsParticle(this->emitters[i])->Process(0.0f, 0.0f, 0.0f); this->emitters[i]->elapsed++; } } } // OFFSET: 0x00719CA0 // FUNCTION: fn_07312 void CDropItemEffect::Slot04() { for (std::size_t i = 0; i < this->emitters.size(); ++i) { if (this->emitters[i]) { AsParticle(this->emitters[i])->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x0071B8C0 // FUNCTION: fn_07318 CJupitelThunderHit::CJupitelThunderHit() : CEffect(-1) { this->unknown_002C = 250; } // OFFSET: 0x0071BAB0 // FUNCTION: fn_07320 void CJupitelThunderHit::SpawnHit() { switch (this->id) { case 94: case 1200: this->textures.push_back("effect\\thunder_plazma_blast_a.bmp"); this->textures.push_back("effect\\thunder_plazma_blast_b.bmp"); this->textures.push_back("effect\\thunder_ball_d.bmp"); this->textures.push_back("effect\\thunder_ball_e.bmp"); this->textures.push_back("effect\\thunder_ball_f.bmp"); break; case 452: this->textures.push_back("effect\\twirl_soft.bmp"); this->textures.push_back("effect\\thunder_ball_b.bmp"); this->textures.push_back("effect\\twirl_soft.bmp"); this->textures.push_back("effect\\thunder_ball_c.bmp"); this->textures.push_back("effect\\twirl_soft.bmp"); break; } CParticle* particle = CreateParticle(reinterpret_cast(this->position), 0x203); particle->unknown_00E8 = -10; particle->scale = 7.5f; particle->unknown_00A8 = 0; particle->SetTexture("effect\\thunder_plazma_blast_a.bmp"); this->unknown_00D8.push_back(reinterpret_cast(particle)); } // OFFSET: 0x0071C270 // FUNCTION: fn_07321 void CJupitelThunderHit::SpawnSpark() { std::string texture = "effect\\thunder_pang.bmp"; if (this->id == 94) { texture = "effect\\thunder_pang.bmp"; } else if (this->id == 452) { texture = "effect\\pokjuk_d.bmp"; } CParticle* particle = CreateParticle(reinterpret_cast(this->position), 0x203); particle->SetTexture(texture); particle->unknown_00C0 = 2.5f; this->emitters.push_back(reinterpret_cast(particle)); } // OFFSET: 0x0071C520 // FUNCTION: fn_07323 void CJupitelThunderHit::Slot01() { if (this->id != 1200) { this->SpawnSpark(); } this->SpawnHit(); } // OFFSET: 0x0071C540 // FUNCTION: fn_07324 bool CJupitelThunderHit::Slot06() { if (this->id == 1200) { return this->ActorExists(); } if (this->actor == 0) { return true; } return this->frame < this->unknown_002C; } // OFFSET: 0x0071C570 // FUNCTION: fn_07325 void CJupitelThunderHit::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { ++(*it)->elapsed; CEZ2STRParticle* particle = AsParticle(*it); if (particle && particle->unknown_00E8 >= 0) { if (particle->unknown_00E8 == 10) { particle->unknown_00E8 = -10; } else { const float scale = static_cast(particle->unknown_00E8) * particle->unknown_00C0; matrix rotation; rotation.MakeXRotation(static_cast(g_modeMgr.GetGameMode())->camera->camera.pitch / 180.0f * 3.141592f); matrix scaling; scaling.SetScale(scale, scale, 1.0f); matrix transform; transform.Compose(scaling, rotation); particle->transform = transform; particle->scale = scale; particle->position.x = this->position[0] + 0.0f; particle->position.y = this->position[1] - 5.0f; particle->position.z = this->position[2] + 0.0f; } } } for (auto it = this->unknown_00D8.begin(); it != this->unknown_00D8.end(); ++it) { ++(*it)->elapsed; CEZ2STRParticle* particle = AsParticle(*it); if (particle && particle->unknown_00E8 >= 0) { std::size_t index = static_cast(particle->unknown_00A8) + 1; if (index >= this->textures.size()) { index = 0; } particle->SetTexture(this->textures[index]); particle->unknown_00A8 = static_cast(index); matrix rotation; rotation.MakeXRotation(static_cast(g_modeMgr.GetGameMode())->camera->camera.pitch / 180.0f * 3.141592f); matrix scaling; scaling.SetScale(particle->scale, particle->scale, 1.0f); matrix transform; transform.Compose(scaling, rotation); particle->transform = transform; particle->position.x = this->position[0] + 0.0f; particle->position.y = this->position[1] - 5.0f; particle->position.z = this->position[2] + 0.0f; } } } // OFFSET: 0x0071CB70 // FUNCTION: fn_07326 void CJupitelThunderHit::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (emitter && emitter->elapsed >= 0) { AsParticle(emitter)->Render(false, 0.0f, 1.0f, 0.0f, 1.0f, false, true); } } for (auto it = this->unknown_00D8.begin(); it != this->unknown_00D8.end(); ++it) { EZ2STREmitter* emitter = *it; if (emitter && emitter->elapsed >= 0) { AsParticle(emitter)->Render(false, 0.0f, 1.0f, 0.0f, 1.0f, false, true); } } } // OFFSET: 0x0071CCB0 // FUNCTION: fn_07327 CNewCloudKillEffect::CNewCloudKillEffect() : CEffect(1196), unknown_00F4(0), unknown_00F8(0) { } // OFFSET: 0x0071D460 // FUNCTION: fn_07333 void CNewCloudKillEffect::Close() { for (auto it = this->unknown_00CC.begin(); it != this->unknown_00CC.end(); ++it) { FreeParticle(AsParticle(*it)); } this->unknown_00CC.clear(); for (auto it = this->unknown_00D4.begin(); it != this->unknown_00D4.end(); ++it) { FreeParticle(AsParticle(*it)); } this->unknown_00D4.clear(); for (auto it = this->unknown_00DC.begin(); it != this->unknown_00DC.end(); ++it) { FreeParticle(AsParticle(*it)); } this->unknown_00DC.clear(); for (auto it = this->unknown_00E4.begin(); it != this->unknown_00E4.end(); ++it) { FreeParticle(AsParticle(*it)); } this->unknown_00E4.clear(); for (auto it = this->unknown_00EC.begin(); it != this->unknown_00EC.end(); ++it) { FreeParticle(AsParticle(*it)); } this->unknown_00EC.clear(); } // OFFSET: 0x0071DEC0 // FUNCTION: fn_07340 bool CNewCloudKillEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x0071E080 // FUNCTION: fn_07341 void CNewCloudKillEffect::Slot03() { const unsigned int now = timeGetTime(); for (auto it = this->unknown_00CC.begin(); it != this->unknown_00CC.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); if (now - static_cast(this->unknown_00F4) > 5000) { particle->alpha = 0xFF; *reinterpret_cast(&particle->unknown_00C4) = 0.0f; particle->unknown_00E8 = 60; particle->unknown_00A8 = 0; InitParticleQuad(particle->vertices); } if (particle != nullptr) { if (particle->unknown_00E8 == 0) { particle->unknown_00A8 = 1; } else { const int angle = static_cast(*reinterpret_cast(&particle->unknown_00C4) + 1.0f) % 360; particle->alpha = static_cast(static_cast(particle->unknown_00E8) * 4.25f); *reinterpret_cast(&particle->unknown_00C4) = static_cast(angle); matrix rotation; rotation.MakeZRotation(static_cast(angle) / 180.0f * 3.141592f); rotation.AppendXRotation(1.5707964f); matrix scale; scale.SetScale(particle->scale, particle->scale, 1.0f); matrix transform; transform.Compose(scale, rotation); particle->transform = transform; particle->position.x = this->position[0]; particle->position.y = this->position[1]; particle->position.z = this->position[2]; } } --particle->unknown_00E8; } if (now - static_cast(this->unknown_00F4) > 5000) { this->unknown_00F4 = static_cast(timeGetTime()); } for (auto it = this->unknown_00D4.begin(); it != this->unknown_00D4.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); if (now - static_cast(this->unknown_00F8) > 5000) { fn_07337(particle); } if (particle != nullptr) { int alpha = particle->alpha - 3; if (alpha < 0) { alpha = 0; } particle->alpha = alpha; if (alpha == 0) { particle->unknown_00A8 = 1; } else { const float fall = particle->unknown_00C0 * 1.0f; particle->scale -= particle->scale / static_cast(alpha); particle->unknown_00D4 -= fall; particle->unknown_00D0 += 0.0f; particle->unknown_00D8 += 0.0f; particle->position.x = particle->unknown_00D0 + this->position[0]; particle->position.y = particle->unknown_00D4 + this->position[1]; particle->position.z = particle->unknown_00D8 + this->position[2]; float angle = *reinterpret_cast(&particle->unknown_00C4) + 1.0f; angle = static_cast(static_cast(angle) % 360); *reinterpret_cast(&particle->unknown_00C4) = angle; matrix rotation; rotation.MakeZRotation(angle / 180.0f * 3.141592f); rotation.AppendXRotation( static_cast(g_modeMgr.GetGameMode())->camera->camera.pitch / 180.0f * 3.141592f ); matrix scale; scale.SetScale(particle->scale, particle->scale, 1.0f); matrix transform; transform.Compose(scale, rotation); particle->transform = transform; } } } if (now - static_cast(this->unknown_00F8) > 5000) { this->unknown_00F8 = static_cast(timeGetTime()); } for (auto it = this->unknown_00DC.begin(); it != this->unknown_00DC.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); ++particle->unknown_00E8; if (particle->unknown_00E8 <= 0 || particle == nullptr) { continue; } const int maximum = *reinterpret_cast(particle->unknown_0048); if (particle->unknown_00A8 == 0) { int alpha = particle->alpha + 2; if (alpha > maximum) { alpha = maximum; } particle->alpha = alpha; if (alpha == maximum) { particle->unknown_00A8 = 1; } } else if (particle->unknown_00A8 == 1) { int alpha = particle->alpha - 1; if (alpha < 0) { alpha = 0; } particle->alpha = alpha; if (alpha == 0) { fn_07339(particle); continue; } particle->scale -= particle->scale / static_cast(alpha); } const float fall = particle->unknown_00C0 * 1.0f; particle->unknown_00D4 -= fall; particle->unknown_00D0 += 0.0f; particle->unknown_00D8 += 0.0f; particle->position.x = particle->unknown_00D0 + this->position[0]; particle->position.y = particle->unknown_00D4 + this->position[1]; particle->position.z = particle->unknown_00D8 + this->position[2]; matrix scale; scale.SetScale(particle->scale, particle->scale, 1.0f); matrix rotation; rotation.MakeXRotation( static_cast(g_modeMgr.GetGameMode())->camera->camera.pitch / 180.0f * 3.141592f ); matrix transform; transform.Compose(scale, rotation); particle->transform = transform; } for (auto it = this->unknown_00E4.begin(); it != this->unknown_00E4.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); if (particle->unknown_00E8 < particle->unknown_004C) { int state = static_cast(particle->unknown_00A8) - 1; if (state < 0) { state = 0; } particle->unknown_00A8 = static_cast(state); particle->alpha = state * 25; matrix scale; scale.SetScale(particle->scale, particle->scale, 1.0f); matrix rotation; rotation.MakeZRotation(*reinterpret_cast(&particle->unknown_00C4) / 180.0f * 3.141592f); rotation.AppendXRotation( static_cast(g_modeMgr.GetGameMode())->camera->camera.pitch / 180.0f * 3.141592f ); matrix transform; transform.Compose(scale, rotation); particle->transform = transform; matrix yaw; yaw.MakeYRotation( static_cast(g_modeMgr.GetGameMode())->camera->camera.yaw / 180.0f * 3.141592f ); particle->position.x = yaw.values[0] * particle->unknown_00D0 + yaw.values[3] * particle->unknown_00D4 + yaw.values[6] * particle->unknown_00D8 + yaw.values[9] + this->position[0]; particle->position.y = yaw.values[1] * particle->unknown_00D0 + yaw.values[4] * particle->unknown_00D4 + yaw.values[7] * particle->unknown_00D8 + yaw.values[10] + this->position[1]; particle->position.z = yaw.values[2] * particle->unknown_00D0 + yaw.values[5] * particle->unknown_00D4 + yaw.values[8] * particle->unknown_00D8 + yaw.values[11] + this->position[2]; } else { fn_07338(particle); } ++particle->unknown_00E8; } for (auto it = this->unknown_00EC.begin(); it != this->unknown_00EC.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); ++particle->unknown_00E8; if (particle->unknown_00E8 <= 0 || particle == nullptr) { continue; } const int maximum = *reinterpret_cast(particle->unknown_0048); if (particle->unknown_00A8 == 0) { int alpha = particle->alpha + 5; if (alpha > maximum) { alpha = maximum; } particle->alpha = alpha; if (alpha == maximum) { particle->unknown_00A8 = 1; } } else if (particle->unknown_00A8 == 1) { int alpha = particle->alpha - 2; if (alpha < 0) { alpha = 0; } particle->alpha = alpha; if (alpha == 0) { fn_07334(particle); continue; } particle->scale = (particle->scale / static_cast(maximum)) * static_cast(alpha); } const float fall = particle->unknown_00C0 * 1.0f; particle->unknown_00D4 -= fall; particle->unknown_00D0 += 0.0f; particle->unknown_00D8 += 0.0f; particle->position.x = particle->unknown_00D0 + this->position[0]; particle->position.y = particle->unknown_00D4 + this->position[1]; particle->position.z = particle->unknown_00D8 + this->position[2]; matrix scale; scale.SetScale(particle->scale, particle->scale, 1.0f); matrix rotation; rotation.MakeXRotation( static_cast(g_modeMgr.GetGameMode())->camera->camera.pitch / 180.0f * 3.141592f ); matrix transform; transform.Compose(scale, rotation); particle->transform = transform; } } // OFFSET: 0x0071F5F0 // FUNCTION: fn_07344 CPetitioEffect::CPetitioEffect() : CEffect(1911) { } // OFFSET: 0x0071F750 // FUNCTION: fn_07346 void CPetitioEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end();) { FreeParticle(AsParticle(*it)); it = this->emitters.erase(it); } } // OFFSET: 0x0071F7A0 // FUNCTION: fn_07347 void CPetitioEffect::Slot01() { CEZ2STRParticle* bottom = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); bottom->Load(std::string("petitio\\petitio_bottom\\petitio_bottom.str")); this->emitters.push_back(reinterpret_cast(bottom)); CEZ2STRParticle* petitio = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); petitio->Load(std::string("petitio\\petitio\\petitio.str")); this->emitters.push_back(reinterpret_cast(petitio)); this->AddEvent(1, nullptr, 6, 0); this->AddEvent(0, "effect\\CD_PETITIO.wav", 0, 0); } // OFFSET: 0x0071FA30 // FUNCTION: fn_07348 bool CPetitioEffect::Slot06() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (emitter->lifetime == 0.0f || static_cast(emitter->elapsed) < emitter->lifetime * 60.0f) { return true; } } return false; } // OFFSET: 0x0071FA90 // FUNCTION: fn_07349 void CPetitioEffect::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (!emitter) { continue; } CEZ2STRParticle* particle = AsParticle(emitter); particle->position.x = this->position[0]; particle->position.y = this->position[1] - 12.0f; particle->position.z = this->position[2]; particle->Process(0.0f, 0.0f, 0.0f); } } // OFFSET: 0x0071FB70 // FUNCTION: fn_07350 void CPetitioEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { if (*it) { AsParticle(*it)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x0071FBF0 // FUNCTION: fn_07351 CPneumaticusProcellaEffect::CPneumaticusProcellaEffect() : CEffect(1957) { } // OFFSET: 0x0071FE40 // FUNCTION: fn_07354 void CPneumaticusProcellaEffect::Slot01() { double scale; switch (this->unknown_00B0) { case 1: case 2: case 3: scale = 0.6f; break; case 4: case 5: case 6: scale = 0.8f; break; case 10: scale = 1.2f; break; default: scale = 1.0; break; } this->emitter = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); if (this->emitter) { AsParticle(this->emitter) ->Load(std::string("pneumaticusprocella\\pneumaticusprocella_target\\pneumaticusprocella_target.str")); AsParticle(this->emitter)->m_loop = true; AsParticle(this->emitter)->m_scale = static_cast(scale); } scale += 0.6f; const double offsetsX[6] = {-12.0, -6.0, 0.0, 0.0, 6.0, 12.0}; const double offsetsZ[9] = {-12.0, -10.0, -6.0, -4.0, 0.0, 4.0, 6.0, 10.0, 12.0}; for (int i = 0; i < 40; ++i) { const double x = scale * offsetsX[std::max(0, rand() % 6)]; const double z = scale * offsetsZ[std::max(0, rand() % 9)]; const int delay = std::max(0, rand() % 200) + (i + i / 10 * 20) * 100; EZ2STREmitter* emitter = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (emitter) { char name[260] = {}; StringFormat(name, sizeof(name), static_cast(-1), "pneumaticusprocella\\pneumaticusprocella\\pneumaticusprocella_%d.str", std::max(1, rand() % 4)); AsParticle(emitter)->Load(std::string(name)); AsParticle(emitter)->m_delay = delay; emitter->offset.y = -25.0f; emitter->offset.x = static_cast(x); emitter->offset.z = static_cast(z); this->emitters.push_back(emitter); } } this->AddEvent(0, "effect\\CD_PNEUMATICUS_PROCELLA.wav", 0, 0); } // OFFSET: 0x00720410 // FUNCTION: fn_07356 void CPneumaticusProcellaEffect::Slot04() { if (this->emitter) { AsParticle(this->emitter)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { if (*it) { AsParticle(*it)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x00720310 // FUNCTION: fn_07355 void CPneumaticusProcellaEffect::Slot03() { if (this->emitter) { AsParticle(this->emitter)->Process(0.0f, -80.0f, 0.0f); } for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (!emitter) { continue; } if (emitter->lifetime != 0.0f && emitter->lifetime * 60.0f <= static_cast(emitter->elapsed)) { FreeParticle(AsParticle(emitter)); *it = nullptr; continue; } CEZ2STRParticle* particle = AsParticle(emitter); particle->position.x = this->position[0] + emitter->offset.x; particle->position.y = this->position[1] + emitter->offset.y; particle->position.z = this->position[2] + emitter->offset.z; particle->Process(0.0f, -80.0f, 0.0f); } } // OFFSET: 0x00720480 // FUNCTION: fn_07357 CRandomHitEffect::CRandomHitEffect() : CEffect(-1), active(true) { } // OFFSET: 0x007208A0 // FUNCTION: fn_07362 void CRandomHitEffect::Slot03() { if (!this->emitter) { return; } CEZ2STRParticle* particle = AsParticle(this->emitter); particle->position.x = this->position[0] + this->emitter->offset.x; particle->position.y = this->position[1] + this->emitter->offset.y; particle->position.z = this->position[2] + this->emitter->offset.z; particle->Process(0.0f, 0.0f, 0.0f); this->active = particle->Process(0.0f, 0.0f, 0.0f); } // OFFSET: 0x00720940 // FUNCTION: fn_07363 void CRandomHitEffect::Slot04() { if (this->emitter) { AsParticle(this->emitter)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } // OFFSET: 0x00720970 // FUNCTION: fn_07364 CRandomOptEffect::CRandomOptEffect() : CEffect(-1), offsets{} { } // OFFSET: 0x00720AF0 // FUNCTION: fn_07366 void CRandomOptEffect::Slot08(int red, int green, int blue) { for (auto it = this->particles.begin(); it != this->particles.end(); ++it) { (*it)->SetColor(red, green, blue); } } // OFFSET: 0x00720B30 // FUNCTION: fn_07367 void CRandomOptEffect::Slot11(std::string name) { for (auto it = this->particles.begin(); it != this->particles.end(); ++it) { (*it)->SetTexture(name); } } // OFFSET: 0x00720C80 // FUNCTION: fn_07368 void CRandomOptEffect::Init() { int ring = 0; int baseAngle = 0; for (int alpha = 50; alpha > 20; alpha -= 10) { for (int i = 0; i <= 21; ++i) { CParticle* particle = CreateParticle(reinterpret_cast(this->position), 5); if (!particle) { continue; } particle->SetTexture("effect\\alpha_down.tga"); particle->unknown_0004 = this->id; particle->unknown_0010 = ring; particle->unknown_000C = i; particle->unknown_0008 = 3; particle->sourceBlend = 5; particle->destinationBlend = 6; particle->alpha = alpha; particle->SetColor(100, 100, 255); *reinterpret_cast(&particle->unknown_00C4) = static_cast(baseAngle); const float level = static_cast(ring); particle->unknown_0050 = 360; particle->unknown_00C0 = 100.0f - level * 10.0f; particle->scale = level * 0.2f + 0.5f; particle->unknown_004C = static_cast(level * 0.5f + 90.0f); particle->unknown_0054 = 0.0f; switch (this->id) { case 1186: particle->SetColor(255, 200, 255); particle->unknown_00C0 = 120.0f - level * 12.0f; particle->scale = level * 0.24f + 0.6f; break; case 1187: particle->SetColor(192, 192, 192); break; case 1188: particle->SetColor(0, 100, 150); break; case 1189: particle->SetColor(255, 255, 128); break; case 1190: particle->SetColor(128, 0, 255); break; case 1191: particle->SetColor(255, 128, 0); break; } InitParticleQuad(particle->vertices); this->particles.push_back(particle); if (particle) { const int segment = particle->unknown_000C; const float height = g_sinTable[segment * 9] * particle->unknown_00C0; const int step = static_cast(static_cast(particle->unknown_0050) / 20.0f); const int angle = static_cast(static_cast(step * segment) + *reinterpret_cast(&particle->unknown_00C4)) % 360; const float spread = height * g_cosTable[particle->unknown_004C]; const float lift = height * g_sinTable[particle->unknown_004C]; const float cosine = g_cosTable[angle]; const float sine = g_sinTable[angle]; const float innerX = particle->scale * cosine; const float innerZ = particle->scale * sine; particle->vertices[3].x = innerX; particle->vertices[3].y = 0.0f; particle->vertices[3].z = innerZ; particle->vertices[1].x = innerX + spread * cosine; particle->vertices[1].y = 0.0f - lift; particle->vertices[1].z = innerZ + spread * sine; if (segment != 0) { particle->vertices[0].x = this->offsets[1].x; particle->vertices[0].y = this->offsets[1].y; particle->vertices[0].z = this->offsets[1].z; particle->vertices[2].x = this->offsets[0].x; particle->vertices[2].y = this->offsets[0].y; particle->vertices[2].z = this->offsets[0].z; } this->offsets[0].x = innerX; this->offsets[0].y = 0.0f; this->offsets[0].z = innerZ; this->offsets[1].x = particle->vertices[1].x; this->offsets[1].y = particle->vertices[1].y; this->offsets[1].z = particle->vertices[1].z; } } ++ring; baseAngle += 120; } } // OFFSET: 0x00721220 // FUNCTION: fn_07370 void CRandomOptEffect::Slot01() { this->Init(); } // OFFSET: 0x00721230 // FUNCTION: fn_07371 bool CRandomOptEffect::Slot06() { if (this->actor == 0) { return false; } CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); if (!gameMode || !gameMode->scene) { return false; } if (!gameMode->scene->GetItemByAID(this->actor)) { return false; } return true; } // OFFSET: 0x00721270 // FUNCTION: fn_07372 void CRandomOptEffect::Slot03() { for (auto it = this->particles.begin(); it != this->particles.end(); ++it) { CParticle* particle = *it; particle->position.x = this->position[0]; particle->position.y = this->position[1]; particle->position.z = this->position[2]; if (particle) { const int angle = static_cast( static_cast(particle->unknown_0010) + *reinterpret_cast(&particle->unknown_00C4) + 3.0f); *reinterpret_cast(&particle->unknown_00C4) = static_cast(angle); matrix rotation; std::memcpy(rotation.values, g_identityMatrix, sizeof(rotation.values)); rotation.MakeYRotation(static_cast(angle) / 180.0f * 3.141592f); particle->transform = rotation; } ++particle->unknown_00E8; } } // OFFSET: 0x00721430 // FUNCTION: fn_07373 void CRandomOptEffect::Slot04() { for (auto it = this->particles.begin(); it != this->particles.end(); ++it) { CParticle* particle = *it; if (!particle) { continue; } const int texPart = particle->unknown_000C; if (texPart <= 0) { continue; } const ParticleQuad quad = {false, 0.0f, 1.0f, static_cast(texPart - 1) / 21.0f, static_cast(texPart) / 21.0f, false, true}; particle->Render(quad); } } // OFFSET: 0x00721560 // FUNCTION: fn_07374 CRichsCoinEffect::CRichsCoinEffect() : CEffect(1135), unknown_00E4(0) { } // OFFSET: 0x007218D0 // FUNCTION: fn_07377 void CRichsCoinEffect::Slot01() { int count = this->unknown_00E4; if (count > 5) { count = 5; } else if (count <= 0) { return; } for (int i = 0; i < count; ++i) { CParticle* particle = CreateParticle(reinterpret_cast(this->position), 5); if (particle) { particle->SetTexture("effect\\rl_richs_coin_b\\rcoin1.bmp"); particle->unknown_0054 = static_cast(360 / count * i) / 180.0f * 3.141592f - 10.0f; reinterpret_cast(particle->unknown_0058) = 0.0f; InitParticleQuad(particle->vertices); this->particles.push_back(particle); } } if (this->unknown_00E4 > 5) { const int lights = this->unknown_00E4 - 5; for (int i = 0; i < lights; ++i) { CParticle* particle = CreateParticle(reinterpret_cast(this->position), 5); if (particle) { particle->SetTexture("effect\\rl_richs_coin_b\\light.bmp"); particle->unknown_0054 = static_cast(i * 72) / 180.0f * 3.141592f - 10.0f; reinterpret_cast(particle->unknown_0058) = 0.0f; particle->scale = 1.5f; matrix scale; std::memcpy(scale.values, g_identityMatrix, sizeof(scale.values)); scale.SetScale(1.5f, 1.5f, 1.0f); particle->transform = scale; particle->alpha = 70; InitParticleQuad(particle->vertices); this->coins.push_back(particle); } } } } // OFFSET: 0x00721AE0 // FUNCTION: fn_07378 bool CRichsCoinEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x00721F80 // FUNCTION: fn_07380 void CRichsCoinEffect::Slot04() { for (auto it = this->particles.begin(); it != this->particles.end(); ++it) { if (*it) { (*it)->Render(false, 0.0f, 1.0f, 0.0f, 1.0f, false, true); } } for (auto it = this->coins.begin(); it != this->coins.end(); ++it) { if (*it) { (*it)->Render(false, 0.0f, 1.0f, 0.0f, 1.0f, false, true); } } } // OFFSET: 0x00722070 // FUNCTION: fn_07381 CSavageImpactEffect::CSavageImpactEffect() : CEffect(1837), offset{} { } // OFFSET: 0x00722460 // FUNCTION: fn_07385 bool CSavageImpactEffect::Slot06() { if (this->emitter->lifetime == 0.0f) { return true; } return static_cast(this->emitter->elapsed) < this->emitter->lifetime * 60.0f; } // OFFSET: 0x007224A0 // FUNCTION: fn_07386 void CSavageImpactEffect::Slot03() { CEZ2STRParticle* particle = AsParticle(this->emitter); particle->position.x = this->position[0]; particle->position.y = this->position[1] - 12.0f; particle->position.z = this->position[2]; particle->Process(0.0f, 0.0f, 0.0f); } // OFFSET: 0x00722560 // FUNCTION: fn_07387 void CSavageImpactEffect::Slot04() { AsParticle(this->emitter)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } // OFFSET: 0x007226A0 // FUNCTION: fn_07389 CServantWeaponEffect::CServantWeaponEffect() : CEffect(-1), offsets{} { } // OFFSET: 0x00722730 // FUNCTION: fn_07390 CServantWeaponNewEffect::CServantWeaponNewEffect() : CEffect(2460) { } // OFFSET: 0x00722A70 // FUNCTION: fn_07394 void CServantWeaponNewEffect::AddWeapon() { if (this->emitters.size() < 5) { float angle = static_cast(this->emitters.size()) * 72.0f + 90.0f; if (angle > 360.0f) { angle -= 360.0f; } CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (particle) { particle->Load(std::string("DRAGON_KNIGHT\\DK_SERVANTWEAPON\\servantweapon_cast\\servantweapon_cast.str")); particle->m_scale = 0.12f; EZ2STREmitter* emitter = reinterpret_cast(particle); emitter->angle = angle; this->emitters.push_back(emitter); this->AddEvent(0, "effect\\DK_SERVANTWEAPON.wav", this->frame, 0); } } } // OFFSET: 0x00722F20 // FUNCTION: fn_07395 void CServantWeaponEffect::SetShoot(int column) { EZ2STREmitter* emitter = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); AsParticle(emitter)->Load(std::string("servantweapon\\servantweapon_shoot\\servantweapon_shoot.str")); if (this->emitters[2][column] != nullptr) { FreeParticle(AsParticle(this->emitters[2][column])); } this->emitters[2][column] = emitter; } // OFFSET: 0x00723080 // FUNCTION: fn_07396 void CServantWeaponEffect::SetPhantomShot(long long column) { if (column < 5) { EZ2STREmitter* emitter = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); if (emitter) { AsParticle(emitter)->Load(std::string("servant_phantom\\servant_phantom_shot\\servant_phantom_shot.str")); if (this->emitters[2][column] != nullptr) { FreeParticle(AsParticle(this->emitters[2][column])); } this->emitters[2][column] = emitter; } } } // OFFSET: 0x00723210 // FUNCTION: fn_07397 void CServantWeaponEffect::SetSignShot(int column) { EZ2STREmitter* emitter = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); AsParticle(emitter)->Load(std::string("servant_sign\\servant_sign_shot\\servant_sign_shot.str")); if (this->emitters[2][column] != nullptr) { FreeParticle(AsParticle(this->emitters[2][column])); } this->emitters[2][column] = emitter; } // OFFSET: 0x007233D0 // FUNCTION: fn_07399 void CServantWeaponNewEffect::Close() { for (EZ2STREmitter* emitter : this->emitters) { FreeParticle(AsParticle(emitter)); } this->emitters.clear(); for (EZ2STREmitter* trail : this->trails) { FreeParticle(AsParticle(trail)); } this->trails.clear(); } // OFFSET: 0x007237E0 // FUNCTION: fn_07400 void CServantWeaponEffect::Slot01() { this->retained = 1; for (int column = 0; column < 5; ++column) { this->emitters[0][column] = nullptr; this->emitters[1][column] = nullptr; this->emitters[2][column] = nullptr; } this->offsets[0] = {-7.0f, -2.0f, 0.0f}; this->offsets[1] = {-10.0f, -12.0f, 0.0f}; this->offsets[2] = {0.0f, -19.0f, 0.0f}; this->offsets[3] = {10.0f, -12.0f, 0.0f}; this->offsets[4] = {7.0f, -2.0f, 0.0f}; for (int attempt = 0; attempt < 5; ++attempt) { for (int column = 0; column < 5; ++column) { if (this->emitters[0][column] != nullptr || this->emitters[1][column] != nullptr) { continue; } EZ2STREmitter* emitter = reinterpret_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); AsParticle(emitter)->Load(std::string("servantweapon\\servantweapon_cast\\servantweapon_cast.str")); this->emitters[1][column] = emitter; } } } // OFFSET: 0x00723A90 // FUNCTION: fn_07401 void CServantWeaponNewEffect::Slot01() { this->retained = 1; } // OFFSET: 0x00723AA0 // FUNCTION: fn_07402 bool CServantWeaponEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x00723AB0 // FUNCTION: fn_07403 bool CServantWeaponNewEffect::Slot06() { return this->ActorExists(); } // OFFSET: 0x00723D90 // FUNCTION: fn_07404 EZ2STREmitter* CServantWeaponNewEffect::CreateShot(float angle, int skill) { CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (!particle) { return nullptr; } switch (skill) { case 5202: particle->Load(std::string("DRAGON_KNIGHT\\DK_SERVANTWEAPON\\servantweapon_shoot\\servantweapon_shoot.str")); break; case 5203: particle->Load(std::string("DRAGON_KNIGHT\\DK_SERVANT_W_SIGN\\servant_sign_shot\\servant_sign_shot.str")); break; case 5204: particle->Load(std::string("DRAGON_KNIGHT\\DK_SERVANT_W_PHANTOM\\servant_phantom_shot\\servant_phantom_shot.str")); break; case 5205: particle->Load( std::string("DRAGON_KNIGHT\\DK_SERVANT_W_DEMOL\\servant_demolition_shot\\servant_demolition_shot.str")); break; } particle->m_scale = 0.12f; EZ2STREmitter* emitter = reinterpret_cast(particle); emitter->angle = angle; return emitter; } // INLINED static __forceinline bool IsServantEmitterAlive(EZ2STREmitter* emitter) { if (emitter->lifetime == 0.0f) { return true; } return emitter->lifetime * 60.0f > static_cast(emitter->elapsed); } // INLINED static __forceinline void PlaceServantEmitter(CServantWeaponEffect* effect, EZ2STREmitter* emitter, unsigned int column) { if (column >= 5) { return; } const vector3d position = {effect->position[0], effect->position[1], effect->position[2]}; const matrix view = static_cast(g_modeMgr.GetGameMode())->camera->frame.view; const matrix& inverse = static_cast(g_modeMgr.GetGameMode())->camera->frame.inverse; const vector3d& offset = effect->offsets[column]; const float viewX = position.x * view.values[0] + position.y * view.values[3] + position.z * view.values[6] + view.values[9] + offset.x; const float viewY = position.x * view.values[1] + position.y * view.values[4] + position.z * view.values[7] + view.values[10] + offset.y; const float viewZ = position.x * view.values[2] + position.y * view.values[5] + position.z * view.values[8] + view.values[11] + offset.z; emitter->position.x = viewX * inverse.values[0] + viewY * inverse.values[3] + viewZ * inverse.values[6] + inverse.values[9]; emitter->position.y = viewX * inverse.values[1] + viewY * inverse.values[4] + viewZ * inverse.values[7] + inverse.values[10]; emitter->position.z = viewX * inverse.values[2] + viewY * inverse.values[5] + viewZ * inverse.values[8] + inverse.values[11]; AsParticle(emitter)->Process(0.0f, 0.0f, 0.0f); ++emitter->elapsed; } // OFFSET: 0x00724100 // FUNCTION: fn_07405 void CServantWeaponEffect::Slot03() { for (int column = 0; column < 5; ++column) { EZ2STREmitter* emitter = this->emitters[0][column]; if (emitter != nullptr) { PlaceServantEmitter(this, emitter, column); } emitter = this->emitters[1][column]; if (emitter != nullptr) { if (IsServantEmitterAlive(emitter)) { PlaceServantEmitter(this, emitter, column); } else { FreeParticle(AsParticle(emitter)); this->emitters[1][column] = nullptr; if (this->emitters[0][column] == nullptr) { CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); particle->Load(std::string("servantweapon\\servantweapon\\servantweapon.str")); particle->m_loop = true; this->emitters[0][column] = reinterpret_cast(particle); } } } emitter = this->emitters[2][column]; if (emitter != nullptr) { if (IsServantEmitterAlive(emitter)) { PlaceServantEmitter(this, emitter, column); } else { FreeParticle(AsParticle(emitter)); this->emitters[2][column] = nullptr; } } } } // INLINED static __forceinline void OrbitServantEmitter(const CServantWeaponNewEffect* effect, EZ2STREmitter* emitter) { const float radians = emitter->angle / 180.0f * 3.141592f; const float sine = std::sin(radians) * 13.0f; const float cosine = std::cos(radians) * 13.0f; emitter->position.z = sine + effect->position[2]; emitter->position.y = effect->position[1] - 10.0f; emitter->position.x = cosine + effect->position[0]; } // OFFSET: 0x00724900 // FUNCTION: fn_07406 void CServantWeaponNewEffect::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end();) { if (!AsParticle(*it)->Process(0.0f, 0.0f, 0.0f)) { const float angle = (*it)->angle; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (!particle) { FreeParticle(AsParticle(*it)); it = this->emitters.erase(it); continue; } particle->Load(std::string("DRAGON_KNIGHT\\DK_SERVANTWEAPON\\servantweapon\\servantweapon.str")); particle->m_loop = true; particle->m_scale = 0.12f; reinterpret_cast(particle)->angle = angle; FreeParticle(AsParticle(*it)); *it = reinterpret_cast(particle); } OrbitServantEmitter(this, *it); ++it; } for (auto it = this->trails.begin(); it != this->trails.end();) { if (!AsParticle(*it)->Process(0.0f, 0.0f, 0.0f)) { FreeParticle(AsParticle(*it)); it = this->trails.erase(it); continue; } OrbitServantEmitter(this, *it); ++it; } } // OFFSET: 0x00724EB0 // FUNCTION: fn_07407 void CServantWeaponEffect::Slot04() { for (int column = 0; column < 5; ++column) { if (this->emitters[0][column] != nullptr) { AsParticle(this->emitters[0][column])->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } if (this->emitters[1][column] != nullptr) { AsParticle(this->emitters[1][column])->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } if (this->emitters[2][column] != nullptr) { AsParticle(this->emitters[2][column])->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x00724F40 // FUNCTION: fn_07408 void CServantWeaponNewEffect::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { AsParticle(*it)->DrawBillboards(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } for (auto it = this->trails.begin(); it != this->trails.end(); ++it) { AsParticle(*it)->DrawBillboards(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } // OFFSET: 0x00725090 // FUNCTION: fn_07409 void CServantWeaponNewEffect::Shoot(int skill) { if (!this->emitters.empty()) { EZ2STREmitter* weapon = this->emitters.back(); EZ2STREmitter* shot = this->CreateShot(weapon->angle, skill); if (shot) { FreeParticle(AsParticle(weapon)); this->emitters.pop_back(); this->trails.push_back(shot); this->AddEvent(0, "effect\\DK_SERVANTWEAPON.wav", this->frame, 0); } } } // OFFSET: 0x00725130 // FUNCTION: fn_07410 CSevereRainStorm::CSevereRainStorm() : CEffect(-1) { this->unknown_0050 = 0; } // OFFSET: 0x0071D650 // FUNCTION: fn_07334 void __stdcall fn_07334(CParticle* particle) { particle->alpha = 0; *reinterpret_cast(particle->unknown_0048) = rand() % 50 + 150; particle->unknown_00A8 = 0; particle->unknown_00E8 = 0; InitParticleQuad(particle->vertices); const int spread = rand() % 15 + 2; const int angle = rand() % 360; particle->unknown_00D4 = 0.0f; particle->unknown_00D8 = static_cast(spread) * g_sinTable[angle]; particle->unknown_00D0 = static_cast(spread) * g_cosTable[angle]; particle->scale = static_cast(rand() % 10) * 0.1f + 0.5f; particle->unknown_00C0 = static_cast(rand() % 30) * 0.01f + 0.05f; } // OFFSET: 0x0071D760 // FUNCTION: fn_07335 void CNewCloudKillEffect::Slot01() { this->unknown_00F4 = static_cast(timeGetTime()); this->unknown_00F8 = static_cast(timeGetTime()); CParticle* circle = CreateParticle(reinterpret_cast(this->position), 5); if (circle) { circle->SetTexture("effect\\new_cloud_kill\\ef_cloud_circle.tga"); circle->unknown_0004 = this->id; circle->unknown_000C = 1; circle->unknown_0010 = 0; circle->unknown_0008 = 1; circle->scale = 20.0f; circle->alpha = 0xFF; circle->unknown_00C4 = 0; circle->unknown_00E8 = 60; circle->unknown_00A8 = 0; InitParticleQuad(circle->vertices); this->unknown_00CC.push_back(reinterpret_cast(circle)); } for (int i = 0; i < 100; ++i) { CParticle* particle = CreateParticle(reinterpret_cast(this->position), 5); if (particle) { particle->unknown_0004 = this->id; particle->unknown_000C = i; particle->unknown_0010 = 0; particle->unknown_0008 = 1; particle->SetTexture("effect\\new_cloud_kill\\ef_cloud_kill.tga"); fn_07337(particle); this->unknown_00D4.push_back(reinterpret_cast(particle)); } } for (int i = 0; i < 30; ++i) { CParticle* particle = CreateParticle(reinterpret_cast(this->position), 5); if (particle) { particle->unknown_0004 = this->id; particle->unknown_000C = i; particle->unknown_0010 = 0; particle->unknown_0008 = 1; particle->SetTexture("effect\\new_cloud_kill\\ef_cloud_kill.tga"); fn_07339(particle); particle->unknown_00E8 = -std::max(0, rand() % 71); this->unknown_00DC.push_back(reinterpret_cast(particle)); } } CParticle* lightning = CreateParticle(reinterpret_cast(this->position), 5); if (lightning) { lightning->unknown_0004 = this->id; lightning->unknown_000C = 1; lightning->unknown_0010 = 0; lightning->unknown_0008 = 1; lightning->SetTexture("effect\\new_cloud_kill\\ef_comm_lightning_cloud.tga"); lightning->alpha = 0xFF; lightning->depthOffset = 0.035f; fn_07338(lightning); lightning->unknown_004C = 50; this->unknown_00E4.push_back(reinterpret_cast(lightning)); } for (int i = 0; i < 30; ++i) { CParticle* particle = CreateParticle(reinterpret_cast(this->position), 5); if (particle) { particle->unknown_0004 = this->id; particle->unknown_000C = i; particle->unknown_0010 = 0; particle->unknown_0008 = 1; particle->SetTexture("effect\\new_cloud_kill\\ef_cloud_kill.tga"); fn_07334(particle); particle->unknown_00E8 = -std::max(0, rand() % 80); this->unknown_00EC.push_back(reinterpret_cast(particle)); } } } // OFFSET: 0x0071DB70 // FUNCTION: fn_07337 void __stdcall fn_07337(CParticle* particle) { particle->unknown_00A9[0] = rand() % 2; const int angle = rand() % 360; particle->unknown_00D4 = 0.0f; particle->alpha = 0xFF; particle->unknown_00D0 = g_cosTable[angle] * 18.0f; particle->unknown_00D8 = g_sinTable[angle] * 18.0f; particle->scale = static_cast(rand() % 31) * 0.1f + 2.0f; particle->unknown_00A8 = 0; particle->unknown_00C0 = static_cast(rand() % 31) * 0.01f + 0.05f; InitParticleQuad(particle->vertices); } // OFFSET: 0x0071DC70 // FUNCTION: fn_07338 void __stdcall fn_07338(CParticle* particle) { particle->scale = static_cast(rand() % 5) + 10.0f; particle->unknown_00A8 = 10; particle->unknown_00A9[0] = rand() % 4; particle->unknown_004C = rand() % 50 + 10; *reinterpret_cast(&particle->unknown_00C4) = static_cast(rand() % 360); particle->unknown_00D8 = static_cast(rand() % 10) - 5.0f; particle->unknown_00D0 = static_cast(rand() % 10) - 5.0f; particle->unknown_00D4 = -10.0f; particle->unknown_00E8 = 0; InitParticleQuad(particle->vertices); } // OFFSET: 0x0071DD80 // FUNCTION: fn_07339 void __stdcall fn_07339(CParticle* particle) { particle->alpha = 0; particle->unknown_00A8 = 0; particle->unknown_00E8 = 0; InitParticleQuad(particle->vertices); particle->scale = static_cast(rand() % 51) * 0.1f + 7.0f; particle->unknown_00D4 = particle->scale * -0.5f; const int spread = rand() % 13 + 2; const int angle = rand() % 360; particle->unknown_00D8 = static_cast(spread) * g_sinTable[angle]; particle->unknown_00D0 = static_cast(spread) * g_cosTable[angle]; particle->unknown_00A9[0] = rand() % 4; *reinterpret_cast(particle->unknown_0048) = rand() % 151 + 50; particle->unknown_00C0 = static_cast(rand() % 10) * 0.01f + 0.05f; } // OFFSET: 0x0070C2B0 // FUNCTION: fn_07128 void CRootTwistEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x0070D000 // FUNCTION: fn_07143 void CSpriteMableEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x0070DE50 // FUNCTION: fn_07165 void CTunaPartyEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x0070EAD0 // FUNCTION: fn_07175 void CCurseOfCubeEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x0070F310 // FUNCTION: fn_07184 void CWarningPlaneEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x00710C40 // FUNCTION: fn_07208 void CEternalSlashAttackEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x00716960 // FUNCTION: fn_07276 void CFullScreenEffect::Close() { for (auto it = this->particles.begin(); it != this->particles.end(); ++it) { FreeParticle(*it); } this->particles.clear(); } // OFFSET: 0x00717DA0 // FUNCTION: fn_07284 void CGaleStorm::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x007211E0 // FUNCTION: fn_07369 void CRandomOptEffect::Close() { for (auto it = this->particles.begin(); it != this->particles.end(); ++it) { FreeParticle(*it); } this->particles.clear(); } // OFFSET: 0x0070FB10 // FUNCTION: fn_07192 void CCylinderEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x0070C520 // FUNCTION: fn_07135 void CSprArrowEffect::Close() { if (this->unknown_00CC != 0) { FreeParticle(reinterpret_cast(this->unknown_00CC)); this->unknown_00CC = 0; } } // OFFSET: 0x0070D460 // FUNCTION: fn_07150 void CStemSpearEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x0070D980 // FUNCTION: fn_07158 void CSvgSpiritEffect::Close() { if (this->emitter != nullptr) { FreeParticle(AsParticle(this->emitter)); this->emitter = nullptr; } } // OFFSET: 0x00711250 // FUNCTION: fn_07215 void CEternalSlashCountEffect::Close() { if (this->emitter != nullptr) { FreeParticle(AsParticle(this->emitter)); this->emitter = nullptr; } } // OFFSET: 0x007205A0 // FUNCTION: fn_07359 void CRandomHitEffect::Close() { if (this->emitter != nullptr) { FreeParticle(AsParticle(this->emitter)); this->emitter = nullptr; } } // OFFSET: 0x00711570 // FUNCTION: fn_07222 void CFallenAngelEffect::Close() { FreeParticle(AsParticle(this->emitter)); } // OFFSET: 0x007221B0 // FUNCTION: fn_07383 void CSavageImpactEffect::Close() { FreeParticle(AsParticle(this->emitter)); } // OFFSET: 0x007193B0 // FUNCTION: fn_07306 void CDropItemEffect::Close() { for (std::size_t i = 0; i < this->emitters.size(); ++i) { if (this->emitters[i] != nullptr) { FreeParticle(AsParticle(this->emitters[i])); } } } // OFFSET: 0x00711A20 // FUNCTION: fn_07230 void CFloralFlareRoad::Close() { FreeParticle(AsParticle(this->unknown_00CC)); for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x0071FD50 // FUNCTION: fn_07353 void CPneumaticusProcellaEffect::Close() { if (this->emitter != nullptr) { FreeParticle(AsParticle(this->emitter)); this->emitter = nullptr; } for (std::vector::iterator it = this->emitters.begin(); it < this->emitters.end(); ++it) { if (*it != nullptr) { FreeParticle(AsParticle(*it)); *it = nullptr; } } this->emitters.clear(); } // OFFSET: 0x00723370 // FUNCTION: fn_07398 void CServantWeaponEffect::Close() { for (int column = 0; column < 5; ++column) { if (this->emitters[0][column] != nullptr) { FreeParticle(AsParticle(this->emitters[0][column])); this->emitters[0][column] = nullptr; } if (this->emitters[1][column] != nullptr) { FreeParticle(AsParticle(this->emitters[1][column])); this->emitters[1][column] = nullptr; } if (this->emitters[2][column] != nullptr) { FreeParticle(AsParticle(this->emitters[2][column])); this->emitters[2][column] = nullptr; } } } // OFFSET: 0x00712350 // FUNCTION: fn_07237 void CFootprintEffect::Close() { for (EZ2STREmitter* emitter : this->emitters) { FreeParticle(AsParticle(emitter)); } this->emitters.clear(); } // OFFSET: 0x00710790 // FUNCTION: fn_07201 void CDancingKnifeEffect::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); for (auto it = this->unknown_00D8.begin(); it != this->unknown_00D8.end(); ++it) { FreeParticle(AsParticle(*it)); } this->unknown_00D8.clear(); } // OFFSET: 0x0071C4B0 // FUNCTION: fn_07322 void CJupitelThunderHit::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); for (auto it = this->unknown_00D8.begin(); it != this->unknown_00D8.end(); ++it) { FreeParticle(AsParticle(*it)); } this->unknown_00D8.clear(); } // OFFSET: 0x00721860 // FUNCTION: fn_07376 void CRichsCoinEffect::Close() { for (auto it = this->particles.begin(); it != this->particles.end(); ++it) { FreeParticle(*it); } this->particles.clear(); for (auto it = this->coins.begin(); it != this->coins.end(); ++it) { FreeParticle(*it); } this->coins.clear(); } // OFFSET: 0x00725490 // FUNCTION: fn_07413 void CSevereRainStorm::SpawnParticles() { switch (this->id) { case 1752: if (this->unknown_00D4 % (4 - (this->unknown_00B0 + 1) / 2) == 0) { this->SpawnCrystalRain(); } break; case 1276: if (this->unknown_00D4 % 3 == 0) { this->SpawnSevereRain(); } break; case 2026: if (this->unknown_00D4 % 5 == 0) { vector3d position = {this->position[0], this->position[1], this->position[2]}; position.x = (static_cast(std::max(0, rand() % 451)) - 225.0f) * 0.1f + position.x; position.z = (static_cast(std::max(0, rand() % 451)) - 225.0f) * 0.1f + position.z; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(&position, 5)); particle->Open(std::string("diamond_storm\\diamond_storm\\diamond_storm.str")); this->emitters.push_back(reinterpret_cast(particle)); } break; case 2165: if (this->unknown_00D4 % (9 - this->unknown_00B0 / 3) == 0) { const long long ranges[10] = {3, 3, 3, 5, 5, 5, 7, 7, 7, 9}; const long long spread = ranges[std::min(static_cast(this->unknown_00B0), 10u) - 1] * 50; vector3d position = {this->position[0], this->position[1], this->position[2]}; const float half = static_cast(spread) * 0.5f; position.x = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + position.x; position.z = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + position.z; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(&position, 5)); particle->Load(std::string("crossrain\\cross_rain\\cross_rain.str")); this->emitters.push_back(reinterpret_cast(particle)); } break; case 2348: if (this->ActorExists() && this->unknown_00D4 % 15 == 0) { this->SpawnTwinklingGalaxy(this->unknown_00D4 / 15 % 4); } break; case 2349: if (this->ActorExists() && this->unknown_00D4 % 18 == 0) { this->SpawnGalaxyStar(); } break; case 2350: if (this->ActorExists() && this->unknown_00D4 % 18 == 0) { this->SpawnStarCannon(); } break; case 2351: if (this->ActorExists() && this->unknown_00D4 % 15 == 0) { this->SpawnGrenades(); } break; case 2354: if (this->ActorExists() && this->unknown_00D4 % 15 == 0) { this->SpawnBombardHits(); } break; case 2462: if (this->unknown_00D4 % (9 - this->unknown_00B0 / 3) == 0) { this->SpawnCrossRain(); } break; } } // OFFSET: 0x00725D60 // FUNCTION: fn_07414 void CSevereRainStorm::SpawnCrossRain() { const int ranges[10] = {3, 3, 3, 5, 5, 5, 7, 7, 7, 9}; const int index = std::max(0, std::min(this->unknown_00B0 - 1, 9)); vector3d position = {this->position[0], this->position[1], this->position[2]}; const int spread = ranges[index] * 50; const float half = static_cast(spread) * 0.5f; position.x = (static_cast(std::max(0, rand() % (spread + 1))) - half) * 0.1f + position.x; position.z = (static_cast(std::max(0, rand() % (spread + 1))) - half) * 0.1f + position.z; position.y -= 10.0f; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(&position, 5)); particle->Load(std::string("IMPERIAL_GUARD\\IG_CROSS_RAIN\\new_crossrain\\new_cross_rain\\new_cross_rain.str")); this->emitters.push_back(reinterpret_cast(particle)); } // OFFSET: 0x00726110 // FUNCTION: fn_07415 void CSevereRainStorm::SpawnGrenades() { const long long ranges[5] = {11, 11, 11, 9, 9}; const long long spread = ranges[std::min(static_cast(this->unknown_00B0), 5u) - 1] * 50; for (int count = 0; count < (this->unknown_00B0 + 1) / 2; ++count) { const float half = static_cast(spread) * 0.5f; const vector3d base = {this->position[0] + 0.0f, this->position[1] - 15.0f, this->position[2] + 0.0f}; vector3d position = base; position.x = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + base.x; position.z = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + base.z; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); if (particle) { particle->Load(std::string("NIGHT_WATCH\\NW_GRENADES_DROPPING\\grenades_dropping_bottom\\grenades_dropping_bottom.str")); particle->position = position; this->emitters.push_back(reinterpret_cast(particle)); } particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (particle) { particle->Load(std::string("NIGHT_WATCH\\NW_GRENADES_DROPPING\\grenades_dropping\\grenades_dropping.str")); particle->position = position; this->emitters.push_back(reinterpret_cast(particle)); } } } // OFFSET: 0x007265B0 // FUNCTION: fn_07416 void CSevereRainStorm::SpawnBombardHits() { for (int count = 0; count < 3; ++count) { const vector3d base = {this->position[0] + 0.0f, this->position[1] - 15.0f, this->position[2] + 0.0f}; vector3d position = base; position.x = (static_cast(std::max(0, rand() % 451)) - 225.0f) * 0.1f + base.x; position.z = (static_cast(std::max(0, rand() % 451)) - 225.0f) * 0.1f + base.z; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (particle) { particle->Load(std::string("NIGHT_WATCH\\NW_MISSION_BOMBARD\\mission_bombard\\mission_bombard_hit.str")); particle->position = position; particle->m_scale = static_cast(std::max(0, rand() % 51) + 50) * 0.01f; this->emitters.push_back(reinterpret_cast(particle)); } } } // OFFSET: 0x00726890 // FUNCTION: fn_07417 void CSevereRainStorm::SpawnCrystalRain() { const long long ranges[5] = {11, 13, 15, 17, 19}; const long long spread = ranges[std::min(static_cast(this->unknown_00B0), 5u) - 1] * 50; vector3d position = {this->position[0], this->position[1], this->position[2]}; const float half = static_cast(spread) * 0.5f; position.x = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + position.x; position.z = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + position.z; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(&position, 13)); particle->Load(std::string("rain_of_crystal\\rain_of_crystal_attack_bottom\\rain_of_crystal_attack_bottom.str")); this->emitters.push_back(reinterpret_cast(particle)); particle = static_cast(CreateEZ2STRParticle(&position, 5)); particle->Load(std::string("rain_of_crystal\\rain_of_crystal_attack\\rain_of_crystal_attack.str")); this->emitters.push_back(reinterpret_cast(particle)); if (rand() % 7 == 0) { this->AddEvent(0, "effect\\AG_RAIN_OF_CRYSTAL.wav", this->frame + 1, 0); } } // OFFSET: 0x00726BE0 // FUNCTION: fn_07418 void CSevereRainStorm::SpawnSevereRain() { vector3d position = {this->position[0], this->position[1], this->position[2]}; position.z += 10.0f; if (std::max(0, rand() % 10) >= 5) { position.x = static_cast(std::max(0, rand() % 251)) * 0.1f + position.x; } else { position.x = position.x - static_cast(std::max(0, rand() % 251)) * 0.1f; } if (std::max(0, rand() % 10) >= 5) { position.z = static_cast(std::max(0, rand() % 251)) * 0.1f + position.z; } else { position.z = position.z - static_cast(std::max(0, rand() % 251)) * 0.1f; } CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(&position, 1)); if (g_serviceOptions.Get(229)) { particle->Open(std::string("mineffect\\new_severerainstorm\\new_severerainstorm\\new_severerainstorm.str")); } else { particle->Open(std::string("new_severerainstorm\\new_severerainstorm\\new_severerainstorm.str")); } particle->flags = 5; this->emitters.push_back(reinterpret_cast(particle)); } // OFFSET: 0x00726E30 // FUNCTION: fn_07419 void CSevereRainStorm::SpawnTwinklingGalaxy(int index) { CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); if (particle) { particle->Load(std::string("SKY_EMPEROR\\SKE_TWINKLING_GALAXY\\twinkling_galaxy_bottom\\ske_twinkling_galaxy_bottom.str")); particle->unknown_00D0 = 0.2f; particle->unknown_00D4 = -13.5f; particle->unknown_00D8 = 0.0f; this->emitters.push_back(reinterpret_cast(particle)); } particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (particle) { particle->Load(FormatOwnedString("SKY_EMPEROR\\SKE_TWINKLING_GALAXY\\twinkling_galaxy\\ske_twinkling_galaxy%02d.str", index)); particle->unknown_00D0 = 0.2f; particle->unknown_00D4 = -13.5f; particle->unknown_00D8 = 0.0f; this->emitters.push_back(reinterpret_cast(particle)); } this->AddEvent(0, "effect\\SKY_EMPEROR\\ske_star_cannon.wav", this->frame + 1, 0); } // OFFSET: 0x00727050 // FUNCTION: fn_07420 void CSevereRainStorm::SpawnStarCannon() { const long long ranges[5] = {13, 13, 11, 11, 9}; const long long spread = ranges[std::min(static_cast(this->unknown_00B0), 5u) - 1] * 50; const vector3d base = {this->position[0] + 0.0f, this->position[1] - 15.0f, this->position[2] + 0.0f}; vector3d position = base; const float half = static_cast(spread) * 0.5f; position.x = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + base.x; position.z = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + base.z; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); if (particle) { particle->Load(std::string("SKY_EMPEROR\\SKE_STAR_CANNON\\star_cannon_bottom\\ske_star_cannon_bottom.str")); particle->position = position; this->emitters.push_back(reinterpret_cast(particle)); } particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (particle) { particle->Load(std::string("SKY_EMPEROR\\SKE_STAR_CANNON\\star_cannon\\ske_star_cannon.str")); particle->position = position; this->emitters.push_back(reinterpret_cast(particle)); } this->AddEvent(0, "effect\\SKY_EMPEROR\\ske_star_cannon.wav", this->frame + 1, 0); } // OFFSET: 0x00727450 // FUNCTION: fn_07421 void CSevereRainStorm::SpawnGalaxyStar() { const long long ranges[5] = {13, 13, 11, 11, 9}; const long long spread = ranges[std::min(static_cast(this->unknown_00B0), 5u) - 1] * 50; const vector3d base = {this->position[0] + 0.0f, this->position[1] - 15.0f, this->position[2] + 0.0f}; vector3d position = base; const float half = static_cast(spread) * 0.5f; position.x = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + base.x; position.z = (static_cast(std::max(0, rand() % (static_cast(spread) + 1))) - half) * 0.1f + base.z; CEZ2STRParticle* particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); if (particle) { particle->Load(std::string("SKY_EMPEROR\\SKE_TWINKLING_GALAXY\\twinkling_galaxy_bottom\\ske_twinkling_galaxy_bottom.str")); particle->position = position; this->emitters.push_back(reinterpret_cast(particle)); } particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (particle) { particle->Load(std::string("SKY_EMPEROR\\SKE_TWINKLING_GALAXY\\twinkling_galaxy\\ske_twinkling_galaxy03.str")); particle->position = position; this->emitters.push_back(reinterpret_cast(particle)); } this->AddEvent(0, "effect\\SKY_EMPEROR\\ske_star_cannon.wav", this->frame + 1, 0); } // OFFSET: 0x00727850 // FUNCTION: fn_07422 void CSevereRainStorm::Close() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { FreeParticle(AsParticle(*it)); } this->emitters.clear(); } // OFFSET: 0x007278C0 // FUNCTION: fn_07423 void CSevereRainStorm::Slot01() { this->unknown_00D4 = 0; if (this->id == 2348) { this->retained = 1; } } // OFFSET: 0x007278E0 // FUNCTION: fn_07424 bool CSevereRainStorm::Slot06() { switch (this->id) { case 1276: return this->unknown_00D4 <= 240; case 2026: return EffectLifetime(2026) >= this->unknown_00D4; case 1752: case 2165: case 2462: return this->ActorExists(); case 2348: case 2349: case 2350: case 2351: case 2354: if (this->ActorExists() != true && this->emitters.size() == 0) { return false; } break; } return true; } // OFFSET: 0x00727A10 // FUNCTION: fn_07425 void CSevereRainStorm::Slot03() { this->SpawnParticles(); for (auto it = this->emitters.begin(); it != this->emitters.end();) { EZ2STREmitter* emitter = *it; if (emitter->lifetime < 0.0f) { FreeParticle(AsParticle(emitter)); it = this->emitters.erase(it); continue; } if (this->id == 2348 && emitter) { const matrix& view = static_cast(g_modeMgr.GetGameMode())->camera->frame.view; const matrix& inverse = static_cast(g_modeMgr.GetGameMode())->camera->frame.inverse; const vector3d position = {this->position[0], this->position[1], this->position[2]}; const float vx = position.y * view.values[3] + position.x * view.values[0] + position.z * view.values[6] + view.values[9] + emitter->offset.x; const float vy = position.x * view.values[1] + position.y * view.values[4] + position.z * view.values[7] + view.values[10] + emitter->offset.y; const float vz = position.x * view.values[2] + position.y * view.values[5] + position.z * view.values[8] + view.values[11] + emitter->offset.z; emitter->position.x = vy * inverse.values[3] + vx * inverse.values[0] + vz * inverse.values[6] + inverse.values[9]; emitter->position.y = vx * inverse.values[1] + vy * inverse.values[4] + vz * inverse.values[7] + inverse.values[10]; emitter->position.z = vx * inverse.values[2] + vy * inverse.values[5] + vz * inverse.values[8] + inverse.values[11]; AsParticle(emitter)->Process(0.0f, 0.0f, 0.0f); } emitter = *it; if (emitter) { AsParticle(emitter)->Process(0.0f, 0.0f, 0.0f); } ++it; } ++this->unknown_00D4; } // OFFSET: 0x00727C70 // FUNCTION: fn_07426 void CSevereRainStorm::Slot04() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { EZ2STREmitter* emitter = *it; if (emitter) { AsParticle(emitter)->DrawScreen(static_cast(g_modeMgr.GetGameMode())->camera->frame.view); } } } // OFFSET: 0x0070B820 // FUNCTION: fn_07119 CCrimsonArrowEffect::CCrimsonArrowEffect() : CEffect(-1), emitter(nullptr) { } // OFFSET: 0x0070B940 // FUNCTION: fn_07121 void CCrimsonArrowEffect::Close() { if (this->emitter) { FreeParticle(AsParticle(this->emitter)); this->emitter = nullptr; } } // OFFSET: 0x0070B970 // FUNCTION: fn_07122 void CCrimsonArrowEffect::Slot01() { CEZ2STRParticle* arrow = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (!arrow) { return; } vector3d to{this->sourcePosition[0], this->sourcePosition[1], this->sourcePosition[2]}; vector3d from{this->position[0], this->position[1], this->position[2]}; 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; const float length = SquareRoot(dy * dy + dx * dx); double angle = static_cast(std::acos(static_cast(dx / length))) / 3.141592f * 180.0f; if (fromY > toY) { angle = 360.0 - angle; } EZ2STREmitter* emitter = reinterpret_cast(arrow); switch (this->id) { case 1756: arrow->Load("crimsonarrow\\crimsonarrow\\crimsonarrow.str"); emitter->offset.x = 0.0f; emitter->offset.y = -13.0f; emitter->offset.z = 0.0f; arrow->m_angle = static_cast(angle); this->AddEvent(0, "effect\\AG_CRIMSON_ARROW1.wav", 0, 0); break; case 2074: arrow->Load("rhythmshooting\\rhythmshooting_shot\\rhythmshooting_shot.str"); emitter->offset.x = 0.0f; emitter->offset.y = -8.0f; emitter->offset.z = 0.0f; arrow->m_angle = static_cast(angle + 180.0); this->AddEvent(0, "effect\\TR_RHYTHMSHOOTING.wav", 0, 0); break; case 2114: arrow->Load("frenzy_shot\\frenzy_shot\\frenzy_shot.str"); emitter->offset.x = 0.0f; emitter->offset.y = -4.0f; emitter->offset.z = 0.0f; arrow->m_angle = static_cast(angle); this->AddEvent(0, "effect\\ABC_FRENZY_SHOT.wav", 0, 0); break; case 2154: arrow->Load("unlucky_rush\\unlucky_rush\\unlucky_rush.str"); arrow->m_angle = static_cast(angle); break; case 2195: arrow->Load("judgement_cross\\judgement_cross_shot\\judgement_cross_shot.str"); arrow->m_angle = static_cast(angle + 180.0); break; } this->emitter = emitter; } // OFFSET: 0x0070DE90 // FUNCTION: fn_07166 void CTunaPartyEffect::Slot01() { CSpriteParticle* particle = static_cast(CreateSpriteParticle(reinterpret_cast(this->position), 0x101)); if (!particle) { return; } particle->unknown_0004 = this->id; particle->unknown_000C = 1; particle->unknown_0010 = 0; particle->unknown_0008 = 1; particle->SetSprite("이팩트\\su_tunaparty\\su_tunaparty.spr"); particle->SetAction("이팩트\\su_tunaparty\\su_tunaparty.act"); particle->unknown_0054 = 0.0f; particle->loopMode = 3; this->unknown_002C = 9999; this->emitters.push_back(reinterpret_cast(particle)); } // OFFSET: 0x0070DF40 // FUNCTION: fn_07168 void CTunaPartyEffect::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end(); ++it) { CSpriteParticle* particle = AsSprite(*it); const float phase = particle->unknown_0054 + 0.05f; particle->unknown_0054 = phase; const float cosine = std::cos(phase) * 10.0f; const float sine = std::sin(phase) * 10.0f; particle->position.y = this->position[1] - 10.0f; particle->position.x = this->position[0] + sine; particle->position.z = this->position[2] + cosine; float angle = phase / 3.141592f * 180.0f; if (angle > 360.0f) { angle = std::fmod(angle, 360.0f); } angle -= static_cast(g_modeMgr.GetGameMode())->camera->camera.yaw; if (angle < 0.0f) { angle += 360.0f; } int direction = static_cast((angle - 157.5) / 45.0); if (direction < 0) { direction += 8; } ++particle->unknown_00E8; particle->frame = direction; } } // OFFSET: 0x0070C2F0 // FUNCTION: fn_07129 void CRootTwistEffect::Slot01() { CSpriteParticle* particle = static_cast(CreateSpriteParticle(reinterpret_cast(this->position), 0x101)); if (particle) { particle->unknown_0004 = this->id; particle->unknown_000C = 0; particle->unknown_0010 = 0; particle->unknown_0008 = 1; particle->SetSprite("이팩트\\su_sv_roottwist\\su_sv_roottwist.spr"); particle->SetAction("이팩트\\su_sv_roottwist\\su_sv_roottwist.act"); particle->unknown_00EC |= 0x200; particle->frameDelay = 8.0f; particle->depthOffset = 0.00015f; this->emitters.push_back(reinterpret_cast(particle)); } this->unknown_002C = 9999; } // OFFSET: 0x0070D9B0 // FUNCTION: fn_07159 void CSvgSpiritEffect::Slot01() { this->emitter = reinterpret_cast(CreateSpriteParticle(reinterpret_cast(this->position), 0x101)); if (!this->emitter) { return; } CSpriteParticle* sprite = AsSprite(this->emitter); sprite->unknown_0004 = this->id; sprite->unknown_000C = 0; sprite->unknown_0010 = 0; sprite->unknown_0008 = 1; sprite->SetSprite("몬스터\\svg_spirit.spr"); sprite->SetAction("몬스터\\svg_spirit.act"); this->emitter->velocity.x = 0.0f; this->emitter->velocity.y = 0.0f; this->emitter->velocity.z = 0.0f; this->unknown_002C = 16; const float deltaX = this->sourcePosition[0] - this->position[0]; const float deltaZ = this->sourcePosition[2] - this->position[2]; float angle = (static_cast(std::atan2(static_cast(deltaX), static_cast(-deltaZ))) / 3.141592f) * 180.0f; if (angle < 0.0f) { angle += 360.0f; } angle = 180.0f - angle; if (angle < 0.0f) { angle += 360.0f; } this->emitter->baseAngle = static_cast(angle); this->emitter->speed = SquareRoot(deltaX * deltaX + deltaZ * deltaZ) / static_cast(this->unknown_002C); this->emitter->elapsed = 0; } // OFFSET: 0x0070E240 // FUNCTION: fn_07172 void CCurseOfCubeEffect::SpawnShock() { CEZ2STRParticle* particle; if (this->id == 0x901) { particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (!particle) { return; } particle->Open("NPC_CURSE_OF_RED_CUBE\\npc_cur_r_shock\\npc_cur_r_shock.str"); } else if (this->id == 0x902) { particle = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (!particle) { return; } particle->Open("NPC_CURSE_OF_BLUE_CUBE\\npc_cur_b_shock\\npc_cur_b_shock.str"); } else { return; } this->emitters.push_back(reinterpret_cast(particle)); } // OFFSET: 0x0071F0C0 // FUNCTION: fn_07342 void CNewCloudKillEffect::Slot04() { for (auto it = this->unknown_00CC.begin(); it != this->unknown_00CC.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); if (particle->unknown_00A8 != 0 || !particle) { continue; } particle->Render(false, 0.0f, 1.0f, 0.0f, 1.0f, false, true); } for (auto it = this->unknown_00D4.begin(); it != this->unknown_00D4.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); if (particle->unknown_00A8 != 0 || !particle) { continue; } const int frame = static_cast(particle->unknown_00A9[0]); const ParticleQuad quad = {false, 0.5f, 0.75f, static_cast(frame + 1) * 0.25f, static_cast(frame + 2) * 0.25f, false, true}; particle->Render(quad); } for (auto it = this->unknown_00DC.begin(); it != this->unknown_00DC.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); if (particle->unknown_00E8 <= 0 || !particle) { continue; } const int frame = static_cast(particle->unknown_00A9[0]); const ParticleQuad quad = {false, 0.0f, 0.25f, static_cast(frame) * 0.25f, static_cast(frame + 1) * 0.25f, false, true}; particle->Render(quad); } for (auto it = this->unknown_00E4.begin(); it != this->unknown_00E4.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); if (static_cast(particle->unknown_00A8) <= 0 || !particle) { continue; } const int frame = static_cast(particle->unknown_00A9[0]); const ParticleQuad quad = {false, static_cast(frame / 2) * 0.5f, static_cast(frame / 2 + 1) * 0.5f, static_cast(frame % 2) * 0.5f, static_cast(frame % 2 + 1) * 0.5f, false, true}; particle->Render(quad); } for (auto it = this->unknown_00EC.begin(); it != this->unknown_00EC.end(); ++it) { CEZ2STRParticle* particle = AsParticle(*it); if (particle->unknown_00E8 <= 0 || !particle) { continue; } const ParticleQuad quad = {false, 0.5f, 0.75f, 0.0f, 0.25f, false, true}; particle->Render(quad); } } // OFFSET: 0x00710270 // FUNCTION: fn_07199 void CDancingKnifeEffect::SpawnFirstStrike() { CEZ2STRParticle* bottom = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); bottom->Load(std::string("dancing_knife\\dancing_knife_bottom\\dancing_knife_skill_bottom_01.str")); this->emitters.push_back(reinterpret_cast(bottom)); CEZ2STRParticle* knife = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); knife->Load(std::string("dancing_knife\\dancing_knife\\dancing_knife_skill_01.str")); this->emitters.push_back(reinterpret_cast(knife)); } // OFFSET: 0x007104F0 // FUNCTION: fn_07200 void CDancingKnifeEffect::SpawnSecondStrike() { this->secondStrike = 1; CEZ2STRParticle* bottom = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); bottom->Load(std::string("dancing_knife\\dancing_knife_bottom\\dancing_knife_skill_bottom_02.str")); bottom->m_loop = true; bottom->m_actorId = this->actor; this->unknown_00D8.push_back(reinterpret_cast(bottom)); CEZ2STRParticle* knife = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); knife->Load(std::string("dancing_knife\\dancing_knife\\dancing_knife_skill_02.str")); knife->m_loop = true; knife->m_actorId = this->actor; this->unknown_00D8.push_back(reinterpret_cast(knife)); } // OFFSET: 0x00710800 // FUNCTION: fn_07202 void CDancingKnifeEffect::Slot01() { this->secondStrike = 0; this->SpawnFirstStrike(); this->AddEvent(0, "effect\\SHC_DANCING_KNIFE.wav", 0, 0); } // OFFSET: 0x00710840 // FUNCTION: fn_07204 void CDancingKnifeEffect::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end();) { EZ2STREmitter* emitter = *it; if (emitter->lifetime == 0.0f || emitter->lifetime * 60.0f > static_cast(emitter->elapsed)) { if (emitter) { emitter->position.x = this->position[0] + 0.0f; emitter->position.y = this->position[1] - 12.0f; emitter->position.z = this->position[2] + 0.0f; AsParticle(emitter)->Process(0.0f, 0.0f, 0.0f); } ++it; } else { FreeParticle(AsParticle(emitter)); it = this->emitters.erase(it); } } if (this->emitters.empty() && !this->secondStrike) { this->SpawnSecondStrike(); } for (auto it = this->unknown_00D8.begin(); it != this->unknown_00D8.end(); ++it) { EZ2STREmitter* emitter = *it; if (emitter) { emitter->position.x = this->position[0] + 0.0f; emitter->position.y = this->position[1] - 12.0f; emitter->position.z = this->position[2] + 0.0f; AsParticle(emitter)->Process(0.0f, 0.0f, 0.0f); } } } // OFFSET: 0x0070E400 // OFFSET: 0x0070E770 // FUNCTION: fn_07174 void CCurseOfCubeEffect::SpawnStart() { if (this->id == 0x901) { CEZ2STRParticle* bottom = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); if (bottom) { bottom->Open("NPC_CURSE_OF_RED_CUBE\\npc_cur_r_cube\\npc_cur_r_cube_bottom_start.str"); this->emitters.push_back(reinterpret_cast(bottom)); } CEZ2STRParticle* cube = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (cube) { cube->Open("NPC_CURSE_OF_RED_CUBE\\npc_cur_r_cube\\npc_cur_r_cube_start.str"); this->emitters.push_back(reinterpret_cast(cube)); } } else if (this->id == 0x902) { CEZ2STRParticle* bottom = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 13)); if (bottom) { bottom->Open("NPC_CURSE_OF_BLUE_CUBE\\npc_cur_b_cube\\npc_cur_b_cube_bottom_start.str"); this->emitters.push_back(reinterpret_cast(bottom)); } CEZ2STRParticle* cube = static_cast(CreateEZ2STRParticle(reinterpret_cast(this->position), 5)); if (cube) { cube->Open("NPC_CURSE_OF_BLUE_CUBE\\npc_cur_b_cube\\npc_cur_b_cube_start.str"); this->emitters.push_back(reinterpret_cast(cube)); } } } // OFFSET: 0x0070EB90 // FUNCTION: fn_07179 void CCurseOfCubeEffect::Slot03() { for (auto it = this->emitters.begin(); it != this->emitters.end();) { EZ2STREmitter* emitter = *it; if (emitter->lifetime == 0.0f || emitter->lifetime * 60.0f > static_cast(emitter->elapsed)) { if (emitter) { const vector3d position = {this->position[0], this->position[1], this->position[2]}; const matrix view = static_cast(g_modeMgr.GetGameMode())->camera->frame.view; const matrix inverse = static_cast(g_modeMgr.GetGameMode())->camera->frame.inverse; const float vx = position.x * view.values[0] + position.y * view.values[3] + position.z * view.values[6] + view.values[9] + this->unknown_00DC; const float vy = position.x * view.values[1] + position.y * view.values[4] + position.z * view.values[7] + view.values[10] + this->unknown_00E0; const float vz = position.y * view.values[5] + position.x * view.values[2] + position.z * view.values[8] + view.values[11] + this->unknown_00E4; emitter->position.x = vy * inverse.values[3] + vx * inverse.values[0] + vz * inverse.values[6] + inverse.values[9]; emitter->position.y = vy * inverse.values[4] + vx * inverse.values[1] + vz * inverse.values[7] + inverse.values[10]; emitter->position.z = vy * inverse.values[5] + vx * inverse.values[2] + vz * inverse.values[8] + inverse.values[11]; AsParticle(emitter)->Process(0.0f, 0.0f, 0.0f); } ++it; } else { FreeParticle(AsParticle(emitter)); it = this->emitters.erase(it); } } if (this->unknown_00D8 == 1 && this->emitters.empty()) { this->SpawnLoop(); if (!this->emitters.empty()) { this->unknown_00D8 = 2; } } }