#include "Client/Mode/ModeMessage.h" #include "UI/Window/UIEnums.h" #include "UI/Window/WindowId.h" #include "Contents/Actors/GameObject.h" #include "Contents/Actors/ActorEnums.h" #include "Network/Packets/NoticeEnums.h" #include "Contents/Jobs/JobId.h" #include "Contents/Effects/Core/EffectTypes.h" #include "Contents/Actors/GameActor.h" #include "Client/Mode/ModeMgr.h" #include "Client/Mode/CGameMode.h" #include "Contents/Camera/Camera.h" #include "Client/Replay/Replay.h" #include "Client/Session/Session.h" #include "Contents/Jobs/JobInheritance.h" #include "Contents/World/World.h" #include "Contents/Actors/RenderObject.h" #include "Contents/Configuration/ServiceConfiguration.h" #include "Contents/Effects/Core/EffectLists.h" #include "Contents/Effects/Core/RagEffect.h" #include "Render/DeviceContext/DCSurface.h" #include "Render/Projection/RendererViewport.h" #include "Render/Renderer.h" #include "Render/Drawing/SpriteGeometry.h" #include "Render/Primitives/SpriteQuad.h" #include "Render/Primitives/TextureQuad.h" #include "Render/Textures/SpriteAtlas.h" #include "Render/Textures/Texture.h" #include "Resource/Bitmap/PaletteResource.h" #include "Resource/ResourceMgr.h" #include "Resource/Sprites/ActionResource.h" #include "Resource/Sprites/SpriteResource.h" #include "Resource/World/TerrainResource.h" #include "Render/Textures/SpritePixels.h" #include "Utility/Math/AffineMatrix.h" #include "Utility/Math/ScalarConversion.h" #include "Contents/Monster/ChampionMonsterMgr.h" #include "Contents/Actors/ActorStateFlags.h" #include "Contents/Actors/Pc.h" #include "Contents/CashEmotion/CashEmotionMgr.h" #include "Contents/Tables/ForbiddenWords.h" #include "Contents/Configuration/ServiceOptions.h" #include "Contents/Effects/Core/Effect.h" #include "Contents/Effects/Primitives/EffectPrim.h" #include "Contents/Effects/Skills/EnergyOrbEffect.h" #include "Contents/Effects/Skills/MonsterSkillEffects.h" #include "Contents/Effects/Core/MsgEffect.h" #include "Contents/Jobs/JobNames.h" #include "Contents/Map/MapSkyMgr.h" #include "Contents/Skills/SkillInfo.h" #include "Contents/Sound/SoundMaker.h" #include "Contents/Sound/SoundMgr.h" #include "Network/Connection/Connection.h" #include "Network/Packets/PacketLengths.h" #include "UI/Chat/UIChatRoomTitle.h" #include "UI/Chat/UINewChatWnd.h" #include "UI/Buttons/UIButton.h" #include "UI/Gauges/UIPcGage.h" #include "UI/Shop/UIMerchantShopTitle.h" #include "UI/Navigation/NavigationLink.h" #include "UI/Text/UITextViewer.h" #include "UI/Text/UITransBalloonText.h" #include "UI/Buttons/UIBalloonOpenBtn.h" #include "UI/Dialogs/UIMessageBox.h" #include "UI/Window/UIWindow.h" #include "UI/Window/UIWindowMgr.h" #include #include #include #include #include #include #include #include #include namespace { // VTABLE: vt_01828 class CGameObjectIDGenerator { public: virtual ~CGameObjectIDGenerator(); }; // GLOBAL: gl_25444 CGameObjectIDGenerator g_gameObjectIdGenerator; } // STATIC: 0x0135EF80 // GLOBAL: gl_21923 std::uint64_t CGameObject::s_nextIdentity = 0; // GLOBAL: gl_24538 float g_cotangent = 0.16f; // OFFSET: 0x00CA8600 // FUNCTION: fn_20989 CGameObject::CGameObject() : identity(++s_nextIdentity) { } // OFFSET: 0x00CA8EC0 // FUNCTION: fn_20995 CGameObject::~CGameObject() = default; // OFFSET: 0x00CA8ED0 // FUNCTION: fn_20996 CGameObjectIDGenerator::~CGameObjectIDGenerator() { } // OFFSET: 0x00CAFD20 // FUNCTION: fn_21087 bool CGameObject::OnProcess() { return true; } // OFFSET: 0x00CBF850 // FUNCTION: fn_21113 void CGameObject::SendMsg(CGameObject*, std::int64_t, std::int64_t, std::int64_t, std::int64_t, std::int64_t, std::int64_t, std::int64_t) { } // OFFSET: 0x00CB20A0 // FUNCTION: fn_21101 void CGameObject::Render(int) { } // OFFSET: 0x00CABF10 // FUNCTION: fn_21041 int CGameObject::GetDir(float rot) { CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); int longitude = 0; if (gameMode) { longitude = static_cast(-gameMode->camera->camera.yaw - rot); while (longitude < 0) { longitude += 360; } while (longitude >= 360) { longitude -= 360; } } return longitude; } // OFFSET: 0x00CAB740 // FUNCTION: fn_21029 int CGameObject::Get8Dir2(float rot, int flag) { CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); if (!gameMode) { return 0; } float relative = -gameMode->camera->camera.yaw - rot; while (relative < 0.0f) { relative += 360.0f; } while (relative >= 360.0f) { relative -= 360.0f; } if (flag != 0) { if (relative < 22.5f || relative > 337.5f) { return 0; } return static_cast((static_cast(relative) + 22.5) / 45.0); } return static_cast(relative / 45.0f); } // OFFSET: 0x00CAB7F0 // FUNCTION: fn_21030 int CGameActor::Get8Dir(float angle) { CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); if (!gameMode) { return 0; } float relative = -gameMode->camera->camera.yaw - angle; while (relative < 0.0f) { relative += 360.0f; } while (relative >= 360.0f) { relative -= 360.0f; } if ((IsPlayerJob(this->GetEffectiveJob(-1)) && (this->render.animation == 0 || this->render.animation == 8 || this->render.animation == 16)) || (this->GetEffectiveJob(-1) > 45 && this->GetEffectiveJob(-1) < 1000 && this->render.animation == 0) || (g_session.IsHirelingJob(this->GetEffectiveJob(-1)) && (this->render.animation == 0 || this->render.animation == 8 || this->render.animation == 16))) { if (relative < 22.5f || relative > 337.5f) { return 0; } return static_cast((static_cast(relative) + 22.5) / 45.0); } return static_cast(relative / 45.0f); } // OFFSET: 0x00CAB920 // FUNCTION: fn_21031 int CGameObject::Get8Dir(float angle) { CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); if (gameMode) { float relative = -gameMode->camera->camera.yaw - angle; while (relative < 0.0f) { relative += 360.0f; } while (relative >= 360.0f) { relative -= 360.0f; } return static_cast(relative / 45.0f); } return 0; } // OFFSET: 0x00CB20B0 // FUNCTION: fn_21102 void CRenderObject::Render(int argument) { if (this->render.visible != 0) { this->DrawShadow(argument, this->render.scale); this->DrawSprite(argument, false, 0.0f); } } // OFFSET: 0x00CB20F0 // FUNCTION: fn_21103 void CRenderObject::DrawSprite(int argument, bool seasonal, float offset) { const float* matrix = reinterpret_cast(argument); CSprRes* sprite = this->render.sprite; CActRes* action = this->render.action; if (g_session.guildLeader == 1 && seasonal == true) { SYSTEMTIME time; GetLocalTime(&time); int job; if (time.wDay % 3 == 0) { job = 0x520; } else if (time.wDay % 3 == 1) { job = 0x56D; } else { job = 0x4F8; } char path[256]; std::sprintf(path, "몬스터\\%s.act", g_session.GetJobName(job, -1)); action = static_cast(ResourceMgr::Instance().Load(path)); std::sprintf(path, "몬스터\\%s.spr", g_session.GetJobName(job, -1)); sprite = static_cast(ResourceMgr::Instance().Load(path)); } const ActionFrame& frame = action->Frame(this->render.previousAnimation, this->render.curMotion); const float* origin = &this->render.position.x; float position[3]; position[0] = origin[0] * matrix[0] + origin[1] * matrix[3] + origin[2] * matrix[6] + matrix[9]; position[1] = origin[0] * matrix[1] + origin[1] * matrix[4] + origin[2] * matrix[7] + matrix[10]; position[2] = origin[0] * matrix[2] + origin[1] * matrix[5] + origin[2] * matrix[8] + matrix[11]; float x; float y; float reciprocalDepth; g_renderer->ProjectViewport(origin, matrix, &x, &y, &reciprocalDepth); float distanceScale = g_renderer->ScaleDistance(reciprocalDepth); float extra = offset; if (CEffect* effect = g_effectMgr->Find(static_cast(this->render.accountId), 2374)) { extra += *reinterpret_cast(&effect->scaleParameter); } if (g_championMonsterMgr->IsChampion(static_cast(this->render.spriteJob))) { extra += g_championMonsterMgr->GetValue(static_cast(this->render.spriteJob)); } if (extra != 0.0f) { distanceScale *= extra; } this->render.distanceScale = distanceScale / g_heightScale; const float scale = g_renderer->UiScaleX() * (static_cast(static_cast(this->render.spriteOffsetX)) * this->render.scale) * this->render.distanceScale; bool drawn = false; RenderBounds bounds; bounds.left = static_cast(g_renderer->viewport.width); bounds.top = static_cast(g_renderer->viewport.height); bounds.right = 0.0f; bounds.bottom = 0.0f; bounds.offset = reciprocalDepth; this->ScaleByPulse(distanceScale); for (int index = 0; index < frame.cellCount; ++index) { const SpriteFrameCell& cell = frame.Cell(index); if (cell.image == -1) { continue; } const SpriteRleImage* image = sprite->images[cell.imageType][cell.image]; float bottomRhw; float topRhw; SpriteQuad quad; int box[4]; std::uint32_t color; if (!static_cast(this)->PrepareSprite( index, index, distanceScale, cell, x, y, position, scale, &bottomRhw, &topRhw, image, &quad, box, &color, 0, 0, 0 )) { continue; } drawn = true; quad.image = image; if (this->render.visibilityOptions[1] != 0) { const float minX = quad.left < quad.right ? quad.left : quad.right; const float maxX = quad.left > quad.right ? quad.left : quad.right; if (bounds.left > minX) { bounds.left = minX; } if (bounds.top > quad.top) { bounds.top = quad.top; } if (maxX > bounds.right) { bounds.right = maxX; } if (quad.bottom > bounds.bottom) { bounds.bottom = quad.bottom; } } const std::uint32_t* palette = sprite->palette; if (this->render.bodyPalette != 0) { palette = static_cast(ResourceMgr::Instance().Load(this->render.name.c_str()))->palette; } int blend = 257; this->DrawSpriteEffects(&quad, &cell, box, scale, bottomRhw, topRhw, palette, image, &blend, color); if (this->render.spriteJob == 0x235) { quad.bottomRhw = bottomRhw - 0.0000003f; quad.topRhw = topRhw - 0.0000003f; } if (IsScreenShakeActive()) { quad.angle = 180.0f; this->DrawQuad(quad, palette, blend, (x - quad.right) + (x - quad.left), (y - quad.bottom) + (y - quad.top), 0); } else { this->DrawQuad(quad, palette, blend, 0.0f, 0.0f, 0); } SpriteQuad overlay = quad; if ((this->render.flags & 0x100) != 0) { const short level = this->render.effectPhase; bool draw = true; if (level <= 5) { overlay.color = (static_cast(level * 50) << 24) + 0xFFFFFF; } else if (level <= 8) { overlay.color = 0xFFFFFFFF; } else if (level <= 11) { overlay.color = (static_cast(895 - level * 80) << 24) + 0xFFFFFF; } else { draw = false; } if (draw) { this->DrawQuad(overlay, palette, 5, 0.0f, 0.0f, 0); } } if ((this->render.flags & 0x4000) != 0) { this->DrawTintedQuad(overlay, 5, 5, 255, palette); } if ((this->render.flags & 0x40000) != 0) { this->DrawTintedQuad(overlay, 255, 5, 5, palette); } } if (drawn) { this->SetRenderInfo(&bounds.left, x, y); } else { this->SetTlvert(x, y); } this->render.effectPhaseBytes[0] = 1; } // OFFSET: 0x00CB2830 // FUNCTION: fn_21104 void CGameActor::RenderMercenary(const float* matrix, int job) { const float* origin = &this->render.position.x; float position[3]; position[0] = origin[0] * matrix[0] + origin[1] * matrix[3] + origin[2] * matrix[6] + matrix[9]; position[1] = origin[0] * matrix[1] + origin[1] * matrix[4] + origin[2] * matrix[7] + matrix[10]; position[2] = origin[0] * matrix[2] + origin[1] * matrix[5] + origin[2] * matrix[8] + matrix[11]; float x; float y; float reciprocalDepth; g_renderer->ProjectViewport(origin, matrix, &x, &y, &reciprocalDepth); const float distanceScale = g_renderer->ScaleDistance(reciprocalDepth); this->render.distanceScale = distanceScale / g_heightScale; const float scale = g_renderer->UiScaleX() * (static_cast(static_cast(this->render.spriteOffsetX)) * this->render.scale) * this->render.distanceScale; char path[256]; char accessory[256]; const ActionFrame* bodyFrame; const ActionFrame* headFrame; int headOffsetX; int headOffsetY; int accessoryOffsetX; int accessoryOffsetY; for (char layer = 0; layer < 8; ++layer) { int offsetX = 0; int offsetY = 0; if (layer >= 5 && job != JT_MER_ARCHER10 && job != JT_MER_LANCER10 && job != JT_MER_SWORDMAN10) { continue; } CSprRes* sprite; CActRes* action; const ActionFrame* frame; if (layer == 0) { sprite = this->render.sprite; action = this->render.action; frame = &action->Frame(this->render.previousAnimation, this->render.curMotion); if (this->render.previousAnimation < 8) { frame = &action->Frame(this->render.previousAnimation, 0); } bodyFrame = frame; } else if (layer == 1) { if (job >= 0x1781 && job <= 0x178A) { std::sprintf(path, "인간족\\머리통\\여\\%d_여.act", job % 23 + 1); action = static_cast(ResourceMgr::Instance().Load(path)); std::sprintf(path, "인간족\\머리통\\여\\%d_여.spr", job % 23 + 1); sprite = static_cast(ResourceMgr::Instance().Load(path)); } else { std::sprintf(path, "인간족\\머리통\\남\\%d_남.act", job % 23 + 1); action = static_cast(ResourceMgr::Instance().Load(path)); std::sprintf(path, "인간족\\머리통\\남\\%d_남.spr", job % 23 + 1); sprite = static_cast(ResourceMgr::Instance().Load(path)); } frame = &action->Frame(this->render.previousAnimation, this->render.curMotion); if (this->render.previousAnimation < 8) { frame = &action->Frame(this->render.previousAnimation, 0); } headFrame = frame; if (headFrame->anchorCount != 0 && bodyFrame->anchorCount != 0 && headFrame->anchors[0].attachmentId == bodyFrame->anchors[0].attachmentId) { headOffsetX = bodyFrame->anchors[0].x - headFrame->anchors[0].x; headOffsetY = bodyFrame->anchors[0].y - headFrame->anchors[0].y; } offsetX = headOffsetX; offsetY = headOffsetY; } else if (layer == 2) { if (job >= 0x1781 && job <= 0x178A) { std::sprintf(path, "인간족\\용병\\활용병_활.act"); action = static_cast(ResourceMgr::Instance().Load(path)); std::sprintf(path, "인간족\\용병\\활용병_활.spr"); sprite = static_cast(ResourceMgr::Instance().Load(path)); } else if (job >= 0x178B && job <= 0x1794) { std::sprintf(path, "인간족\\용병\\창용병_창.act"); action = static_cast(ResourceMgr::Instance().Load(path)); std::sprintf(path, "인간족\\용병\\창용병_창.spr"); sprite = static_cast(ResourceMgr::Instance().Load(path)); } else { std::sprintf(path, "인간족\\용병\\검용병_검.act"); action = static_cast(ResourceMgr::Instance().Load(path)); std::sprintf(path, "인간족\\용병\\검용병_검.spr"); sprite = static_cast(ResourceMgr::Instance().Load(path)); } frame = &action->Frame(this->render.previousAnimation, this->render.curMotion); } else if (layer == 3) { if (job >= 0x1781 && job <= 0x178A) { continue; } if (job >= 0x178B && job <= 0x1794) { std::sprintf(path, "인간족\\용병\\창용병_창_검광.act"); action = static_cast(ResourceMgr::Instance().Load(path)); std::sprintf(path, "인간족\\용병\\창용병_창_검광.spr"); sprite = static_cast(ResourceMgr::Instance().Load(path)); } else { std::sprintf(path, "인간족\\용병\\검용병_검_검광.act"); action = static_cast(ResourceMgr::Instance().Load(path)); std::sprintf(path, "인간족\\용병\\검용병_검_검광.spr"); sprite = static_cast(ResourceMgr::Instance().Load(path)); } frame = &action->Frame(this->render.previousAnimation, this->render.curMotion); } else if (layer == 4) { switch (job) { case JT_MER_ARCHER01: std::sprintf(accessory, "여\\여_털모자"); break; case JT_MER_ARCHER02: std::sprintf(accessory, "여\\여_골든헤드기어"); break; case JT_MER_ARCHER03: std::sprintf(accessory, "여\\여_대형금방울"); break; case JT_MER_ARCHER04: std::sprintf(accessory, "여\\여_엘리스모자"); break; case JT_MER_ARCHER05: std::sprintf(accessory, "여\\여_가부키가면"); break; case JT_MER_ARCHER06: std::sprintf(accessory, "여\\여_여왕의투구"); break; case JT_MER_ARCHER07: std::sprintf(accessory, "여\\여_지르타스가면"); break; case JT_MER_ARCHER08: std::sprintf(accessory, "여\\여_실버티아라"); break; case JT_MER_ARCHER09: std::sprintf(accessory, "여\\여_고양이모자"); break; case JT_MER_ARCHER10: std::sprintf(accessory, "여\\여_예쁜리본"); break; case JT_MER_LANCER01: std::sprintf(accessory, "남\\남_간호모"); break; case JT_MER_LANCER02: std::sprintf(accessory, "남\\남_검은눈가리개"); break; case JT_MER_LANCER03: std::sprintf(accessory, "남\\남_고블린가면4호"); break; case JT_MER_LANCER04: std::sprintf(accessory, "남\\남_양모자"); break; case JT_MER_LANCER05: std::sprintf(accessory, "남\\남_귀찮은너구리"); break; case JT_MER_LANCER06: std::sprintf(accessory, "남\\남_돈잃은자의마음"); break; case JT_MER_LANCER07: std::sprintf(accessory, "남\\남_마리오넷모자"); break; case JT_MER_LANCER08: std::sprintf(accessory, "남\\남_라이온마스크"); break; case JT_MER_LANCER09: std::sprintf(accessory, "남\\남_클로스헬멧"); break; case JT_MER_LANCER10: std::sprintf(accessory, "남\\남_로드카호의뿔"); break; case JT_MER_SWORDMAN01: std::sprintf(accessory, "남\\남_반창고"); break; case JT_MER_SWORDMAN02: std::sprintf(accessory, "남\\남_분노입"); break; case JT_MER_SWORDMAN03: std::sprintf(accessory, "남\\남_코끼리모자"); break; case JT_MER_SWORDMAN04: std::sprintf(accessory, "남\\남_야구모자"); break; case JT_MER_SWORDMAN05: std::sprintf(accessory, "남\\남_장례건"); break; case JT_MER_SWORDMAN06: std::sprintf(accessory, "남\\남_부용"); break; case JT_MER_SWORDMAN07: std::sprintf(accessory, "남\\남_타이거마스크"); break; case JT_MER_SWORDMAN08: std::sprintf(accessory, "남\\남_초승달투구"); break; case JT_MER_SWORDMAN09: std::sprintf(accessory, "남\\남_엔젤링모자"); break; case JT_MER_SWORDMAN10: std::sprintf(accessory, "남\\남_태양신의모자"); break; } } else if (layer == 5) { switch (job) { case JT_MER_ARCHER10: std::sprintf(accessory, "여\\여_꽃잎"); break; case JT_MER_LANCER10: std::sprintf(accessory, "남\\남_토스트"); break; } } else if (layer == 6) { switch (job) { case JT_MER_ARCHER10: std::sprintf(accessory, "여\\여_요정의귀"); break; case JT_MER_LANCER10: case JT_MER_SWORDMAN10: std::sprintf(accessory, "남\\남_요정의귀"); break; } } else if (layer == 7) { switch (job) { case JT_MER_ARCHER10: std::sprintf(accessory, "여\\여_미스틱로즈"); break; case JT_MER_LANCER10: std::sprintf(accessory, "남\\남_타키우스눈가리개"); break; case JT_MER_SWORDMAN10: std::sprintf(accessory, "남\\남_사이클롭스아이"); break; } } if (layer >= 4) { std::sprintf(path, "악세사리\\%s.act", accessory); action = static_cast(ResourceMgr::Instance().Load(path)); std::sprintf(path, "악세사리\\%s.spr", accessory); sprite = static_cast(ResourceMgr::Instance().Load(path)); frame = &action->Frame(this->render.previousAnimation, this->render.curMotion); if (this->render.previousAnimation < 8) { frame = &action->Frame(this->render.previousAnimation, 0); } if (frame->anchorCount != 0 && headFrame->anchorCount != 0 && frame->anchors[0].attachmentId == headFrame->anchors[0].attachmentId) { accessoryOffsetX = headFrame->anchors[0].x - frame->anchors[0].x; accessoryOffsetY = headFrame->anchors[0].y - frame->anchors[0].y; } offsetX = headOffsetX + accessoryOffsetX; offsetY = headOffsetY + accessoryOffsetY; } bool drawn = false; float bounds[5]; bounds[0] = static_cast(g_renderer->viewport.width); bounds[1] = static_cast(g_renderer->viewport.height); bounds[2] = 0.0f; bounds[3] = 0.0f; bounds[4] = reciprocalDepth; for (int index = 0; index < frame->cellCount; ++index) { const SpriteFrameCell& cell = frame->Cell(index); if (cell.image == -1) { continue; } const SpriteRleImage* image = sprite->images[cell.imageType][cell.image]; float bottomRhw; float topRhw; SpriteQuad quad; int box[4]; std::uint32_t color; if (!this->PrepareSprite(index, index, distanceScale, cell, x, y, position, scale, &bottomRhw, &topRhw, image, &quad, box, &color, offsetX, offsetY, 0)) { continue; } const unsigned char* argb = reinterpret_cast(&color); int blend = 0x101; if (argb[3] < 0xFF) { blend = 1; } drawn = true; quad.image = image; if (layer >= 4) { blend = 1; const float bias = static_cast(layer - 4) * 0.00004f + 0.0001f; quad.bottomRhw += bias; quad.topRhw += bias; } if (this->render.visibilityOptions[1] != 0) { const float minX = quad.left < quad.right ? quad.left : quad.right; const float maxX = quad.left > quad.right ? quad.left : quad.right; if (bounds[0] > minX) { bounds[0] = minX; } if (bounds[1] > quad.top) { bounds[1] = quad.top; } if (maxX > bounds[2]) { bounds[2] = maxX; } if (quad.bottom > bounds[3]) { bounds[3] = quad.bottom; } } const std::uint32_t* palette = sprite->palette; if (this->render.bodyPalette != 0) { palette = static_cast(ResourceMgr::Instance().Load(this->render.name.c_str()))->palette; } if (layer <= 1) { this->DrawPulseOutline(cell, box, scale, bottomRhw, topRhw, palette, image, color); } if (IsScreenShakeActive()) { this->DrawFlippedQuad(quad, palette, blend, x, y, 0); } else { this->DrawQuad(quad, palette, blend, 0.0f, 0.0f, 0); } } if (drawn) { this->SetRenderInfo(bounds, x, y); } else { this->SetTlvert(x, y); } } this->render.effectPhaseBytes[0] = 1; } // OFFSET: 0x00CB35F0 // FUNCTION: fn_21105 void CRenderObject::DrawShadow(int argument, float scale) { if (this->render.spriteJob == 565 || this->render.visibilityOptions[0] == 0 || this->render.shadowScale == 0.0f || scale == 0.0f) { return; } const float* matrix = reinterpret_cast(argument); const float z = this->render.position.z; CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); float position[3]; position[0] = this->render.position.x; position[1] = mode->scene->terrain->Height(this->render.position.x, this->render.position.z); position[2] = z; const float depth = position[1] * matrix[5] + position[0] * matrix[2] + z * matrix[8] + matrix[11]; float screenX; float screenY; float reciprocalDepth; g_renderer->ProjectViewport(position, matrix, &screenX, &screenY, &reciprocalDepth); const float distanceScale = g_renderer->ScaleDistance(reciprocalDepth); CSprRes* sprite = static_cast(ResourceMgr::Instance().Load("shadow.spr")); CActRes* action = static_cast(ResourceMgr::Instance().Load("shadow.act")); const SpriteFrameCell& cell = action->Frame(0, 0).Cell(0); if (cell.image == -1) { return; } float pulse = 1.0f; const SpriteRleImage* image = sprite->images[cell.imageType][0]; this->ScaleByPulse(pulse); const double size = pulse * scale * this->render.shadowScale; const double factor = distanceScale; SpriteQuad quad; quad.top = static_cast(cell.y * factor * cell.scaleY * size + screenY); quad.bottom = static_cast(((image->doubleHeight + 1) * image->height + cell.y) * factor * cell.scaleY * size + screenY + 1.0); float left = static_cast(cell.x * factor * cell.scaleX * size + screenX); quad.right = static_cast(((image->doubleWidth + 1) * image->width + cell.x) * factor * cell.scaleX * size + screenX + 1.0); if (quad.top >= static_cast(g_renderer->viewport.height) || quad.bottom <= 0.0f || left >= static_cast(g_renderer->viewport.width) || quad.right <= 0.0f) { return; } quad.left = static_cast(static_cast(this->render.spriteOffsetX)) + left; quad.right = static_cast(static_cast(this->render.spriteOffsetX)) + quad.right; quad.color = static_cast(((static_cast(cell.color >> 24) << 8 | (cell.color & 0xFF)) << 8 | (cell.color >> 8 & 0xFF)) << 8) | (cell.color >> 16 & 0xFF); quad.bottomRhw = 1.0f / (depth - (quad.bottom - screenY) * g_cotangent) + 0.00001f; quad.topRhw = 1.0f / (depth - (quad.top - screenY) * g_cotangent) + 0.00001f; quad.angle = static_cast(cell.rotation); quad.image = image; if (IsScreenShakeActive()) { this->DrawFlippedQuad(quad, sprite->palette, 257, screenX, screenY, 0); return; } this->DrawQuad(quad, sprite->palette, 257, 0.0f, 0.0f, 0); } // OFFSET: 0x00CB39A0 // FUNCTION: fn_21106 void CRenderObject::DrawQuad(const SpriteQuad& quad, const std::uint32_t* palette, int blend, float offsetX, float offsetY, int depthOffset) { SpriteAtlasRecord record{}; const SpriteRleImage* image = quad.image; CTexture* texture = static_cast(const_cast(image->surface)); if (!texture) { texture = g_renderer->atlas.Find(image, palette, record); if (!texture) { texture = g_renderer->atlas.Insert(*GetRenderSystemHolder()->system, image, palette, record, g_serviceType, g_resourceReuseDelay); } } else { record.u0 = 0.0f; record.v0 = 0.0f; record.u1 = static_cast(texture->ImageWidth()) / static_cast(texture->TextureWidth()); record.v1 = static_cast(texture->ImageHeight()) / static_cast(texture->TextureHeight()); } const double depthBias = depthOffset * 0.000001; const double rhwBias = depthOffset * 0.0002; const float bottomRhw = static_cast(rhwBias + quad.bottomRhw); const float topRhw = static_cast(quad.topRhw + rhwBias); const float bottomZ = RhwToDepth(static_cast(depthBias + quad.bottomRhw)); const float topZ = RhwToDepth(static_cast(quad.topRhw + depthBias)); RenderPrimitive* primitive = g_renderer->primitives.Acquire(); RenderVertex* vertices = primitive->vertices; primitive->texture = texture; if (quad.angle != 0.0f) { const float cosine = ClientCosine(static_cast(quad.angle)); const float sine = ClientSine(static_cast(quad.angle)); const float halfWidth = (quad.right - quad.left) * 0.5f; const float halfHeight = (quad.bottom - quad.top) * 0.5f; const float centerX = quad.left + halfWidth + offsetX; const float centerY = quad.top + halfHeight + offsetY; const float minX = -halfWidth; const float minY = -halfHeight; vertices[0].x = minX * cosine - minY * sine + centerX + 0.5f; vertices[0].y = minY * cosine + minX * sine + centerY + 0.5f; vertices[1].x = halfWidth * cosine - minY * sine + centerX + 0.5f; vertices[1].y = minY * cosine + halfWidth * sine + centerY + 0.5f; vertices[2].x = minX * cosine - halfHeight * sine + centerX + 0.5f; vertices[2].y = halfHeight * cosine + minX * sine + centerY + 0.5f; vertices[3].x = halfWidth * cosine - halfHeight * sine + centerX + 0.5f; vertices[3].y = halfHeight * cosine + halfWidth * sine + centerY + 0.5f; } else { vertices[0].x = quad.left + 0.5f; vertices[0].y = quad.top + 0.5f; vertices[1].x = quad.right + 0.5f; vertices[1].y = quad.top + 0.5f; vertices[2].x = quad.left + 0.5f; vertices[2].y = quad.bottom + 0.5f; vertices[3].x = quad.right + 0.5f; vertices[3].y = quad.bottom + 0.5f; } if (*palette == 0xD77B) { record.u1 -= 0.002f; } vertices[0].rhw = topRhw; vertices[0].z = topZ; vertices[0].u = record.u0; vertices[0].v = record.v0; vertices[0].diffuse = quad.color; vertices[1].rhw = topRhw; vertices[1].z = topZ; vertices[1].u = record.u1; vertices[1].v = record.v0; vertices[1].diffuse = quad.color; vertices[2].rhw = bottomRhw; vertices[2].z = bottomZ; vertices[2].u = record.u0; vertices[2].v = record.v1; vertices[2].diffuse = quad.color; vertices[3].rhw = bottomRhw; vertices[3].z = bottomZ; vertices[3].u = record.u1; vertices[3].v = record.v1; vertices[3].diffuse = quad.color; primitive->sourceBlend = 5; primitive->destinationBlend = blend != 5 ? 6 : 2; primitive->depthFlags = g_renderer->twoPass != 1; g_renderer->queues.Submit(primitive, static_cast(blend | this->render.blendFlags), g_renderer->fog.supported != 0, g_depthStart, g_depthEnd); } // OFFSET: 0x00CB3F40 // FUNCTION: fn_21107 void CRenderObject::DrawFlippedQuad(SpriteQuad& quad, const std::uint32_t* palette, int blend, float x, float y, int depthOffset) { quad.angle = 180.0f; this->DrawQuad(quad, palette, blend, (x - quad.right) + (x - quad.left), (y - quad.bottom) + (y - quad.top), depthOffset); } // OFFSET: 0x00CB3FA0 // FUNCTION: fn_21108 void CRenderObject::SetActionSpeed(float speed) { speed *= this->render.action->Speed(this->render.animation); if (g_replayAnimationScaling != 0) { speed *= Replay::Instance().AnimationScale(); } if (speed > 0.0f) { this->render.speed = speed; } } // OFFSET: 0x00CB3FF0 // FUNCTION: fn_21109 void CRenderObject::SendValueMessage(int value, CGameObject* target) { if (target) { target->SendMsg(nullptr, AM_REQ_ATTACK, value, 0, 0, 0, 0, 0); } } // OFFSET: 0x00CB4070 // FUNCTION: fn_21110 void CGameActor::ApplySavedShapeShift() { const int pending = this->savedShapeShift; this->shapeShift = pending; this->savedShapeShift = 0; this->render.isLieOnGround = false; this->render.visibilityOptions[1] = 1; this->render.isMotionFreezed = false; this->render.specialMotionLocked = 0; int value; switch (pending) { case 1: value = 0x1104; break; case 2: value = 0x1105; break; default: value = this->job; break; } this->SetSprJob(value); this->SetBodyPaletteName(); if (this->render.state != 3) { this->render.state = 0; this->SetState(0, 1); } } // OFFSET: 0x00CB4100 // FUNCTION: fn_21111 void EffectActor::SendMsg(CGameObject* sender, std::int64_t message, std::int64_t b, std::int64_t c, std::int64_t, std::int64_t, std::int64_t, std::int64_t) { switch (message) { case AM_MASTER_DEAD: for (auto it = this->state.effects.begin(); it != this->state.effects.end(); ++it) { (*it)->SendMsg(this, AM_MASTER_DEAD, 0, 0, 0, 0, 0, 0); } this->state.effects.clear(); break; case AM_MAKE_MAGIC_TARGET: if (!this->state.second) { this->state.second = this->LaunchEffect(static_cast(b), vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (!this->state.second) { return; } } this->state.second->SendMsg(nullptr, AM_SET_DURATION, 0, c, 0, 0, 0, 0); break; case AM_DELETE_MAGIC_TARGET: if (this->state.second) { const auto end = this->state.effects.end(); const auto it = std::find(this->state.effects.begin(), end, this->state.second); if (it != end) { (*it)->SendMsg(this, AM_MASTER_DEAD, 0, 0, 0, 0, 0, 0); this->state.effects.erase(it); } this->state.second = nullptr; } break; case AM_MAKE_BEGIN_SPELL: if (!this->state.first) { this->state.first = this->LaunchEffect(static_cast(b), vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (!this->state.first) { return; } } if (b > 0) { this->SendMsg(nullptr, AM_DELETE_EFFECT, b, 0, 0, 0, 0, 0); this->state.first = this->LaunchEffect(static_cast(b), vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (!this->state.first) { return; } } this->state.first->SendMsg(nullptr, AM_SET_DURATION, 0, c, 0, 0, 0, 0); break; case AM_DELETE_BEGIN_SPELL: if (this->state.first) { const auto end = this->state.effects.end(); const auto it = std::find(this->state.effects.begin(), end, this->state.first); if (it != end) { (*it)->SendMsg(this, AM_MASTER_DEAD, 0, 0, 0, 0, 0, 0); this->state.effects.erase(it); } this->state.first = nullptr; } break; case AM_EFFECT_DEAD: if (sender) { CRagEffect* const effect = static_cast(sender); for (auto it = this->state.effects.begin(); it != this->state.effects.end(); ++it) { if (*it == effect) { this->state.effects.erase(it); if (this->state.first == effect) { this->state.first = nullptr; } else if (this->state.second == effect) { this->state.second = nullptr; } return; } } } break; case AM_DELETE_EFFECT: if (g_effectMgr->HasAllocator(static_cast(b)) && sender) { g_effectMgr->Retire(static_cast(sender)->render.accountId, static_cast(b)); return; } for (auto it = this->state.effects.begin(); it != this->state.effects.end(); ++it) { CRagEffect* const effect = *it; if (!effect) { this->state.effects.erase(it); } else if (effect->state.type == b) { effect->SendMsg(this, AM_MASTER_DEAD, 0, 0, 0, 0, 0, 0); this->state.effects.erase(it); return; } } break; case AM_DELETE_TAGGED_EFFECT: if (g_effectMgr->HasAllocator(static_cast(c)) && sender) { g_effectMgr->Retire(static_cast(sender)->render.accountId, static_cast(b), static_cast(c)); return; } for (auto it = this->state.effects.begin(); it != this->state.effects.end(); ++it) { CRagEffect* const effect = *it; if (!effect) { this->state.effects.erase(it); } else if (static_cast(effect->state.tag) == b && effect->state.type == c) { effect->SendMsg(this, AM_MASTER_DEAD, 0, 0, 0, 0, 0, 0); this->state.effects.erase(it); return; } } break; case AM_EFFECT_LOOP_COUNT: for (auto it = this->state.effects.begin(); it != this->state.effects.end(); ++it) { CRagEffect* const effect = *it; if (!effect) { this->state.effects.erase(it); } else if (effect->state.type == b) { effect->SendMsg(this, AM_EFFECT_LOOP_COUNT, 0, b, static_cast(c), static_cast(c >> 32), 0, 0); } } break; case AM_IS_HAVE_EFFECT: if (g_effectMgr->HasAllocator(static_cast(b)) == true && sender) { if (g_effectMgr->Has(static_cast(sender)->render.accountId, static_cast(b)) == true) { *reinterpret_cast(static_cast(b)) = -1; } return; } for (auto it = this->state.effects.begin(); it != this->state.effects.end(); ++it) { CRagEffect* const effect = *it; if (!effect) { this->state.effects.erase(it); } else if (*reinterpret_cast(static_cast(b)) == effect->state.type) { *reinterpret_cast(static_cast(b)) = -1; return; } } break; } } namespace { // INLINED int TransformSlot(int efst) { switch (efst) { case EFST_MONSTER_TRANSFORM: return 0; case EFST_KYOUGAKU: return 1; case EFST_ACTIVE_MONSTER_TRANSFORM: return 2; default: return 0; } } struct PcStandEntry { /* 0000 */ std::uint32_t aid; /* 0004 */ int job; /* 0008 */ int sex; /* 000C */ int cellX; /* 0010 */ int cellY; /* 0014 */ int direction; /* 0018 */ int speed; /* 001C */ int xSize; /* 0020 */ int ySize; /* 0024 */ int head; /* 0028 */ int weapon; /* 002C */ int secondaryWeapon; /* 0030 */ int accessory; /* 0034 */ int state; /* 0038 */ int bodyState; /* 003C */ int effectState; /* 0040 */ std::uint32_t stateFlags; /* 0044 */ int healthState; /* 0048 */ int shield; /* 004C */ int accessory2; /* 0050 */ int accessory3; /* 0054 */ int headPalette; /* 0058 */ int bodyPalette; /* 005C */ int headDir; /* 0060 */ int guildId; /* 0064 */ int guildVersion; /* 0068 */ int honor; /* 006C */ int virtue; /* 0070 */ int robe; /* 0074 */ int pkState; /* 0078 */ int bodyStyle; }; struct PcMoveEntry { /* 0000 */ std::uint32_t aid; /* 0004 */ int job; /* 0008 */ int sex; /* 000C */ std::uint32_t moveStartTick; /* 0010 */ int startX; /* 0014 */ int startY; /* 0018 */ int startSubX; /* 001C */ int startSubY; /* 0020 */ int destX; /* 0024 */ int destY; /* 0028 */ int speed; /* 002C */ int head; /* 0030 */ int weapon; /* 0034 */ int secondaryWeapon; /* 0038 */ int accessory; /* 003C */ int bodyState; /* 0040 */ int effectState; /* 0044 */ std::uint32_t stateFlags; /* 0048 */ int healthState; /* 004C */ int shield; /* 0050 */ int accessory2; /* 0054 */ int accessory3; /* 0058 */ int headPalette; /* 005C */ int bodyPalette; /* 0060 */ int headDir; /* 0064 */ int guildId; /* 0068 */ int guildVersion; /* 006C */ int honor; /* 0070 */ int virtue; /* 0074 */ int robe; /* 0078 */ int pkState; /* 007C */ int bodyStyle; }; } // OFFSET: 0x00CB4800 // FUNCTION: fn_21112 void CGameActor::SendMsg(CGameObject* sender, std::int64_t message, std::int64_t b, std::int64_t c, std::int64_t d, std::int64_t e, std::int64_t f, std::int64_t g) { CGameMode* mode = static_cast(g_modeMgr.GetGameMode()); bool visible = true; if (this->render.accountId != g_session.m_aid) { if (StateFlag01(this->stateFlags) || StateFlag02(this->stateFlags) || StateFlag06(this->stateFlags)) { visible = false; } } switch (message) { case 21: break; case AM_DELETE_PROHIBITEFFECT: if (this->statusEffects[10] != 0) { static_cast(g_modeMgr.GetGameMode())->scene->RemoveObject(reinterpret_cast(this->statusEffects[10])); } break; case AM_MSGEFFECT_DEAD: for (auto it = this->msgEffectList.begin(); it != this->msgEffectList.end(); ++it) { if (*it == sender) { const int effect = reinterpret_cast(*it); if (this->statusEffects[1] == effect) { this->statusEffects[1] = 0; } else if (this->statusEffects[0] == effect) { this->statusEffects[0] = 0; } else if (this->statusEffects[2] == effect) { this->statusEffects[2] = 0; } else if (this->statusEffects[3] == effect) { this->statusEffects[3] = 0; } else if (this->statusEffects[4] == effect) { this->statusEffects[4] = 0; } else if (this->statusEffects[5] == effect) { this->statusEffects[5] = 0; } else if (this->statusEffects[6] == effect) { this->statusEffects[6] = 0; } else if (this->statusEffects[8] == effect) { this->statusEffects[8] = 0; } else if (this->statusEffects[9] == effect) { this->statusEffects[9] = 0; } else if (this->statusEffects[7] == effect) { this->statusEffects[7] = 0; } else if (this->statusEffects[10] == effect) { this->statusEffects[10] = 0; } else if (this->statusEffects[11] == effect) { this->statusEffects[11] = 0; } else if (this->statusEffects[12] == effect) { this->statusEffects[12] = 0; } this->msgEffectList.erase(it); break; } } break; case AM_SET_TARGET_AID: this->hitTargetAid = static_cast(b); break; case AM_MAKE_NUMBER: { if ((g_session.GetSiegeMode() != 0 || g_session.GetBattleFieldMode() != 0) && g_session.IsAdminSubMode(7) == 0) { break; } if (b == -29999) { break; } int damage = static_cast(b); if (this->render.accountId == g_session.m_aid && (g_session.HasActiveSkillEffect(0x22) != 0 || g_session.HasActiveSkillEffect(0x15B) != 0)) { damage = std::rand() % 22001 + 10000; } this->SpawnDamageDigits(reinterpret_cast(sender), damage, static_cast(c), static_cast(d)); break; } case AM_MVP: { CRagEffect* effect = this->LaunchEffect(0x44, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (effect != nullptr) { effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); } break; } case AM_MAKE_MISS: { if (!g_serviceOptions.Get(0x35)) { break; } CMsgEffect* effect = mode->SpawnDamageMsgEffect(&this->render.position, static_cast(b), this->render.accountId); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); ++this->damageNumberCount; break; } case AM_MAKE_GUARD: this->LaunchEffect(0x150, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); break; case AM_MAKE_LUCKY: { CMsgEffect* effect = mode->SpawnMsgEffect(0xD, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); break; } case AM_CASH_EMOTION: { if (mode == nullptr) { break; } if (!g_cashEmotionMgr->IsChatProhibitEmotion(static_cast(c))) { CMsgEffect* effect = mode->SpawnCashEmotion(reinterpret_cast(&this->render.position), static_cast(b), static_cast(c), this->render.accountId); if (effect != nullptr) { this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); break; } } else { if (this->statusEffects[10] != 0) { break; } CGameObject** slot = reinterpret_cast(&this->statusEffects[10]); *slot = mode->SpawnCashEmotion(reinterpret_cast(&this->render.position), static_cast(b), static_cast(c), this->render.accountId); if (*slot != nullptr) { this->msgEffectList.push_back(*slot); (*slot)->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); break; } } mode->SendMsg(MM_PLAYER_EMOTION_END, 0, 0, 0, 0); break; } case AM_MAKE_CART: { if (StateFlag01(this->stateFlags) || StateFlag02(this->stateFlags) || this->statusEffects[1] != 0) { break; } int effectIds[14][2] = { {0x15, 0x1E}, {0x01, 0x19}, {0x10, 0x1A}, {0x11, 0x1B}, {0x12, 0x1C}, {0x13, 0x1D}, {0x2D, 0x2E}, {0x2F, 0x30}, {0x31, 0x32}, {0x33, 0x34}, {0x36, 0x37}, {0x38, 0x39}, {0x3A, 0x3B}, {0x41, 0x42}, }; int row = this->maxHp; int baby = 0; if (g_session.IsSuperNoviceClass(this->GetEffectiveJob(-1))) { row = 0; } if (g_session.IsBabyJob(this->GetEffectiveJob(-1))) { baby = 1; } CMsgEffect* effect = mode->SpawnMsgEffect(effectIds[row][baby], &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[1] = reinterpret_cast(effect); break; } case AM_DELETE_CART: case AM_DELETE_WOLF: if (this->statusEffects[1] != 0) { reinterpret_cast(this->statusEffects[1])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[1] = 0; } break; case AM_MAKE_BIRD: { if (this->statusEffects[0] != 0) { break; } int effectId; switch (this->GetEffectiveJob(-1)) { case JT_HUNTER_H: effectId = 0x17; break; case JT_HUNTER_B: effectId = 0x18; break; case JT_GANGSI: effectId = 0x20; break; case JT_DEATHKNIGHT: effectId = 0x21; break; case JT_COLLECTOR: effectId = 0x22; break; case JT_RANGER: case JT_RANGER_H: case JT_RANGER_B: effectId = 0x35; break; case JT_WINDHAWK: case JT_WOLF_WINDHAWK: effectId = 0x45; break; default: effectId = 0xE; break; } CMsgEffect* effect = mode->SpawnMsgEffect(effectId, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); effect->SetAnimationSpeed(6.0f); this->statusEffects[0] = reinterpret_cast(effect); break; } case AM_DELETE_BIRD: if (this->statusEffects[0] != 0) { reinterpret_cast(this->statusEffects[0])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); reinterpret_cast(this->statusEffects[0])->m_visible = false; this->statusEffects[0] = 0; } break; case AM_MAKE_WOLF: { if (this->statusEffects[1] != 0) { break; } int effectId; switch (this->GetEffectiveJob(-1)) { case JT_RANGER: case JT_RANGER_H: case JT_WOLF_RANGER: case JT_WOLF_RANGER_H: effectId = 0x23; break; case JT_RANGER_B: case JT_WOLF_RANGER_B: effectId = 0x2A; break; case JT_WINDHAWK: case JT_WOLF_WINDHAWK: effectId = 0x46; break; default: return; } CMsgEffect* effect = mode->SpawnMsgEffect(effectId, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[1] = reinterpret_cast(effect); break; } case AM_MAKE_PKEFFECT: { if (this->statusEffects[2] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(2, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[2] = reinterpret_cast(effect); break; } case AM_DELETE_PKEFFECT: if (this->statusEffects[2] != 0) { reinterpret_cast(this->statusEffects[2])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[2] = 0; } break; case AM_MAKE_ICEEFFECT: { if (this->statusEffects[3] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(3, &this->render.position, this->render.accountId, visible); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[3] = reinterpret_cast(effect); break; } case AM_DELETE_ICEEFFECT: case 107: if (this->statusEffects[3] != 0) { reinterpret_cast(this->statusEffects[3])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[3] = 0; } break; case AM_MAKE_CURSE_EFFECT: { if (this->statusEffects[4] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(6, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[4] = reinterpret_cast(effect); break; } case AM_DELETE_CURSE_EFFECT: if (this->statusEffects[4] != 0) { reinterpret_cast(this->statusEffects[4])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[4] = 0; } break; case AM_MAKE_SLEEP_EFFECT: { if (this->statusEffects[5] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(7, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[5] = reinterpret_cast(effect); this->SetState(10, 1); break; } case AM_DELETE_SLEEP_EFFECT: if (this->statusEffects[5] != 0) { reinterpret_cast(this->statusEffects[5])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[5] = 0; this->SetState(this->render.previousState, 1); } break; case AM_MAKE_STUN_EFFECT: { if (this->statusEffects[6] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(8, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[6] = reinterpret_cast(effect); break; } case AM_DELETE_STUN_EFFECT: if (this->statusEffects[6] != 0) { reinterpret_cast(this->statusEffects[6])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[6] = 0; } break; case AM_MAKE_BURNING_EFFECT: this->LaunchEffect(0x316, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); break; case AM_DELETE_BURNING_EFFECT: this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x316, 0, 0, 0, 0, 0); break; case AM_MAKE_SILENCE_EFFECT: { if (this->statusEffects[7] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(9, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[7] = reinterpret_cast(effect); break; } case AM_DELETE_SILENCE_EFFECT: if (this->statusEffects[7] != 0) { reinterpret_cast(this->statusEffects[7])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[7] = 0; } break; case AM_MAKE_ANGELUS_EFFECT: { if (this->statusEffects[8] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(0xA, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[8] = reinterpret_cast(effect); break; } case AM_DELETE_ANGELUS_EFFECT: if (this->statusEffects[8] != 0) { reinterpret_cast(this->statusEffects[8])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[8] = 0; } break; case AM_MAKE_CRUCIS_EFFECT: { if (this->statusEffects[9] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(0xB, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[9] = reinterpret_cast(effect); break; } case AM_DELETE_CRUCIS_EFFECT: if (this->statusEffects[9] != 0) { reinterpret_cast(this->statusEffects[9])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[9] = 0; } break; case AM_MAKE_SMOKE_FFECT: { CMsgEffect* effect = mode->SpawnMsgEffect(5, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); break; } case AM_MAKE_ICEBREAKE_FFECT: { CMsgEffect* effect = mode->SpawnMsgEffect(4, &this->render.position, this->render.accountId, visible); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); break; } case AM_MAKE_CRITICAL_EFFECT: { CMsgEffect* effect = mode->SpawnMsgEffect(0xC, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); break; } case 131: { CMsgEffect* effect = mode->SpawnMsgEffect(0x16, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); break; } case AM_USEITEM_EFFECT: this->LaunchEffect(7, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); break; case AM_WILL_BE_DEAD: if (g_session.IsHomunAid(this->render.accountId)) { if (g_windowMgr.homunInfoWnd != nullptr) { g_windowMgr.CloseWindow(WID_HOMUNINFOWND); } if (g_windowMgr.homunculusSkillWnd != nullptr) { g_windowMgr.CloseWindow(WID_SKILLLISTWND_SKILLTAB_0); } g_session.ClearHomunStatus(); g_session.homunAlive = 0; } if (g_session.IsMercenaryAid(this->render.accountId)) { if (g_windowMgr.mercenaryInfoWnd != nullptr) { g_windowMgr.CloseWindow(WID_MERINFOWND); } if (g_windowMgr.mercenarySkillWnd != nullptr) { g_windowMgr.CloseWindow(WID_SKILLLISTWND_SKILLTAB_1); } g_session.ResetMercenaryStats(); g_session.mercenaryAlive = 0; break; } if (this->willBeAttackedList.size() != 0 && !IsPlayerJob(this->GetEffectiveJob(-1))) { this->willBeDead = 1; break; } this->SetState(3, 1); break; case 134: { if (this->statusEffects[11] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(0x1F, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[11] = reinterpret_cast(effect); break; } case 135: if (this->statusEffects[11] != 0) { reinterpret_cast(this->statusEffects[11])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[11] = 0; } break; case AM_MAKE_LASSITUDE_EFFECT: { if (this->statusEffects[12] != 0) { break; } CMsgEffect* effect = mode->SpawnMsgEffect(0x43, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->statusEffects[12] = reinterpret_cast(effect); break; } case AM_DELETE_LASSITUDE_EFFECT: if (this->statusEffects[12] != 0) { reinterpret_cast(this->statusEffects[12])->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->statusEffects[12] = 0; } break; case AM_DEAD: if (this->render.state == 3) { break; } this->SetState(3, 1); break; case AM_ATTACKED_SKILL: case AM_ATTACKEDNOMOTION_SKILL: { this->render.overrideColor = 0xFFFF0000; const int effectId = reinterpret_cast(static_cast(b))->effectId; if (effectId == 0xC6) { this->render.overrideColor = 0xFF042BA4; } else { this->render.overrideColor = effectId == 0xB8 ? 0xFF505050 : this->render.overrideColor; } } case AM_ATTACKED: case AM_ATTACKEDNOMOTION: case AM_ATTACKED_CRITICAL: case AM_ATTACKED_LUCKY: case AM_ATTACKED_MANYTIMES: case AM_ATTACKED_MANYTIMES2: case AM_ATTACKED_MANYTIMES2DASH: case 148: { const std::int64_t id = message; const ActorDamageMessage* damageMessage = reinterpret_cast(static_cast(b)); const int hitEffect = damageMessage->hitEffect; const int skillEffect = damageMessage->effectId; const int monsterValue = damageMessage->monsterValue; int damage = damageMessage->damage; const std::uint32_t attackerAid = damageMessage->attackerAid; const float motionSpeed = damageMessage->motionSpeed; if (damageMessage->cellX != -1 && damageMessage->cellY != -1) { mode->CellCenter(damageMessage->cellX, damageMessage->cellY, this->damageDestX, this->damageDestZ); } else { this->damageDestX = this->render.position.x; this->damageDestZ = this->render.position.z; } int job = this->GetEffectiveJob(-1); std::int64_t weapon = -1; CGameActor* attacker; if (g_session.m_aid == attackerAid) { attacker = mode->scene->player; } else { attacker = static_cast(mode->scene->GetGameActorByAID(attackerAid)); } if (attacker != nullptr) { job = attacker->GetEffectiveJob(-1); weapon = attacker->GetWeapon(); } if (id == 94) { this->SendMsg(nullptr, AM_MAKE_LUCKY, 0, 0, 0, 0, 0, 0); } else { bool negative = false; if (damage < 0 && damage != -29999) { damage = -damage; negative = true; } if (damage == 0) { if (attacker != nullptr) { if ((job >= 45 && job < 1000) || g_session.IsMonsterClass(job)) { if ((this->render.accountId == g_session.m_aid && g_session.HasActiveSkillEffect(19)) || (this->render.accountId == g_session.m_aid && g_session.HasActiveSkillEffect(104))) { this->SendMsg(nullptr, AM_MAKE_GUARD, 0, 0, 0, 0, 0, 0); } else { attacker->SendMsg(nullptr, AM_MAKE_MISS, static_cast(-1), 0, 0, 0, 0, 0); } } else { attacker->SendMsg(nullptr, AM_MAKE_MISS, 0xFFFF0000u, 0, 0, 0, 0, 0); } } } else { if (g_session.m_aid == attackerAid) { this->lastDamage = damage; } else { this->damageNumberCount = 0; } this->render.started = timeGetTime(); vector3d offset{0.0f, 0.0f, 0.0f}; if (damageMessage->effectId == -2) { this->render.isSingleColor = false; } else { this->render.isSingleColor = true; const vector3d* playerPosition = &mode->scene->player->render.position; const char* wave; if (id == 96 || id == 97) { wave = g_session.GetWeaponHitWaveName(-1); } else if (IsPlayerJob(this->GetEffectiveJob(-1))) { wave = g_session.GetHitWaveName(this->GetEffectiveJob(-1)); } else { bool genericWeapon = true; int view = 0; if (attacker != nullptr) { switch (attacker->GetEffectiveJob(-1)) { case JT_ASSASSIN: case JT_ASSASSIN_H: case JT_ASSASSIN_B: case JT_GUILLOTINE_CROSS: case JT_GUILLOTINE_CROSS_H: case JT_GUILLOTINE_CROSS_B: case JT_SHADOW_CROSS: case JT_ABYSS_CHASER: view = static_cast(weapon); if (view > 1000) { view = GetWeaponViewValue(view); } genericWeapon = false; break; case JT_DRUID: case JT_DRUID_B: case JT_KARNOS: case JT_KARNOS_B: case JT_ALITEA: if (attacker->shapeShift != 0) { view = -1; genericWeapon = false; } break; } } if (genericWeapon) { std::int64_t weaponView = weapon; if (weaponView > 1000) { weaponView = GetWeaponViewValue(static_cast(weaponView)); } view = static_cast(weaponView); } wave = g_session.GetWeaponHitWaveName(view); } g_soundMgr->PlayWave(wave, this->render.position.x - playerPosition->x, 0.0f, this->render.position.z - playerPosition->z, 250, 40, 1.0f, 0); } if (hitEffect == 1567) { vector3d position = this->render.position; const int offsetX = std::max(rand() % 10, 0); const int offsetY = std::max(rand() % 10, 0); const int direction = std::max(rand() % 4, 0); if (direction == 0) { position.x += static_cast(offsetX); position.y += static_cast(offsetY); } else if (direction == 1) { position.x += static_cast(offsetX); position.y -= static_cast(offsetY); } else if (direction == 2) { position.x -= static_cast(offsetX); position.y += static_cast(offsetY); } else if (direction == 3) { position.x -= static_cast(offsetX); position.y -= static_cast(offsetY); } CEffect* effect = g_effectMgr->Spawn(0, 1567, position, 0, -1, 0, 0); effect->SetAngle(static_cast(std::max(rand() % 360, 0))); } else if (hitEffect == 2132) { const vector3d& playerPosition = static_cast(g_modeMgr.GetGameMode())->scene->player->render.position; g_soundMgr->PlayWave("effect\\ABC_DEFT_STAB2.wav", playerPosition.x - this->render.position.x, 0.0f, playerPosition.z - this->render.position.z, 250, 40, 1.0f, 0); } else if (g_effectMgr->Spawn(this->render.accountId, hitEffect, offset, 0, -1, 0, 0) == nullptr) { CRagEffect* effect = this->LaunchEffect(hitEffect, offset, 0.0f); if (effect != nullptr) { effect->SendMsg(nullptr, AM_SETCOUNT, 0, 1, 0, 0, 0, 0); } } if (skillEffect == 122) { this->render.angle += 90.0f; if (this->render.angle > 360.0f) { this->render.angle -= 360.0f; } } else if (!CSkillActionMgr::GetInstance()->OnSkillDamage(damageMessage->skillId, reinterpret_cast(attacker), this, reinterpret_cast(damageMessage->skillTarget))) { if (g_effectMgr->Spawn(this->render.accountId, skillEffect, offset, 0, -1, 0, 0) == nullptr) { CRagEffect* effect = this->LaunchEffect(skillEffect, offset, 0.0f); if (effect != nullptr) { effect->SendMsg(nullptr, AM_SETCOUNT, 0, 1, 0, 0, 0, 0); if ((skillEffect == 216 || skillEffect == 217) && attacker != nullptr) { effect->SendMsg(nullptr, AM_SETPOS, 0, reinterpret_cast(&attacker->render.position), 0, 0, 0, 0); } } } } int value = damage; std::uint32_t color; std::int64_t kind; std::int64_t preMessage; if (negative) { kind = 0; preMessage = 0; color = 0x72; } else if (id == 93) { preMessage = 92; kind = 16; color = 0xFFFFFF00; } else if ((this->GetEffectiveJob(-1) >= 45 && this->GetEffectiveJob(-1) < 1000) || g_session.IsMonsterClass(this->GetEffectiveJob(-1))) { value = monsterValue; if (id == 129) { preMessage = 131; kind = 22; color = 0xFFFFFFFF; } else { color = 0xFFFFFFFF; kind = 22; preMessage = 0; if (id == 132) { } else if (id == 111) { this->SendMsg(nullptr, AM_MAKE_NUMBER, damage, damageMessage->blueDamage != 0 ? 0xFF1E90FFu : 0xFFFFFFFFu, 21, 0, 0, 0); kind = 22; color = 0xFFFFFF00; } else if (id == 148) { this->SendMsg(nullptr, AM_MAKE_CRITICAL_EFFECT, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, AM_MAKE_NUMBER, damage, 0xFFFFFF00u, 16, 0, 0, 0); kind = 22; color = 0xFFFFFF00; } else { value = damage; color = 0xFFFFFFFF; kind = 14; } } } else { kind = 14; preMessage = 0; color = 0xFFFF0000; } if (damageMessage->blueDamage != 0) { color = 0xFF1E90FF; } if (preMessage != 0) { this->SendMsg(nullptr, preMessage, 0, 0, 0, 0, 0, 0); } this->SendMsg(nullptr, AM_MAKE_NUMBER, value, static_cast(color), kind, 0, 0, 0); if (!negative) { const auto isPlayer = IsPlayerJob(this->GetEffectiveJob(-1)); if (this->render.state != 3) { if (isPlayer) { if (this->render.state != 2 && id != 39 && id != 97) { this->SetState(4, 1); if (motionSpeed > 1.0f) { this->render.motionFinishThreshold = motionSpeed; } else { this->SetAnimationSpeed(motionSpeed * this->render.speed); } } } else if (id != 39 && id != 97) { if (!g_session.IsMonsterActor(this) || !(motionSpeed < -0.00001f)) { this->SetState(4, 1); if (motionSpeed > 1.0f) { this->render.motionFinishThreshold = motionSpeed; } else { this->SetAnimationSpeed(motionSpeed); } } } } } } } if (this->willBeDead != 0 && this->willBeAttackedList.size() <= 1) { this->SendMsg(nullptr, AM_DEAD, 0, 0, 0, 0, 0, 0); } break; } case 108: this->SetState(2, 1); this->attackMotion = static_cast(this->GetAttackMotion()); this->RegisterPos(); this->render.motionStartTime = timeGetTime(); this->render.curMotion = 4; this->SetAnimationSpeed(1.0f); this->render.previousAnimation += this->Get8Dir(this->render.angle); if (this->willBeDead != 0 && this->willBeAttackedList.size() <= 1) { this->SendMsg(nullptr, AM_DEAD, 0, 0, 0, 0, 0, 0); } break; case AM_WILL_BE_ATTACKED: { ActorDamageMessage* damage = reinterpret_cast(static_cast(b)); if (c != 0) { if (damage->action != 0x94) { damage->action = 0x6F; } damage->monsterValue = static_cast(c); } this->willBeAttackedList.push_back(*damage); break; } case AM_WILL_BE_ATTACKED2: { ActorDamageMessage* damage = reinterpret_cast(static_cast(b)); if (c != 0) { damage->monsterValue = static_cast(c); damage->action = 0x81; } this->willBeAttackedList.push_back(*damage); break; } case AM_WILL_BE_ATTACKED2DASH: { ActorDamageMessage* damage = reinterpret_cast(static_cast(b)); if (c != 0) { damage->monsterValue = static_cast(c); damage->action = 0x84; } this->willBeAttackedList.push_back(*damage); break; } case AM_SKILL: this->ProcessSkillMessage(sender, reinterpret_cast(static_cast(b)), static_cast(c), static_cast(d)); break; case AM_GROUNDSKILL: this->PlayGroundSkillEffect(reinterpret_cast(sender), reinterpret_cast(static_cast(b)), static_cast(c), static_cast(d)); break; case AM_ACTION: this->ProcessHitMessage(sender, reinterpret_cast(static_cast(b)), static_cast(c), static_cast(d)); break; case AM_ATTR_STATE_CHANGE: { const int* states = reinterpret_cast(static_cast(b)); this->SetStatusStates(states[0], states[1], states[2], true); this->healthState = states[3]; break; } case AM_STANDENTRY: { PcStandEntry* entry = reinterpret_cast(static_cast(b)); switch (this->objectType) { case 8: case 9: case 10: case 13: case 14: this->emblemVersion = entry->pkState; break; } mode->CellCenter(entry->cellX, entry->cellY, this->render.position.x, this->render.position.z); this->render.position.y = mode->scene->terrain->Height(this->render.position.x, this->render.position.z); this->render.angle = static_cast((entry->direction + 4) * 45); this->xSize = entry->xSize; this->ySize = entry->ySize; this->movementState[5] = entry->cellX; *reinterpret_cast(this->destinationAndSpeed) = entry->cellY; *reinterpret_cast(this->headType) = entry->head; this->SetStatusStates(entry->bodyState, entry->stateFlags, entry->effectState, false); this->SetState(0, 1); this->SetGuildInfo(entry->guildId, entry->guildVersion); break; } case AM_INITSTANDENTRY: { PcStandEntry* entry = reinterpret_cast(static_cast(b)); switch (this->objectType) { case 8: case 9: case 10: case 13: case 14: this->emblemVersion = entry->pkState; break; } this->render.accountId = entry->aid; entry->job = MountedJob(entry->job, entry->stateFlags); this->SetSprAct(entry->job, entry->sex, 0); mode->CellCenter(entry->cellX, entry->cellY, this->render.position.x, this->render.position.z); this->render.position.y = mode->scene->terrain->Height(this->render.position.x, this->render.position.z); if (this->objectType == 5) { const int job = this->job; if ((job >= 0x5248 && job <= 0x5260) || (job >= 0x5817 && job <= 0x588C)) { entry->direction += 4; } } this->render.angle = static_cast((entry->direction + 4) * 45); this->xSize = entry->xSize; this->ySize = entry->ySize; this->movementState[5] = entry->cellX; *reinterpret_cast(this->destinationAndSpeed) = entry->cellY; *reinterpret_cast(&this->destinationAndSpeed[4]) = entry->speed; *reinterpret_cast(this->headType) = entry->head; this->SetStatusStates(entry->bodyState, entry->stateFlags, entry->effectState, false); if (Replay::Instance().state == 6) { switch (entry->state) { case PCSTATE_DEAD: this->SetState(3, 1); break; case PCSTATE_SIT: this->SetState(6, 1); break; default: this->SetState(0, 1); break; } } else { this->SetState(0, 1); } this->healthState = entry->healthState; if (entry->job == 0x2D2 || entry->job == 0x508) { this->SetGuildInfo(entry->guildId, entry->guildVersion); } this->SetPropensityInfo(entry->honor, entry->virtue); if (c == 1) { this->render.color = 0x44FFFFFF; } else if (c == 2 && !StateFlag06(entry->stateFlags)) { this->LaunchEffect(0x158, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case AM_INITMOVEENTRY: case AM_INITMOVEENTRY2: { PcMoveEntry* entry = reinterpret_cast(static_cast(b)); switch (this->objectType) { case 8: case 9: case 10: case 13: case 14: this->emblemVersion = entry->pkState; break; } this->render.accountId = entry->aid; entry->job = MountedJob(entry->job, entry->stateFlags); *reinterpret_cast(this->headType) = entry->head; this->SetSprAct(entry->job, entry->sex, 0); this->SetStatusStates(entry->bodyState, entry->stateFlags, entry->effectState, false); this->healthState = entry->healthState; this->SetGuildInfo(entry->guildId, entry->guildVersion); this->SetPropensityInfo(entry->honor, entry->virtue); *reinterpret_cast(&this->destinationAndSpeed[4]) = entry->speed; this->routeStartTick = entry->moveStartTick; CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); gameMode->CellCenter(entry->startX, entry->startY, this->render.position.x, this->render.position.z); this->render.position.x += static_cast(gameMode->scene->terrain->cellSize) * 0.0625f * static_cast(entry->startSubX - 8); this->render.position.z += static_cast(gameMode->scene->terrain->cellSize) * 0.0625f * static_cast(entry->startSubY - 8); this->render.position.y = gameMode->scene->terrain->Height(this->render.position.x, this->render.position.z); if (this->FindRoute(entry->startX, entry->startY, entry->startSubX, entry->startSubY, entry->destX, entry->destY, entry->speed)) { this->movementState[5] = entry->destX; *reinterpret_cast(this->destinationAndSpeed) = entry->destY; this->SetState(1, 1); this->pathStartCell = 0; } else { this->SetState(0, 1); } break; } case AM_MOVEENTRY: case AM_MOVEENTRY2: { PcMoveEntry* entry = reinterpret_cast(static_cast(b)); switch (this->objectType) { case 8: case 9: case 10: case 13: case 14: this->emblemVersion = entry->pkState; break; } *reinterpret_cast(&this->destinationAndSpeed[4]) = entry->speed; this->routeStartTick = entry->moveStartTick; entry->job = MountedJob(entry->job, entry->stateFlags); *reinterpret_cast(this->headType) = entry->head; this->SetSprAct(entry->job, entry->sex, 1); this->SetStatusStates(entry->bodyState, entry->stateFlags, entry->effectState, false); this->healthState = entry->healthState; this->SetGuildInfo(entry->guildId, entry->guildVersion); this->SetPropensityInfo(entry->honor, entry->virtue); if (this->FindRoute(entry->startX, entry->startY, entry->startSubX, entry->startSubY, entry->destX, entry->destY, entry->speed)) { this->movementState[5] = entry->destX; *reinterpret_cast(this->destinationAndSpeed) = entry->destY; this->SetState(1, 1); this->pathStartCell = 0; } break; } case AM_MOVE: { const int* route = reinterpret_cast(static_cast(b)); this->routeStartTick = route[0]; if (this->FindRoute(route[1], route[2], route[3], route[4], route[5], route[6], *reinterpret_cast(&this->destinationAndSpeed[4]))) { this->movementState[5] = route[5]; *reinterpret_cast(this->destinationAndSpeed) = route[6]; this->SetState(1, 1); this->pathStartCell = 0; } else { this->SetState(0, 1); } break; } case AM_DELETE_SHOP_TITLE: if (this->actorWindows[2] != 0) { g_windowMgr.Schedule(reinterpret_cast(this->actorWindows[2])); this->actorWindows[2] = 0; } break; case AM_DELETE_CHAT_TITLE: if (this->actorWindows[1] != 0) { g_windowMgr.Schedule(reinterpret_cast(this->actorWindows[1])); this->actorWindows[1] = 0; } break; case AM_MAKE_SHOP_TITLE: if (this->actorWindows[2] == 0) { UIMerchantShopTitle* title = new UIMerchantShopTitle(0); this->actorWindows[2] = reinterpret_cast(title); title->SetSize(140, 34); g_windowMgr.AddRenderedFront(reinterpret_cast(this->actorWindows[2])); } reinterpret_cast(this->actorWindows[2])->SendMsg(nullptr, UM_SETINFO, b, static_cast(this->render.fontSequenceState), 0, 0); reinterpret_cast(this->actorWindows[2])->SendMsg(nullptr, UM_SETNAID, this->render.accountId, 0, 0, 0); reinterpret_cast(this->actorWindows[2])->Move(this->render.screenX - 70, this->render.screenY - ScaleByScreenHeight(110)); if (!g_serviceOptions.Get(0xFA)) { reinterpret_cast(this->actorWindows[2])->Show(0); } break; case AM_MAKE_CHAT_TITLE: if (this->actorWindows[1] == 0) { UIChatRoomTitle* title = new UIChatRoomTitle(); this->actorWindows[1] = reinterpret_cast(title); title->SetSize(140, 34); g_windowMgr.AddRenderedFront(reinterpret_cast(this->actorWindows[1])); } reinterpret_cast(this->actorWindows[1])->SendMsg(nullptr, UM_SETINFO, b, static_cast(this->render.fontSequenceState), 0, 0); reinterpret_cast(this->actorWindows[1])->Move(this->render.screenX - 70, this->render.screenY - ScaleByScreenHeight(110)); break; case AM_MAKE_SKILL_RECHARGEGAGE: if (this->actorWindows[3] == 0) { // GLOBAL: gl_25466 static float s_gaugeOffsetY = static_cast(g_renderer->viewport.height * 81 / 480); UIRechargeGage* gauge = new UIRechargeGage(); this->actorWindows[3] = reinterpret_cast(gauge); gauge->SetSize(60, 6); reinterpret_cast(this->actorWindows[3])->Move( this->render.screenX - 30, static_cast(static_cast(this->render.screenY) - s_gaugeOffsetY * this->render.distanceScale) ); g_windowMgr.AddRenderedFront(reinterpret_cast(this->actorWindows[3])); } this->skillRechargeEndTick = timeGetTime() + static_cast(b); this->skillRechargeStartTick = timeGetTime(); break; case AM_DELETE_SKILL_RECHARGEGAGE: if (this->actorWindows[3] != 0) { g_windowMgr.Schedule(reinterpret_cast(this->actorWindows[3])); this->actorWindows[3] = 0; if (this->render.accountId == g_session.m_aid) { if (g_session.playerStatusFlags[0x0A] == true) { std::int16_t packetType = 0x2F1; GetRagConnection().Send(reinterpret_cast(GetRagConnection()).FixedLength(packetType), &packetType); } g_session.playerStatusFlags[0x0A] = false; } } break; case AM_CHAT: { std::string text(reinterpret_cast(static_cast(b))); if (g_windowMgr.chatWnd != nullptr) { text = static_cast(g_windowMgr.chatWnd)->ConvertChat(std::string(reinterpret_cast(static_cast(b)))); } std::string linked; DisplayNavigationLink(linked, text); text = std::move(linked); const char* message = reinterpret_cast(static_cast(b)); if (text.size() != 0) { message = text.c_str(); } if (std::strlen(message) != 0) { this->chatTick = timeGetTime(); if (this->actorWindows[0] == 0) { UITransBalloonText* created = new UITransBalloonText(); this->actorWindows[0] = reinterpret_cast(created); created->SetSize(8, 8); g_windowMgr.AddRenderedFront(reinterpret_cast(this->actorWindows[0])); } if (this->actorWindows[0] != 0) { if (this->render.accountId == g_session.m_aid) { reinterpret_cast(this->actorWindows[0])->m_fontMode = static_cast(g_session.chatFont[0]); } else { reinterpret_cast(this->actorWindows[0])->m_fontMode = static_cast(this->render.fontSequenceState); } reinterpret_cast(this->actorWindows[0])->SetText(message, 35); if (c != 0) { reinterpret_cast(this->actorWindows[0])->m_textColor = static_cast(c); reinterpret_cast(this->actorWindows[0])->m_outlineColor = 0; } UIWindow* balloon = reinterpret_cast(this->actorWindows[0]); const int width = balloon->geometry.width; this->chatWidth = width; this->chatHeight = balloon->geometry.height; balloon->Move(this->render.screenX - width / 2, this->render.screenY - ScaleByScreenHeight(110)); } } break; } case AM_STOPMOVE: { this->routeStartTick = g_session.GetServerTime(); int column; int row; int subColumn; int subRow; mode->WorldSubcell(this->render.position.x, this->render.position.z, column, row, subColumn, subRow); bool found; if (static_cast(d) == 0) { found = this->FindRoute(column, row, subColumn, subRow, static_cast(b), static_cast(c), this->isAsuraAttack != 0 ? 55 : 2); } else { found = this->FindRoute(column, row, subColumn, subRow, static_cast(b), static_cast(c), static_cast(d)); } bool lying = false; if (e == 0) { lying = true; } this->movementState[5] = static_cast(b); *reinterpret_cast(this->destinationAndSpeed) = static_cast(c); if (found) { CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); gameMode->CellCenter(static_cast(b), static_cast(c), this->render.position.x, this->render.position.z); this->render.position.y = gameMode->scene->terrain->Height(this->render.position.x, this->render.position.z); this->UnRegisterPos(); if (lying == true) { this->damageDestX = this->render.position.x; this->damageDestZ = this->render.position.z; } this->SetState(1, lying); this->pathStartCell = 0; if (static_cast(d) == 0) { this->isNeverAnimation = 1; } else { this->isNeverAnimation = 0; this->render.isMotionFreezed = true; } } else { if (g_session.moveHomunEnemy != 0) { g_session.homunEnemyX = 0; g_session.homunEnemyY = 0; g_session.moveHomunEnemy = 0; } if (g_session.moveMercenaryEnemy != 0) { g_session.mercenaryEnemyX = 0; g_session.mercenaryEnemyY = 0; g_session.moveMercenaryEnemy = 0; } this->SetState(0, lying); } break; } case AM_CORRECT_POS: this->movementState[5] = static_cast(b); *reinterpret_cast(this->destinationAndSpeed) = static_cast(c); mode->CellCenter(static_cast(b), static_cast(c), this->render.position.x, this->render.position.z); break; case AM_RESURRECTION: this->render.state = 0; this->render.isLieOnGround = false; this->render.visibilityOptions[1] = 1; static_cast(g_modeMgr.GetGameMode())->waitingUseItemAck = 0; this->SetState(0, 1); break; case AM_OUTOFSIGHT: this->render.accountId = 0xFFFFFFFF; this->levelAuraType = 0; this->render.visibilityOptions[0] = 0; this->vanishTime = static_cast(timeGetTime() + 510); if (this->vanishTime == 0) { this->vanishTime = 1; } break; case AM_MAKE_LEVEL99_EFFECT: { this->hasLevelAura = 1; const unsigned char showFirst = g_serviceOptions.Get(0x6A); if (*reinterpret_cast(g_session.maxBaseLevel) == static_cast(this->levelAuraType)) { break; } if (StateFlag06(this->stateFlags) || StateFlag01(this->stateFlags) || StateFlag02(this->stateFlags)) { break; } if (g_session.IsMonsterClass(this->GetEffectiveJob(-1))) { break; } if (this->GetEffectiveJob(-1) >= 0x52C && this->GetEffectiveJob(-1) <= 0x553) { break; } if (this->GetEffectiveJob(-1) >= 0x792 && this->GetEffectiveJob(-1) <= 0x7A3) { break; } if (this->GetEffectiveJob(-1) == JT_G_TREASURE_BOX) { break; } this->levelAuraType = static_cast(*reinterpret_cast(g_session.maxBaseLevel)); int first = 200; int second = 0x3D0; if (*reinterpret_cast(&g_session.jobLevelLimits[0x30]) > 0) { first = *reinterpret_cast(&g_session.jobLevelLimits[0x30]); } if (*reinterpret_cast(&g_session.jobLevelLimits[0x34]) > 0) { second = *reinterpret_cast(&g_session.jobLevelLimits[0x34]); } if (g_session.IsBabyJob(this->GetEffectiveJob(-1))) { if (*reinterpret_cast(&g_session.jobLevelLimits[0x38]) > 0) { first = *reinterpret_cast(&g_session.jobLevelLimits[0x38]); } if (*reinterpret_cast(&g_session.jobLevelLimits[0x3C]) > 0) { second = *reinterpret_cast(&g_session.jobLevelLimits[0x3C]); } } else if (this->GetEffectiveJob(-1) > 0xFA0 || g_session.IsSuperNoviceJob(this->GetEffectiveJob(-1))) { if (*reinterpret_cast(&g_session.jobLevelLimits[0x40]) > 0) { first = *reinterpret_cast(&g_session.jobLevelLimits[0x40]); } if (*reinterpret_cast(&g_session.jobLevelLimits[0x44]) > 0) { second = *reinterpret_cast(&g_session.jobLevelLimits[0x44]); } } else if (g_session.IsMercenaryJob(this->GetEffectiveJob(-1))) { if (*reinterpret_cast(&g_session.jobLevelLimits[0x48]) > 0) { first = *reinterpret_cast(&g_session.jobLevelLimits[0x48]); } if (*reinterpret_cast(&g_session.jobLevelLimits[0x4C]) > 0) { second = *reinterpret_cast(&g_session.jobLevelLimits[0x4C]); } } else if (this->GetEffectiveJob(-1) >= 0x666) { break; } const vector3d offset{0.0f, 0.0f, 0.0f}; if (showFirst != 0) { if (g_effectMgr->Spawn(this->render.accountId, first, this->render.position, 0, -1, 0, 0) == nullptr) { this->LaunchEffect(first, offset, 0.0f); } } if (g_effectMgr->Spawn(this->render.accountId, second, this->render.position, 0, -1, 0, 0) == nullptr) { this->LaunchEffect(second, offset, 0.0f); } break; } case AM_MAKE_LEVEL160_EFFECT: case 147: { this->hasLevelAura = 1; const unsigned char showFirst = g_serviceOptions.Get(0x6A); if (*reinterpret_cast(g_session.maxBaseLevel) == static_cast(this->levelAuraType)) { break; } if (StateFlag06(this->stateFlags) || StateFlag01(this->stateFlags) || StateFlag02(this->stateFlags)) { break; } this->levelAuraType = static_cast(*reinterpret_cast(g_session.maxBaseLevel)); int first = 0x3FE; int second; if (g_session.IsMercenaryJob(this->GetEffectiveJob(-1))) { first = 0x3D2; second = 0x3D3; if (*reinterpret_cast(&g_session.jobLevelLimits[0x64]) > 0) { first = *reinterpret_cast(&g_session.jobLevelLimits[0x64]); } if (*reinterpret_cast(&g_session.jobLevelLimits[0x68]) > 0) { second = *reinterpret_cast(&g_session.jobLevelLimits[0x68]); } } else if (message == 0x93) { first = 0x3D2; second = 0x3D3; if (*reinterpret_cast(&g_session.jobLevelLimits[0x54]) > 0) { first = *reinterpret_cast(&g_session.jobLevelLimits[0x54]); } if (*reinterpret_cast(&g_session.jobLevelLimits[0x58]) > 0) { second = *reinterpret_cast(&g_session.jobLevelLimits[0x58]); } } else { second = 0x3FF; if (message == 0x92) { if (*reinterpret_cast(&g_session.jobLevelLimits[0x5C]) > 0) { first = *reinterpret_cast(&g_session.jobLevelLimits[0x5C]); } if (*reinterpret_cast(&g_session.jobLevelLimits[0x60]) > 0) { second = *reinterpret_cast(&g_session.jobLevelLimits[0x60]); } } } const vector3d offset{0.0f, 0.0f, 0.0f}; if (showFirst != 0) { if (g_effectMgr->Spawn(this->render.accountId, first, this->render.position, 0, -1, 0, 0) == nullptr) { this->LaunchEffect(first, offset, 0.0f); } } if (g_effectMgr->Spawn(this->render.accountId, second, this->render.position, 0, -1, 0, 0) == nullptr) { this->LaunchEffect(second, offset, 0.0f); } break; } case 150: { this->hasLevelAura = 1; const unsigned char showFirst = g_serviceOptions.Get(0x6A); if (*reinterpret_cast(g_session.maxBaseLevel) == static_cast(this->levelAuraType)) { break; } if (StateFlag06(this->stateFlags) || StateFlag01(this->stateFlags) || StateFlag02(this->stateFlags)) { break; } this->levelAuraType = static_cast(*reinterpret_cast(g_session.maxBaseLevel)); int first = 0x4CA; int second = 0x4CB; if (*reinterpret_cast(&g_session.jobLevelLimits[0x6C]) > 0) { first = *reinterpret_cast(&g_session.jobLevelLimits[0x6C]); } if (*reinterpret_cast(&g_session.jobLevelLimits[0x70]) > 0) { second = *reinterpret_cast(&g_session.jobLevelLimits[0x70]); } const vector3d offset{0.0f, 0.0f, 0.0f}; if (showFirst != 0) { if (g_effectMgr->Spawn(this->render.accountId, first, this->render.position, 0, -1, 0, 0) == nullptr) { this->LaunchEffect(first, offset, 0.0f); } } if (g_effectMgr->Spawn(this->render.accountId, second, this->render.position, 0, -1, 0, 0) == nullptr) { this->LaunchEffect(second, offset, 0.0f); } break; } case AM_MAKE_LEVEL4TH_EFFECT: { this->hasLevelAura = 1; const unsigned char showFirst = g_serviceOptions.Get(0x6A); if (*reinterpret_cast(g_session.maxBaseLevel) == static_cast(this->levelAuraType)) { break; } if (StateFlag06(this->stateFlags) == true || StateFlag01(this->stateFlags) == true || StateFlag02(this->stateFlags) == true) { break; } this->levelAuraType = static_cast(*reinterpret_cast(g_session.maxBaseLevel)); const int first = g_session.m_maxLevelEffect4th; const int second = g_session.m_maxLevelEffect4thSub; const vector3d offset{0.0f, 0.0f, 0.0f}; if (showFirst != 0) { if (g_effectMgr->Spawn(this->render.accountId, first, this->render.position, 0, -1, 0, 0) == nullptr) { this->LaunchEffect(first, offset, 0.0f); } } if (g_effectMgr->Spawn(this->render.accountId, second, this->render.position, 0, -1, 0, 0) == nullptr) { this->LaunchEffect(second, offset, 0.0f); } break; } case AM_STATUS_CHANGE: { const bool on = c != 0; switch (b) { case EFST_PROVOKE: case EFST_ENDURE: case EFST_CONCENTRATION: case EFST_HIDING: case EFST_CLOAKING: case EFST_ENCHANTPOISON: case EFST_POISONREACT: case EFST_QUAGMIRE: case EFST_ANGELUS: case EFST_BLESSING: case EFST_CRUCIS: case EFST_INC_AGI: case EFST_DEC_AGI: case EFST_SLOWPOISON: case EFST_IMPOSITIO: case EFST_SUFFRAGIUM: case EFST_ASPERSIO: case EFST_BENEDICTIO: case EFST_KYRIE: case EFST_MAGNIFICAT: case EFST_GLORIA: case EFST_LEXAETERNA: case EFST_ADRENALINE: case EFST_WEAPONPERFECT: case EFST_MAXIMIZE: case EFST_RIDING: case EFST_FALCON: case EFST_SHOUT: case EFST_BROKENARMOR: case EFST_BROKENWEAPON: case EFST_WEAPONPROPERTY: case EFST_ADRENALINE2: case EFST_PLUSATTACKPOWER: case EFST_PLUSMAGICPOWER: case EFST_CRYSTAL: case EFST_ATKER_ASPD: case EFST_ATKER_MOVESPEED: case EFST_ANGEL_PROTECT: case EFST_ELECTRICSHOCKER: case EFST_WUGRIDER: case EFST_ADORAMUS: case EFST_ACARAJE: case EFST_WATER_SCREEN: break; case EFST_TWOHANDQUICKEN: case EFST_ONEHANDQUICKEN: { while (this->HasId(0xA6)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0xA6, 0, 0, 0, 0, 0); } if (on) { CRagEffect* effect = this->LaunchEffect(0xA6, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (effect) { effect->SendMsg(nullptr, AM_SETCOUNT, 0, 0xFFFF, 0x82, 0, 0, 0); } } break; } case EFST_SPEARQUICKEN: { while (this->HasId(0xA6)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0xA6, 0, 0, 0, 0, 0); } if (on) { CRagEffect* effect = this->LaunchEffect(0xA6, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (effect) { effect->SendMsg(nullptr, AM_SETCOUNT, 0, 0xFFFF, 0xFA, 0, 0, 0); } } break; } case EFST_NJ_BUNSINJYUTSU: while (this->HasId(0x269)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x269, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x269, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_DEVIL1: { if (this->render.accountId != g_session.m_aid) { break; } if (static_cast(d) >= 2 && static_cast(d) <= 11) { const int effectId = static_cast(d) + 0x1D9; if (!this->HasId(effectId)) { this->LaunchEffect(0x1E7, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); g_soundMgr->PlayWave("effect\\_blind.wav", 0.0f, -100.0f, 0.0f, 250, 40, 1.0f, 0); } for (int id = 0x1DB; id < 0x1E5; ++id) { if (this->HasId(id) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, id, 0, 0, 0, 0, 0); } } this->LaunchEffect(effectId, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } this->SendMsg(nullptr, AM_DELETE_EFFECT, 0xA4, 0, 0, 0, 0, 0); CPlayer* player = static_cast(g_modeMgr.GetGameMode())->scene->player; if (player) { player->skillEffect = this->LaunchEffect(0xA4, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_PROPERTYUNDEAD: if (!on) { if (this->HasId(0x28F)) { vector3d position{0.0f, 0.0f, 0.0f}; do { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x28F, 0, 0, 0, 0, 0); this->LaunchEffect(0x290, position, 0.0f); } while (this->HasId(0x28F)); } } else if (!this->HasId(0x28F)) { this->LaunchEffect(0x28F, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_SKE: if (this->render.accountId == g_session.m_aid) { if (on) { g_weatherEffect.StartDarken(); } else { g_weatherEffect.StopDarken(); } } break; case EFST_SMA_READY: if (on) { this->LaunchEffect(0x222, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_EXPLOSIONSPIRITS: { while (this->HasId(0xCB)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0xCB, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0xCB, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } while (this->HasId(0xA6)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0xA6, 0, 0, 0, 0, 0); } if (on) { CRagEffect* effect = this->LaunchEffect(0xA6, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (effect) { effect->SendMsg(nullptr, AM_SETCOUNT, 0, 0xFFFE, 0x80, 0, 0, 0); } } break; } case EFST_STEELBODY: case EFST_DBARRIER: { while (this->HasId(0xFE)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0xFE, 0, 0, 0, 0, 0); this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x46C, 0, 0, 0, 0, 0); } if (this->render.accountId == g_session.m_gid || IsPlayerJob(this->GetEffectiveJob(-1))) { CPc* pc = static_cast(this); pc->virtue = on ? pc->virtue | 0x10 : pc->virtue ^ 0x10; if ((pc->virtue & 0x10) == 0 && (pc->appearanceFlags & 0x10) == 0) { break; } } else if (!on) { break; } this->LaunchEffect(0xFE, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); this->LaunchEffect(0x46C, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); break; } case EFST_SG_SUN_WARM: case EFST_SG_MOON_WARM: case EFST_SG_STAR_WARM: while (this->HasId(0x1A2)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1A2, 0, 0, 0, 0, 0); } while (this->HasId(0x220)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x220, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x1A2, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); this->LaunchEffect(0x220, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_DAURA: while (this->HasId(0x1A2)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1A2, 0, 0, 0, 0, 0); } while (this->HasId(0x220)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x220, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x1A2, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); this->LaunchEffect(0x220, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_KAITE: while (this->HasId(0x1A3)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1A3, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x1A3, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_AURABLADE: while (this->HasId(0x16F)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x16F, 0, 0, 0, 0, 0); } while (this->HasId(0x1DA)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1DA, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x16F, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); this->LaunchEffect(0x1DA, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_STOP: while (this->HasId(0x1C5)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1C5, 0, 0, 0, 0, 0); } break; case EFST_MOON: { while (this->HasId(0x18A)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x18A, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x18A, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_MARIONETTE_MASTER: { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x18B, 0, 0, 0, 0, 0); while (this->HasId(0x18C)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x18C, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x18C, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_MARIONETTE: { while (this->HasId(0x18C)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x18C, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x18C, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_LKCONCENTRATION: { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x171, 0, 0, 0, 0, 0); if (on) { this->LaunchEffect(0x171, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_BERSERK: { while (this->HasId(0x170)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x170, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x170, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_RG_CCONFINE_M: case EFST_RG_CCONFINE_S: { while (this->HasId(0x25C)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x25C, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x25C, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_WZ_SIGHTBLASTER: { while (this->HasId(0x259)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x259, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x259, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_POWERUP: { while (this->HasId(0x1C7)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1C7, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x1C7, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_RUN: { if (this->HasId(0x1BA)) { this->LaunchEffect(0x1BB, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1BA, 0, 0, 0, 0, 0); break; } case EFST_TING: { const vector3d& player = reinterpret_cast(static_cast(g_modeMgr.GetGameMode())->scene->player)->render.position; g_soundMgr->PlayWave("effect\\T_벽튕김.wav", this->render.position.x - player.x, 0.0f, this->render.position.z - player.z, 250, 40, 1.0f, 0); this->LaunchEffect(0x1AA, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (this->HasId(0x1BA)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1BA, 0, 0, 0, 0, 0); } break; } case EFST_MAGICPOWER: { while (this->HasId(0x17E)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x17E, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x17E, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_OVERTHRUST: case EFST_OVERTHRUSTMAX: { while (this->HasId(0xA6)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0xA6, 0, 0, 0, 0, 0); } if (on) { CRagEffect* effect = this->LaunchEffect(0xA6, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (effect) { effect->SendMsg(nullptr, AM_SETCOUNT, 0, 0xFFFF, 0x80, 0, 0, 0); } } break; } case EFST_ENERGYCOAT: { while (this->HasId(0xA6)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0xA6, 0, 0, 0, 0, 0); } if (on) { CRagEffect* effect = this->LaunchEffect(0xA6, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (effect) { effect->SendMsg(nullptr, AM_SETCOUNT, 0, 0xFFFF, 0xA9, 0, 0, 0); } } break; } case EFST_TRICKDEAD: if (on) { this->isTrickDead = 1; if (this->GetMonsterTransformJob() > 0) { this->SetAction(0x20, 4, 1); } else { this->SetAction(this->WeaponActionBase(3), 4, 1); } this->render.isMotionFreezed = true; } else { this->isTrickDead = 0; this->render.state = 0; this->render.isLieOnGround = false; this->render.visibilityOptions[1] = 1; this->SetState(0, 1); this->render.isMotionFreezed = false; } break; case EFST_ILLUSION: if (this->render.accountId == g_session.m_aid) { if (on) { g_screenShakeDuration = 10000; g_screenShakeMode = 2; CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); if (gameMode) { gameMode->camera->frame.up[0] = 0.0f; gameMode->camera->frame.up[1] = -1.0f; gameMode->camera->frame.up[2] = 0.0f; } } else { g_screenShakeDuration = 0; CGameMode* gameMode = static_cast(g_modeMgr.GetGameMode()); if (gameMode) { gameMode->camera->frame.up[0] = 0.0f; gameMode->camera->frame.up[1] = 1.0f; gameMode->camera->frame.up[2] = 0.0f; } } } break; case EFST_RICHMANKIM: { if (this->HasId(0x363) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x363, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x363, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_WHISTLE: case EFST_HUMMING: case EFST_INTOABYSS: { if (this->HasId(0x361) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x361, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x361, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_RINGNIBELUNGEN: case EFST_SIEGFRIED: { if (this->HasId(0x36B) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x36B, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x36B, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_APPLEIDUN: { if (this->HasId(0x35D) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x35D, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x35D, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_ETERNALCHAOS: case EFST_ROKISWEIL: { if (this->HasId(0x360) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x360, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x360, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_ASSASSINCROSS: case EFST_SERVICEFORYOU: { if (this->HasId(0x369) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x369, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x369, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_FORTUNEKISS: case EFST_DRUMBATTLEFIELD: { if (this->HasId(0x367) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x367, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x367, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_DONTFORGETME: { if (this->HasId(0x26) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x26, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x26, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_POEMBRAGI: { if (this->HasId(0x36D) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x36D, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x36D, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_DA_CONTRACT: { while (this->HasId(0x24A)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x24A, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x24A, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_DUPLELIGHT: { while (this->HasId(0x341)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x341, 0, 0, 0, 0, 0); } if (on) { CRagEffect* effect = this->LaunchEffect(0x341, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); vector3d scale{2.0f, 0.0f, 0.0f}; if (effect) { effect->SendMsg(nullptr, AM_SETPOS, 0, reinterpret_cast(&scale), 0, 0, 0, 0); } } break; } case EFST_ORATIO: while (this->HasId(0x2F3)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x2F3, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x2F3, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_BLOOD_SUCKER: while (this->HasId(0x170)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x170, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x170, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_BANDING: if (on) { if (!this->HasId(0x38D)) { this->LaunchEffect(0x38D, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } } break; case EFST_CURSEDCIRCLE_ATKER: case EFST_CURSEDCIRCLE_TARGET: { unsigned char shade; bool frozen; if (on) { shade = 0x40; frozen = true; } else { shade = 0xFF; frozen = false; } unsigned char* const argb = reinterpret_cast(&this->render.color); this->render.isMotionFreezed = frozen; this->render.colorLocked = frozen; argb[2] = shade; argb[1] = shade; argb[0] = shade; break; } case EFST_STRIKING: if (IsPlayerJob(this->GetEffectiveJob(-1))) { while (this->HasId(0x39D)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x39D, 0, 0, 0, 0, 0); } if (on) { CRagEffect* effect = this->LaunchEffect(0x39D, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (effect) { effect->SendMsg(nullptr, AM_SETCOUNT, 0, 0xFFFF, 0x39D, 0, 0, 0); } } } break; case EFST_VENOMIMPRESS: while (this->HasId(0x314)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x314, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x314, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_FROSTMISTY: while (this->HasId(0x315)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x315, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x315, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_COLD: while (this->HasId(0x3AC)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x3AC, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x3AC, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_SUMMON1: this->SetTetraBall(0, static_cast(c), static_cast(d)); break; case EFST_SUMMON2: this->SetTetraBall(1, static_cast(c), static_cast(d)); break; case EFST_SUMMON3: this->SetTetraBall(2, static_cast(c), static_cast(d)); break; case EFST_SUMMON4: this->SetTetraBall(3, static_cast(c), static_cast(d)); break; case EFST_SUMMON5: this->SetTetraBall(4, static_cast(c), static_cast(d)); break; case EFST_ROLLINGCUTTER: if (c == 1) { const std::int64_t level = d; if (level <= 10 && level > 0) { for (int id = 0x514; id < 0x51E; ++id) { g_effectMgr->Retire(this->render.accountId, id); g_effectMgr->Retire(this->render.accountId, id + 0xA7); } if (g_serviceOptions.Get(0xE5)) { g_effectMgr->Spawn(this->render.accountId, static_cast(d) + 0x5BA, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Spawn(this->render.accountId, static_cast(d) + 0x513, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } } } else { for (int id = 0x514; id < 0x51E; ++id) { g_effectMgr->Retire(this->render.accountId, id); g_effectMgr->Retire(this->render.accountId, id + 0xA7); } } break; case EFST_SIREN: if (c == 1) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x36F, 0, 0, 0, 0, 0); this->LaunchEffect(0x36F, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } else { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x36F, 0, 0, 0, 0, 0); } break; case EFST_SONG_OF_MANA: if (c == 1) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x361, 0, 0, 0, 0, 0); this->LaunchEffect(0x361, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_DANCE_WITH_WUG: if (c == 1) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x363, 0, 0, 0, 0, 0); this->LaunchEffect(0x363, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_LERADS_DEW: if (c == 1) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x367, 0, 0, 0, 0, 0); this->LaunchEffect(0x367, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_DEEP_SLEEP: if (c == 1) { this->SendMsg(nullptr, AM_MAKE_SLEEP_EFFECT, 0, 0, 0, 0, 0, 0); } else { this->SendMsg(nullptr, AM_DELETE_SLEEP_EFFECT, 0, 0, 0, 0, 0, 0); } break; case EFST_NETHERWORLD: while (this->HasId(0x1C5)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1C5, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x1C5, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_CAMOUFLAGE: while (this->HasId(0x2E8)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x2E8, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x2E8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } if (this->hpGaugeWindow) { if (on) { reinterpret_cast(this->hpGaugeWindow)->SetMode(0); } else { reinterpret_cast(this->hpGaugeWindow)->SetMode(1); } } break; case EFST_MANHOLE: while (this->HasId(0x337)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x337, 0, 0, 0, 0, 0); } if (on) { g_soundMgr->PlayWave("effect\\manhole.wav", 0.0f, 0.0f, 0.0f, 250, 40, 1.0f, 0); if (!g_session.IsMonsterClass(this->GetEffectiveJob(-1))) { this->LaunchEffect(0x337, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } } break; case EFST_SHADOWFORM: while (this->HasId(0x329)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x329, 0, 0, 0, 0, 0); this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x333, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x329, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_STEALTHFIELD: while (this->HasId(0x2E8)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x2E8, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x2E8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_WUGDASH: while (this->HasId(0x2F0)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x2F0, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x2F0, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_DA_BLACK: if (this->render.accountId == g_session.m_aid) { g_screenShakeDuration = 1; g_screenShakeMode = 1; } break; case EFST_MER_FLEE: { const int text = reinterpret_cast("FLEE UP!"); this->SendMsg(nullptr, AM_CHAT, text, 0, 0, 0, 0, 0); this->LaunchEffect(0x1C8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); break; } case EFST_MER_ATK: { const int text = reinterpret_cast("ATK UP!"); this->SendMsg(nullptr, AM_CHAT, text, 0, 0, 0, 0, 0); this->LaunchEffect(0x1C8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); break; } case EFST_MER_HIT: { const int text = reinterpret_cast("DEX UP!"); this->SendMsg(nullptr, AM_CHAT, text, 0, 0, 0, 0, 0); this->LaunchEffect(0x1C8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); break; } case EFST_MER_HP: { const int text = reinterpret_cast("HP UP!"); this->SendMsg(nullptr, AM_CHAT, text, 0, 0, 0, 0, 0); this->LaunchEffect(0x1C8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); break; } case EFST_MER_SP: { const int text = reinterpret_cast("SP UP!"); this->SendMsg(nullptr, AM_CHAT, text, 0, 0, 0, 0, 0); this->LaunchEffect(0x1C8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); break; } case EFST_UNLIMITED_HUMMING_VOICE: if (this->HasId(0x361) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x361, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x361, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_MELODYOFSINK: if (this->HasId(0x57) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x57, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x57, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_BEYOND_OF_WARCRY: if (this->HasId(0x31F) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x31F, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x31F, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_SATURDAY_NIGHT_FEVER: if (this->HasId(0x360) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x360, 0, 0, 0, 0, 0); } if (on == true) { this->LaunchEffect(0x360, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_PAIN_KILLER: case EFST_LIGHT_OF_REGENE: case EFST_OVERED_BOOST: case EFST_STYLE_CHANGE: case EFST_MAGMA_FLOW: case EFST_PYROCLASTIC: case EFST_VOLCANIC_ASH: case EFST_OVERLAPEXPUP: case EFST_EQUIPPED_DIVINE_ARMOR: case EFST_EQUIPPED_HOLY_ARMOR: break; case EFST_EQUIPED_FLOOR: g_effectMgr->SpawnLevel99(this, on, static_cast(d)); break; case EFST_GRANITIC_ARMOR: while (this->HasId(0x3C3)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x3C3, 0, 0, 0, 0, 0); } if (c != 0) { this->LaunchEffect(0x3C3, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_ALL_RIDING: { if (this->render.state == 25) { this->SetState(0, 1); } int job; if (c > 0) { this->SetRiding(1); job = this->GetEffectiveJob(-1); } else { this->SetRiding(0); job = JobNames::BaseJob(this->GetEffectiveJob(-1)); } this->SetActorJob(job); this->SetBodyPalette(this->render.bodyPalette); this->SetSprRobe(0, 1); break; } case EFST_MONSTER_TRANSFORM: case EFST_KYOUGAKU: case EFST_ACTIVE_MONSTER_TRANSFORM: { int slot; switch (b) { case EFST_MONSTER_TRANSFORM: slot = 0; break; case EFST_KYOUGAKU: slot = 1; break; case EFST_ACTIVE_MONSTER_TRANSFORM: slot = 2; break; default: slot = 0; break; } if (c != 0) { this->SetTransformJob(static_cast(d), slot); } else { this->SetTransformJob(-1, slot); if (this->render.state == 3) { this->SetAction(this->WeaponActionBase(3), 4, 1); } } break; } case EFST_KO_JYUMONJIKIRI: g_effectMgr->Retire(this->render.accountId, 0x3E6); if (c != 0 && g_effectMgr->HasAllocator(0x3E6)) { g_effectMgr->Spawn(this->render.accountId, 0x3E6, this->render.position, 0, -1, 0, 0); } break; case EFST_KYOMU: while (this->HasId(0x3C3)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x3C3, 0, 0, 0, 0, 0); } if (c != 0) { this->LaunchEffect(0x3C3, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_KAGEMUSYA: while (this->HasId(0x3EC)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x3EC, 0, 0, 0, 0, 0); } if (c != 0) { this->LaunchEffect(0x3EC, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_MEIKYOUSISUI: while (this->HasId(0x18A)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x18A, 0, 0, 0, 0, 0); } if (c != 0) { CRagEffect* effect = this->LaunchEffect(0x18A, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (effect) { effect->state.colorChannels[0] = 180; effect->state.colorChannels[1] = 180; effect->state.colorChannels[2] = -1; } } break; case EFST_ZANGETSU: while (this->HasId(0x3EB)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x3EB, 0, 0, 0, 0, 0); } if (c != 0) { this->LaunchEffect(0x3EB, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_GENSOU: g_effectMgr->Retire(this->render.accountId, 0x3ED); if (c != 0 && g_effectMgr->HasAllocator(0x3ED)) { CEffect* orb = g_effectMgr->Spawn(this->render.accountId, 0x3ED, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); if (orb) { static_cast(orb)->SetCount(10); } } while (this->HasId(0x3C3)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x3C3, 0, 0, 0, 0, 0); } if (c != 0) { this->LaunchEffect(0x3C3, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_AKAITSUKI: while (this->HasId(0x220)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x220, 0, 0, 0, 0, 0); } if (c != 0) { this->LaunchEffect(0x220, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_SHOW_HP: this->hasHp = c != 0; break; case EFST_HAT_EFFECT: this->SetStackedEffect(0x3F4, c > 0); break; case EFST_MOONSTAR: this->SetStackedEffect(0x40A, c > 0); break; case EFST_DECORATION_OF_MUSIC: this->SetStackedEffect(0x437, c > 0); break; case EFST_STRANGELIGHTS: this->SetStackedEffect(0x40B, c > 0); break; case EFST_SUPER_STAR: this->SetStackedEffect(0x40C, c > 0); break; case EFST_ON_PUSH_CART: if (c != 0) { if (static_cast(this->maxHp) == d) { break; } this->maxHp = static_cast(d); if (StateFlag01(this->stateFlags) || StateFlag02(this->stateFlags) || StateFlag06(this->stateFlags)) { break; } this->SendMsg(nullptr, AM_DELETE_CART, 0, 0, 0, 0, 0, 0); this->SendMsg(nullptr, AM_MAKE_CART, 0, 0, 0, 0, 0, 0); } else { this->maxHp = 0; this->SendMsg(nullptr, AM_DELETE_CART, 0, 0, 0, 0, 0, 0); } break; case EFST_FLOWER_LEAF: if (c != 0) { g_effectMgr->Spawn(this->render.accountId, 0x3F5, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x3F5); } break; case EFST_RAY_OF_PROTECTION: while (this->HasId(0x3FB)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x3FB, 0, 0, 0, 0, 0); } if (c != 0) { this->LaunchEffect(0x3FB, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_CLAN_INFO: this->hasSp = e != 0; this->spGaugeValue = static_cast(e); break; case EFST_HANDICAPSTATE_ICEEXPLO: if (c != 0) { this->SendMsg(nullptr, AM_CHANGE_COLOR, 50, 150, 255, 0, 0, 0); } break; case EFST_MAXPAIN: while (this->HasId(0x40D)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x40D, 0, 0, 0, 0, 0); } if (c != 0) { this->LaunchEffect(0x40D, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_DARKCROW: while (this->HasId(0x410)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x410, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x410, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); this->LaunchEffect(0x410, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); this->LaunchEffect(0x410, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_TELEKINESIS_INTENSE: while (this->HasId(0x418)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x418, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x418, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_ILLUSIONDOPING: while (this->HasId(0x419)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x419, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x419, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_WIDEWEB: while (this->HasId(0x194)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x194, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x194, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_BURNT: while (this->HasId(0x41E)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x41E, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x41E, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_CHILL: while (this->HasId(0x41F)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x41F, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x41F, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_OFFERTORIUM: while (this->HasId(0x421)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x421, 0, 0, 0, 0, 0); } if (on) { this->LaunchEffect(0x421, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_C_MARKER: this->SendMsg(this, AM_DELETE_EFFECT, 0x433, 0, 0, 0, 0, 0); if (on) { if (g_session.actorEffectStates.find(static_cast(this->render.accountId)) == g_session.actorEffectStates.end() && g_effectMgr->HasAllocator(0x425)) { g_effectMgr->Spawn(this->render.accountId, 0x425, this->render.position, 0, -1, 0, 0); } if (g_effectMgr->HasAllocator(0x433)) { g_effectMgr->Spawn(this->render.accountId, 0x433, this->render.position, 0, -1, 0, 0); } } break; case EFST_ANTI_M_BLAST: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x470, this->render.position, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x470); } break; case EFST_E_QD_SHOT_READY: g_effectMgr->Spawn(0, 0x434, this->render.position, 0, -1, 0, 0); break; case EFST_H_MINE: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x473, this->render.position, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x473); } break; case EFST_H_MINE_SPLASH: if (on) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x425, 0, 0, 0, 0, 0); g_effectMgr->Spawn(0, 0x432, this->render.position, 0, -1, 0, 0); } break; case EFST_FLOWERSMOKE: this->SetStackedEffect(0x43B, c > 0); break; case EFST_HALLUCINATIONWALK: case EFST_NPC_HALLUCINATIONWALK: { while (this->HasId(0x318)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x318, 0, 0, 0, 0, 0); } if (c == 0) { break; } CRagEffect* effect = this->LaunchEffect(0x318, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); if (!effect) { break; } effect->SendMsg(nullptr, AM_SETCOUNT, 0, 0xFFFF, 0x318, 0, 0, 0); break; } case EFST_SOULLINK: case EFST_SOULSHADOW: case EFST_SOULFAIRY: case EFST_SOULFALCON: case EFST_SOULGOLEM: if (this->render.accountId == g_session.m_aid) { if (c != 0) { g_weatherEffect.StartDarken(); break; } g_weatherEffect.StopDarken(); } else if (c != 0) { break; } while (this->HasId(0x1A8)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x1A8, 0, 0, 0, 0, 0); } break; case EFST_HAPPINESS_STAR: if (c > 0) { this->SetStackedEffect(0x43F, true); } else { this->SetStackedEffect(0x43F, false); } break; case EFST_FSTONE: if (c > 0) { this->SetStackedEffect(0x43C, true); } else { this->SetStackedEffect(0x43C, false); } break; case EFST_QSCARABA: if (c > 0) { this->SetStackedEffect(0x43D, true); } else { this->SetStackedEffect(0x43D, false); } break; case EFST_LJOSALFAR: if (c > 0) { this->SetStackedEffect(0x43E, true); } else { this->SetStackedEffect(0x43E, false); } break; case EFST_MAPLE_FALLS: if (c > 0) { this->SetStackedEffect(0x441, true); } else { this->SetStackedEffect(0x441, false); } break; case EFST_DRESS_UP: g_session.playerAppearanceState = g_session.m_aid == this->render.accountId ? static_cast(d) : g_session.playerAppearanceState; this->mountedState = static_cast(d); this->SetSprJob(this->GetEffectiveJob(-1)); this->SetImfFileName(); this->SetHeadPaletteName(); this->SetBodyPaletteName(); break; case EFST_MARKING_USE_CHANGEMONSTER: if (c != 0) { g_effectMgr->Spawn(this->render.accountId, 0x442, this->render.position, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x442); } break; case EFST_MAGICAL_FEATHER: if (c > 0) { this->SetStackedEffect(0x443, true); } else { this->SetStackedEffect(0x443, false); } break; case EFST_MERMAID_LONGING: if (c > 0) { this->SetStackedEffect(0x444, true); } else { this->SetStackedEffect(0x444, false); } break; case EFST_TIME_ACCESSORY: if (c > 0) { this->SetStackedEffect(0x447, true); } else { this->SetStackedEffect(0x447, false); } break; case EFST_HEAD_EQUIPMENT_EFFECT: this->SetStackedEffect(static_cast(d) + 0x9B5, c > 0); break; case EFST_SPRITEMABLE: g_effectMgr->Retire(this->render.accountId, 0x448); if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x448, this->render.position, 0, -1, 0, 0); } break; case EFST_SUHIDE: if (on == true) { this->SetState(0x30, 1); this->render.isMotionFreezed = true; this->render.specialMotionLocked = 1; } else { this->render.state = 0; this->render.isLieOnGround = false; this->render.visibilityOptions[1] = 1; this->SetState(0, 1); this->render.isMotionFreezed = false; this->render.specialMotionLocked = 0; } break; case EFST_SU_STOOP: if (on == true) { this->SetState(0x2F, 1); } else if (this->render.specialMotionLocked == 0) { this->SetState(0, 1); } break; case EFST_TUNAPARTY: if (on == true) { if (!g_effectMgr->Has(this->render.accountId, 0x449)) { g_effectMgr->Spawn(this->render.accountId, 0x449, this->render.position, 0, -1, 0, 0); } } else { g_effectMgr->Retire(this->render.accountId, 0x449); } break; case EFST_SV_ROOTTWIST: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x456, this->render.position, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x456); } break; case EFST_BITESCAR: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x45F, this->render.position, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x45F); } break; case EFST_SOULATTACK: if (g_session.IsDoramJob(this->GetEffectiveJob(-1))) { static_cast(this)->alternateDoramAppearance[0] = on; } break; case EFST_IMMUNE_PROPERTY_NOTHING: case EFST_IMMUNE_PROPERTY_WATER: case EFST_IMMUNE_PROPERTY_GROUND: case EFST_IMMUNE_PROPERTY_FIRE: case EFST_IMMUNE_PROPERTY_WIND: case EFST_IMMUNE_PROPERTY_POISON: case EFST_IMMUNE_PROPERTY_SAINT: case EFST_IMMUNE_PROPERTY_DARKNESS: case EFST_IMMUNE_PROPERTY_TELEKINESIS: case EFST_IMMUNE_PROPERTY_UNDEAD: { const int effectId = static_cast(b) + 0x87; if (on == true) { g_effectMgr->Spawn(this->render.accountId, effectId, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else if (g_effectMgr->Has(this->render.accountId, effectId) == true) { g_effectMgr->Retire(this->render.accountId, effectId); } break; } case EFST_GRAVITYCONTROL: { if (g_effectMgr->Has(this->render.accountId, 0x4B1) == true) { g_effectMgr->Retire(this->render.accountId, 0x4B1); } if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x4B1, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); const vector3d& player = reinterpret_cast(static_cast(g_modeMgr.GetGameMode())->scene->player)->render.position; g_soundMgr->PlayWave( "effect\\t_캐스팅.wav", this->render.position.x - player.x, 0.0f, this->render.position.z - player.z, 250, 40, 1.0f, 0 ); } break; } case EFST_SUNSTANCE: if (g_effectMgr->Has(this->render.accountId, 0x4BE) == true) { g_effectMgr->Retire(this->render.accountId, 0x4BE); } if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x4BE, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; case EFST_LUNARSTANCE: if (g_effectMgr->Has(this->render.accountId, 0x4BF) == true) { g_effectMgr->Retire(this->render.accountId, 0x4BF); } if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x4BF, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; case EFST_STARSTANCE: if (g_effectMgr->Has(this->render.accountId, 0x4C0) == true) { g_effectMgr->Retire(this->render.accountId, 0x4C0); } if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x4C0, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; case EFST_UNIVERSESTANCE: if (g_effectMgr->Has(this->render.accountId, 0x4C1) == true) { g_effectMgr->Retire(this->render.accountId, 0x4C1); } if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x4C1, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; case EFST_FLASHKICK: if (g_effectMgr->Has(this->render.accountId, 0x4C5) == true) { g_effectMgr->Retire(this->render.accountId, 0x4C5); } if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x4C5, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; case EFST_SOULUNITY: { if (g_effectMgr->Has(this->render.accountId, 0x4C6) == true) { g_effectMgr->Retire(this->render.accountId, 0x4C6); } if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x4C6, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); const vector3d& player = reinterpret_cast(static_cast(g_modeMgr.GetGameMode())->scene->player)->render.position; g_soundMgr->PlayWave( "effect\\t_바람방출.wav", this->render.position.x - player.x, 0.0f, this->render.position.z - player.z, 250, 40, 1.0f, 0 ); } break; } case EFST_ASSUMPTIO: case EFST_ASSUMPTIO2: case EFST_ASSUMPTIO_BUFF: while (this->HasId(0x177)) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x177, 0, 0, 0, 0, 0); } if (c != 0) { this->LaunchEffect(0x177, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_SOULCURSE: if (c == 1) { if (g_effectMgr->Find(this->render.accountId, 0x552)) { break; } g_effectMgr->Spawn(this->render.accountId, 0x552, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); g_soundMgr->PlayWave("effect\\s포이즌버스터.wav", 0.0f, 0.0f, 0.0f, 250, 40, 1.0f, 0); } else { CEffect* const effect = g_effectMgr->Find(this->render.accountId, 0x552); if (effect) { g_effectMgr->Retire(effect, 1); } } break; case EFST_SOUND_OF_DESTRUCTION: { CGameActor* actor = this; if (actor->render.accountId != g_session.m_aid) { actor = static_cast(mode->scene->GetGameActorByAID(actor->render.accountId)); } if (!actor) { break; } g_effectMgr->Retire(actor->render.accountId, 0x66C); if (on == true) { g_effectMgr->Spawn(actor->render.accountId, 0x66C, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; } case EFST_EARTHSHAKER: { CGameActor* actor = this; if (actor->render.accountId != g_session.m_aid) { actor = static_cast(mode->scene->GetGameActorByAID(actor->render.accountId)); } if (actor->HasId(0x576)) { actor->SendMsg(nullptr, AM_DELETE_EFFECT, 0x576, 0, 0, 0, 0, 0); } if (on == true) { actor->LaunchEffect(0x576, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_MAGIC_POISON: { CGameActor* actor = this; if (actor->render.accountId != g_session.m_aid) { actor = static_cast(mode->scene->GetGameActorByAID(actor->render.accountId)); } if (actor->HasId(0x18C)) { actor->SendMsg(nullptr, AM_DELETE_EFFECT, 0x18C, 0, 0, 0, 0, 0); } if (on == true) { actor->LaunchEffect(0x18C, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_MISTY_FROST: { CGameActor* actor = this; if (actor->render.accountId != g_session.m_aid) { actor = static_cast(mode->scene->GetGameActorByAID(actor->render.accountId)); } if (actor->HasId(0x58C)) { actor->SendMsg(nullptr, AM_DELETE_EFFECT, 0x58C, 0, 0, 0, 0, 0); } if (on == true) { actor->LaunchEffect(0x58C, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_PRISON: { CGameActor* actor = this; if (actor->render.accountId != g_session.m_aid) { actor = static_cast(mode->scene->GetGameActorByAID(actor->render.accountId)); } if (actor->HasId(0x5F5)) { actor->SendMsg(nullptr, AM_DELETE_EFFECT, 0x5F5, 0, 0, 0, 0, 0); } if (on == true) { actor->LaunchEffect(0x5F5, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_MADOGEAR_TYPE: { CGameActor* target = this; if (this->render.accountId != g_session.m_aid) { target = static_cast(mode->scene->GetGameActorByAID(this->render.accountId)); } if (!target) { break; } if (c == 1) { if (d == 2) { target->SetMadogearType(2); if (this->render.accountId == g_session.m_aid) { g_session.madogearType = 2; } } else { target->SetMadogearType(1); if (this->render.accountId == g_session.m_aid) { g_session.madogearType = 1; } } target->SendMsg(nullptr, AM_MAKE_CHICKEN, 0, 0, 0, 0, 0, 0); } else { target->SetMadogearType(0); if (this->render.accountId == g_session.m_aid) { g_session.madogearType = 0; } target->SendMsg(nullptr, AM_DELETE_CHICKEN, 0, 0, 0, 0, 0, 0); } break; } case EFST_RELIEVE_DAMAGE: { CGameActor* actor = this; if (actor->render.accountId != g_session.m_aid) { actor = static_cast(mode->scene->GetGameActorByAID(actor->render.accountId)); } if (!actor) { break; } g_effectMgr->Retire(actor->render.accountId, 0x63F); if (on == true) { g_effectMgr->Spawn(actor->render.accountId, 0x63F, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; } case EFST_LOCKON_LASER: { CGameActor* actor = this; if (actor->render.accountId != g_session.m_aid) { actor = static_cast(mode->scene->GetGameActorByAID(actor->render.accountId)); } if (!actor) { break; } if (actor->HasId(0x641)) { actor->SendMsg(nullptr, AM_DELETE_EFFECT, 0x641, 0, 0, 0, 0, 0); } if (on == true) { actor->LaunchEffect(0x641, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_HELLS_PLANT_ARMOR: { CGameActor* actor = this; if (actor->render.accountId != g_session.m_aid) { actor = static_cast(mode->scene->GetGameActorByAID(actor->render.accountId)); } if (!actor) { break; } if (actor->HasId(0x64A)) { actor->SendMsg(nullptr, AM_DELETE_EFFECT, 0x64A, 0, 0, 0, 0, 0); } if (on == true) { actor->LaunchEffect(0x64A, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; } case EFST_HANDICAPSTATE_DEEPBLIND: if (this->render.accountId != g_session.m_aid) { break; } if (c == 1) { if (!this->HasId(0x14E)) { this->LaunchEffect(0x14E, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } g_overrideColor = 1; this->SendMsg(nullptr, AM_DELETE_EFFECT, 0xA4, 0, 0, 0, 0, 0); CPlayer* const player = static_cast(g_modeMgr.GetGameMode())->scene->player; if (player) { player->skillEffect = this->LaunchEffect(0xA4, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } } else { if (this->HasId(0x14E) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x14E, 0, 0, 0, 0, 0); } g_overrideColor = 0; } this->SetInstantValue(0x10, on); break; case EFST_HANDICAPSTATE_DEEPSILENCE: if (c == 1) { this->SendMsg(nullptr, AM_MAKE_DEEPSILENCE_EFFECT, 0, 0, 0, 0, 0, 0); } else { this->SendMsg(nullptr, AM_DELETE_DEEPSILENCE_EFFECT, 0, 0, 0, 0, 0, 0); } break; case EFST_HANDICAPSTATE_SWOONING: if (c == 1) { this->SendMsg(nullptr, AM_MAKE_DEEPSILENCE_EFFECT, 0, 0, 0, 0, 0, 0); g_effectMgr->Retire(this->render.accountId, 0x8C5); g_effectMgr->Spawn(this->render.accountId, 0x8C5, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { this->SendMsg(nullptr, AM_DELETE_DEEPSILENCE_EFFECT, 0, 0, 0, 0, 0, 0); g_effectMgr->Retire(this->render.accountId, 0x8C5); } break; case EFST_HANDICAPSTATE_LASSITUDE: if (c == 1) { this->SendMsg(nullptr, AM_MAKE_LASSITUDE_EFFECT, 0, 0, 0, 0, 0, 0); g_effectMgr->Spawn(this->render.accountId, 0x8C4, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { this->SendMsg(nullptr, AM_DELETE_LASSITUDE_EFFECT, 0, 0, 0, 0, 0, 0); } break; case EFST_HANDICAPSTATE_FROSTBITE: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x8DE, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else if (g_effectMgr->Has(this->render.accountId, 0x8DE)) { g_effectMgr->Retire(this->render.accountId, 0x8DE); g_effectMgr->Spawn(this->render.accountId, 0x8DF, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } if (this->render.accountId == g_session.m_aid) { if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x8E0, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x8E0); } } break; case EFST_HANDICAPSTATE_LIGHTNINGSTRIKE: g_effectMgr->Retire(this->render.accountId, 0x8DC); if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x8DC, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; case EFST_HANDICAPSTATE_CRYSTALLIZATION: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x8E1, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x8E1); } break; case EFST_HANDICAPSTATE_HOLYFLAME: if (on == true) { this->SendMsg(nullptr, AM_CHANGE_COLOR, 0xFF, 0, 0, 0, 0, 0); g_effectMgr->Spawn(this->render.accountId, 0x8C6, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { this->SendMsg(nullptr, AM_RETURN_COLOR, 0, 0, 0, 0, 0, 0); } this->SetInstantValue(0xC, on); break; case EFST_HANDICAPSTATE_CONFLAGRATION: if (on == true) { this->SendMsg(nullptr, AM_CHANGE_COLOR, 0xFF, 0, 0, 0, 0, 0); g_effectMgr->Retire(this->render.accountId, 0x8DD); g_effectMgr->Spawn(this->render.accountId, 0x8DD, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { this->SendMsg(nullptr, AM_RETURN_COLOR, 0, 0, 0, 0, 0, 0); g_effectMgr->Retire(this->render.accountId, 0x8DD); } this->SetInstantValue(0xD, on); break; case EFST_HANDICAPSTATE_MISFORTUNE: if (on == true) { this->SendMsg(nullptr, AM_MAKE_MISFORTUNE_EFFECT, 0, 0, 0, 0, 0, 0); } else { this->SendMsg(nullptr, AM_DELETE_MISFORTUNE_EFFECT, 0, 0, 0, 0, 0, 0); } break; case EFST_HANDICAPSTATE_DEADLYPOISON: if (on == true) { this->SendMsg(nullptr, AM_CHANGE_COLOR, 0xAA, 0, 0xAA, 0, 0, 0); g_effectMgr->Spawn(this->render.accountId, 0x8E2, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { this->SendMsg(nullptr, AM_RETURN_COLOR, 0, 0, 0, 0, 0, 0); g_effectMgr->Retire(this->render.accountId, 0x8E2); } this->SetInstantValue(0xE, on); break; case EFST_HANDICAPSTATE_DEPRESSION: if (on == true) { this->SendMsg(nullptr, AM_CHANGE_COLOR, 0, 0, 0xFF, 0, 0, 0); } else { this->SendMsg(nullptr, AM_RETURN_COLOR, 0, 0, 0, 0, 0, 0); } this->SetInstantValue(0xF, on); break; case EFST_DRAGONIC_AURA: { CGameActor* actor = this; if (actor->render.accountId != g_session.m_aid) { actor = static_cast(mode->scene->GetGameActorByAID(actor->render.accountId)); } if (!actor) { break; } g_effectMgr->Retire(actor->render.accountId, 0x6BE); g_effectMgr->Retire(actor->render.accountId, 0x6BF); if (on == true) { g_effectMgr->Spawn(actor->render.accountId, 0x6BE, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); g_effectMgr->Spawn(actor->render.accountId, 0x6BF, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; } case EFST_FIRST_BRAND: { if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x806, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); CEffect* const second = g_effectMgr->Spawn(this->render.accountId, 0x803, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); CEffect* const third = g_effectMgr->Spawn(this->render.accountId, 0x804, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); if (second) { second->SetDelay(1000); } if (third) { third->SetDelay(1000); } } else { g_effectMgr->Retire(this->render.accountId, 0x803); g_effectMgr->Retire(this->render.accountId, 0x804); } break; } case EFST_SECOND_BRAND: { if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x821, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); CEffect* const second = g_effectMgr->Spawn(this->render.accountId, 0x81e, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); CEffect* const third = g_effectMgr->Spawn(this->render.accountId, 0x81f, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); if (second) { second->SetDelay(1000); } if (third) { third->SetDelay(1000); } } else { g_effectMgr->Retire(this->render.accountId, 0x821); g_effectMgr->Retire(this->render.accountId, 0x81e); g_effectMgr->Retire(this->render.accountId, 0x81f); } break; } case EFST_HOLY_OIL: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x7c7, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x7c7); } break; case EFST_MEDIALE: if (on == true) { if (!g_serviceOptions.Get(0xe5) && !g_effectMgr->Find(this->render.accountId, 0x712)) { g_effectMgr->Spawn(this->render.accountId, 0x712, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } } else { g_effectMgr->Retire(this->render.accountId, 0x712); } break; case EFST_DEADLY_DEFEASANCE: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x6cd, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else if (CEffect* const effect = g_effectMgr->Find(this->render.accountId, 0x6cd)) { g_effectMgr->Retire(effect, 1); } break; case EFST_CLIMAX: g_effectMgr->Retire(this->render.accountId, 0x708); if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x708, this->render.position, 0, static_cast(d), 0, 0); } break; case EFST_FIRST_FAITH_POWER: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x7b7, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x7b7); } break; case EFST_SECOND_JUDGE: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x7ba, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); g_effectMgr->Spawn(this->render.accountId, 0x7bd, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x7ba); g_effectMgr->Retire(this->render.accountId, 0x7bd); } break; case EFST_THIRD_EXOR_FLAME: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x7be, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); g_effectMgr->Spawn(this->render.accountId, 0x7c1, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x7be); g_effectMgr->Retire(this->render.accountId, 0x7c1); } break; case EFST_ABYSS_SLAYER: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x84e, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x84e); } break; case EFST_D_MACHINE: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x719, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); this->LaunchEffect(0x8f8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } else if (CEffect* const effect = g_effectMgr->Find(this->render.accountId, 0x719)) { g_effectMgr->Retire(effect, 1); } break; case EFST_A_MACHINE: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x72f, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x72f); } break; case EFST_COMPETENTIA: if (on == true) { if (!g_effectMgr->Find(this->render.accountId, 0x73d)) { g_effectMgr->Spawn(this->render.accountId, 0x73d, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } if (!g_serviceOptions.Get(0xe5) && !g_effectMgr->Find(this->render.accountId, 0x73c)) { g_effectMgr->Spawn(this->render.accountId, 0x73c, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } this->LaunchEffect(0x8fc, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } else { if (CEffect* const effect = g_effectMgr->Find(this->render.accountId, 0x73d)) { g_effectMgr->Retire(effect, 1); } if (!g_serviceOptions.Get(0xe5)) { if (CEffect* const effect = g_effectMgr->Find(this->render.accountId, 0x73c)) { g_effectMgr->Retire(effect, 1); } } } break; case EFST_CLIMAX_CRYIMP: if (on == true) { if (!g_effectMgr->Find(this->render.accountId, 0x743)) { g_effectMgr->Spawn(this->render.accountId, 0x743, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } } else { g_effectMgr->Retire(this->render.accountId, 0x743); } break; case EFST_SOUNDBLEND: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x80c, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else if (CEffect* const effect = g_effectMgr->Find(this->render.accountId, 0x80c)) { g_effectMgr->Retire(effect, 1); } break; case EFST_WINDSIGN: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x76c, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x76c); } break; case EFST_E_SLASH_COUNT: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x779, this->render.position, 0, static_cast(d), 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x779); } break; case EFST_ELEMENTAL_VEIL: if (on == true) { this->LaunchEffect(0x2e8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } else if (this->HasId(0x2e8) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x2e8, 0, 0, 0, 0, 0); } break; case EFST_BO_HELL_DUSTY: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x8be, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; case EFST_CLOUD_POISON: if (on == true) { this->LaunchEffect(0x8e5, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } else if (this->HasId(0x8e5) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x8e5, 0, 0, 0, 0, 0); } break; case EFST_SPORE_EXPLOSION_DEBUFF: if (on == true) { this->LaunchEffect(0x8e6, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } else if (this->HasId(0x8e6) == true) { this->SendMsg(nullptr, AM_DELETE_EFFECT, 0x8e6, 0, 0, 0, 0, 0); } break; case EFST_FIRM_FAITH: case EFST_MUSICAL_INTERLUDE: if (on == true) { this->LaunchEffect(0x8f8, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_POWERFUL_FAITH: if (on == true) { this->LaunchEffect(0x8f6, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_SPELL_ENCHANTING: if (on == true) { this->LaunchEffect(0x8fa, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_PRE_ACIES: if (on == true) { this->LaunchEffect(0x8f0, vector3d{0.0f, 0.0f, 0.0f}, 0.0f); } break; case EFST_KILLING_AURA: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x8fe, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else { g_effectMgr->Retire(this->render.accountId, 0x8fe); g_effectMgr->Spawn(this->render.accountId, 0x8ff, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } break; case EFST_CURSE_R_CUBE: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x901, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else if (CEffect* const effect = g_effectMgr->Find(this->render.accountId, 0x901)) { static_cast(effect)->Restart(); } break; case EFST_CURSE_B_CUBE: if (on == true) { g_effectMgr->Spawn(this->render.accountId, 0x902, vector3d{0.0f, 0.0f, 0.0f}, 0, -1, 0, 0); } else if (CEffect* const effect = g_effectMgr->Find(this->render.accountId, 0x902)) { static_cast(effect)->Restart(); } break; case EFST_BLOCK: if (on == true) { g_windowMgr.EnsureWindow(WID_DEBUFFREMOVEWND); } else { g_windowMgr.CloseWindow(WID_DEBUFFREMOVEWND); } break; default: if (on == true) { CSkillActionMgr::GetInstance()->OnEfstBegin(static_cast(b), static_cast(d), static_cast(e), static_cast(g), this, f != 0); } else { CSkillActionMgr::GetInstance()->OnEfstEnd(static_cast(b), static_cast(d), static_cast(e), static_cast(g), this); } break; } break; } case AM_CHANGE_COLOR: { this->render.colorLocked = 1; unsigned char* const saved = reinterpret_cast(&this->render.savedTintColor); if (saved[0] == 0 && saved[1] == 0 && saved[2] == 0 && saved[3] == 0) { const unsigned char* const argb = reinterpret_cast(&this->render.color); saved[0] = argb[3]; saved[1] = argb[2]; saved[2] = argb[1]; saved[3] = argb[0]; } unsigned char* const target = reinterpret_cast(&this->render.targetTintColor); target[0] = 0xFF; target[1] = static_cast(b); target[2] = static_cast(c); target[3] = static_cast(d); const std::int64_t red = b; const std::int64_t green = c; this->render.color = static_cast(red << 16 | green << 8 | static_cast(d) | 0xFF000000); break; } case AM_RETURN_COLOR: this->render.savedTintColor = 0; this->render.colorLocked = 0; break; case AM_DELETE_BUYING_SHOP_TITLE: if (this->actorWindows[4] == 0) { break; } g_windowMgr.Schedule(reinterpret_cast(this->actorWindows[4])); this->actorWindows[4] = 0; break; case AM_MAKE_BUYING_SHOP_TITLE: { if (this->actorWindows[4] == 0) { UIMerchantShopTitle* const title = new UIMerchantShopTitle(1); this->actorWindows[4] = reinterpret_cast(title); title->SetSize(140, 34); g_windowMgr.AddRenderedFront(reinterpret_cast(this->actorWindows[4])); } reinterpret_cast(this->actorWindows[4])->SendMsg(nullptr, UM_SETINFO, b, static_cast(this->render.fontSequenceState), 0, 0); reinterpret_cast(this->actorWindows[4])->SendMsg(nullptr, UM_SETNAID, this->render.accountId, 0, 0, 0); reinterpret_cast(this->actorWindows[4])->Move(this->render.screenX - 70, this->render.screenY - ScaleByScreenHeight(110)); if (g_serviceOptions.Get(0xFA)) { break; } reinterpret_cast(this->actorWindows[4])->Show(0); break; } case AM_FALLEN_ANGEL: if (g_session.IsPlayerJobId(this->GetEffectiveJob(-1))) { if (CRagEffect* effect = this->LaunchEffect(0xA6, vector3d{0.0f, 0.0f, 0.0f}, 0.0f)) { effect->SendMsg(nullptr, AM_SETCOUNT, 0, 0xFFFF, 0xF3, 0, 100, 0); } if (CEffect* fallenAngel = g_effectMgr->Find(this->render.accountId, 0x474)) { vector3d position{0.0f, 0.0f, 0.0f}; static_cast(g_modeMgr.GetGameMode())->CellCenter(static_cast(b), static_cast(c), position.x, position.z); position.y = this->render.position.y; static_cast(fallenAngel)->SetPosition(position); } } this->SendMsg(nullptr, AM_STOPMOVE, b, d, 15, 0, 0, 0); this->SetAction(this->render.animation, 6, 1); this->render.curMotion = 7; this->render.isMotionFreezed = true; break; case AM_UPDATE_MONSTER_GAGE: if (b == -1 || c == -1) { if (this->hpGaugeWindow == 0) { break; } g_windowMgr.Schedule(reinterpret_cast(this->hpGaugeWindow)); this->hpGaugeWindow = 0; break; } if (d == 1 && this->hpGaugeWindow != 0) { g_windowMgr.Schedule(reinterpret_cast(this->hpGaugeWindow)); this->hpGaugeWindow = 0; } if (this->hpGaugeWindow == 0) { UIMonsterGage* const gage = new UIMonsterGage(); this->hpGaugeWindow = reinterpret_cast(gage); gage->SetSize(60, 5); g_windowMgr.AddRenderedFront(reinterpret_cast(this->hpGaugeWindow)); } reinterpret_cast(this->hpGaugeWindow)->SetValues(static_cast(b), static_cast(c)); reinterpret_cast(this->hpGaugeWindow)->Move( this->render.screenX - reinterpret_cast(this->hpGaugeWindow)->geometry.width / 2, static_cast(static_cast(ScaleByScreenHeight(12)) * this->render.distanceScale + static_cast(this->render.screenY)) ); break; case AM_MAKE_MISFORTUNE_EFFECT: if (this->firstFollowerEffect == 0) { CGameObject* effect = mode->SpawnMsgEffect(68, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->firstFollowerEffect = reinterpret_cast(effect); } break; case AM_DELETE_MISFORTUNE_EFFECT: if (this->firstFollowerEffect != 0) { reinterpret_cast(this->firstFollowerEffect)->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->firstFollowerEffect = 0; } break; case AM_MAKE_DEEPSILENCE_EFFECT: if (this->secondFollowerEffect == 0) { CGameObject* effect = mode->SpawnMsgEffect(9, &this->render.position, this->render.accountId, true); this->msgEffectList.push_back(effect); effect->SendMsg(this, AM_SET_MASTER_POINTER, 0, 0, 0, 0, 0, 0); this->secondFollowerEffect = reinterpret_cast(effect); } break; case AM_DELETE_DEEPSILENCE_EFFECT: if (this->secondFollowerEffect != 0) { reinterpret_cast(this->secondFollowerEffect)->SendMsg(nullptr, AM_DISAPPEAR, 0, 0, 0, 0, 0, 0); this->secondFollowerEffect = 0; } break; default: this->EffectActor::SendMsg(sender, message, b, c, d, e, 0, 0); break; } }