From cf1ac5f864154a6102775c4812c36645396e75d2 Mon Sep 17 00:00:00 2001 From: Indev Date: Wed, 24 Dec 2025 16:48:54 +0300 Subject: [PATCH 1/4] Expose item timers to lua --- src/deh_tables.c | 5 + src/h_timers.cpp | 246 ++++++++++++++--------------------------------- src/h_timers.h | 18 ++-- src/lua_hudlib.c | 40 ++++++++ src/lua_libs.h | 1 + 5 files changed, 131 insertions(+), 179 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 4128d21f7..83ecae201 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -32,6 +32,7 @@ #include "k_kart.h" // awardscaledrings_t #include "k_hud.h" // scoreboardmod_e #include "k_waypoint.h" // waypoint values (for lua) +#include "h_timers.h" // timerflags_t #include "deh_tables.h" @@ -1821,5 +1822,9 @@ struct int_const_s const INT_CONST[] = { {"AIRDROP_HEAVY", AIRDROP_HEAVY}, {"AIRDROP_FUSION", AIRDROP_FUSION}, + // item timer stuff + {"TIMER_COUNTER", TIMER_COUNTER}, + {"TIMER_BAD", TIMER_BAD}, + {NULL,0} }; diff --git a/src/h_timers.cpp b/src/h_timers.cpp index 9920f1aab..61080e650 100644 --- a/src/h_timers.cpp +++ b/src/h_timers.cpp @@ -38,18 +38,15 @@ consvar_t cv_itemtimers = CVAR_INIT ("itemtimers", "On", CV_SAVE, CV_OnOff, NULL); -typedef struct itimer_s +typedef struct itimer_s { const char* name; // name of timer INT32 timer; // current time - //std::vector patches; // timer graphics (big/freeplay) - std::vector patches_small; // timer graphics (small) - INT32 anim_frames; // tic duration for each graphic - //INT32 xoffs = 0, yoffs = 0; // offsets for freeplay mode - INT32 xoffs_small = 0, yoffs_small = 0; // offsets for normal mode - boolean counter; // not a timer, show a counter instead - boolean badtimer; // timer is colored red - INT32 *timer_p; // if defined, uses a pointer for the timer instead + std::vector patches; // timer graphics (small) + INT32 anim_frames = 1; // tic duration for each graphic + UINT8 flags = 0; // settings for timer + lua_Integer timer_func_ref = LUA_NOREF; // if set, calls a function from registry to get timer value + boolean timer_func_error = false; // don't print error every frame } itimer_t; static bool sorttimers(itimer_t a, itimer_t b) @@ -141,30 +138,24 @@ static std::vector addTimers; static std::vector addTimers_unsorted; void K_AddItemTimerEx( - const char *name, - INT32 *timer, - /*char **patches,*/ char **patches_small, - /*INT32 patches_size,*/ INT32 patches_small_size, + const char *name, + lua_Integer timer_func_ref, + const char **patches, + INT32 patches_size, INT32 anim_duration, - INT32 xoffs, INT32 yoffs, - INT32 xoffs_small, INT32 yoffs_small, - boolean counter, boolean unsorted) + UINT8 flags, boolean unsorted) { itimer_t t; // woo yeah baby t.name = name; - t.timer_p = timer; + t.timer_func_ref = timer_func_ref; INT32 i; - //for (i = 0; i < patches_size; i++) - //t.patches.push_back(qche(patches[i])); - for (i = 0; i < patches_small_size; i++) - t.patches_small.push_back(qche(patches_small[i])); + for (i = 0; i < patches_size; i++) + t.patches.push_back(qche(patches[i])); t.anim_frames = anim_duration; - //t.xoffs = xoffs; t.yoffs = yoffs; - t.xoffs_small = xoffs_small; t.yoffs_small = yoffs_small; - t.counter = counter; + t.flags = flags; // yeah if (unsorted) @@ -204,6 +195,40 @@ void K_getTimersDrawinfo(drawinfo_t *out) out->flags = flags; } +static INT32 getLuaTimer(itimer_t *itimer) +{ + lua_pushcfunction(gL, LUA_GetErrorMessage); + + lua_Integer errorh = lua_gettop(gL); + + lua_getfield(gL, LUA_REGISTRYINDEX, LREG_TIMERS); + lua_rawgeti(gL, -1, itimer->timer_func_ref); + + I_Assert(lua_isfunction(gL, -1)); + + LUA_PushUserdata(gL, stplyr, META_PLAYER); + + if (lua_pcall(gL, 1, 1, errorh) != 0) + { + if (!itimer->timer_func_error) + { + itimer->timer_func_error = true; + CONS_Alert(CONS_WARNING, "%s\n", lua_tostring(gL, -1)); + } + + lua_pop(gL, 3); // Pop error message, timers table and error handler + return 0; + } + + // TODO - print error if returned value isn't integer? + if (lua_isnumber(gL, -1)) + itimer->timer = lua_tointeger(gL, -1); + + lua_pop(gL, 3); // Pop function result, timers table and error handler + + return itimer->timer; +} + void K_DisplayItemTimers(void) { if (!cv_itemtimers.value) return; @@ -211,72 +236,63 @@ void K_DisplayItemTimers(void) if (!LUA_HudEnabled(hud_itemtimers)) return; if (stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) return; // I love and hate you C++ - std::vector timers = + std::vector timers = { { // sneaker "shoe", stplyr->sneakertimer, - //{qche("K_ISSHOE")}, {qche("K_TISHOE")}, - 1, /*-15, -15,*/ -4, -2, + 1, }, { // invinc "invincible", stplyr->invincibilitytimer, - //{qche("K_ISINV1"), qche("K_ISINV2"), qche("K_ISINV3"), qche("K_ISINV4"), qche("K_ISINV5"), qche("K_ISINV6")}, {qche("K_TIINV1"), qche("K_TIINV2"), qche("K_TIINV3"), qche("K_TIINV4"), qche("K_TIINV5"), qche("K_TIINV6")}, - 3, /*-15, -15,*/ -4, -2, + 3, }, { // grow "grow", std::max(0, stplyr->growshrinktimer), - //{qche("K_ISGROW")}, {qche("K_TIGROW")}, - 1, /*-15, -15,*/ -4, -2, + 1, }, { // rocket sneakers "rocketsneakers", stplyr->rocketsneakertimer, - //{qche("K_ISRSHE")}, {qche("K_TIRSHE")}, - 1, /*-15, -15,*/ -4, -2, + 1, }, { // hyudoro "hyudoro", stplyr->hyudorotimer, - //{qche("K_ISHYUD")}, {qche("K_TIHYUD")}, - 1, /*-15, -15,*/ -4, -2, + 1, }, { // drift boost "driftsparkboost", stplyr->driftboost, - //{qche("K_DRSP1"), qche("K_DRSP2")}, {qche("K_TISRK1"), qche("K_TISRK2")}, - 2, /*-15, -15,*/ -4, -2, + 2, }, { // start boost "startboost", stplyr->startboost, - //{qche("K_ISSTB")}, {qche("K_TISTB")}, - 1, /*-15, -15,*/ -4, -2, + 1, }, { // ring boost "ringboost", stplyr->ringboost, - //{qche("K_ISRING")}, {qche("K_TIRING")}, - 1, /*-15, -15,*/ -4, -2, + 1, }, { // flameshield "flameshield", stplyr->flametimer, - //{qche("K_ISFLMS")}, {qche("K_TIFLMS")}, - 1, /*-15, -15,*/ -4, -2, + 1, }, - }; + }; // insert sortable timers timers.insert(timers.end(), addTimers.begin(), addTimers.end()); @@ -289,41 +305,27 @@ void K_DisplayItemTimers(void) { // spinout "spinout", std::max(stplyr->spinouttimer, stplyr->wipeoutslow), - //{qche("K_DIZZ1"), qche("K_DIZZ2"), qche("K_DIZZ3"), qche("K_DIZZ4")}, {qche("K_TISPN1"), qche("K_TISPN2"), qche("K_TISPN3"), qche("K_TISPN4")}, - 3, /*-15, -15,*/ -4, -2, - false, - true, + 3, + TIMER_BAD, } ); timers.push_back( { // shrink "shrink", std::max(0, -stplyr->growshrinktimer), - //{qche("K_ISSHRK")}, {qche("K_TISHRK")}, 1, - //-15, - //-15, - -4, - -2, - false, - true, + TIMER_BAD, } ); timers.push_back( { // spb "spb", (INT32)spbTimers[stplyr-players], - //{qche("K_ISSPB")}, {qche("K_TISPB")}, 1, - //-15, - //-15, - -4, - -2, - false, - (stplyr->position == K_GetBestRank()) + (UINT8)((stplyr->position == K_GetBestRank()) ? TIMER_BAD : 0), } ); // Same with boost stacks @@ -331,39 +333,20 @@ void K_DisplayItemTimers(void) { // boosts "boosts", K_StackingActive() ? stplyr->numboosts : 0, - //{qche("K_DRSP1"), qche("K_DRSP2")}, {qche("K_TISRK1"), qche("K_TISRK2")}, - 2, /*-15, -15,*/ -3, -2, - true, + 2, + TIMER_BAD, } ); timers.push_back( { // item cooldown "itemusecooldown", (INT32)stplyr->itemusecooldown, - //{qche("K_ISFLMS")}, {qche("SERVLOCK")}, - 1, /*-15, -15,*/ -4, -2, - false, false + 1, } ); - /*timers.push_back( - { // dead - "spr_realisticexplosion", - stplyr->deadtimer ? std::max(0, TICRATE - stplyr->deadtimer) : 0, - //{qche("IT_DED1"), qche("IT_DED2"), qche("IT_DED3"), qche("IT_DED4"), qche("IT_DED5"), qche("IT_DED6"), qche("IT_DED7"), qche("IT_DED8")}, - {qche("IT_DEAD1"), qche("IT_DEAD2"), qche("IT_DEAD3"), qche("IT_DEAD4"), qche("IT_DEAD5"), qche("IT_DEAD6"), qche("IT_DEAD7"), qche("IT_DEAD8")}, - 1, - //0, - //-4, - 0, - 0, - false, - true, - } - );*/ - // insert unsortable timers timers.insert(timers.end(), addTimers_unsorted.begin(), addTimers_unsorted.end()); @@ -390,7 +373,7 @@ void K_DisplayItemTimers(void) for (itimer_t& t : timers) { INT32 timer = t.timer; - if (t.timer_p) timer = *t.timer_p; + if (t.timer_func_ref != LUA_NOREF) timer = getLuaTimer(&t); if (stplyr->deadtimer && !fastcmp(t.name, "spr_realisticexplosion")) continue; if (timer > 0) offs += stepx; @@ -403,7 +386,6 @@ void K_DisplayItemTimers(void) if (t.timer <= 0) continue; if (stplyr->deadtimer && !fastcmp(t.name, "spr_realisticexplosion")) continue; INT32 timer = t.timer; - if (t.timer_p) timer = *t.timer_p; INT32 seconds = timer / TICRATE, _centiseconds = G_TicsToCentiseconds(timer); const char* str = va("%d.%02d", seconds, _centiseconds); @@ -411,7 +393,7 @@ void K_DisplayItemTimers(void) str = va("%d.%01d", seconds, G_TicsToDeciseconds(timer)); if (seconds > 99) // exceeds 100 seconds? str = va("%d", seconds); - INT32 animsize = t.patches_small.size(); + INT32 animsize = t.patches.size(); INT32 patchnum = (leveltime % (t.anim_frames * animsize) / t.anim_frames); UINT8 *cmap = NULL, *textcmap = NULL; INT32 font = TINY_FONT; @@ -434,7 +416,7 @@ void K_DisplayItemTimers(void) flags |= V_GREENMAP; } - if (t.counter) // don't show up as time + if (t.flags & TIMER_COUNTER) // don't show up as time { str = va("%d", timer); font = HU_FONT; @@ -442,14 +424,14 @@ void K_DisplayItemTimers(void) INT32 width = V_StringScaledWidth(FRACUNIT, FRACUNIT, FRACUNIT, flags|V_MONOSPACE, font, str) >> FRACBITS; // very bad! - if (t.badtimer) + if (t.flags & TIMER_BAD) { flags |= V_REDMAP; //textcmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); } - patch_t *item = t.patches_small[patchnum]; - V_DrawFixedPatch((itemx - (item->width/2))<width/2))<leftoffset) << FRACBITS, (itemy + 12) << FRACBITS, @@ -466,86 +448,4 @@ void K_DisplayItemTimers(void) itemx += stepx; } } - /*else // FREEPLAY - Move these to the left side, where the rankings usually are - { - if (r_splitscreen) // How? - return; - - INT32 fx = 9, fy = 92, step = 20; - INT32 flags = V_SNAPTOLEFT|V_SLIDEIN; - - // center it - INT32 offs = -step; - for (itimer_t& t : timers) - { - INT32 timer = t.timer; - if (t.timer_p) timer = *t.timer_p; - if (stplyr->deadtimer && !fastcmp(t.name, "spr_realisticexplosion")) continue; - if (timer > 0) - offs += step; - } - fy -= offs/2; - - // draw relevant timers - for (itimer_t& t : timers) - { - if (t.timer <= 0) continue; - if (stplyr->deadtimer && !fastcmp(t.name, "spr_realisticexplosion")) continue; - INT32 timer = t.timer; - if (t.timer_p) timer = *t.timer_p; - INT32 seconds = timer / TICRATE, _centiseconds = G_TicsToCentiseconds(timer); - INT32 patchnum = (leveltime % (t.anim_frames * t.patches.size()) / t.anim_frames); - UINT8 *cmap = NULL, *textcmap = NULL; - - const char* str = va("%d.%02d", seconds, _centiseconds); - if (seconds > 9) // exceeds 9 seconds? - str = va("%d.%01d", seconds, G_TicsToDeciseconds(timer)); - if (seconds > 99) // exceeds 100 seconds? - str = va("%d", seconds); - boolean bost = false; - if (fastcmp(t.name, "boosts")) // don't show up as time - { - str = va("%d", timer); - bost = true; - } - INT32 width = V_StringScaledWidth(FRACUNIT, FRACUNIT, FRACUNIT, flags|V_MONOSPACE, OPPRNK_FONT, str) >> FRACBITS; - - if (fastcmp(t.name, "driftsparkboost")) - { - if (timer > 85) // rainbow - cmap = R_GetTranslationColormap(TC_DEFAULT, (skincolornum_t)K_RainbowColor(leveltime), GTC_CACHE); - else if (timer > 50 && K_PurpleDriftActive()) // purple - cmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_PURPLE, GTC_CACHE); - else if (timer > 25) // ketchup - cmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_KETCHUP, GTC_CACHE); - else // blu - cmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_SAPPHIRE, GTC_CACHE); - textcmap = cmap; - } - - // very bad! - if (t.badtimer) - textcmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); - - patch_t *item = t.patches[patchnum]; - V_DrawFixedPatch((fx+t.xoffs)<width/2) - (width/2) - item->leftoffset + t.xoffs) << FRACBITS, - (fy+5) << FRACBITS, - FRACUNIT, - FRACUNIT, - FRACUNIT, - FRACUNIT, - flags|V_MONOSPACE, - textcmap, - TINY_FONT, - str - ); - - - fy += step; - } - } - */ } diff --git a/src/h_timers.h b/src/h_timers.h index 4632284a5..4d7494cf5 100644 --- a/src/h_timers.h +++ b/src/h_timers.h @@ -19,6 +19,14 @@ extern "C" { #include "command.h" #include "d_player.h" #include "k_hud.h" +#include "lua_script.h" +#include "lua_libs.h" + +typedef enum timerflags_e +{ + TIMER_COUNTER = 0x1, + TIMER_BAD = 0x2, +} timerflags_t; extern consvar_t cv_itemtimers; extern tic_t spbTimers[MAXPLAYERS]; @@ -27,13 +35,11 @@ void K_UpdateSPBTimer(void); // Adds an item timer. void K_AddItemTimerEx( const char *name, - INT32 *timer, - char **patches, char **patches_small, - INT32 patches_size, INT32 patches_small_size, + lua_Integer timer_func_ref, + const char **patches, + INT32 patches_size, INT32 anim_duration, - INT32 xoffs, INT32 yoffs, - INT32 xoffs_small, INT32 yoffs_small, - boolean counter, boolean unsorted); + UINT8 flags, boolean unsorted); void K_DisplayItemTimers(void); void K_getTimersDrawinfo(drawinfo_t *out); diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index dd4295e4a..e4d78fe30 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -26,6 +26,7 @@ #include "w_wad.h" #include "z_zone.h" #include "hu_stuff.h" +#include "h_timers.h" #include "lua_script.h" #include "lua_libs.h" @@ -1770,6 +1771,41 @@ static int lib_hudgetoffsets(lua_State *L) return 2; } +static int lib_hudaddtimer(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + + luaL_checktype(L, 2, LUA_TFUNCTION); // We get to it later + + luaL_checktype(L, 3, LUA_TTABLE); + + INT32 patches_size = lua_objlen(L, 3); + CLEANUP(pfree) const char **patches = calloc(patches_size, sizeof(const char*)); + for (INT32 i = 0; i < patches_size; ++i) + { + lua_rawgeti(L, 3, i); + patches[i] = lua_tostring(L, -1); + lua_pop(L, 1); + } + + INT32 anim_duration = luaL_optinteger(L, 4, 1); + + UINT8 flags = (UINT8)luaL_optinteger(L, 5, 0); + + boolean unsorted = lua_toboolean(L, 6); + + lua_Integer timer_func_ref; + + // Store function into registry + lua_getfield(L, LUA_REGISTRYINDEX, LREG_TIMERS); + lua_pushvalue(L, 2); + timer_func_ref = luaL_ref(L, -2); + + K_AddItemTimerEx(name, timer_func_ref, patches, patches_size, anim_duration, flags, unsorted); + + return 0; +} + // add a HUD element for rendering extern int lib_hudadd(lua_State *L); @@ -1780,6 +1816,7 @@ static luaL_Reg lib_hud[] = { {"add", lib_hudadd}, {"setVoteBackground", lib_hudsetvotebackground}, {"getOffsets", lib_hudgetoffsets}, + {"addTimer", lib_hudaddtimer}, {NULL, NULL} }; @@ -1795,6 +1832,9 @@ int LUA_HudLib(lua_State *L) luaL_register(L, NULL, lib_draw); lib_draw_ref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, LREG_TIMERS); + luaL_newmetatable(L, META_COLORMAP); lua_pushcfunction(L, colormap_get); lua_setfield(L, -2, "__index"); diff --git a/src/lua_libs.h b/src/lua_libs.h index 5155bd3cb..aaa9725fe 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -25,6 +25,7 @@ extern lua_State *gL; #define LREG_STATEACTION "STATE_ACTION" #define LREG_ACTIONS "MOBJ_ACTION" #define LREG_METATABLES "METATABLES" +#define LREG_TIMERS "ITEM_TIMERS" #define META_STATE "STATE_T*" #define META_MOBJINFO "MOBJINFO_T*" From e96b1bfdce39fd51352170965ff1f3cd95fae0ee Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 13 Feb 2026 23:11:42 -0500 Subject: [PATCH 2/4] Make flags field of timers act like a bitfield and add new flags --- src/deh_tables.c | 17 +++++++++++- src/h_timers.cpp | 72 +++++++++++++++++++++++++++++------------------- src/h_timers.h | 21 ++++++++++++-- src/lua_hudlib.c | 2 +- 4 files changed, 78 insertions(+), 34 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 83ecae201..618b8161a 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1824,7 +1824,22 @@ struct int_const_s const INT_CONST[] = { // item timer stuff {"TIMER_COUNTER", TIMER_COUNTER}, - {"TIMER_BAD", TIMER_BAD}, + {"TIMER_NONUMBER",TIMER_NONUMBER}, + {"TIMER_PURPLE", TIMER_PURPLE}, + {"TIMER_YELLOW", TIMER_YELLOW}, + {"TIMER_GREEN", TIMER_GREEN}, + {"TIMER_BLUE", TIMER_BLUE}, + {"TIMER_RED", TIMER_RED}, + {"TIMER_GRAY", TIMER_GRAY}, + {"TIMER_ORANGE", TIMER_ORANGE}, + {"TIMER_SKY", TIMER_SKY}, + {"TIMER_LAVENDER", TIMER_LAVENDER}, + {"TIMER_GOLD", TIMER_GOLD}, + {"TIMER_AQUA", TIMER_AQUA}, + {"TIMER_MAGENTA", TIMER_MAGENTA}, + {"TIMER_PINK", TIMER_PINK}, + {"TIMER_BROWN", TIMER_BROWN}, + {"TIMER_TAN", TIMER_TAN}, {NULL,0} }; diff --git a/src/h_timers.cpp b/src/h_timers.cpp index 61080e650..86c69101f 100644 --- a/src/h_timers.cpp +++ b/src/h_timers.cpp @@ -44,7 +44,7 @@ typedef struct itimer_s INT32 timer; // current time std::vector patches; // timer graphics (small) INT32 anim_frames = 1; // tic duration for each graphic - UINT8 flags = 0; // settings for timer + UINT32 flags = 0; // settings for timer lua_Integer timer_func_ref = LUA_NOREF; // if set, calls a function from registry to get timer value boolean timer_func_error = false; // don't print error every frame } itimer_t; @@ -143,7 +143,7 @@ void K_AddItemTimerEx( const char **patches, INT32 patches_size, INT32 anim_duration, - UINT8 flags, boolean unsorted) + UINT32 flags, boolean unsorted) { itimer_t t; // woo yeah baby @@ -307,7 +307,7 @@ void K_DisplayItemTimers(void) std::max(stplyr->spinouttimer, stplyr->wipeoutslow), {qche("K_TISPN1"), qche("K_TISPN2"), qche("K_TISPN3"), qche("K_TISPN4")}, 3, - TIMER_BAD, + TIMER_RED, } ); timers.push_back( @@ -316,7 +316,7 @@ void K_DisplayItemTimers(void) std::max(0, -stplyr->growshrinktimer), {qche("K_TISHRK")}, 1, - TIMER_BAD, + static_cast((K_IsAltShrunk(stplyr) ? 0 : TIMER_RED)), } ); timers.push_back( @@ -325,7 +325,7 @@ void K_DisplayItemTimers(void) (INT32)spbTimers[stplyr-players], {qche("K_TISPB")}, 1, - (UINT8)((stplyr->position == K_GetBestRank()) ? TIMER_BAD : 0), + static_cast((stplyr->position == K_GetBestRank()) ? TIMER_RED : 0), } ); // Same with boost stacks @@ -335,7 +335,7 @@ void K_DisplayItemTimers(void) K_StackingActive() ? stplyr->numboosts : 0, {qche("K_TISRK1"), qche("K_TISRK2")}, 2, - TIMER_BAD, + TIMER_GREEN|TIMER_COUNTER, } ); timers.push_back( @@ -411,11 +411,6 @@ void K_DisplayItemTimers(void) textcmap = cmap; } - if (fastcmp(t.name, "boosts")) - { - flags |= V_GREENMAP; - } - if (t.flags & TIMER_COUNTER) // don't show up as time { str = va("%d", timer); @@ -423,27 +418,46 @@ void K_DisplayItemTimers(void) } INT32 width = V_StringScaledWidth(FRACUNIT, FRACUNIT, FRACUNIT, flags|V_MONOSPACE, font, str) >> FRACBITS; - // very bad! - if (t.flags & TIMER_BAD) - { - flags |= V_REDMAP; - //textcmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); - } +#define COLORTIMER(color) \ + if (t.flags & TIMER_##color) \ + flags |= V_##color##MAP\ + + // Color timers! + COLORTIMER(RED); + COLORTIMER(PURPLE); + COLORTIMER(YELLOW); + COLORTIMER(GREEN); + COLORTIMER(BLUE); + COLORTIMER(RED); + COLORTIMER(GRAY); + COLORTIMER(ORANGE); + COLORTIMER(SKY); + COLORTIMER(LAVENDER); + COLORTIMER(GOLD); + COLORTIMER(AQUA); + COLORTIMER(MAGENTA); + COLORTIMER(PINK); + COLORTIMER(BROWN); + COLORTIMER(TAN); patch_t *item = t.patches[patchnum]; V_DrawFixedPatch((itemx - (item->width/2))<leftoffset) << FRACBITS, - (itemy + 12) << FRACBITS, - FRACUNIT, - FRACUNIT, - FRACUNIT, - FRACUNIT, - flags|V_MONOSPACE, - textcmap, - font, - str - ); + + if (!(t.flags & TIMER_NONUMBER)) + { + V_DrawStringScaledEx( + (itemx - (width/2) - item->leftoffset) << FRACBITS, + (itemy + 12) << FRACBITS, + FRACUNIT, + FRACUNIT, + FRACUNIT, + FRACUNIT, + flags|V_MONOSPACE, + textcmap, + font, + str + ); + } itemx += stepx; } diff --git a/src/h_timers.h b/src/h_timers.h index 4d7494cf5..9643c1965 100644 --- a/src/h_timers.h +++ b/src/h_timers.h @@ -24,8 +24,23 @@ extern "C" { typedef enum timerflags_e { - TIMER_COUNTER = 0x1, - TIMER_BAD = 0x2, + TIMER_COUNTER = 1, + TIMER_NONUMBER = 1<<1, + TIMER_PURPLE = 1<<2, + TIMER_YELLOW = 1<<3, + TIMER_GREEN = 1<<4, + TIMER_BLUE = 1<<5, + TIMER_RED = 1<<6, + TIMER_GRAY = 1<<7, + TIMER_ORANGE = 1<<8, + TIMER_SKY = 1<<9, + TIMER_LAVENDER = 1<<10, + TIMER_GOLD = 1<<11, + TIMER_AQUA = 1<<12, + TIMER_MAGENTA = 1<<13, + TIMER_PINK = 1<<14, + TIMER_BROWN = 1<<15, + TIMER_TAN = 1<<16, } timerflags_t; extern consvar_t cv_itemtimers; @@ -39,7 +54,7 @@ void K_AddItemTimerEx( const char **patches, INT32 patches_size, INT32 anim_duration, - UINT8 flags, boolean unsorted); + UINT32 flags, boolean unsorted); void K_DisplayItemTimers(void); void K_getTimersDrawinfo(drawinfo_t *out); diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index e4d78fe30..edc324c24 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -1790,7 +1790,7 @@ static int lib_hudaddtimer(lua_State *L) INT32 anim_duration = luaL_optinteger(L, 4, 1); - UINT8 flags = (UINT8)luaL_optinteger(L, 5, 0); + UINT32 flags = luaL_optinteger(L, 5, 0); boolean unsorted = lua_toboolean(L, 6); From d2ddf2a27a8b294255b7e1c9efc68e53efe103b7 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 13 Feb 2026 23:26:17 -0500 Subject: [PATCH 3/4] Use hudtranshalf for splitscreen timers Why not make this respect player choice --- src/h_timers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/h_timers.cpp b/src/h_timers.cpp index 86c69101f..21f0c9e6f 100644 --- a/src/h_timers.cpp +++ b/src/h_timers.cpp @@ -187,7 +187,7 @@ void K_getTimersDrawinfo(drawinfo_t *out) if (r_splitscreen > 0) { flags &= ~V_HUDTRANS; - flags |= V_30TRANS; + flags |= V_HUDTRANSHALF; } out->x = fx; From e9cdbf97c3472c93b895ee04820b574a96c95695 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 13 Feb 2026 23:37:35 -0500 Subject: [PATCH 4/4] Add flag that always forces the timer to the end of the list --- src/deh_tables.c | 1 + src/h_timers.cpp | 8 ++++++++ src/h_timers.h | 31 ++++++++++++++++--------------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 618b8161a..078c223c6 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1825,6 +1825,7 @@ struct int_const_s const INT_CONST[] = { // item timer stuff {"TIMER_COUNTER", TIMER_COUNTER}, {"TIMER_NONUMBER",TIMER_NONUMBER}, + {"TIMER_END", TIMER_END}, {"TIMER_PURPLE", TIMER_PURPLE}, {"TIMER_YELLOW", TIMER_YELLOW}, {"TIMER_GREEN", TIMER_GREEN}, diff --git a/src/h_timers.cpp b/src/h_timers.cpp index 21f0c9e6f..deed799a8 100644 --- a/src/h_timers.cpp +++ b/src/h_timers.cpp @@ -51,6 +51,14 @@ typedef struct itimer_s static bool sorttimers(itimer_t a, itimer_t b) { + const bool enda = (a.flags & TIMER_END); + const bool endb = (b.flags & TIMER_END); + + // Check if timer should always be at the end? + if (enda != endb) + return !enda; + + // Just sort by time please. return a.timer < b.timer; } diff --git a/src/h_timers.h b/src/h_timers.h index 9643c1965..f2f7f7945 100644 --- a/src/h_timers.h +++ b/src/h_timers.h @@ -26,21 +26,22 @@ typedef enum timerflags_e { TIMER_COUNTER = 1, TIMER_NONUMBER = 1<<1, - TIMER_PURPLE = 1<<2, - TIMER_YELLOW = 1<<3, - TIMER_GREEN = 1<<4, - TIMER_BLUE = 1<<5, - TIMER_RED = 1<<6, - TIMER_GRAY = 1<<7, - TIMER_ORANGE = 1<<8, - TIMER_SKY = 1<<9, - TIMER_LAVENDER = 1<<10, - TIMER_GOLD = 1<<11, - TIMER_AQUA = 1<<12, - TIMER_MAGENTA = 1<<13, - TIMER_PINK = 1<<14, - TIMER_BROWN = 1<<15, - TIMER_TAN = 1<<16, + TIMER_END = 1<<2, + TIMER_PURPLE = 1<<3, + TIMER_YELLOW = 1<<4, + TIMER_GREEN = 1<<5, + TIMER_BLUE = 1<<6, + TIMER_RED = 1<<7, + TIMER_GRAY = 1<<8, + TIMER_ORANGE = 1<<9, + TIMER_SKY = 1<<10, + TIMER_LAVENDER = 1<<11, + TIMER_GOLD = 1<<12, + TIMER_AQUA = 1<<13, + TIMER_MAGENTA = 1<<14, + TIMER_PINK = 1<<15, + TIMER_BROWN = 1<<16, + TIMER_TAN = 1<<17, } timerflags_t; extern consvar_t cv_itemtimers;