From 02728b254453a64ce9c22d607f92c784d50c4920 Mon Sep 17 00:00:00 2001 From: ragnarok-clanker Date: Sat, 31 Oct 2026 01:49:32 +0000 Subject: [PATCH] Decompile 2 functions in Source/Client/Mode/GameModeMiniMap.cpp, Source/Client/Mode/CGameMode.h Batch #6755 by superintelligence: 0 byte-identical, 2 confirmed by peer review. - fn_21259 CGameMode::DrawTimeAttackTimer: logic confirmed by peer review - fn_21260 CGameMode::DrawTimeAttackCountdown: logic confirmed by peer review Peer-reviewed-by: ragnarok-clanker, eAthena Accepted without human review: peers confirmed the logic ("same" ahead by at least 3, with 70% of the votes). --- Source/Client/Mode/CGameMode.h | 2 + Source/Client/Mode/GameModeMiniMap.cpp | 217 +++++++++++++++++++++++++ 2 files changed, 219 insertions(+) diff --git a/Source/Client/Mode/CGameMode.h b/Source/Client/Mode/CGameMode.h index 0824ac32..95968754 100644 --- a/Source/Client/Mode/CGameMode.h +++ b/Source/Client/Mode/CGameMode.h @@ -211,6 +211,8 @@ public: void ShowGuildEmblem(CGameActor* actor, int guildId, int unused); void RemoveUnknown0604(int window); void DrawMiniMapNumber(); + void DrawTimeAttackTimer(); + void DrawTimeAttackCountdown(std::uint32_t value, int mode); CMsgEffect* SpawnCashEmotion(int aid, int packId, int emotionId, std::uint32_t duration); void ApplyMapFog(int enabled); void SpawnGrannyCorpse(float x, float y, float z, float angle, const char* name); diff --git a/Source/Client/Mode/GameModeMiniMap.cpp b/Source/Client/Mode/GameModeMiniMap.cpp index 46587a75..a7475dbf 100644 --- a/Source/Client/Mode/GameModeMiniMap.cpp +++ b/Source/Client/Mode/GameModeMiniMap.cpp @@ -18,6 +18,7 @@ #include "Utility/Math/AffineMatrix.h" #include "Utility/Text/FormatString.h" +#include #include // GLOBAL: gl_24934 @@ -443,3 +444,219 @@ void CGameMode::DrawMiniMapNumber() { ); } } + +// GLOBAL: gl_25047 +std::uint32_t g_timeAttackCountdown; +// GLOBAL: gl_25048 +int g_timeAttackStart; +// GLOBAL: gl_25049 +int g_timeAttackVisible; +// GLOBAL: gl_25050 +int g_timeAttackFrame; + +// OFFSET: 0x00CD1EE0 +// FUNCTION: fn_21259 +void CGameMode::DrawTimeAttackTimer() { + const char* player = reinterpret_cast(this->scene->player); + const int coordinateX = *reinterpret_cast(player + 0x494); + const int coordinateY = *reinterpret_cast(player + 0x498); + if (coordinateY < 1 || coordinateX < 1) { + return; + } + if (coordinateY < 2 || coordinateX != 1) { + return; + } + + const int start = g_timeAttackStart; + if (start == 0) { + g_timeAttackVisible = 0; + } + + const std::uint32_t elapsed = timeGetTime() - static_cast(start); + int seconds = static_cast(elapsed / 1000); + int minutes = 0; + int hours = 0; + int days = 0; + if (seconds > 59) { + minutes = seconds / 60; + seconds %= 60; + if (minutes > 59) { + hours = minutes / 60; + minutes %= 60; + if (hours > 23) { + days = hours / 24; + hours %= 24; + } + } + } + + CActRes* action = static_cast(ResourceMgr::Instance().Load("이팩트\\TimeFont.act")); + CSprRes* sprite = static_cast(ResourceMgr::Instance().Load("이팩트\\TimeFont.spr")); + + int digits[11] = {}; + int count = 2; + int value = seconds; + if (value > 9999) { + value = 9999; + } + if (value < 0) { + value = 0; + } + digits[0] = value % 10; + digits[1] = value / 10 % 10; + if (minutes != 0) { + count = 4; + digits[3] = minutes % 10; + digits[4] = minutes / 10 % 10; + } + if (hours != 0) { + count = 6; + digits[6] = hours % 10; + digits[7] = hours / 10 % 10; + } + + int total; + int offset; + int startIndex; + if (days == 0) { + total = count + (count - 2) / 2; + offset = total * -20; + startIndex = total - 1; + } else { + digits[9] = days % 10; + digits[10] = days / 10 % 10; + total = 11; + offset = -220; + startIndex = 10; + } + + const int center = g_renderer->viewport.width / 2; + for (int index = 0; index < total; ++index, --startIndex) { + const int digit = (index + 1) % 3 == 0 ? 10 : digits[startIndex]; + DrawActionSprite( + reinterpret_cast(*this), center + offset + index * 40, 40, action, sprite, digit, 0, 1.0f, 0.0f, 0xFFFFFFFF, 0x201 + ); + } + + if (g_timeAttackStart != 0 && timeGetTime() - static_cast(g_timeAttackStart) < 3000) { + CActRes* titleAction = static_cast(ResourceMgr::Instance().Load("이팩트\\TimeAttack.act")); + CSprRes* titleSprite = static_cast(ResourceMgr::Instance().Load("이팩트\\TimeAttack.spr")); + const int frame = g_timeAttackFrame / 3; + if (frame < static_cast(titleAction->FrameCount(0))) { + const int titleX = g_renderer->viewport.width / 2 - titleSprite->images[0][0]->height / 2; + DrawActionSprite( + reinterpret_cast(*this), titleX, 120, titleAction, titleSprite, 0, frame, 1.0f, 0.0f, 0xFFFFFFFF, 0x201 + ); + ++g_timeAttackFrame; + } + } +} + +// OFFSET: 0x00CD2430 +// FUNCTION: fn_21260 +void CGameMode::DrawTimeAttackCountdown(std::uint32_t value, int mode) { + std::uint32_t remaining = 0; + switch (mode) { + case 0: + if (this->unknown_0464 == 0) { + this->unknown_0464 = timeGetTime(); + } + remaining = value; + if (timeGetTime() - this->unknown_0464 > 5000) { + this->unknown_0464 = 0; + g_timeAttackVisible = 0; + } + break; + case 1: + remaining = timeGetTime() - value; + if (value == 0) { + g_timeAttackVisible = 0; + } + break; + case 2: + remaining = value - timeGetTime(); + if (remaining == 0) { + g_timeAttackVisible = 0; + } + break; + case 3: + remaining = g_timeAttackCountdown / 30; + --g_timeAttackCountdown; + if (remaining == 0) { + g_timeAttackVisible = 0; + } + break; + default: + break; + } + + int seconds = static_cast(remaining / 1000); + int minutes = 0; + int hours = 0; + int days = 0; + if (seconds > 59) { + minutes = seconds / 60; + seconds %= 60; + if (minutes > 59) { + hours = minutes / 60; + minutes %= 60; + if (hours > 23) { + days = hours / 24; + hours %= 24; + } + } + } + + CActRes* action = static_cast(ResourceMgr::Instance().Load("이팩트\\TimeFont.act")); + CSprRes* sprite = static_cast(ResourceMgr::Instance().Load("이팩트\\TimeFont.spr")); + + int display = mode != 3 ? seconds : static_cast(remaining); + if (mode == 3) { + minutes = 0; + hours = 0; + days = 0; + } + int digits[11] = {}; + int count = 2; + if (display > 9999) { + display = 9999; + } + if (display < 0) { + display = 0; + } + digits[0] = display % 10; + digits[1] = display / 10 % 10; + if (minutes != 0) { + count = 4; + digits[3] = minutes % 10; + digits[4] = minutes / 10 % 10; + } + if (hours != 0) { + count = 6; + digits[6] = hours % 10; + digits[7] = hours / 10 % 10; + } + + int total; + int offset; + int startIndex; + if (days == 0) { + total = count + (count - 2) / 2; + offset = total * -20; + startIndex = total - 1; + } else { + digits[9] = days % 10; + digits[10] = days / 10 % 10; + total = 11; + offset = -220; + startIndex = 10; + } + + const int center = g_renderer->viewport.width / 2; + for (int index = 0; index < total; ++index, --startIndex) { + const int digit = (index + 1) % 3 == 0 ? 10 : digits[startIndex]; + DrawActionSprite( + reinterpret_cast(*this), center + offset + index * 40, 40, action, sprite, digit, 0, 1.0f, 0.0f, 0xFFFFFFFF, 0x201 + ); + } +}