From cf1ac5f864154a6102775c4812c36645396e75d2 Mon Sep 17 00:00:00 2001 From: Indev Date: Wed, 24 Dec 2025 16:48:54 +0300 Subject: [PATCH] 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*"