From 7edec43d726f7fa96cdba5c0e6957f314cce9c3f Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 28 Aug 2022 23:15:45 +0100 Subject: [PATCH 01/15] Port Gametype Preference and associated changes Notably, makes the PWR-based Encore scrambling component of G_SometimesGetDifferentGametype WAY less spaghettified. --- src/d_clisrv.c | 5 ++- src/d_netcmd.c | 41 ++++++++++++++++++------ src/d_netcmd.h | 1 + src/g_game.c | 86 ++++++++++++++++++++++++++++---------------------- src/g_game.h | 3 +- src/k_kart.c | 1 + src/y_inter.c | 9 +++--- 7 files changed, 93 insertions(+), 53 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 19c45d82b..4507652d5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -890,6 +890,9 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) UINT8 *p; size_t mirror_length; const char *httpurl = cv_httpsource.string; + UINT8 prefgametype = (cv_kartgametypepreference.value == -1) + ? gametype + : cv_kartgametypepreference.value; netbuffer->packettype = PT_SERVERINFO; netbuffer->u.serverinfo._255 = 255; @@ -914,7 +917,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) else netbuffer->u.serverinfo.refusereason = 0; - strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], + strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[prefgametype], sizeof netbuffer->u.serverinfo.gametypename); netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9b310eb66..e5f57c6ec 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -383,6 +383,8 @@ static CV_PossibleValue_t kartencore_cons_t[] = {{-1, "Auto"}, {0, "Off"}, {1, " consvar_t cv_kartencore = CVAR_INIT ("kartencore", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartencore_cons_t, KartEncore_OnChange); static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}}; consvar_t cv_kartvoterulechanges = CVAR_INIT ("kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL); +static CV_PossibleValue_t kartgametypepreference_cons_t[] = {{-1, "None"}, {GT_RACE, "Race"}, {GT_BATTLE, "Battle"}, {0, NULL}}; +consvar_t cv_kartgametypepreference = CVAR_INIT ("kartgametypepreference", "None", CV_NETVAR, kartgametypepreference_cons_t, NULL); static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}}; consvar_t cv_kartspeedometer = CVAR_INIT ("kartdisplayspeed", "Percentage", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; @@ -2436,27 +2438,28 @@ void D_SetupVote(void) UINT8 buf[5*2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes UINT8 *p = buf; INT32 i; - UINT8 secondgt = G_SometimesGetDifferentGametype(); + UINT8 gt = (cv_kartgametypepreference.value == -1) ? gametype : cv_kartgametypepreference.value; + UINT8 secondgt = G_SometimesGetDifferentGametype(gt); INT16 votebuffer[4] = {-1,-1,-1,0}; - if ((cv_kartencore.value == 1) && (gametyperules & GTR_CIRCUIT)) - WRITEUINT8(p, (gametype|0x80)); + if ((cv_kartencore.value == 1) && (gametypedefaultrules[gt] & GTR_CIRCUIT)) + WRITEUINT8(p, (gt|VOTEMODIFIER_ENCORE)); else - WRITEUINT8(p, gametype); + WRITEUINT8(p, gt); WRITEUINT8(p, secondgt); - secondgt &= ~0x80; + secondgt &= ~VOTEMODIFIER_ENCORE; for (i = 0; i < 4; i++) { UINT16 m; if (i == 2) // sometimes a different gametype m = G_RandMap(G_TOLFlag(secondgt), prevmap, ((secondgt != gametype) ? 2 : 0), 0, true, votebuffer); - else if (i >= 3) // unknown-random and force-unknown MAP HELL - m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, (i-2), (i < 4), votebuffer); + else if (i >= 3) // unknown-random and formerly force-unknown MAP HELL + m = G_RandMap(G_TOLFlag(gt), prevmap, 0, (i-2), (i < 4), votebuffer); else - m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, true, votebuffer); + m = G_RandMap(G_TOLFlag(gt), prevmap, 0, 0, true, votebuffer); if (i < 3) - votebuffer[i] = m; // min() is a dumb workaround for gcc 4.4 array-bounds error + votebuffer[i] = m; WRITEUINT16(p, m); } @@ -4877,6 +4880,13 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) gt = (UINT8)READUINT8(*cp); secondgt = (UINT8)READUINT8(*cp); + // Strip illegal Encore flag. + if ((gt & VOTEMODIFIER_ENCORE) + && !(gametypedefaultrules[(gt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT)) + { + gt &= ~VOTEMODIFIER_ENCORE; + } + for (i = 0; i < 4; i++) { votelevels[i][0] = (UINT16)READUINT16(*cp); @@ -4885,6 +4895,19 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) P_AllocMapHeader(votelevels[i][0]); } + // If third entry has an illelegal Encore flag... (illelegal!?) + if ((secondgt & VOTEMODIFIER_ENCORE) + && !(gametypedefaultrules[(secondgt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT)) + { + secondgt &= ~VOTEMODIFIER_ENCORE; + // Apply it to the second entry instead, gametype permitting! + if (gametypedefaultrules[gt] & GTR_CIRCUIT) + { + votelevels[1][1] |= VOTEMODIFIER_ENCORE; + } + } + + // Finally, set third entry's gametype/Encore status. votelevels[2][1] = secondgt; G_SetGamestate(GS_VOTING); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index e84342795..44e3aedcc 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -77,6 +77,7 @@ extern consvar_t cv_kartfrantic; extern consvar_t cv_kartcomeback; extern consvar_t cv_kartencore; extern consvar_t cv_kartvoterulechanges; +extern consvar_t cv_kartgametypepreference; extern consvar_t cv_kartspeedometer; extern consvar_t cv_kartvoices; extern consvar_t cv_kartbot; diff --git a/src/g_game.c b/src/g_game.c index 60261e368..6bdd5e028 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3260,51 +3260,53 @@ boolean G_GametypeHasSpectators(void) // // Oh, yeah, and we sometimes flip encore mode on here too. // -INT16 G_SometimesGetDifferentGametype(void) +INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype) { - boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1) && (gametyperules & GTR_CIRCUIT)); + // Most of the gametype references in this condition are intentionally not prefgametype. + // This is so a server CAN continue playing a gametype if they like the taste of it. + // The encore check needs prefgametype so can't use G_RaceGametype... + boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1) + && ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT)); + UINT8 encoremodifier = 0; - if (!cv_kartvoterulechanges.value // never - && encorescramble != 1) // destroying the code for this one instance - return gametype; - - if (randmapbuffer[NUMMAPS] > 0 && (encorepossible || cv_kartvoterulechanges.value != 3)) + if (encorepossible) { - randmapbuffer[NUMMAPS]--; - if (encorepossible) + if (encorescramble != -1) { - if (encorescramble != -1) - encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission - else - { - switch (cv_kartvoterulechanges.value) - { - case 3: // always - randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set - break; - case 2: // frequent - encorepossible = M_RandomChance(FRACUNIT>>1); - break; - case 1: // sometimes - default: - encorepossible = M_RandomChance(FRACUNIT>>2); - break; - } - } - if (encorepossible != (cv_kartencore.value == 1)) - return (gametype|0x80); + encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission } - return gametype; + else + { + switch (cv_kartvoterulechanges.value) + { + case 3: // always + encorepossible = true; + break; + case 2: // frequent + encorepossible = M_RandomChance(FRACUNIT>>1); + break; + case 1: // sometimes + encorepossible = M_RandomChance(FRACUNIT>>2); + break; + default: + break; + } + } + if (encorepossible != (cv_kartencore.value == 1)) + encoremodifier = VOTEMODIFIER_ENCORE; } - if (!cv_kartvoterulechanges.value) // never (again) - return gametype; + if (!cv_kartvoterulechanges.value) // never + return (gametype|encoremodifier); + + if (randmapbuffer[NUMMAPS] > 0 && (cv_kartvoterulechanges.value != 3)) + { + randmapbuffer[NUMMAPS]--; + return (gametype|encoremodifier); + } switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv? { - case 3: // always - randmapbuffer[NUMMAPS] = 1; // every other vote (or always if !encorepossible) - break; case 1: // sometimes randmapbuffer[NUMMAPS] = 5; // per "cup" break; @@ -3315,9 +3317,17 @@ INT16 G_SometimesGetDifferentGametype(void) break; } - if (gametype == GT_BATTLE) - return GT_RACE; - return GT_BATTLE; + // Only this response is prefgametype-based. + // todo custom gametypes + if (prefgametype == GT_BATTLE) + { + // Intentionally does not use encoremodifier! + if (cv_kartencore.value == 1) + return (GT_RACE|VOTEMODIFIER_ENCORE); + return (GT_RACE); + } + // This might appear wrong HERE, but the game will display the Encore possibility on the second voting choice instead. + return (GT_BATTLE|encoremodifier); } // diff --git a/src/g_game.h b/src/g_game.h index 52c7c7a0d..9fbbd5861 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -199,7 +199,8 @@ boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); -INT16 G_SometimesGetDifferentGametype(void); +#define VOTEMODIFIER_ENCORE 0x80 +INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype); UINT8 G_GetGametypeColor(INT16 gt); void G_ExitLevel(void); void G_NextLevel(void); diff --git a/src/k_kart.c b/src/k_kart.c index f47bf3b17..11f4fc68a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -248,6 +248,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartcomeback); CV_RegisterVar(&cv_kartencore); CV_RegisterVar(&cv_kartvoterulechanges); + CV_RegisterVar(&cv_kartgametypepreference); CV_RegisterVar(&cv_kartspeedometer); CV_RegisterVar(&cv_kartvoices); CV_RegisterVar(&cv_kartbot); diff --git a/src/y_inter.c b/src/y_inter.c index 2959853e0..807f10b4a 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1588,6 +1588,7 @@ void Y_VoteTicker(void) void Y_StartVote(void) { INT32 i = 0; + boolean battlemode = ((votelevels[0][1] & ~VOTEMODIFIER_ENCORE) == GT_BATTLE); // todo gametyperules votetic = -1; @@ -1596,8 +1597,8 @@ void Y_StartVote(void) I_Error("voteendtic is dirty"); #endif - widebgpatch = W_CachePatchName(((gametype == GT_BATTLE) ? "BATTLSCW" : "INTERSCW"), PU_STATIC); - bgpatch = W_CachePatchName(((gametype == GT_BATTLE) ? "BATTLSCR" : "INTERSCR"), PU_STATIC); + widebgpatch = W_CachePatchName((battlemode ? "BATTLSCW" : "INTERSCW"), PU_STATIC); + bgpatch = W_CachePatchName((battlemode ? "BATTLSCR" : "INTERSCR"), PU_STATIC); cursor = W_CachePatchName("M_CURSOR", PU_STATIC); cursor1 = W_CachePatchName("P1CURSOR", PU_STATIC); cursor2 = W_CachePatchName("P2CURSOR", PU_STATIC); @@ -1631,8 +1632,8 @@ void Y_StartVote(void) lumpnum_t lumpnum; // set up the encore - levelinfo[i].encore = (votelevels[i][1] & 0x80); - votelevels[i][1] &= ~0x80; + levelinfo[i].encore = (votelevels[i][1] & VOTEMODIFIER_ENCORE); + votelevels[i][1] &= ~VOTEMODIFIER_ENCORE; // set up the levelstring if (mapheaderinfo[votelevels[i][0]]->levelflags & LF_NOZONE || !mapheaderinfo[votelevels[i][0]]->zonttl[0]) From ceeda5c826594c93e5c5634b0763324e82c17a71 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Sep 2022 09:26:56 -0700 Subject: [PATCH 02/15] Lua hooklib refactor squash commit 8d382e49fb3411cad1a3ef5ee1e546030c3a9d93 Author: James R Date: Tue Nov 17 04:14:45 2020 -0800 Big Large Lua Hooklib Refactor * Hooks are no longer a mess of lua boiler plate. Helper functions reduce hooks to, at the most basic level, only two calls. * Lua tables (the array part) are used to index hooks. Such tables contain only hooks of the same type. * Hook types are defined in one place so you no longer need to sync up the enum and name array. commit 3f7c2ae0b0c450cb8993ffe7664ead05fd9f5000 Author: James R Date: Thu Dec 10 05:39:53 2020 -0800 Avoid using multiple tables to fetch hook String hooks still use a table to fetch the id, but the id indexes a C array. Also I fixed a missing call in the MusicChange hook. commit dbd8903a538e7b87061795ce27ec5c72c26743af Author: James R Date: Thu Dec 10 08:50:23 2020 -0800 Use ref for pushing error handler commit 9ddeb5f5896de0407b2b6fce8c949295a9e6d5e4 Author: James R Date: Sat Dec 12 02:05:21 2020 -0800 Resolve GameQuit hook conflicts commit 93e4f43e4b3a24fc2d8032e8730cbf427e75b297 Author: James R Date: Sat Dec 12 03:06:57 2020 -0800 Hooklib macros names -> uppercase + documentation commit 80fe39bbd1433c91131d2cdb36ba709f37d3b5f3 Author: Steel Titanium Date: Mon May 3 01:40:02 2021 -0400 Fix MusicChange hook not returning some values correctly commit 46ca9613c68422a26910cbb034a9f3e004a967c3 Author: James R Date: Thu Jun 10 18:09:39 2021 -0700 Pop hook id fetched from table commit a75d4a1c360874a3c301a494e38ea49cd89b6616 Author: James R Date: Tue Jul 6 18:42:08 2021 -0700 Automatically count hook values commit 331329306cad257d52f84e47a92d9214d9eaa8d3 Author: James R Date: Tue Jul 6 19:12:47 2021 -0700 Refactor hook ref allocation commit ae57b6ca8664e00ff4d9544339dbf29a41138040 Author: James R Date: Tue Jul 6 20:23:38 2021 -0700 MORE MACROS I just can't stop myself! commit b4fa98d2fbab180f487ce3efedb8ab715e5f3390 Author: James R Date: Wed Jul 7 00:23:51 2021 -0700 Refactor hudlib hooks to hooklib HUD hooks now meet the standard of hooklib. HUD registry magic numbers are gone. HUD hooks may also be added using addHook. addHook('HUD', fn[, type]) hud.add still exists, but the intention is to remove it eventually. commit cb619fad5d762ba8d8dd20e9a979a04ced7c943f Author: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com> Date: Wed Jul 7 19:57:28 2021 -0500 Rebase on !1307 commit f271f88c7f3084523a03ca7f47060d6527724796 Author: LJ Sonic Date: Sun Dec 5 17:59:33 2021 +0100 Fix MusicChange hook crashing when called BRUH MOMENT BRUH MOMENT PREPARE THE HALL OF SHAME commit 0a0c17da7c793ec29e80c5bdaa66b02da0884078 Author: James R Date: Tue Feb 1 02:27:27 2022 -0800 PARANOIA: I_Error if mobj hook is called with MT_NULL commit 86336d6bed80bee6f8168078aa8856109091e50f Author: katsy Date: Mon Mar 7 18:33:15 2022 -0600 remove MODID check from hooklib to fix compile issue --- src/lua_hook.h | 234 ++-- src/lua_hooklib.c | 2869 ++++++++++++++++----------------------------- src/s_sound.h | 10 + 3 files changed, 1179 insertions(+), 1934 deletions(-) diff --git a/src/lua_hook.h b/src/lua_hook.h index 7760c188b..fc6a5f4ee 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2012-2016 by John "JTE" Muniz. -// Copyright (C) 2012-2020 by Sonic Team Junior. +// Copyright (C) 2012-2022 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -12,115 +12,139 @@ #include "r_defs.h" #include "d_player.h" -#include "lua_hudlib_drawlist.h" +#include "s_sound.h" +#include "d_event.h" -enum hook { - hook_NetVars=0, - hook_MapChange, - hook_MapLoad, - hook_PlayerJoin, - hook_PreThinkFrame, - hook_ThinkFrame, - hook_PostThinkFrame, - hook_MobjSpawn, - hook_MobjCollide, - hook_MobjLineCollide, - hook_MobjMoveCollide, - hook_TouchSpecial, - hook_MobjFuse, - hook_MobjThinker, - hook_BossThinker, - hook_ShouldDamage, - hook_MobjDamage, - hook_MobjDeath, - hook_BossDeath, - hook_MobjRemoved, - hook_JumpSpecial, - hook_AbilitySpecial, - hook_SpinSpecial, - hook_JumpSpinSpecial, - hook_BotTiccmd, - hook_BotAI, - hook_BotRespawn, - hook_LinedefExecute, - hook_PlayerMsg, - hook_HurtMsg, - hook_PlayerSpawn, - hook_ShieldSpawn, - hook_ShieldSpecial, - hook_MobjMoveBlocked, - hook_MapThingSpawn, - hook_FollowMobj, - hook_PlayerCanDamage, - hook_PlayerQuit, - hook_MusicChange, - hook_TeamSwitch, - hook_ViewpointSwitch, - hook_PlayerThink, - hook_ShouldJingleContinue, - hook_GameQuit, - hook_PlayerCmd, +/* +Do you know what an 'X Macro' is? Such a macro is called over each element of +a list and expands the input. I use it for the hook lists because both an enum +and array of hook names need to be kept in order. The X Macro handles this +automatically. +*/ - // SRB2Kart - hook_IntermissionThinker, - hook_VoteThinker, +#define MOBJ_HOOK_LIST(X) \ + X (MobjSpawn),/* P_SpawnMobj */\ + X (MobjCollide),/* PIT_CheckThing */\ + X (MobjLineCollide),/* ditto */\ + X (MobjMoveCollide),/* tritto */\ + X (TouchSpecial),/* P_TouchSpecialThing */\ + X (MobjFuse),/* when mobj->fuse runs out */\ + X (MobjThinker),/* P_MobjThinker, P_SceneryThinker */\ + X (BossThinker),/* P_GenericBossThinker */\ + X (ShouldDamage),/* P_DamageMobj (Should mobj take damage?) */\ + X (MobjDamage),/* P_DamageMobj (Mobj actually takes damage!) */\ + X (MobjDeath),/* P_KillMobj */\ + X (BossDeath),/* A_BossDeath */\ + X (MobjRemoved),/* P_RemoveMobj */\ + X (BotRespawn),/* B_CheckRespawn */\ + X (MobjMoveBlocked),/* P_XYMovement (when movement is blocked) */\ + X (MapThingSpawn),/* P_SpawnMapThing */\ + X (FollowMobj),/* P_PlayerAfterThink Smiles mobj-following */\ - hook_MAX // last hook -}; -extern const char *const hookNames[]; +#define HOOK_LIST(X) \ + X (NetVars),/* add to archive table (netsave) */\ + X (MapChange),/* (before map load) */\ + X (MapLoad),\ + X (PlayerJoin),/* Got_AddPlayer */\ + X (PreThinkFrame)/* frame (before mobj and player thinkers) */,\ + X (ThinkFrame),/* frame (after mobj and player thinkers) */\ + X (PostThinkFrame),/* frame (at end of tick, ie after overlays, precipitation, specials) */\ + X (JumpSpecial),/* P_DoJumpStuff (Any-jumping) */\ + X (AbilitySpecial),/* P_DoJumpStuff (Double-jumping) */\ + X (SpinSpecial),/* P_DoSpinAbility (Spin button effect) */\ + X (JumpSpinSpecial),/* P_DoJumpStuff (Spin button effect (mid-air)) */\ + X (BotTiccmd),/* B_BuildTiccmd */\ + X (PlayerMsg),/* chat messages */\ + X (HurtMsg),/* imhurttin */\ + X (PlayerSpawn),/* G_SpawnPlayer */\ + X (ShieldSpawn),/* P_SpawnShieldOrb */\ + X (ShieldSpecial),/* shield abilities */\ + X (PlayerCanDamage),/* P_PlayerCanDamage */\ + X (PlayerQuit),\ + X (IntermissionThinker),/* Y_Ticker */\ + X (TeamSwitch),/* team switching in... uh... *what* speak, spit it the fuck out */\ + X (ViewpointSwitch),/* spy mode (no trickstabs) */\ + X (SeenPlayer),/* MT_NAMECHECK */\ + X (PlayerThink),/* P_PlayerThink */\ + X (GameQuit),\ + X (PlayerCmd),/* building the player's ticcmd struct (Ported from SRB2Kart) */\ + X (MusicChange),\ + X (PlayerHeight),/* override player height */\ + X (PlayerCanEnterSpinGaps),\ + X (KeyDown),\ + X (KeyUp),\ + +#define STRING_HOOK_LIST(X) \ + X (BotAI),/* B_BuildTailsTiccmd by skin name */\ + X (LinedefExecute),\ + X (ShouldJingleContinue),/* should jingle of the given music continue playing */\ + +#define HUD_HOOK_LIST(X) \ + X (game),\ + X (scores),/* emblems/multiplayer list */\ + X (title),/* titlescreen */\ + X (titlecard),\ + X (intermission),\ + +/* +I chose to access the hook enums through a macro as well. This could provide +a hint to lookup the macro's definition instead of the enum's definition. +(Since each enumeration is not defined in the source code, but by the list +macros above, it is not greppable.) The name passed to the macro can also be +grepped and found in the lists above. +*/ + +#define MOBJ_HOOK(name) mobjhook_ ## name +#define HOOK(name) hook_ ## name +#define HUD_HOOK(name) hudhook_ ## name +#define STRING_HOOK(name) stringhook_ ## name + +#define ENUM(X) enum { X ## _LIST (X) X(MAX) } + +ENUM (MOBJ_HOOK); +ENUM (HOOK); +ENUM (HUD_HOOK); +ENUM (STRING_HOOK); + +#undef ENUM + +/* dead simple, LUA_HOOK(GameQuit) */ +#define LUA_HOOK(type) LUA_HookVoid(HOOK(type)) +#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type)) extern boolean hook_cmd_running; -extern int hook_defrosting; -void LUAh_MapChange(INT16 mapnumber); // Hook for map change (before load) -void LUAh_MapLoad(void); // Hook for map load -void LUAh_PlayerJoin(int playernum); // Hook for Got_AddPlayer -void LUAh_PreThinkFrame(void); // Hook for frame (before mobj and player thinkers) -void LUAh_ThinkFrame(void); // Hook for frame (after mobj and player thinkers) -void LUAh_PostThinkFrame(void); // Hook for frame (at end of tick, ie after overlays, precipitation, specials) -boolean LUAh_MobjHook(mobj_t *mo, enum hook which); -boolean LUAh_PlayerHook(player_t *plr, enum hook which); -#define LUAh_MobjSpawn(mo) LUAh_MobjHook(mo, hook_MobjSpawn) // Hook for P_SpawnMobj by mobj type -UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which); -UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which); -#define LUAh_MobjCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjCollide) // Hook for PIT_CheckThing by (thing) mobj type -#define LUAh_MobjLineCollide(thing, line) LUAh_MobjLineCollideHook(thing, line, hook_MobjLineCollide) // Hook for PIT_CheckThing by (thing) mobj type -#define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type -boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type -#define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type -boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type -#define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type -UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Should mobj take damage?) -boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) -boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for P_KillMobj by mobj type -#define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type -#define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type -#define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping) -#define LUAh_AbilitySpecial(player) LUAh_PlayerHook(player, hook_AbilitySpecial) // Hook for P_DoJumpStuff (Double-jumping) -#define LUAh_SpinSpecial(player) LUAh_PlayerHook(player, hook_SpinSpecial) // Hook for P_DoSpinAbility (Spin button effect) -#define LUAh_JumpSpinSpecial(player) LUAh_PlayerHook(player, hook_JumpSpinSpecial) // Hook for P_DoJumpStuff (Spin button effect (mid-air)) -boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd); // Hook for B_BuildTiccmd -boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name -boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails); // Hook for B_CheckRespawn -boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors -boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg, int mute); // Hook for chat messages -boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for hurt messages -#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer -void LUAh_PlayerQuit(player_t *plr, kickreason_t reason); // Hook for player quitting -boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, - UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms); // Hook for music changes +void LUA_HookVoid(int hook); +void LUA_HookHUD(int hook); -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Allows to write to player cmd before the game does anything with them. +int LUA_HookMobj(mobj_t *, int hook); +int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook); +void LUA_HookInt(INT32 integer, int hook); +void LUA_HookBool(boolean value, int hook); +int LUA_HookPlayer(player_t *, int hook); +int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook); +int LUA_HookKey(event_t *event, int hook); // Hooks for key events -void LUAh_IntermissionThinker(void); // Hook for Y_Ticker -void LUAh_VoteThinker(void); // Hook for Y_VoteTicker - -#define LUAh_MobjMoveBlocked(mo) LUAh_MobjHook(mo, hook_MobjMoveBlocked) // Hook for P_XYMovement (when movement is blocked) -boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type -boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj); // Hook for P_PlayerAfterThink Smiles mobj-following -UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj); // Hook for P_PlayerCanDamage -boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh.... -UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode -#define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink -boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname); // Hook for whether a jingle of the given music should continue playing -void LUAh_GameQuit(boolean quitting); // Hook for game quitting +void LUA_HookThinkFrame(void); +int LUA_HookMobjLineCollide(mobj_t *, line_t *); +int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher); +int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); +int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); +int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); +int LUA_HookMobjMoveBlocked(mobj_t *, mobj_t *, line_t *); +int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); +void LUA_HookLinedefExecute(line_t *, mobj_t *, sector_t *); +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg); +int LUA_HookHurtMsg(player_t *, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); +int LUA_HookMapThingSpawn(mobj_t *, mapthing_t *); +int LUA_HookFollowMobj(player_t *, mobj_t *); +int LUA_HookPlayerCanDamage(player_t *, mobj_t *); +void LUA_HookPlayerQuit(player_t *, kickreason_t); +int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); +int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); +int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend); +int LUA_HookShouldJingleContinue(player_t *, const char *musname); +int LUA_HookPlayerCmd(player_t *, ticcmd_t *); +int LUA_HookMusicChange(const char *oldname, struct MusicChange *); +fixed_t LUA_HookPlayerHeight(player_t *player); +int LUA_HookPlayerCanEnterSpinGaps(player_t *player); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 771182a59..dc5069319 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2012-2016 by John "JTE" Muniz. -// Copyright (C) 2012-2020 by Sonic Team Junior. +// Copyright (C) 2012-2022 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -27,1939 +27,1150 @@ #include "d_netcmd.h" // for cv_perfstats #include "i_system.h" // I_GetPreciseTime -static UINT8 hooksAvailable[(hook_MAX/8)+1]; +/* ========================================================================= + ABSTRACTION + ========================================================================= */ -const char *const hookNames[hook_MAX+1] = { - "NetVars", - "MapChange", - "MapLoad", - "PlayerJoin", - "PreThinkFrame", - "ThinkFrame", - "PostThinkFrame", - "MobjSpawn", - "MobjCollide", - "MobjLineCollide", - "MobjMoveCollide", - "TouchSpecial", - "MobjFuse", - "MobjThinker", - "BossThinker", - "ShouldDamage", - "MobjDamage", - "MobjDeath", - "BossDeath", - "MobjRemoved", - "JumpSpecial", - "AbilitySpecial", - "SpinSpecial", - "JumpSpinSpecial", - "BotTiccmd", - "BotAI", - "BotRespawn", - "LinedefExecute", - "PlayerMsg", - "HurtMsg", - "PlayerSpawn", - "ShieldSpawn", - "ShieldSpecial", - "MobjMoveBlocked", - "MapThingSpawn", - "FollowMobj", - "PlayerCanDamage", - "PlayerQuit", - "MusicChange", - "TeamSwitch", - "ViewpointSwitch", - "PlayerThink", - "ShouldJingleContinue", - "GameQuit", - "PlayerCmd", +#define LIST(id, M) \ + static const char * const id [] = { M (TOSTR) NULL } - // SRB2Kart - "IntermissionThinker", - "VoteThinker", +LIST (mobjHookNames, MOBJ_HOOK_LIST); +LIST (hookNames, HOOK_LIST); +LIST (hudHookNames, HUD_HOOK_LIST); +LIST (stringHookNames, STRING_HOOK_LIST); - NULL -}; +#undef LIST -// Hook metadata -struct hook_s +typedef struct { + int numHooks; + int *ids; +} hook_t; + +typedef struct { + int numGeneric; + int ref; +} stringhook_t; + +static hook_t hookIds[HOOK(MAX)]; +static hook_t hudHookIds[HUD_HOOK(MAX)]; +static hook_t mobjHookIds[NUMMOBJTYPES][MOBJ_HOOK(MAX)]; + +// Lua tables are used to lookup string hook ids. +static stringhook_t stringHooks[STRING_HOOK(MAX)]; + +// This will be indexed by hook id, the value of which fetches the registry. +static int * hookRefs; +static int nextid; + +// After a hook errors once, don't print the error again. +static UINT8 * hooksErrored; + +static int errorRef; + +static boolean mobj_hook_available(int hook_type, mobjtype_t mobj_type) { - struct hook_s *next; - enum hook type; - UINT16 id; - union { - mobjtype_t mt; - char *str; - } s; - boolean error; -}; -typedef struct hook_s* hook_p; + return + ( + mobjHookIds [MT_NULL] [hook_type].numHooks > 0 || + mobjHookIds[mobj_type][hook_type].numHooks > 0 + ); +} -#define FMT_HOOKID "hook_%d" +static int hook_in_list +( + const char * const name, + const char * const * const list +){ + int type; -// For each mobj type, a linked list to its thinker and collision hooks. -// That way, we don't have to iterate through all the hooks. -// We could do that with all other mobj hooks, but it would probably just be -// a waste of memory since they are only called occasionally. Probably... -static hook_p mobjthinkerhooks[NUMMOBJTYPES]; -static hook_p mobjcollidehooks[NUMMOBJTYPES]; + for (type = 0; list[type] != NULL; ++type) + { + if (strcmp(name, list[type]) == 0) + break; + } -// For each mobj type, a linked list for other mobj hooks -static hook_p mobjhooks[NUMMOBJTYPES]; + return type; +} -// A linked list for player hooks -static hook_p playerhooks; - -// A linked list for linedef executor hooks -static hook_p linedefexecutorhooks; - -// For other hooks, a unique linked list -hook_p roothook; - -static void PushHook(lua_State *L, hook_p hookp) +static void get_table(lua_State *L) { - lua_pushfstring(L, FMT_HOOKID, hookp->id); - lua_gettable(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, -1); + lua_rawget(L, -3); + + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_createtable(L, 1, 0); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, -5); + } + + lua_remove(L, -2); +} + +static void add_hook_to_table(lua_State *L, int n) +{ + lua_pushnumber(L, nextid); + lua_rawseti(L, -2, n); +} + +static void add_string_hook(lua_State *L, int type) +{ + stringhook_t * hook = &stringHooks[type]; + + char * string = NULL; + + switch (type) + { + case STRING_HOOK(BotAI): + case STRING_HOOK(ShouldJingleContinue): + if (lua_isstring(L, 3)) + { // lowercase copy + string = Z_StrDup(lua_tostring(L, 3)); + strlwr(string); + } + break; + + case STRING_HOOK(LinedefExecute): + string = Z_StrDup(luaL_checkstring(L, 3)); + strupr(string); + break; + } + + if (hook->ref > 0) + lua_getref(L, hook->ref); + else + { + lua_newtable(L); + lua_pushvalue(L, -1); + hook->ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + if (string) + { + lua_pushstring(L, string); + get_table(L); + add_hook_to_table(L, 1 + lua_objlen(L, -1)); + } + else + add_hook_to_table(L, ++hook->numGeneric); +} + +static void add_hook(hook_t *map) +{ + Z_Realloc(map->ids, (map->numHooks + 1) * sizeof *map->ids, + PU_STATIC, &map->ids); + map->ids[map->numHooks++] = nextid; +} + +static void add_mobj_hook(lua_State *L, int hook_type) +{ + mobjtype_t mobj_type = luaL_optnumber(L, 3, MT_NULL); + + luaL_argcheck(L, mobj_type < NUMMOBJTYPES, 3, "invalid mobjtype_t"); + + add_hook(&mobjHookIds[mobj_type][hook_type]); +} + +static void add_hud_hook(lua_State *L, int idx) +{ + add_hook(&hudHookIds[luaL_checkoption(L, + idx, "game", hudHookNames)]); +} + +static void add_hook_ref(lua_State *L, int idx) +{ + if (!(nextid & 7)) + { + Z_Realloc(hooksErrored, + BIT_ARRAY_SIZE (nextid + 1) * sizeof *hooksErrored, + PU_STATIC, &hooksErrored); + hooksErrored[nextid >> 3] = 0; + } + + Z_Realloc(hookRefs, (nextid + 1) * sizeof *hookRefs, PU_STATIC, &hookRefs); + + // set the hook function in the registry. + lua_pushvalue(L, idx); + hookRefs[nextid++] = luaL_ref(L, LUA_REGISTRYINDEX); } // Takes hook, function, and additional arguments (mobj type to act on, etc.) static int lib_addHook(lua_State *L) { - static struct hook_s hook = {NULL, 0, 0, {0}, false}; - static UINT32 nextid; - hook_p hookp, *lastp; - - hook.type = luaL_checkoption(L, 1, NULL, hookNames); - lua_remove(L, 1); - - luaL_checktype(L, 1, LUA_TFUNCTION); + const char * name; + int type; if (!lua_lumploading) return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); - switch(hook.type) + name = luaL_checkstring(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + + /* this is a very special case */ + if (( type = hook_in_list(name, stringHookNames) ) < STRING_HOOK(MAX)) { - // Take a mobjtype enum which this hook is specifically for. - case hook_MobjSpawn: - case hook_MobjCollide: - case hook_MobjLineCollide: - case hook_MobjMoveCollide: - case hook_TouchSpecial: - case hook_MobjFuse: - case hook_MobjThinker: - case hook_BossThinker: - case hook_ShouldDamage: - case hook_MobjDamage: - case hook_MobjDeath: - case hook_BossDeath: - case hook_MobjRemoved: - case hook_HurtMsg: - case hook_MobjMoveBlocked: - case hook_MapThingSpawn: - case hook_FollowMobj: - hook.s.mt = MT_NULL; - if (lua_isnumber(L, 2)) - hook.s.mt = lua_tonumber(L, 2); - luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t"); - break; - case hook_BotAI: - case hook_ShouldJingleContinue: - hook.s.str = NULL; - if (lua_isstring(L, 2)) - { // lowercase copy - hook.s.str = Z_StrDup(lua_tostring(L, 2)); - strlwr(hook.s.str); - } - break; - case hook_LinedefExecute: // Linedef executor functions - hook.s.str = Z_StrDup(luaL_checkstring(L, 2)); - strupr(hook.s.str); - break; - default: - break; + add_string_hook(L, type); } - lua_settop(L, 1); // lua stack contains only the function now. - - hooksAvailable[hook.type/8] |= 1<<(hook.type%8); - - // set hook.id to the highest id + 1 - hook.id = nextid++; - - // Special cases for some hook types (see the comments above mobjthinkerhooks declaration) - switch(hook.type) + else if (( type = hook_in_list(name, mobjHookNames) ) < MOBJ_HOOK(MAX)) { - case hook_MobjThinker: - lastp = &mobjthinkerhooks[hook.s.mt]; - break; - case hook_MobjCollide: - case hook_MobjLineCollide: - case hook_MobjMoveCollide: - lastp = &mobjcollidehooks[hook.s.mt]; - break; - case hook_MobjSpawn: - case hook_TouchSpecial: - case hook_MobjFuse: - case hook_BossThinker: - case hook_ShouldDamage: - case hook_MobjDamage: - case hook_MobjDeath: - case hook_BossDeath: - case hook_MobjRemoved: - case hook_MobjMoveBlocked: - case hook_MapThingSpawn: - case hook_FollowMobj: - lastp = &mobjhooks[hook.s.mt]; - break; - case hook_JumpSpecial: - case hook_AbilitySpecial: - case hook_SpinSpecial: - case hook_JumpSpinSpecial: - case hook_PlayerSpawn: - case hook_PlayerCanDamage: - case hook_TeamSwitch: - case hook_ViewpointSwitch: - case hook_ShieldSpawn: - case hook_ShieldSpecial: - case hook_PlayerThink: - lastp = &playerhooks; - break; - case hook_LinedefExecute: - lastp = &linedefexecutorhooks; - break; - default: - lastp = &roothook; - break; + add_mobj_hook(L, type); + } + else if (( type = hook_in_list(name, hookNames) ) < HOOK(MAX)) + { + add_hook(&hookIds[type]); + } + else if (strcmp(name, "HUD") == 0) + { + add_hud_hook(L, 3); + } + else + { + return luaL_argerror(L, 1, lua_pushfstring(L, "invalid hook " LUA_QS, name)); } - // iterate the hook metadata structs - // set lastp to the last hook struct's "next" pointer. - for (hookp = *lastp; hookp; hookp = hookp->next) - lastp = &hookp->next; - // allocate a permanent memory struct to stuff hook. - hookp = ZZ_Alloc(sizeof(struct hook_s)); - memcpy(hookp, &hook, sizeof(struct hook_s)); - // tack it onto the end of the linked list. - *lastp = hookp; + add_hook_ref(L, 2);/* the function */ - // set the hook function in the registry. - lua_pushfstring(L, FMT_HOOKID, hook.id); - lua_pushvalue(L, 1); - lua_settable(L, LUA_REGISTRYINDEX); return 0; } int LUA_HookLib(lua_State *L) { - memset(hooksAvailable,0,sizeof(UINT8[(hook_MAX/8)+1])); - roothook = NULL; + lua_pushcfunction(L, LUA_GetErrorMessage); + errorRef = luaL_ref(L, LUA_REGISTRYINDEX); + lua_register(L, "addHook", lib_addHook); + return 0; } -boolean LUAh_MobjHook(mobj_t *mo, enum hook which) +/* TODO: remove in next backwards incompatible release */ +int lib_hudadd(lua_State *L);/* yeah compiler */ +int lib_hudadd(lua_State *L) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return false; + if (!lua_lumploading) + return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); - I_Assert(mo->type < NUMMOBJTYPES); + luaL_checktype(L, 1, LUA_TFUNCTION); - if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) - return false; + add_hud_hook(L, 2); + add_hook_ref(L, 1); + return 0; +} + +typedef struct Hook_State Hook_State; +typedef void (*Hook_Callback)(Hook_State *); + +struct Hook_State { + INT32 status;/* return status to calling function */ + void * userdata; + int hook_type; + mobjtype_t mobj_type;/* >0 if mobj hook */ + const char * string;/* used to fetch table, ran first if set */ + int top;/* index of last argument passed to hook */ + int id;/* id to fetch ref */ + int values;/* num arguments passed to hook */ + int results;/* num values returned by hook */ + Hook_Callback results_handler;/* callback when hook successfully returns */ +}; + +enum { + EINDEX = 1,/* error handler */ + SINDEX = 2,/* string itself is pushed in case of string hook */ +}; + +static void push_error_handler(void) +{ + lua_getref(gL, errorRef); +} + +/* repush hook string */ +static void push_string(void) +{ + lua_pushvalue(gL, SINDEX); +} + +static boolean begin_hook_values(Hook_State *hook) +{ + hook->top = lua_gettop(gL); + return true; +} + +static void start_hook_stack(void) +{ lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); + push_error_handler(); +} - // Look for all generic mobj hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) +static boolean init_hook_type +( + Hook_State * hook, + int status, + int hook_type, + mobjtype_t mobj_type, + const char * string, + int nonzero +){ + hook->status = status; + + if (nonzero) { - if (hookp->type != which) - continue; + start_hook_stack(); + hook->hook_type = hook_type; + hook->mobj_type = mobj_type; + hook->string = string; + return begin_hook_values(hook); + } + else + return false; +} - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; +static boolean prepare_hook +( + Hook_State * hook, + int default_status, + int hook_type +){ + return init_hook_type(hook, default_status, + hook_type, 0, NULL, + hookIds[hook_type].numHooks); +} + +static boolean prepare_mobj_hook +( + Hook_State * hook, + int default_status, + int hook_type, + mobjtype_t mobj_type +){ +#ifdef PARANOIA + if (mobj_type == MT_NULL) + I_Error("MT_NULL has been passed to a mobj hook\n"); +#endif + return init_hook_type(hook, default_status, + hook_type, mobj_type, NULL, + mobj_hook_available(hook_type, mobj_type)); +} + +static boolean prepare_string_hook +( + Hook_State * hook, + int default_status, + int hook_type, + const char * string +){ + if (init_hook_type(hook, default_status, + hook_type, 0, string, + stringHooks[hook_type].ref)) + { + lua_pushstring(gL, string); + return begin_hook_values(hook); + } + else + return false; +} + +static void init_hook_call +( + Hook_State * hook, + int results, + Hook_Callback results_handler +){ + const int top = lua_gettop(gL); + hook->values = (top - hook->top); + hook->top = top; + hook->results = results; + hook->results_handler = results_handler; +} + +static void get_hook(Hook_State *hook, const int *ids, int n) +{ + hook->id = ids[n]; + lua_getref(gL, hookRefs[hook->id]); +} + +static void get_hook_from_table(Hook_State *hook, int n) +{ + lua_rawgeti(gL, -1, n); + hook->id = lua_tonumber(gL, -1); + lua_pop(gL, 1); + lua_getref(gL, hookRefs[hook->id]); +} + +static int call_single_hook_no_copy(Hook_State *hook) +{ + if (lua_pcall(gL, hook->values, hook->results, EINDEX) == 0) + { + if (hook->results > 0) + { + (*hook->results_handler)(hook); + lua_pop(gL, hook->results); + } + } + else + { + /* print the error message once */ + if (cv_debug & DBG_LUA || !in_bit_array(hooksErrored, hook->id)) + { + CONS_Alert(CONS_WARNING, "%s\n", lua_tostring(gL, -1)); + set_bit_array(hooksErrored, hook->id); } - if (lua_toboolean(gL, -1)) - hooked = true; lua_pop(gL, 1); } - for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; + return 1; +} - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } +static int call_single_hook(Hook_State *hook) +{ + int i; + + for (i = -(hook->values) + 1; i <= 0; ++i) + lua_pushvalue(gL, hook->top + i); + + return call_single_hook_no_copy(hook); +} + +static int call_hook_table_for(Hook_State *hook, int n) +{ + int k; + + for (k = 1; k <= n; ++k) + { + get_hook_from_table(hook, k); + call_single_hook(hook); + } + + return n; +} + +static int call_hook_table(Hook_State *hook) +{ + return call_hook_table_for(hook, lua_objlen(gL, -1)); +} + +static int call_mapped(Hook_State *hook, const hook_t *map) +{ + int k; + + for (k = 0; k < map->numHooks; ++k) + { + get_hook(hook, map->ids, k); + call_single_hook(hook); + } + + return map->numHooks; +} + +static int call_string_hooks(Hook_State *hook) +{ + const stringhook_t *map = &stringHooks[hook->hook_type]; + + int calls = 0; + + lua_getref(gL, map->ref); + + /* call generic string hooks first */ + calls += call_hook_table_for(hook, map->numGeneric); + + push_string(); + lua_rawget(gL, -2); + calls += call_hook_table(hook); + + return calls; +} + +static int call_mobj_type_hooks(Hook_State *hook, mobjtype_t mobj_type) +{ + return call_mapped(hook, &mobjHookIds[mobj_type][hook->hook_type]); +} + +static int call_hooks +( + Hook_State * hook, + int results, + Hook_Callback results_handler +){ + int calls = 0; + + init_hook_call(hook, results, results_handler); + + if (hook->string) + { + calls += call_string_hooks(hook); + } + else if (hook->mobj_type > 0) + { + /* call generic mobj hooks first */ + calls += call_mobj_type_hooks(hook, MT_NULL); + calls += call_mobj_type_hooks(hook, hook->mobj_type); + + ps_lua_mobjhooks.value.i += calls; + } + else + calls += call_mapped(hook, &hookIds[hook->hook_type]); + + lua_settop(gL, 0); + + return calls; +} + +/* ========================================================================= + COMMON RESULT HANDLERS + ========================================================================= */ + +#define res_none NULL + +static void res_true(Hook_State *hook) +{ + if (lua_toboolean(gL, -1)) + hook->status = true; +} + +static void res_false(Hook_State *hook) +{ + if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1)) + hook->status = false; +} + +static void res_force(Hook_State *hook) +{ + if (!lua_isnil(gL, -1)) + { if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + hook->status = 1; // Force yes + else + hook->status = 2; // Force no } - - lua_settop(gL, 0); - return hooked; } -boolean LUAh_PlayerHook(player_t *plr, enum hook which) +/* ========================================================================= + GENERALISED HOOKS + ========================================================================= */ + +int LUA_HookMobj(mobj_t *mobj, int hook_type) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = playerhooks; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_mobj_hook(&hook, false, hook_type, mobj->type)) { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, plr, META_PLAYER); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 1, res_true); } - - lua_settop(gL, 0); - return hooked; + return hook.status; } -// Hook for map change (before load) -void LUAh_MapChange(INT16 mapnumber) +int LUA_Hook2Mobj(mobj_t *t1, mobj_t *t2, int hook_type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - lua_pushinteger(gL, mapnumber); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, hook_type, t1->type)) { - if (hookp->type != hook_MapChange) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + LUA_PushUserdata(gL, t1, META_MOBJ); + LUA_PushUserdata(gL, t2, META_MOBJ); + call_hooks(&hook, 1, res_force); } - - lua_settop(gL, 0); + return hook.status; } -// Hook for map load -void LUAh_MapLoad(void) +void LUA_HookVoid(int type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_MapLoad/8] & (1<<(hook_MapLoad%8)))) - return; + Hook_State hook; + if (prepare_hook(&hook, 0, type)) + call_hooks(&hook, 0, res_none); +} - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - lua_pushinteger(gL, gamemap); - - for (hookp = roothook; hookp; hookp = hookp->next) +void LUA_HookInt(INT32 number, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) { - if (hookp->type != hook_MapLoad) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + lua_pushinteger(gL, number); + call_hooks(&hook, 0, res_none); } - - lua_settop(gL, 0); } -// Hook for Got_AddPlayer -void LUAh_PlayerJoin(int playernum) +void LUA_HookBool(boolean value, int hook_type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PlayerJoin/8] & (1<<(hook_PlayerJoin%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - lua_pushinteger(gL, playernum); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) { - if (hookp->type != hook_PlayerJoin) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + lua_pushboolean(gL, value); + call_hooks(&hook, 0, res_none); } - - lua_settop(gL, 0); } -// Hook for frame (before mobj and player thinkers) -void LUAh_PreThinkFrame(void) +int LUA_HookPlayer(player_t *player, int hook_type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PreThinkFrame/8] & (1<<(hook_PreThinkFrame%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) { - if (hookp->type != hook_PreThinkFrame) - continue; - - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, res_true); } - - lua_pop(gL, 1); // Pop error handler + return hook.status; } -// Hook for frame (after mobj and player thinkers) -void LUAh_ThinkFrame(void) +int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) { - hook_p hookp; + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, cmd, META_TICCMD); + + if (hook_type == HOOK(PlayerCmd)) + hook_cmd_running = true; + + call_hooks(&hook, 1, res_true); + + if (hook_type == HOOK(PlayerCmd)) + hook_cmd_running = false; + } + return hook.status; +} + +int LUA_HookKey(event_t *event, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) + { + LUA_PushUserdata(gL, event, META_KEYEVENT); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +void LUA_HookHUD(int hook_type) +{ + const hook_t * map = &hudHookIds[hook_type]; + Hook_State hook; + if (map->numHooks > 0) + { + start_hook_stack(); + begin_hook_values(&hook); + + LUA_SetHudHook(hook_type); + + hud_running = true; // local hook + init_hook_call(&hook, 0, res_none); + call_mapped(&hook, map); + hud_running = false; + } +} + +/* ========================================================================= + SPECIALIZED HOOKS + ========================================================================= */ + +void LUA_HookThinkFrame(void) +{ + const int type = HOOK(ThinkFrame); + // variables used by perf stats int hook_index = 0; precise_t time_taken = 0; - if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8)))) - return; - lua_pushcfunction(gL, LUA_GetErrorMessage); + Hook_State hook; - for (hookp = roothook; hookp; hookp = hookp->next) + const hook_t * map = &hookIds[type]; + int k; + + if (prepare_hook(&hook, 0, type)) { - if (hookp->type != hook_ThinkFrame) - continue; + init_hook_call(&hook, 0, res_none); - if (cv_perfstats.value == PS_THINKFRAME) - time_taken = I_GetPreciseTime(); - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - if (cv_perfstats.value == PS_THINKFRAME) + for (k = 0; k < map->numHooks; ++k) { - lua_Debug ar; - time_taken = I_GetPreciseTime() - time_taken; - // we need the function, let's just retrieve it again - PushHook(gL, hookp); - lua_getinfo(gL, ">S", &ar); - PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); - hook_index++; - } - } + get_hook(&hook, map->ids, k); - lua_pop(gL, 1); // Pop error handler -} - -// Hook for frame (at end of tick, ie after overlays, precipitation, specials) -void LUAh_PostThinkFrame(void) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PostThinkFrame/8] & (1<<(hook_PostThinkFrame%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PostThinkFrame) - continue; - - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - } - - lua_pop(gL, 1); // Pop error handler -} - -// Hook for mobj collisions -UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) -{ - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return 0; - - I_Assert(thing1->type < NUMMOBJTYPES); - - if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing1->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj collision hooks - for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing1, META_MOBJ); - LUA_PushUserdata(gL, thing2, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing1, META_MOBJ); - LUA_PushUserdata(gL, thing2, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldCollide; -} - -UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which) -{ - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return 0; - - I_Assert(thing->type < NUMMOBJTYPES); - - if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj collision hooks - for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing, META_MOBJ); - LUA_PushUserdata(gL, line, META_LINE); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjcollidehooks[thing->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing, META_MOBJ); - LUA_PushUserdata(gL, line, META_LINE); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldCollide; -} - -// Hook for mobj thinkers -boolean LUAh_MobjThinker(mobj_t *mo) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8)))) - return false; - - I_Assert(mo->type < NUMMOBJTYPES); - - if (!(mobjthinkerhooks[MT_NULL] || mobjthinkerhooks[mo->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj thinker hooks - for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next) - { - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next) - { - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_TouchSpecialThing by mobj type -boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_TouchSpecial/8] & (1<<(hook_TouchSpecial%8)))) - return false; - - I_Assert(special->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[special->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic touch special hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TouchSpecial) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, special, META_MOBJ); - LUA_PushUserdata(gL, toucher, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TouchSpecial) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, special, META_MOBJ); - LUA_PushUserdata(gL, toucher, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_DamageMobj by mobj type (Should mobj take damage?) -UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) -{ - hook_p hookp; - UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_ShouldDamage/8] & (1<<(hook_ShouldDamage%8)))) - return 0; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic should damage hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldDamage) - continue; - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldDamage; -} - -// Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) -boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8)))) - return false; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj damage hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_KillMobj by mobj type -boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MobjDeath/8] & (1<<(hook_MobjDeath%8)))) - return false; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj death hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDeath) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDeath) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for B_BuildTiccmd -boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_BotTiccmd/8] & (1<<(hook_BotTiccmd%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotTiccmd) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, bot, META_PLAYER); - LUA_PushUserdata(gL, cmd, META_TICCMD); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for B_BuildTailsTiccmd by skin name -boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) -{ -#if 1 - (void)sonic; - (void)tails; - (void)cmd; - return false; -#else - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotAI - || (hookp->s.str && strcmp(hookp->s.str, ((skin_t*)tails->skin)->name))) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, sonic, META_MOBJ); - LUA_PushUserdata(gL, tails, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 8, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - - // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. - if (lua_istable(gL, 2+1)) { - boolean forward=false, backward=false, left=false, right=false, strafeleft=false, straferight=false, jump=false, spin=false; -#define CHECKFIELD(field) \ - lua_getfield(gL, 2+1, #field);\ - if (lua_toboolean(gL, -1))\ - field = true;\ - lua_pop(gL, 1); - - CHECKFIELD(forward) - CHECKFIELD(backward) - CHECKFIELD(left) - CHECKFIELD(right) - CHECKFIELD(strafeleft) - CHECKFIELD(straferight) - CHECKFIELD(jump) - CHECKFIELD(spin) -#undef CHECKFIELD - B_KeysToTiccmd(tails, cmd, forward, backward, left, right, strafeleft, straferight, jump, spin); - } else - B_KeysToTiccmd(tails, cmd, lua_toboolean(gL, 2+1), lua_toboolean(gL, 2+2), lua_toboolean(gL, 2+3), lua_toboolean(gL, 2+4), lua_toboolean(gL, 2+5), lua_toboolean(gL, 2+6), lua_toboolean(gL, 2+7), lua_toboolean(gL, 2+8)); - - lua_pop(gL, 8); - hooked = true; - } - - lua_settop(gL, 0); - return hooked; -#endif -} - -// Hook for B_CheckRespawn -boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails) -{ - hook_p hookp; - UINT8 shouldRespawn = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_BotRespawn/8] & (1<<(hook_BotRespawn%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotRespawn) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, sonic, META_MOBJ); - LUA_PushUserdata(gL, tails, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldRespawn = 1; // Force yes - else - shouldRespawn = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldRespawn; -} - -// Hook for linedef executors -boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_LinedefExecute/8] & (1<<(hook_LinedefExecute%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next) - { - if (strcmp(hookp->s.str, line->stringargs[0])) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, line, META_LINE); - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, sector, META_SECTOR); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - hooked = true; - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for player chat -// Added the "mute" field. It's set to true if the message was supposed to be eaten by spam protection. -// But for netgame consistency purposes, this hook is ran first reguardless, so this boolean allows for modders to adapt if they so desire. -boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg, int mute) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_PlayerMsg/8] & (1<<(hook_PlayerMsg%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerMsg) - continue; - - if (lua_gettop(gL) == 1) - { - if (lua_gettop(gL) == 0) + if (cv_perfstats.value == 3) { - LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player - if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c - lua_pushinteger(gL, 3); // type - lua_pushnil(gL); // target - } else if (target == -1) { // sayteam - lua_pushinteger(gL, 1); // type - lua_pushnil(gL); // target - } else if (target == 0) { // say - lua_pushinteger(gL, 0); // type - lua_pushnil(gL); // target - } else { // sayto - lua_pushinteger(gL, 2); // type - LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target + lua_pushvalue(gL, -1);/* need the function again */ + time_taken = I_GetPreciseTime(); + } + + call_single_hook(&hook); + + if (cv_perfstats.value == 3) + { + lua_Debug ar; + time_taken = I_GetPreciseTime() - time_taken; + lua_getinfo(gL, ">S", &ar); + PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + hook_index++; + } + } + + lua_settop(gL, 0); + } +} + +int LUA_HookMobjLineCollide(mobj_t *mobj, line_t *line) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, MOBJ_HOOK(MobjLineCollide), mobj->type)) + { + LUA_PushUserdata(gL, mobj, META_MOBJ); + LUA_PushUserdata(gL, line, META_LINE); + call_hooks(&hook, 1, res_force); + } + return hook.status; +} + +int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(TouchSpecial), special->type)) + { + LUA_PushUserdata(gL, special, META_MOBJ); + LUA_PushUserdata(gL, toucher, META_MOBJ); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +static int damage_hook +( + mobj_t *target, + mobj_t *inflictor, + mobj_t *source, + INT32 damage, + UINT8 damagetype, + int hook_type, + Hook_Callback results_handler +){ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, hook_type, target->type)) + { + LUA_PushUserdata(gL, target, META_MOBJ); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + if (hook_type != MOBJ_HOOK(MobjDeath)) + lua_pushinteger(gL, damage); + lua_pushinteger(gL, damagetype); + call_hooks(&hook, 1, results_handler); + } + return hook.status; +} + +int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) +{ + return damage_hook(target, inflictor, source, damage, damagetype, + MOBJ_HOOK(ShouldDamage), res_force); +} + +int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) +{ + return damage_hook(target, inflictor, source, damage, damagetype, + MOBJ_HOOK(MobjDamage), res_true); +} + +int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) +{ + return damage_hook(target, inflictor, source, 0, damagetype, + MOBJ_HOOK(MobjDeath), res_true); +} + +int LUA_HookMobjMoveBlocked(mobj_t *t1, mobj_t *t2, line_t *line) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, MOBJ_HOOK(MobjMoveBlocked), t1->type)) + { + LUA_PushUserdata(gL, t1, META_MOBJ); + LUA_PushUserdata(gL, t2, META_MOBJ); + LUA_PushUserdata(gL, line, META_LINE); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +typedef struct { + mobj_t * tails; + ticcmd_t * cmd; +} BotAI_State; + +static boolean checkbotkey(const char *field) +{ + return lua_toboolean(gL, -1) && strcmp(lua_tostring(gL, -2), field) == 0; +} + +static void res_botai(Hook_State *hook) +{ + BotAI_State *botai = hook->userdata; + + int k[8]; + + int fields = 0; + + // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. + if (lua_istable(gL, -8)) { + lua_pushnil(gL); // key + while (lua_next(gL, -9)) { +#define CHECK(n, f) (checkbotkey(f) ? (k[(n)-1] = 1) : 0) + if ( + CHECK(1, "forward") || CHECK(2, "backward") || + CHECK(3, "left") || CHECK(4, "right") || + CHECK(5, "strafeleft") || CHECK(6, "straferight") || + CHECK(7, "jump") || CHECK(8, "spin") + ){ + if (8 <= ++fields) + { + lua_pop(gL, 2); // pop key and value + break; } - lua_pushstring(gL, msg); // msg - if (mute) - lua_pushboolean(gL, true); // the message was supposed to be eaten by spamprotecc. - else - lua_pushboolean(gL, false); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for hurt messages -boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_HurtMsg/8] & (1<<(hook_HurtMsg%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_HurtMsg - || (hookp->s.mt && !(inflictor && hookp->s.mt == inflictor->type))) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -void LUAh_NetArchiveHook(lua_CFunction archFunc) -{ - hook_p hookp; - int errorhandlerindex; - if (!gL || !(hooksAvailable[hook_NetVars/8] & (1<<(hook_NetVars%8)))) - return; - - // stack: tables - I_Assert(lua_gettop(gL) > 0); - I_Assert(lua_istable(gL, -1)); - - lua_pushcfunction(gL, LUA_GetErrorMessage); - errorhandlerindex = lua_gettop(gL); - - // tables becomes an upvalue of archFunc - lua_pushvalue(gL, -2); - lua_pushcclosure(gL, archFunc, 1); - // stack: tables, archFunc - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_NetVars) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); // archFunc - if (lua_pcall(gL, 1, 0, errorhandlerindex)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - } - - lua_pop(gL, 2); // Pop archFunc and error handler - // stack: tables -} - -boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MapThingSpawn/8] & (1<<(hook_MapThingSpawn%8)))) - return false; - - if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj map thing spawn hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MapThingSpawn) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, mthing, META_MAPTHING); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MapThingSpawn) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, mthing, META_MAPTHING); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_PlayerAfterThink Smiles mobj-following -boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_FollowMobj/8] & (1<<(hook_FollowMobj%8)))) - return 0; - - if (!(mobjhooks[MT_NULL] || mobjhooks[mobj->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj follow item hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_FollowMobj) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, mobj, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[mobj->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_FollowMobj) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, mobj, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_PlayerCanDamage -UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj) -{ - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_PlayerCanDamage/8] & (1<<(hook_PlayerCanDamage%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = playerhooks; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerCanDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, mobj, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldCollide; -} - -void LUAh_PlayerQuit(player_t *plr, kickreason_t reason) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerQuit) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit - lua_pushinteger(gL, reason); // Reason for quitting - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - } - - lua_settop(gL, 0); -} - -// Hook for Y_Ticker -void LUAh_IntermissionThinker(void) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_IntermissionThinker/8] & (1<<(hook_IntermissionThinker%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_IntermissionThinker) - continue; - - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - } - - lua_pop(gL, 1); // Pop error handler -} - -// Hook for team switching -// It's just an edit of LUAh_ViewpointSwitch. -boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble) -{ - hook_p hookp; - boolean canSwitchTeam = true; - if (!gL || !(hooksAvailable[hook_TeamSwitch/8] & (1<<(hook_TeamSwitch%8)))) - return true; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = playerhooks; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TeamSwitch) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - lua_pushinteger(gL, newteam); - lua_pushboolean(gL, fromspectators); - lua_pushboolean(gL, tryingautobalance); - lua_pushboolean(gL, tryingscramble); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1)) - canSwitchTeam = false; // Can't switch team - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return canSwitchTeam; -} - -// Hook for spy mode -UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced) -{ - hook_p hookp; - UINT8 canSwitchView = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_ViewpointSwitch/8] & (1<<(hook_ViewpointSwitch%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - hud_running = true; // local hook - - for (hookp = playerhooks; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ViewpointSwitch) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER); - lua_pushboolean(gL, forced); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave canSwitchView = 0. - if (lua_toboolean(gL, -1)) - canSwitchView = 1; // Force viewpoint switch - else - canSwitchView = 2; // Skip viewpoint switch - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - - hud_running = false; - - return canSwitchView; -} - -boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname) -{ - hook_p hookp; - boolean keepplaying = false; - if (!gL || !(hooksAvailable[hook_ShouldJingleContinue/8] & (1<<(hook_ShouldJingleContinue%8)))) - return true; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - hud_running = true; // local hook - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldJingleContinue - || (hookp->s.str && strcmp(hookp->s.str, musname))) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - lua_pushstring(gL, musname); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1) && lua_toboolean(gL, -1)) - keepplaying = true; // Keep playing this boolean - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - - hud_running = false; - - return keepplaying; -} - -// Hook for music changes -boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, - UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms) -{ - hook_p hookp; - boolean hooked = false; - - if (!gL || !(hooksAvailable[hook_MusicChange/8] & (1<<(hook_MusicChange%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_MusicChange) - { - PushHook(gL, hookp); - lua_pushstring(gL, oldname); - lua_pushstring(gL, newname); - lua_pushinteger(gL, *mflags); - lua_pushboolean(gL, *looping); - lua_pushinteger(gL, *position); - lua_pushinteger(gL, *prefadems); - lua_pushinteger(gL, *fadeinms); - if (lua_pcall(gL, 7, 6, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1)); - lua_pop(gL, 1); - continue; } - // output 1: true, false, or string musicname override - if (lua_isboolean(gL, -6) && lua_toboolean(gL, -6)) - hooked = true; - else if (lua_isstring(gL, -6)) - strncpy(newname, lua_tostring(gL, -6), 7); - // output 2: mflags override - if (lua_isnumber(gL, -5)) - *mflags = lua_tonumber(gL, -5); - // output 3: looping override - if (lua_isboolean(gL, -4)) - *looping = lua_toboolean(gL, -4); - // output 4: position override - if (lua_isnumber(gL, -3)) - *position = lua_tonumber(gL, -3); - // output 5: prefadems override - if (lua_isnumber(gL, -2)) - *prefadems = lua_tonumber(gL, -2); - // output 6: fadeinms override - if (lua_isnumber(gL, -1)) - *fadeinms = lua_tonumber(gL, -1); - - lua_pop(gL, 7); // Pop returned values and error handler + lua_pop(gL, 1); // pop value +#undef CHECK } - - lua_settop(gL, 0); - newname[6] = 0; - return hooked; -} - -// Hook for Y_VoteTicker -void LUAh_VoteThinker(void) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_VoteThinker/8] & (1<<(hook_VoteThinker%8)))) - return; - - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_VoteThinker) + } else { + while (fields < 8) { - lua_pushfstring(gL, FMT_HOOKID, hookp->id); - lua_gettable(gL, LUA_REGISTRYINDEX); - if (lua_pcall(gL, 0, 0, 0)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - } -} - -// Hook for game quitting -void LUAh_GameQuit(boolean quitting) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_GameQuit/8] & (1<<(hook_GameQuit%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_GameQuit) - continue; - - PushHook(gL, hookp); - lua_pushboolean(gL, quitting); - if (lua_pcall(gL, 1, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; + k[fields] = lua_toboolean(gL, -8 + fields); + fields++; } } - lua_pop(gL, 1); // Pop error handler + B_KeysToTiccmd(botai->tails, botai->cmd, + k[0],k[1],k[2],k[3],k[4],k[5],k[6],k[7]); + + hook->status = true; +} + +int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) +{ + const char *skin = ((skin_t *)tails->skin)->name; + + Hook_State hook; + BotAI_State botai; + + if (prepare_string_hook(&hook, false, STRING_HOOK(BotAI), skin)) + { + LUA_PushUserdata(gL, sonic, META_MOBJ); + LUA_PushUserdata(gL, tails, META_MOBJ); + + botai.tails = tails; + botai.cmd = cmd; + + hook.userdata = &botai; + + call_hooks(&hook, 8, res_botai); + } + + return hook.status; +} + +void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) +{ + Hook_State hook; + if (prepare_string_hook + (&hook, 0, STRING_HOOK(LinedefExecute), line->stringargs[0])) + { + LUA_PushUserdata(gL, line, META_LINE); + LUA_PushUserdata(gL, mo, META_MOBJ); + LUA_PushUserdata(gL, sector, META_SECTOR); + ps_lua_mobjhooks.value.i += call_hooks(&hook, 0, res_none); + } +} + +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg) +{ + Hook_State hook; + if (prepare_hook(&hook, false, HOOK(PlayerMsg))) + { + LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player + if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c + lua_pushinteger(gL, 3); // type + lua_pushnil(gL); // target + } else if (target == -1) { // sayteam + lua_pushinteger(gL, 1); // type + lua_pushnil(gL); // target + } else if (target == 0) { // say + lua_pushinteger(gL, 0); // type + lua_pushnil(gL); // target + } else { // sayto + lua_pushinteger(gL, 2); // type + LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target + } + lua_pushstring(gL, msg); // msg + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +int LUA_HookHurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) +{ + Hook_State hook; + if (prepare_hook(&hook, false, HOOK(HurtMsg))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + lua_pushinteger(gL, damagetype); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +void LUA_HookNetArchive(lua_CFunction archFunc) +{ + const hook_t * map = &hookIds[HOOK(NetVars)]; + Hook_State hook; + /* this is a remarkable case where the stack isn't reset */ + if (map->numHooks > 0) + { + // stack: tables + I_Assert(lua_gettop(gL) > 0); + I_Assert(lua_istable(gL, -1)); + + push_error_handler(); + lua_insert(gL, EINDEX); + + begin_hook_values(&hook); + + // tables becomes an upvalue of archFunc + lua_pushvalue(gL, -1); + lua_pushcclosure(gL, archFunc, 1); + // stack: tables, archFunc + + init_hook_call(&hook, 0, res_none); + call_mapped(&hook, map); + + lua_pop(gL, 1); // pop archFunc + lua_remove(gL, EINDEX); // pop error handler + // stack: tables + } +} + +int LUA_HookMapThingSpawn(mobj_t *mobj, mapthing_t *mthing) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(MapThingSpawn), mobj->type)) + { + LUA_PushUserdata(gL, mobj, META_MOBJ); + LUA_PushUserdata(gL, mthing, META_MAPTHING); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +int LUA_HookFollowMobj(player_t *player, mobj_t *mobj) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(FollowMobj), mobj->type)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +int LUA_HookPlayerCanDamage(player_t *player, mobj_t *mobj) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerCanDamage))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 1, res_force); + } + return hook.status; +} + +void LUA_HookPlayerQuit(player_t *plr, kickreason_t reason) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerQuit))) + { + LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit + lua_pushinteger(gL, reason); // Reason for quitting + call_hooks(&hook, 0, res_none); + } +} + +int LUA_HookTeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble) +{ + Hook_State hook; + if (prepare_hook(&hook, true, HOOK(TeamSwitch))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + lua_pushinteger(gL, newteam); + lua_pushboolean(gL, fromspectators); + lua_pushboolean(gL, tryingautobalance); + lua_pushboolean(gL, tryingscramble); + call_hooks(&hook, 1, res_false); + } + return hook.status; +} + +int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(ViewpointSwitch))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER); + lua_pushboolean(gL, forced); + + hud_running = true; // local hook + call_hooks(&hook, 1, res_force); + hud_running = false; + } + return hook.status; +} + +int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend) +{ + Hook_State hook; + if (prepare_hook(&hook, true, HOOK(SeenPlayer))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, seenfriend, META_PLAYER); + + hud_running = true; // local hook + call_hooks(&hook, 1, res_false); + hud_running = false; + } + return hook.status; +} + +int LUA_HookShouldJingleContinue(player_t *player, const char *musname) +{ + Hook_State hook; + if (prepare_string_hook + (&hook, false, STRING_HOOK(ShouldJingleContinue), musname)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + push_string(); + + hud_running = true; // local hook + call_hooks(&hook, 1, res_true); + hud_running = false; + } + return hook.status; } -// Hook for G_BuildTicCmd boolean hook_cmd_running = false; -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd) + +static void update_music_name(struct MusicChange *musicchange) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_PlayerCmd/8] & (1<<(hook_PlayerCmd%8)))) - return false; + size_t length; + const char * new = lua_tolstring(gL, -6, &length); - lua_settop(gL, 0); + if (length < 7) + { + strcpy(musicchange->newname, new); + lua_pushvalue(gL, -6);/* may as well keep it for next call */ + } + else + { + memcpy(musicchange->newname, new, 6); + musicchange->newname[6] = '\0'; + lua_pushlstring(gL, new, 6); + } - hook_cmd_running = true; - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_PlayerCmd) + lua_replace(gL, -7); +} + +static void res_musicchange(Hook_State *hook) +{ + struct MusicChange *musicchange = hook->userdata; + + // output 1: true, false, or string musicname override + if (lua_isstring(gL, -6)) + update_music_name(musicchange); + else if (lua_isboolean(gL, -6) && lua_toboolean(gL, -6)) + hook->status = true; + + // output 2: mflags override + if (lua_isnumber(gL, -5)) + *musicchange->mflags = lua_tonumber(gL, -5); + // output 3: looping override + if (lua_isboolean(gL, -4)) + *musicchange->looping = lua_toboolean(gL, -4); + // output 4: position override + if (lua_isnumber(gL, -3)) + *musicchange->position = lua_tonumber(gL, -3); + // output 5: prefadems override + if (lua_isnumber(gL, -2)) + *musicchange->prefadems = lua_tonumber(gL, -2); + // output 6: fadeinms override + if (lua_isnumber(gL, -1)) + *musicchange->fadeinms = lua_tonumber(gL, -1); +} + +int LUA_HookMusicChange(const char *oldname, struct MusicChange *param) +{ + const int type = HOOK(MusicChange); + const hook_t * map = &hookIds[type]; + + Hook_State hook; + + int k; + + if (prepare_hook(&hook, false, type)) + { + init_hook_call(&hook, 6, res_musicchange); + hook.values = 7;/* values pushed later */ + hook.userdata = param; + + lua_pushstring(gL, oldname);/* the only constant value */ + lua_pushstring(gL, param->newname);/* semi constant */ + + for (k = 0; k < map->numHooks; ++k) { - if (lua_gettop(gL) == 0) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, cmd, META_TICCMD); - } - lua_pushfstring(gL, FMT_HOOKID, hookp->id); - lua_gettable(gL, LUA_REGISTRYINDEX); + get_hook(&hook, map->ids, k); + lua_pushvalue(gL, -3); lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 0)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + lua_pushinteger(gL, *param->mflags); + lua_pushboolean(gL, *param->looping); + lua_pushinteger(gL, *param->position); + lua_pushinteger(gL, *param->prefadems); + lua_pushinteger(gL, *param->fadeinms); + + call_single_hook_no_copy(&hook); } - hook_cmd_running = false; - lua_settop(gL, 0); - return hooked; + lua_settop(gL, 0); + } + + return hook.status; +} + +static void res_playerheight(Hook_State *hook) +{ + if (!lua_isnil(gL, -1)) + { + fixed_t returnedheight = lua_tonumber(gL, -1); + // 0 height has... strange results, but it's not problematic like negative heights are. + // when an object's height is set to a negative number directly with lua, it's forced to 0 instead. + // here, I think it's better to ignore negatives so that they don't replace any results of previous hooks! + if (returnedheight >= 0) + hook->status = returnedheight; + } +} + +fixed_t LUA_HookPlayerHeight(player_t *player) +{ + Hook_State hook; + if (prepare_hook(&hook, -1, HOOK(PlayerHeight))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, res_playerheight); + } + return hook.status; +} + +int LUA_HookPlayerCanEnterSpinGaps(player_t *player) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerCanEnterSpinGaps))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, res_force); + } + return hook.status; } diff --git a/src/s_sound.h b/src/s_sound.h index 515fcd4f4..461f376af 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -243,6 +243,16 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst); // Music Playback // +/* this is for the sake of the hook */ +struct MusicChange { + char * newname; + UINT16 * mflags; + boolean * looping; + UINT32 * position; + UINT32 * prefadems; + UINT32 * fadeinms; +}; + enum { MUS_SPECIAL = 1,/* powerups--invincibility, grow */ From b4d1fade5fa6fc9ba9501388f92f0dd8394f10ad Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Sep 2022 10:03:10 -0700 Subject: [PATCH 03/15] Apply b4fa98d2f to lua_hudlib.c Continued from 544812a45 --- src/lua_hook.h | 8 +- src/lua_hooklib.c | 8 +- src/lua_hud.h | 6 +- src/lua_hudlib.c | 238 +++++----------------------------------------- 4 files changed, 34 insertions(+), 226 deletions(-) diff --git a/src/lua_hook.h b/src/lua_hook.h index fc6a5f4ee..2c69e6b03 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -1,4 +1,4 @@ -// SONIC ROBO BLAST 2 + //----------------------------------------------------------------------------- // Copyright (C) 2012-2016 by John "JTE" Muniz. // Copyright (C) 2012-2022 by Sonic Team Junior. @@ -15,6 +15,8 @@ #include "s_sound.h" #include "d_event.h" +#include "lua_hudlib_drawlist.h" + /* Do you know what an 'X Macro' is? Such a macro is called over each element of a list and expands the input. I use it for the hook lists because both an enum @@ -110,12 +112,12 @@ ENUM (STRING_HOOK); /* dead simple, LUA_HOOK(GameQuit) */ #define LUA_HOOK(type) LUA_HookVoid(HOOK(type)) -#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type)) +//#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type)) extern boolean hook_cmd_running; void LUA_HookVoid(int hook); -void LUA_HookHUD(int hook); +void LUA_HookHUD(huddrawlist_h, int hook); int LUA_HookMobj(mobj_t *, int hook); int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index dc5069319..b2029cc95 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -630,6 +630,7 @@ int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) return hook.status; } +<<<<<<< Updated upstream int LUA_HookKey(event_t *event, int hook_type) { Hook_State hook; @@ -641,7 +642,10 @@ int LUA_HookKey(event_t *event, int hook_type) return hook.status; } -void LUA_HookHUD(int hook_type) +void LUA_HookHUD(int hook_type, huddrawlist_h list) +======= +void LUA_HookHUD(huddrawlist_h list, int hook_type) +>>>>>>> Stashed changes { const hook_t * map = &hudHookIds[hook_type]; Hook_State hook; @@ -650,7 +654,7 @@ void LUA_HookHUD(int hook_type) start_hook_stack(); begin_hook_values(&hook); - LUA_SetHudHook(hook_type); + LUA_SetHudHook(hook_type, list); hud_running = true; // local hook init_hook_call(&hook, 0, res_none); diff --git a/src/lua_hud.h b/src/lua_hud.h index 35db4ef87..fc91811f0 100644 --- a/src/lua_hud.h +++ b/src/lua_hud.h @@ -47,10 +47,6 @@ extern boolean hud_running; boolean LUA_HudEnabled(enum hud option); -void LUAh_GameHUD(player_t *stplyr, huddrawlist_h list); -void LUAh_ScoresHUD(huddrawlist_h list); -void LUAh_TitleHUD(huddrawlist_h list); -void LUAh_TitleCardHUD(player_t *stplayr, huddrawlist_h list); -void LUAh_IntermissionHUD(huddrawlist_h list); +void LUA_SetHudHook(int hook, huddrawlist_h list); #endif // __LUA_HUD_H__ diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index ad6555541..c8d0d3b10 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -29,14 +29,13 @@ #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" +#include "lua_hook.h" #define HUDONLY if (!hud_running) return luaL_error(L, "HUD rendering code should not be called outside of rendering hooks!"); boolean hud_running = false; static UINT8 hud_enabled[(hud_MAX/8)+1]; -static UINT8 hudAvailable; // hud hooks field - static UINT8 camnum = 1; // must match enum hud in lua_hud.h @@ -79,21 +78,6 @@ static const char *const patch_opt[] = { "topoffset", NULL}; -enum hudhook { - hudhook_game = 0, - hudhook_scores, - hudhook_intermission, - hudhook_title, - hudhook_titlecard -}; -static const char *const hudhook_opt[] = { - "game", - "scores", - "intermission", - "title", - "titlecard", - NULL}; - // alignment types for v.drawString enum align { align_left = 0, @@ -1249,6 +1233,8 @@ static luaL_Reg lib_draw[] = { {NULL, NULL} }; +static int lib_draw_ref; + // // lib_hud // @@ -1282,28 +1268,7 @@ static int lib_hudenabled(lua_State *L) } // add a HUD element for rendering -static int lib_hudadd(lua_State *L) -{ - enum hudhook field; - - luaL_checktype(L, 1, LUA_TFUNCTION); - field = luaL_checkoption(L, 2, "game", hudhook_opt); - - if (!lua_lumploading) - return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); - - lua_getfield(L, LUA_REGISTRYINDEX, "HUD"); - I_Assert(lua_istable(L, -1)); - lua_rawgeti(L, -1, field+2); // HUD[2+] - I_Assert(lua_istable(L, -1)); - lua_remove(L, -2); - - lua_pushvalue(L, 1); - lua_rawseti(L, -2, (int)(lua_objlen(L, -2) + 1)); - - hudAvailable |= 1< Date: Mon, 5 Sep 2022 10:54:38 -0700 Subject: [PATCH 04/15] Fix conflicts 522467a88 --- src/d_clisrv.c | 14 ++--- src/d_netcmd.c | 8 +-- src/doomtype.h | 2 + src/f_finale.c | 3 +- src/g_game.c | 8 +-- src/hu_stuff.c | 4 +- src/k_bot.c | 2 +- src/lua_hook.h | 15 ++---- src/lua_hooklib.c | 131 ++-------------------------------------------- src/lua_script.c | 4 +- src/lua_script.h | 2 +- src/p_enemy.c | 2 +- src/p_inter.c | 10 ++-- src/p_map.c | 6 +-- src/p_mobj.c | 22 ++++---- src/p_setup.c | 4 +- src/p_spec.c | 4 +- src/p_tick.c | 12 ++--- src/p_user.c | 14 ++--- src/s_sound.c | 11 +++- src/sdl/i_video.c | 2 +- src/st_stuff.c | 5 +- src/y_inter.c | 6 +-- 23 files changed, 86 insertions(+), 205 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f26ec7274..d56d8399c 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2348,14 +2348,14 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) K_CalculateBattleWanted(); - LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting + LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting // don't look through someone's view who isn't there if (playernum == displayplayers[0] && !demo.playback) { // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); + LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); displayplayers[0] = consoleplayer; } @@ -2859,7 +2859,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (playernode[pnum] == playernode[consoleplayer]) { - LUAh_GameQuit(false); + LUA_HookBool(false, HOOK(GameQuit)); #ifdef DUMPCONSISTENCY if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); #endif @@ -3377,7 +3377,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) if (server && multiplayer && motd[0] != '\0') COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); - LUAh_PlayerJoin(newplayernum); + LUA_HookInt(newplayernum, HOOK(PlayerJoin)); #ifdef HAVE_DISCORDRPC DRPC_UpdatePresence(); @@ -3458,7 +3458,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false); } - LUAh_PlayerJoin(newplayernum); + LUA_HookInt(newplayernum, HOOK(PlayerJoin)); } static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *name2, const char *name3, const char *name4) @@ -3803,7 +3803,7 @@ static void HandleConnect(SINT8 node) static void HandleShutdown(SINT8 node) { (void)node; - LUAh_GameQuit(false); + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); CL_Reset(); D_StartTitle(); @@ -3818,7 +3818,7 @@ static void HandleShutdown(SINT8 node) static void HandleTimeout(SINT8 node) { (void)node; - LUAh_GameQuit(false); + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); CL_Reset(); D_StartTitle(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 1fef5acd1..92e3e01f1 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3412,7 +3412,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) } // Don't switch team, just go away, please, go awaayyyy, aaauuauugghhhghgh - if (!LUAh_TeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled)) + if (!LUA_HookTeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled)) return; //Make sure that the right team number is sent. Keep in mind that normal clients cannot change to certain teams in certain gametypes. @@ -3508,7 +3508,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) { if (localplayertable[i] == playernum) { - LUAh_ViewpointSwitch(players+playernum, players+playernum, true); + LUA_HookViewpointSwitch(players+playernum, players+playernum, true); displayplayers[i] = playernum; break; } @@ -4428,7 +4428,7 @@ static void Command_Playintro_f(void) */ FUNCNORETURN static ATTRNORETURN void Command_Quit_f(void) { - LUAh_GameQuit(true); + LUA_HookBool(true, HOOK(GameQuit)); I_Quit(); } @@ -4992,7 +4992,7 @@ void Command_ExitGame_f(void) { INT32 i; - LUAh_GameQuit(false); + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); CL_Reset(); diff --git a/src/doomtype.h b/src/doomtype.h index 7037c41cc..5d14fceae 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -368,6 +368,8 @@ typedef UINT32 tic_t; #define UINT2RGBA(a) (UINT32)((a&0xff)<<24)|((a&0xff00)<<8)|((a&0xff0000)>>8)|(((UINT32)a&0xff000000)>>24) #endif +#define TOSTR(x) #x + /* preprocessor dumb and needs second macro to expand input */ #define WSTRING2(s) L ## s #define WSTRING(s) WSTRING2 (s) diff --git a/src/f_finale.c b/src/f_finale.c index 52f2556f4..871913cd1 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -41,6 +41,7 @@ #include "fastcmp.h" #include "lua_hud.h" +#include "lua_hook.h" // Stage of animation: // 0 = text, 1 = art screen @@ -2118,7 +2119,7 @@ luahook: if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_title); - LUAh_TitleHUD(luahuddrawlist_title); + LUA_HookHUD(luahuddrawlist_title, HUD_HOOK(title)); } LUA_HUD_DrawList(luahuddrawlist_title); } diff --git a/src/g_game.c b/src/g_game.c index 155a62a0f..02d1c3636 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1223,7 +1223,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) */ if (addedtogame && gamestate == GS_LEVEL) { - LUAh_PlayerCmd(player, cmd); + LUA_HookTiccmd(player, cmd, HOOK(PlayerCmd)); // Send leveltime when this tic was generated to the server for control lag calculations. // Only do this when in a level. Also do this after the hook, so that it can't overwrite this. @@ -1253,7 +1253,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(player, &players[consoleplayer], true); + LUA_HookViewpointSwitch(player, &players[consoleplayer], true); displayplayers[0] = consoleplayer; } } @@ -2532,7 +2532,7 @@ void G_SpawnPlayer(INT32 playernum) P_SpawnPlayer(playernum); G_MovePlayerToSpawnOrStarpost(playernum); - LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) + LUA_HookPlayer(&players[playernum], HOOK(PlayerSpawn)); // Lua hook for player spawning :) } void G_MovePlayerToSpawnOrStarpost(INT32 playernum) @@ -4666,7 +4666,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer); else { - LUAh_MapChange(gamemap); + LUA_HookInt(HOOK(MapChange), gamemap); G_DoLoadLevel(resetplayer); } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index f4f52f5cf..492a2b45a 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -694,7 +694,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first. - if (LUAh_PlayerMsg(playernum, target, flags, msg, spam_eatmsg)) + if (LUA_HookPlayerMsg(playernum, target, flags, msg, spam_eatmsg)) return; if (spam_eatmsg) @@ -2123,7 +2123,7 @@ void HU_Drawer(void) if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_scores); - LUAh_ScoresHUD(luahuddrawlist_scores); + LUA_HookHUD(luahuddrawlist_scores, HUD_HOOK(scores)); } LUA_HUD_DrawList(luahuddrawlist_scores); } diff --git a/src/k_bot.c b/src/k_bot.c index e64089aef..d7955435e 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1247,7 +1247,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } // Complete override of all ticcmd functionality - if (LUAh_BotTiccmd(player, cmd) == true) + if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)) == true) { return; } diff --git a/src/lua_hook.h b/src/lua_hook.h index 2c69e6b03..ce98a5ec7 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -69,15 +69,11 @@ automatically. X (SeenPlayer),/* MT_NAMECHECK */\ X (PlayerThink),/* P_PlayerThink */\ X (GameQuit),\ - X (PlayerCmd),/* building the player's ticcmd struct (Ported from SRB2Kart) */\ + X (PlayerCmd),/* building the player's ticcmd struct */\ X (MusicChange),\ - X (PlayerHeight),/* override player height */\ - X (PlayerCanEnterSpinGaps),\ - X (KeyDown),\ - X (KeyUp),\ + X (VoteThinker),/* Y_VoteTicker */\ #define STRING_HOOK_LIST(X) \ - X (BotAI),/* B_BuildTailsTiccmd by skin name */\ X (LinedefExecute),\ X (ShouldJingleContinue),/* should jingle of the given music continue playing */\ @@ -115,6 +111,7 @@ ENUM (STRING_HOOK); //#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type)) extern boolean hook_cmd_running; +extern int hook_defrosting; void LUA_HookVoid(int hook); void LUA_HookHUD(huddrawlist_h, int hook); @@ -134,9 +131,8 @@ int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); int LUA_HookMobjMoveBlocked(mobj_t *, mobj_t *, line_t *); -int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); void LUA_HookLinedefExecute(line_t *, mobj_t *, sector_t *); -int LUA_HookPlayerMsg(int source, int target, int flags, char *msg); +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg, int mute); int LUA_HookHurtMsg(player_t *, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); int LUA_HookMapThingSpawn(mobj_t *, mapthing_t *); int LUA_HookFollowMobj(player_t *, mobj_t *); @@ -146,7 +142,4 @@ int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend); int LUA_HookShouldJingleContinue(player_t *, const char *musname); -int LUA_HookPlayerCmd(player_t *, ticcmd_t *); int LUA_HookMusicChange(const char *oldname, struct MusicChange *); -fixed_t LUA_HookPlayerHeight(player_t *player); -int LUA_HookPlayerCanEnterSpinGaps(player_t *player); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index b2029cc95..efb0522df 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -123,7 +123,6 @@ static void add_string_hook(lua_State *L, int type) switch (type) { - case STRING_HOOK(BotAI): case STRING_HOOK(ShouldJingleContinue): if (lua_isstring(L, 3)) { // lowercase copy @@ -630,22 +629,7 @@ int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) return hook.status; } -<<<<<<< Updated upstream -int LUA_HookKey(event_t *event, int hook_type) -{ - Hook_State hook; - if (prepare_hook(&hook, false, hook_type)) - { - LUA_PushUserdata(gL, event, META_KEYEVENT); - call_hooks(&hook, 1, res_true); - } - return hook.status; -} - -void LUA_HookHUD(int hook_type, huddrawlist_h list) -======= void LUA_HookHUD(huddrawlist_h list, int hook_type) ->>>>>>> Stashed changes { const hook_t * map = &hudHookIds[hook_type]; Hook_State hook; @@ -789,82 +773,6 @@ int LUA_HookMobjMoveBlocked(mobj_t *t1, mobj_t *t2, line_t *line) return hook.status; } -typedef struct { - mobj_t * tails; - ticcmd_t * cmd; -} BotAI_State; - -static boolean checkbotkey(const char *field) -{ - return lua_toboolean(gL, -1) && strcmp(lua_tostring(gL, -2), field) == 0; -} - -static void res_botai(Hook_State *hook) -{ - BotAI_State *botai = hook->userdata; - - int k[8]; - - int fields = 0; - - // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. - if (lua_istable(gL, -8)) { - lua_pushnil(gL); // key - while (lua_next(gL, -9)) { -#define CHECK(n, f) (checkbotkey(f) ? (k[(n)-1] = 1) : 0) - if ( - CHECK(1, "forward") || CHECK(2, "backward") || - CHECK(3, "left") || CHECK(4, "right") || - CHECK(5, "strafeleft") || CHECK(6, "straferight") || - CHECK(7, "jump") || CHECK(8, "spin") - ){ - if (8 <= ++fields) - { - lua_pop(gL, 2); // pop key and value - break; - } - } - - lua_pop(gL, 1); // pop value -#undef CHECK - } - } else { - while (fields < 8) - { - k[fields] = lua_toboolean(gL, -8 + fields); - fields++; - } - } - - B_KeysToTiccmd(botai->tails, botai->cmd, - k[0],k[1],k[2],k[3],k[4],k[5],k[6],k[7]); - - hook->status = true; -} - -int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) -{ - const char *skin = ((skin_t *)tails->skin)->name; - - Hook_State hook; - BotAI_State botai; - - if (prepare_string_hook(&hook, false, STRING_HOOK(BotAI), skin)) - { - LUA_PushUserdata(gL, sonic, META_MOBJ); - LUA_PushUserdata(gL, tails, META_MOBJ); - - botai.tails = tails; - botai.cmd = cmd; - - hook.userdata = &botai; - - call_hooks(&hook, 8, res_botai); - } - - return hook.status; -} - void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) { Hook_State hook; @@ -878,7 +786,7 @@ void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) } } -int LUA_HookPlayerMsg(int source, int target, int flags, char *msg) +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg, int mute) { Hook_State hook; if (prepare_hook(&hook, false, HOOK(PlayerMsg))) @@ -898,6 +806,8 @@ int LUA_HookPlayerMsg(int source, int target, int flags, char *msg) LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target } lua_pushstring(gL, msg); // msg + lua_pushboolean(gL, mute); // the message was supposed to be eaten by spamprotecc. + call_hooks(&hook, 1, res_true); } return hook.status; @@ -1143,38 +1053,3 @@ int LUA_HookMusicChange(const char *oldname, struct MusicChange *param) return hook.status; } - -static void res_playerheight(Hook_State *hook) -{ - if (!lua_isnil(gL, -1)) - { - fixed_t returnedheight = lua_tonumber(gL, -1); - // 0 height has... strange results, but it's not problematic like negative heights are. - // when an object's height is set to a negative number directly with lua, it's forced to 0 instead. - // here, I think it's better to ignore negatives so that they don't replace any results of previous hooks! - if (returnedheight >= 0) - hook->status = returnedheight; - } -} - -fixed_t LUA_HookPlayerHeight(player_t *player) -{ - Hook_State hook; - if (prepare_hook(&hook, -1, HOOK(PlayerHeight))) - { - LUA_PushUserdata(gL, player, META_PLAYER); - call_hooks(&hook, 1, res_playerheight); - } - return hook.status; -} - -int LUA_HookPlayerCanEnterSpinGaps(player_t *player) -{ - Hook_State hook; - if (prepare_hook(&hook, 0, HOOK(PlayerCanEnterSpinGaps))) - { - LUA_PushUserdata(gL, player, META_PLAYER); - call_hooks(&hook, 1, res_force); - } - return hook.status; -} diff --git a/src/lua_script.c b/src/lua_script.c index 1511fdf0d..8a1e307bb 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -1687,7 +1687,7 @@ void LUA_Archive(UINT8 **p) WRITEUINT32(*p, UINT32_MAX); // end of mobjs marker, replaces mobjnum. - LUAh_NetArchiveHook(NetArchive); // call the NetArchive hook in archive mode + LUA_HookNetArchive(NetArchive); // call the NetArchive hook in archive mode } ArchiveTables(p); @@ -1726,7 +1726,7 @@ void LUA_UnArchive(UINT8 **p) } } while(mobjnum != UINT32_MAX); // repeat until end of mobjs marker. - LUAh_NetArchiveHook(NetUnArchive); // call the NetArchive hook in unarchive mode + LUA_HookNetArchive(NetUnArchive); // call the NetArchive hook in unarchive mode } UnArchiveTables(p); diff --git a/src/lua_script.h b/src/lua_script.h index 1c6d4587d..6268407f3 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -63,7 +63,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c void LUA_CVarChanged(void *cvar); // lua_consolelib.c int Lua_optoption(lua_State *L, int narg, const char *def, const char *const lst[]); -void LUAh_NetArchiveHook(lua_CFunction archFunc); +void LUA_HookNetArchive(lua_CFunction archFunc); void LUA_PushTaggableObjectArray ( lua_State *L, diff --git a/src/p_enemy.c b/src/p_enemy.c index 2c69e7bfe..d78089e06 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3495,7 +3495,7 @@ void A_BossDeath(mobj_t *mo) } bossjustdie: - if (LUAh_BossDeath(mo)) + if (LUA_HookMobj(mo, MOBJ_HOOK(BossDeath))) return; else if (P_MobjWasRemoved(mo)) return; diff --git a/src/p_inter.c b/src/p_inter.c index 0974e11d7..c7007bd7f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -219,7 +219,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->flags & (MF_ENEMY|MF_BOSS) && special->flags2 & MF2_FRET) return; - if (LUAh_TouchSpecial(special, toucher) || P_MobjWasRemoved(special)) + if (LUA_HookTouchSpecial(special, toucher) || P_MobjWasRemoved(special)) return; if ((special->flags & (MF_ENEMY|MF_BOSS)) && !(special->flags & MF_MISSILE)) @@ -969,7 +969,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->shadowscale = 0; } - if (LUAh_MobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target)) return; //K_SetHitLagForObjects(target, inflictor, MAXHITLAGTICS, true); @@ -1855,7 +1855,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da // Everything above here can't be forced. if (!metalrecording) { - UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage, damagetype); + UINT8 shouldForce = LUA_HookShouldDamage(target, inflictor, source, damage, damagetype); if (P_MobjWasRemoved(target)) return (shouldForce == 1); // mobj was removed if (shouldForce == 1) @@ -1881,7 +1881,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!force && target->flags2 & MF2_FRET) // Currently flashing from being hit return false; - if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target)) return true; if (target->health > 1) @@ -1911,7 +1911,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!P_KillPlayer(player, inflictor, source, damagetype)) return false; } - else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) + else if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype)) { return true; } diff --git a/src/p_map.c b/src/p_map.c index d667b0346..4df2d17c0 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -656,7 +656,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) } { - UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type + UINT8 shouldCollide = LUA_Hook2Mobj(thing, tmthing, MOBJ_HOOK(MobjCollide)); // checks hook for thing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) return BMIT_CONTINUE; // one of them was removed??? if (shouldCollide == 1) @@ -664,7 +664,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) else if (shouldCollide == 2) return BMIT_CONTINUE; // force no collide - shouldCollide = LUAh_MobjMoveCollide(tmthing, thing); // checks hook for tmthing's type + shouldCollide = LUA_Hook2Mobj(tmthing, thing, MOBJ_HOOK(MobjMoveCollide)); // checks hook for tmthing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) return BMIT_CONTINUE; // one of them was removed??? if (shouldCollide == 1) @@ -1640,7 +1640,7 @@ static BlockItReturn_t PIT_CheckLine(line_t *ld) blockingline = ld; { - UINT8 shouldCollide = LUAh_MobjLineCollide(tmthing, blockingline); // checks hook for thing's type + UINT8 shouldCollide = LUA_HookMobjLineCollide(tmthing, blockingline); // checks hook for thing's type if (P_MobjWasRemoved(tmthing)) return BMIT_CONTINUE; // one of them was removed??? if (shouldCollide == 1) diff --git a/src/p_mobj.c b/src/p_mobj.c index 8e6e0eb2a..f85fc3454 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1568,7 +1568,7 @@ void P_XYMovement(mobj_t *mo) // blocked move moved = false; - if (LUAh_MobjMoveBlocked(mo)) + if (LUA_HookMobjMoveBlocked(mo, tmhitthing, blockingline)) { if (P_MobjWasRemoved(mo)) return; @@ -3963,7 +3963,7 @@ static void P_RingThinker(mobj_t *mobj) if (!mobj->fuse) { - if (!LUAh_MobjFuse(mobj)) + if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse))) { mobj->renderflags &= ~RF_DONTDRAW; spark = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_SIGNSPARKLE); // Spawn a fancy sparkle @@ -5343,7 +5343,7 @@ static boolean P_ParticleGenSceneryThink(mobj_t *mobj) static void P_MobjSceneryThink(mobj_t *mobj) { - if (LUAh_MobjThinker(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker))) return; if (P_MobjWasRemoved(mobj)) return; @@ -6205,7 +6205,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->fuse--; if (!mobj->fuse) { - if (!LUAh_MobjFuse(mobj)) + if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse))) P_RemoveMobj(mobj); return; } @@ -6225,7 +6225,7 @@ static boolean P_MobjPushableThink(mobj_t *mobj) static boolean P_MobjBossThink(mobj_t *mobj) { - if (LUAh_BossThinker(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(BossThinker))) { if (P_MobjWasRemoved(mobj)) return false; @@ -9147,7 +9147,7 @@ static boolean P_FuseThink(mobj_t *mobj) if (mobj->fuse) return true; - if (LUAh_MobjFuse(mobj) || P_MobjWasRemoved(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)) || P_MobjWasRemoved(mobj)) ; else if (mobj->info->flags & MF_MONITOR) { @@ -9379,13 +9379,13 @@ void P_MobjThinker(mobj_t *mobj) // Check for a Lua thinker first if (!mobj->player) { - if (LUAh_MobjThinker(mobj) || P_MobjWasRemoved(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)) || P_MobjWasRemoved(mobj)) return; } else if (!mobj->player->spectator) { // You cannot short-circuit the player thinker like you can other thinkers. - LUAh_MobjThinker(mobj); + LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)); if (P_MobjWasRemoved(mobj)) return; } @@ -9977,7 +9977,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // DANGER! This can cause P_SpawnMobj to return NULL! // Avoid using P_RemoveMobj on the newly created mobj in "MobjSpawn" Lua hooks! - if (LUAh_MobjSpawn(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjSpawn))) { if (P_MobjWasRemoved(mobj)) return NULL; @@ -10577,7 +10577,7 @@ void P_RemoveMobj(mobj_t *mobj) return; // something already removing this mobj. mobj->thinker.function.acp1 = (actionf_p1)P_RemoveThinkerDelayed; // shh. no recursing. - LUAh_MobjRemoved(mobj); + LUA_HookMobj(mobj, MOBJ_HOOK(MobjRemoved)); mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; // needed for P_UnsetThingPosition, etc. to work. // Rings only, please! @@ -12174,7 +12174,7 @@ static void P_SnapToFinishLine(mobj_t *mobj) static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean *doangle) { - boolean override = LUAh_MapThingSpawn(mobj, mthing); + boolean override = LUA_HookMapThingSpawn(mobj, mthing); if (P_MobjWasRemoved(mobj)) return false; diff --git a/src/p_setup.c b/src/p_setup.c index 43f97e666..6893210a5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -66,7 +66,7 @@ #include "md5.h" // map MD5 -// for LUAh_MapLoad +// for MapLoad hook #include "lua_script.h" #include "lua_hook.h" @@ -4485,7 +4485,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } P_PreTicker(2); P_MapStart(); // just in case MapLoad modifies tmthing - LUAh_MapLoad(); + LUA_HookInt(gamemap, HOOK(MapLoad)); P_MapEnd(); // just in case MapLoad modifies tmthing } diff --git a/src/p_spec.c b/src/p_spec.c index 942e72fb6..681e5e8e9 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -36,7 +36,7 @@ #include "v_video.h" // V_ALLOWLOWERCASE #include "m_misc.h" #include "m_cond.h" //unlock triggers -#include "lua_hook.h" // LUAh_LinedefExecute +#include "lua_hook.h" // LUA_HookLinedefExecute #include "f_finale.h" // control text prompt #include "r_skins.h" // skins @@ -3028,7 +3028,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 443: // Calls a named Lua function if (line->stringargs[0]) - LUAh_LinedefExecute(line, mo, callsec); + LUA_HookLinedefExecute(line, mo, callsec); else CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in arg0str)\n", sizeu1(line-lines)); break; diff --git a/src/p_tick.c b/src/p_tick.c index 843461db6..98f97a45a 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -581,7 +581,7 @@ void P_Ticker(boolean run) ps_lua_mobjhooks = 0; ps_checkposition_calls = 0; - LUAh_PreThinkFrame(); + LUA_HOOK(PreThinkFrame); ps_playerthink_time = I_GetPreciseTime(); @@ -654,7 +654,7 @@ void P_Ticker(boolean run) } ps_lua_thinkframe_time = I_GetPreciseTime(); - LUAh_ThinkFrame(); + LUA_HOOK(ThinkFrame); ps_lua_thinkframe_time = I_GetPreciseTime() - ps_lua_thinkframe_time; } @@ -748,7 +748,7 @@ void P_Ticker(boolean run) // Always move the camera. P_RunChaseCameras(); - LUAh_PostThinkFrame(); + LUA_HOOK(PostThinkFrame); if (run) { @@ -802,7 +802,7 @@ void P_PreTicker(INT32 frames) K_KartUpdatePosition(&players[i]); // OK! Now that we got all of that sorted, players can think! - LUAh_PreThinkFrame(); + LUA_HOOK(PreThinkFrame); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) @@ -825,7 +825,7 @@ void P_PreTicker(INT32 frames) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); - LUAh_ThinkFrame(); + LUA_HOOK(ThinkFrame); // Run shield positioning P_RunOverlays(); @@ -833,7 +833,7 @@ void P_PreTicker(INT32 frames) P_UpdateSpecials(); P_RespawnSpecials(); - LUAh_PostThinkFrame(); + LUA_HOOK(PostThinkFrame); R_UpdateLevelInterpolators(); R_UpdateViewInterpolation(); diff --git a/src/p_user.c b/src/p_user.c index 4030232f7..903e874d8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -664,7 +664,7 @@ boolean P_EvaluateMusicStatus(UINT16 status, const char *musname) break; case JT_OTHER: // Other state - result = LUAh_ShouldJingleContinue(&players[i], musname); + result = LUA_HookShouldJingleContinue(&players[i], musname); break; case JT_NONE: // Null state @@ -3671,7 +3671,7 @@ boolean P_SpectatorJoinGame(player_t *player) else changeto = (P_RandomFixed() & 1) + 1; - if (!LUAh_TeamSwitch(player, changeto, true, false, false)) + if (!LUA_HookTeamSwitch(player, changeto, true, false, false)) return false; } @@ -3697,7 +3697,7 @@ boolean P_SpectatorJoinGame(player_t *player) { if (localplayertable[i] == (player-players)) { - LUAh_ViewpointSwitch(player, player, true); + LUA_HookViewpointSwitch(player, player, true); displayplayers[i] = (player-players); break; } @@ -4207,7 +4207,7 @@ void P_PlayerThink(player_t *player) if (player->playerstate == PST_DEAD) { - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } } @@ -4256,7 +4256,7 @@ void P_PlayerThink(player_t *player) else player->mo->renderflags &= ~RF_GHOSTLYMASK; P_DeathThink(player); - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } @@ -4461,7 +4461,7 @@ void P_PlayerThink(player_t *player) if (player->carry == CR_SLIDING) player->carry = CR_NONE; - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); } // @@ -4568,7 +4568,7 @@ void P_PlayerAfterThink(player_t *player) if (player->followmobj) { - if (LUAh_FollowMobj(player, player->followmobj) || P_MobjWasRemoved(player->followmobj)) + if (LUA_HookFollowMobj(player, player->followmobj) || P_MobjWasRemoved(player->followmobj)) {;} else { diff --git a/src/s_sound.c b/src/s_sound.c index 9d3456f59..9bddda377 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -2121,6 +2121,15 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 { char newmusic[7]; + struct MusicChange hook_param = { + newmusic, + &mflags, + &looping, + &position, + &prefadems, + &fadeinms + }; + if (S_MusicDisabled() || demo.rewinding // Don't mess with music while rewinding! || demo.title) // SRB2Kart: Demos don't interrupt title screen music @@ -2128,7 +2137,7 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 strncpy(newmusic, mmusic, 7); - if (LUAh_MusicChange(music_name, newmusic, &mflags, &looping, &position, &prefadems, &fadeinms)) + if (LUA_HookMusicChange(music_name, &hook_param)) return; newmusic[6] = 0; diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index ac23eb926..36e769918 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1149,7 +1149,7 @@ void I_GetEvent(void) M_SetupJoystickMenu(0); break; case SDL_QUIT: - LUAh_GameQuit(true); + LUA_HookBool(true, HOOK(GameQuit)); I_Quit(); break; } diff --git a/src/st_stuff.c b/src/st_stuff.c index be5e3cc97..c0e91c34f 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -47,6 +47,7 @@ #include "lua_hudlib_drawlist.h" #include "lua_hud.h" +#include "lua_hook.h" // SRB2Kart #include "k_hud.h" // SRB2kart @@ -962,7 +963,7 @@ luahook: if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_titlecard); - LUAh_TitleCardHUD(stplyr, luahuddrawlist_titlecard); + LUA_HookHUD(luahuddrawlist_titlecard, HUD_HOOK(titlecard)); } LUA_HUD_DrawList(luahuddrawlist_titlecard); } @@ -1009,7 +1010,7 @@ static void ST_overlayDrawer(void) if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_game); - LUAh_GameHUD(stplyr, luahuddrawlist_game); + LUA_HookHUD(luahuddrawlist_game, HUD_HOOK(game)); } LUA_HUD_DrawList(luahuddrawlist_game); } diff --git a/src/y_inter.c b/src/y_inter.c index d66a82dcb..69df1ba8a 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -478,7 +478,7 @@ void Y_IntermissionDrawer(void) if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_intermission); - LUAh_IntermissionHUD(luahuddrawlist_intermission); + LUA_HookHUD(luahuddrawlist_intermission, HUD_HOOK(intermission)); } LUA_HUD_DrawList(luahuddrawlist_intermission); @@ -777,7 +777,7 @@ void Y_Ticker(void) if (paused || P_AutoPause()) return; - LUAh_IntermissionThinker(); + LUA_HOOK(IntermissionThinker); intertic++; @@ -1371,7 +1371,7 @@ void Y_VoteTicker(void) if (paused || P_AutoPause() || !voteclient.loaded) return; - LUAh_VoteThinker(); + LUA_HOOK(VoteThinker); votetic++; From aa5063668e6049e20b47e9178fb612d85c35ef08 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Sep 2022 10:56:48 -0700 Subject: [PATCH 05/15] hooklib: revert ps_lua_mobjhooks to pre perfstats averaging refactor (77ecfb9cdc) Revert later --- src/lua_hooklib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index efb0522df..8d47b7389 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -506,7 +506,7 @@ static int call_hooks calls += call_mobj_type_hooks(hook, MT_NULL); calls += call_mobj_type_hooks(hook, hook->mobj_type); - ps_lua_mobjhooks.value.i += calls; + ps_lua_mobjhooks += calls; } else calls += call_mapped(hook, &hookIds[hook->hook_type]); @@ -782,7 +782,7 @@ void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) LUA_PushUserdata(gL, line, META_LINE); LUA_PushUserdata(gL, mo, META_MOBJ); LUA_PushUserdata(gL, sector, META_SECTOR); - ps_lua_mobjhooks.value.i += call_hooks(&hook, 0, res_none); + ps_lua_mobjhooks += call_hooks(&hook, 0, res_none); } } From b17717babe2a2c2f89a37d5de5fb7eebef0ba15e Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Feb 2022 02:42:33 -0800 Subject: [PATCH 06/15] A_LobShot: remove ??? MT_NULL spawning, not cool bro. (cherry picked from commit f6f002e70b645982dddd83d6f831a601faa0fdae) --- src/p_enemy.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index d78089e06..ffac4524b 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -2474,7 +2474,7 @@ void A_LobShot(mobj_t *actor) { INT32 locvar1 = var1; INT32 locvar2 = var2 >> 16; - mobj_t *shot, *hitspot; + mobj_t *shot; angle_t an; fixed_t z; fixed_t dist; @@ -2502,11 +2502,6 @@ void A_LobShot(mobj_t *actor) shot->destscale = actor->scale; P_SetScale(shot, actor->scale); - // Keep track of where it's going to land - hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL); - hitspot->tics = airtime; - P_SetTarget(&shot->tracer, hitspot); - P_SetTarget(&shot->target, actor); // where it came from P_InitAngle(shot, actor->angle); From ba1b6bb253d20cdddba96fdab8821a9ef36644af Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Feb 2022 03:29:38 -0800 Subject: [PATCH 07/15] Add P_CheckMove Checks if P_TryMove would succeed without actually moving. (cherry picked from commit 518de0ce104166c3eacd10ebe6ade422307ce671) --- src/deh_tables.c | 1 + src/info.c | 27 ++++++++++++++++++++++++ src/info.h | 1 + src/p_local.h | 1 + src/p_map.c | 54 ++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 47a6ca515..f6d206d00 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4506,6 +4506,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi // because sadly no one remembers this place while searching for full state names. const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity testing later. "MT_NULL", + "MT_RAY", "MT_UNKNOWN", "MT_THOK", // Thok! mobj diff --git a/src/info.c b/src/info.c index 8fd5f929c..28880ac82 100644 --- a/src/info.c +++ b/src/info.c @@ -5120,6 +5120,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_RAY + -1, // doomednum + S_NULL, // spawnstate + 0, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 0, // radius + 0, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags + S_NULL // raisestate + }, + { // MT_UNKNOWN -1, // doomednum S_UNKNOWN, // spawnstate diff --git a/src/info.h b/src/info.h index 175de9cab..79c746849 100644 --- a/src/info.h +++ b/src/info.h @@ -5532,6 +5532,7 @@ extern playersprite_t free_spr2; typedef enum mobj_type { MT_NULL, + MT_RAY, // General purpose mobj MT_UNKNOWN, MT_THOK, // Thok! mobj diff --git a/src/p_local.h b/src/p_local.h index 0e03b8482..6c885191c 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -410,6 +410,7 @@ boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing); boolean P_IsLineTripWire(const line_t *ld); boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y); boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam); +boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); fixed_t P_BaseStepUp(void); fixed_t P_GetThingStepUp(mobj_t *thing); boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); diff --git a/src/p_map.c b/src/p_map.c index 4df2d17c0..53fa01f69 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2489,21 +2489,19 @@ fixed_t P_GetThingStepUp(mobj_t *thing) return maxstep; } -// -// P_TryMove -// Attempt to move to a new position. -// -boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) +static boolean +increment_move +( mobj_t * thing, + fixed_t x, + fixed_t y, + boolean allowdropoff, + fixed_t * return_stairjank) { fixed_t tryx = thing->x; fixed_t tryy = thing->y; - fixed_t oldx = tryx; - fixed_t oldy = tryy; fixed_t radius = thing->radius; fixed_t thingtop; - fixed_t startingonground = P_IsObjectOnGround(thing); fixed_t stairjank = 0; - pslope_t *oldslope = thing->standingslope; floatok = false; // reset this to 0 at the start of each trymove call as it's only used here @@ -2644,7 +2642,45 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) } } while (tryx != x || tryy != y); + if (return_stairjank) + *return_stairjank = stairjank; + + return true; +} + +// +// P_CheckMove +// Check if a P_TryMove would be successful. +// +boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) +{ + boolean moveok; + mobj_t *hack = P_SpawnMobjFromMobj(thing, 0, 0, 0, MT_RAY); + + hack->radius = thing->radius; + hack->height = thing->height; + + moveok = increment_move(hack, x, y, allowdropoff, NULL); + P_RemoveMobj(hack); + + return moveok; +} + +// +// P_TryMove +// Attempt to move to a new position. +// +boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) +{ + fixed_t oldx = thing->x; + fixed_t oldy = thing->y; + fixed_t startingonground = P_IsObjectOnGround(thing); + fixed_t stairjank = 0; + pslope_t *oldslope = thing->standingslope; + // The move is ok! + if (!increment_move(thing, x, y, allowdropoff, &stairjank)) + return false; // If it's a pushable object, check if anything is // standing on top and move it, too. From 72342338b1aa78ecd6aa05c382d01fbf39839e2f Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Feb 2022 03:40:26 -0800 Subject: [PATCH 08/15] Use P_CheckMove (cherry picked from commit 9dfa153e7497403d874e980868b7aa6d15595286) --- src/p_enemy.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index ffac4524b..1467f66a0 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3141,10 +3141,8 @@ void A_SkullAttack(mobj_t *actor) UINT32 oldflags = mobjinfo[MT_NULL].flags; fixed_t oldradius = mobjinfo[MT_NULL].radius; fixed_t oldheight = mobjinfo[MT_NULL].height; - mobj_t *check; INT32 i, j; static INT32 k;/* static for (at least) GCC 9.1 weirdness */ - boolean allow; angle_t testang = 0; mobjinfo[MT_NULL].spawnstate = S_INVISIBLE; @@ -3163,15 +3161,12 @@ void A_SkullAttack(mobj_t *actor) j = 9; } -#define dostuff(q) check = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_NULL);\ +#define dostuff(q) \ testang = actor->angle + ((i+(q))*ANG10);\ - allow = (P_TryMove(check,\ - P_ReturnThrustX(check, testang, dist + 2*actor->radius),\ - P_ReturnThrustY(check, testang, dist + 2*actor->radius),\ - true));\ - P_RemoveMobj(check);\ - if (allow)\ - break; + if (P_CheckMove(actor,\ + P_ReturnThrustX(actor, testang, dist + 2*actor->radius),\ + P_ReturnThrustY(actor, testang, dist + 2*actor->radius),\ + true)) break; if (P_RandomChance(FRACUNIT/2)) // port priority 2? { From 707f487f92e2e50a956e149bb05acee71bd3bd93 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Feb 2022 04:04:53 -0800 Subject: [PATCH 09/15] Never spawn MT_NULL (cherry picked from commit a8c658b545e3c7709212a43489163f33ffbe91f0) --- src/p_mobj.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index f85fc3454..e6749e3d8 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9876,7 +9876,17 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) const mobjinfo_t *info = &mobjinfo[type]; SINT8 sc = -1; state_t *st; - mobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); + mobj_t *mobj; + + if (type == MT_NULL) + { +#ifdef PARANOIA + I_Error("Tried to spawn MT_NULL\n"); +#endif + return NULL; + } + + mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); // this is officially a mobj, declared as soon as possible. mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; From 3a2fe2248588b6549c8f56b91351f126955e7f0e Mon Sep 17 00:00:00 2001 From: spherallic Date: Fri, 4 Feb 2022 20:09:37 +0100 Subject: [PATCH 10/15] Don't read or set MT_NULL's properties in A_SkullAttack (cherry picked from commit 878b4dc5b6025e1deef775ccb72b8a871833598a) --- src/p_enemy.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 1467f66a0..d5f999624 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3137,18 +3137,18 @@ void A_SkullAttack(mobj_t *actor) actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90; else if (locvar1 == 3) { - statenum_t oldspawnstate = mobjinfo[MT_NULL].spawnstate; - UINT32 oldflags = mobjinfo[MT_NULL].flags; - fixed_t oldradius = mobjinfo[MT_NULL].radius; - fixed_t oldheight = mobjinfo[MT_NULL].height; + statenum_t oldspawnstate = mobjinfo[MT_RAY].spawnstate; + UINT32 oldflags = mobjinfo[MT_RAY].flags; + fixed_t oldradius = mobjinfo[MT_RAY].radius; + fixed_t oldheight = mobjinfo[MT_RAY].height; INT32 i, j; static INT32 k;/* static for (at least) GCC 9.1 weirdness */ angle_t testang = 0; - mobjinfo[MT_NULL].spawnstate = S_INVISIBLE; - mobjinfo[MT_NULL].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP; - mobjinfo[MT_NULL].radius = mobjinfo[actor->type].radius; - mobjinfo[MT_NULL].height = mobjinfo[actor->type].height; + mobjinfo[MT_RAY].spawnstate = S_INVISIBLE; + mobjinfo[MT_RAY].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP; + mobjinfo[MT_RAY].radius = mobjinfo[actor->type].radius; + mobjinfo[MT_RAY].height = mobjinfo[actor->type].height; if (P_RandomChance(FRACUNIT/2)) // port priority 1? { @@ -3192,10 +3192,10 @@ void A_SkullAttack(mobj_t *actor) #undef dostuff - mobjinfo[MT_NULL].spawnstate = oldspawnstate; - mobjinfo[MT_NULL].flags = oldflags; - mobjinfo[MT_NULL].radius = oldradius; - mobjinfo[MT_NULL].height = oldheight; + mobjinfo[MT_RAY].spawnstate = oldspawnstate; + mobjinfo[MT_RAY].flags = oldflags; + mobjinfo[MT_RAY].radius = oldradius; + mobjinfo[MT_RAY].height = oldheight; } an = actor->angle >> ANGLETOFINESHIFT; From c73d3b424496e2f73d047a348758389b9fcee2ea Mon Sep 17 00:00:00 2001 From: SteelT Date: Wed, 2 Mar 2022 12:35:03 -0500 Subject: [PATCH 11/15] Spawn MT_RAY when attempting to spawn MT_NULL Some code assumes that P_SpawnMobj can never return NULL So spawn MT_RAY in it's place when attempting to spawn MT_NULL and show a console warning (cherry picked from commit 34f8464cbfc01adf1650da1311a0751fce5b0678) --- src/p_mobj.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index e6749e3d8..69c87975e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9880,10 +9880,17 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) if (type == MT_NULL) { +#if 0 #ifdef PARANOIA I_Error("Tried to spawn MT_NULL\n"); #endif return NULL; +#endif + // Hack: Some code assumes that P_SpawnMobj can never return NULL + // So replace MT_NULL with MT_RAY in the meantime + // Remove when dealt properly + CONS_Alert(CONS_WARNING, "Tried to spawn MT_NULL, using MT_RAY\n"); + type = MT_RAY; } mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); From cb46f8de39be7f367b1a65db704bcf3a6e53fa78 Mon Sep 17 00:00:00 2001 From: SteelT Date: Wed, 2 Mar 2022 12:46:24 -0500 Subject: [PATCH 12/15] Turn the console warning into a devmode print because turns out it happens more often than I thought (cherry picked from commit 893ea10a677274b8cb3aa0987f22f162521b6311) --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 69c87975e..aff165618 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9889,7 +9889,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // Hack: Some code assumes that P_SpawnMobj can never return NULL // So replace MT_NULL with MT_RAY in the meantime // Remove when dealt properly - CONS_Alert(CONS_WARNING, "Tried to spawn MT_NULL, using MT_RAY\n"); + CONS_Debug(DBG_GAMELOGIC, "Tried to spawn MT_NULL, using MT_RAY\n"); type = MT_RAY; } From 787b5283fc3a0833acae4a507138c7e7acb7a8d0 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Sep 2022 12:16:59 -0700 Subject: [PATCH 13/15] Fix big blunder by MEEEEEEE Blame 37c3a55dd --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 02d1c3636..4bdafb46b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4666,7 +4666,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer); else { - LUA_HookInt(HOOK(MapChange), gamemap); + LUA_HookInt(gamemap, HOOK(MapChange)); G_DoLoadLevel(resetplayer); } From c145d12db3b9182305aa8406a231bab353301959 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Jan 2022 07:51:05 -0500 Subject: [PATCH 14/15] Use hashes for terrain loading --- src/k_terrain.c | 92 ++++++++++++++++++++++++++++++------------------- src/k_terrain.h | 14 ++++---- 2 files changed, 64 insertions(+), 42 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 44b8b3163..5b12295df 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -91,6 +91,7 @@ t_splash_t *K_GetSplashByIndex(size_t checkIndex) --------------------------------------------------*/ t_splash_t *K_GetSplashByName(const char *checkName) { + UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN); size_t i; if (numSplashDefs == 0) @@ -102,7 +103,7 @@ t_splash_t *K_GetSplashByName(const char *checkName) { t_splash_t *s = &splashDefs[i]; - if (stricmp(checkName, s->name) == 0) + if (checkHash == s->hash && !strncmp(checkName, s->name, TERRAIN_NAME_LEN)) { // Name matches. return s; @@ -159,6 +160,7 @@ t_footstep_t *K_GetFootstepByIndex(size_t checkIndex) --------------------------------------------------*/ t_footstep_t *K_GetFootstepByName(const char *checkName) { + UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN); size_t i; if (numFootstepDefs == 0) @@ -170,7 +172,7 @@ t_footstep_t *K_GetFootstepByName(const char *checkName) { t_footstep_t *fs = &footstepDefs[i]; - if (stricmp(checkName, fs->name) == 0) + if (checkHash == fs->hash && !strncmp(checkName, fs->name, TERRAIN_NAME_LEN)) { // Name matches. return fs; @@ -227,21 +229,20 @@ terrain_t *K_GetTerrainByIndex(size_t checkIndex) --------------------------------------------------*/ terrain_t *K_GetTerrainByName(const char *checkName) { + UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN); size_t i; - if (numTerrainDefs == 0) + if (numTerrainDefs > 0) { - return NULL; - } - - for (i = 0; i < numTerrainDefs; i++) - { - terrain_t *t = &terrainDefs[i]; - - if (stricmp(checkName, t->name) == 0) + for (i = 0; i < numTerrainDefs; i++) { - // Name matches. - return t; + terrain_t *t = &terrainDefs[i]; + + if (checkHash == t->hash && !strncmp(checkName, t->name, TERRAIN_NAME_LEN)) + { + // Name matches. + return t; + } } } @@ -265,20 +266,19 @@ terrain_t *K_GetDefaultTerrain(void) --------------------------------------------------*/ terrain_t *K_GetTerrainForTextureName(const char *checkName) { + UINT32 checkHash = quickncasehash(checkName, 8); size_t i; - if (numTerrainFloorDefs == 0) + if (numTerrainFloorDefs > 0) { - return NULL; - } - - for (i = 0; i < numTerrainFloorDefs; i++) - { - t_floor_t *f = &terrainFloorDefs[i]; - - if (strncasecmp(checkName, f->textureName, 8) == 0) + for (i = 0; i < numTerrainFloorDefs; i++) { - return K_GetTerrainByIndex(f->terrainID); + t_floor_t *f = &terrainFloorDefs[i]; + + if (checkHash == f->textureHash && !strncmp(checkName, f->textureName, 8)) + { + return K_GetTerrainByIndex(f->terrainID); + } } } @@ -294,15 +294,15 @@ terrain_t *K_GetTerrainForTextureName(const char *checkName) --------------------------------------------------*/ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) { - texture_t *tex = NULL; - - if (textureNum < 0 || textureNum >= numtextures) + if (textureNum >= 0 && textureNum < numtextures) { - return NULL; + texture_t *tex = textures[textureNum]; + return K_GetTerrainForTextureName(tex->name); } - tex = textures[textureNum]; - return K_GetTerrainForTextureName(tex->name); + // This texture doesn't have a terrain directly applied to it, + // so we fallback to the default terrain. + return K_GetDefaultTerrain(); } /*-------------------------------------------------- @@ -1187,6 +1187,7 @@ static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(size_t, char *, c static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { char *tkn = M_GetToken((char *)data); + UINT32 tknHash = 0; size_t pos = 0; size_t i; @@ -1211,11 +1212,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { t_splash_t *s = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numSplashDefs; i++) { s = &splashDefs[i]; - if (stricmp(tkn, s->name) == 0) + if (tknHash == s->hash && !strncmp(tkn, s->name, TERRAIN_NAME_LEN)) { break; } @@ -1227,6 +1230,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) s = &splashDefs[i]; strncpy(s->name, tkn, TERRAIN_NAME_LEN); + s->hash = tknHash; + CONS_Printf("Created new Splash type '%s'\n", s->name); } @@ -1248,11 +1253,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { t_footstep_t *fs = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numFootstepDefs; i++) { fs = &footstepDefs[i]; - if (stricmp(tkn, fs->name) == 0) + if (tknHash == fs->hash && !strncmp(tkn, fs->name, TERRAIN_NAME_LEN)) { break; } @@ -1264,6 +1271,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) fs = &footstepDefs[i]; strncpy(fs->name, tkn, TERRAIN_NAME_LEN); + fs->hash = tknHash; + CONS_Printf("Created new Footstep type '%s'\n", fs->name); } @@ -1285,11 +1294,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { terrain_t *t = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numTerrainDefs; i++) { t = &terrainDefs[i]; - if (stricmp(tkn, t->name) == 0) + if (tknHash == t->hash && !strncmp(tkn, t->name, TERRAIN_NAME_LEN)) { break; } @@ -1301,6 +1312,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) t = &terrainDefs[i]; strncpy(t->name, tkn, TERRAIN_NAME_LEN); + t->hash = tknHash; + CONS_Printf("Created new Terrain type '%s'\n", t->name); } @@ -1333,11 +1346,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { t_floor_t *f = NULL; + tknHash = quickncasehash(tkn, 8); + for (i = 0; i < numTerrainFloorDefs; i++) { f = &terrainFloorDefs[i]; - if (stricmp(tkn, f->textureName) == 0) + if (f->textureHash == tknHash && !strncmp(tkn, f->textureName, 8)) { break; } @@ -1348,7 +1363,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) K_NewTerrainFloorDefs(); f = &terrainFloorDefs[i]; - strncpy(f->textureName, tkn, 9); + strncpy(f->textureName, tkn, 8); + f->textureHash = tknHash; } Z_Free(tkn); @@ -1398,11 +1414,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { terrain_t *t = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numTerrainDefs; i++) { t = &terrainDefs[i]; - if (stricmp(tkn, t->name) == 0) + if (tknHash == t->hash && !strncmp(tkn, t->name, TERRAIN_NAME_LEN)) { break; } @@ -1435,11 +1453,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { t_footstep_t *fs = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numFootstepDefs; i++) { fs = &footstepDefs[i]; - if (stricmp(tkn, fs->name) == 0) + if (tknHash == fs->hash && !strncmp(tkn, fs->name, TERRAIN_NAME_LEN)) { break; } diff --git a/src/k_terrain.h b/src/k_terrain.h index 924f7a1b1..c83f199d2 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -28,6 +28,7 @@ typedef struct t_splash_s // These are particles spawned when hitting the floor. char name[TERRAIN_NAME_LEN]; // Lookup name. + UINT32 hash; // Lookup name's hash. UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. UINT16 sfx; // Sound to play. @@ -48,6 +49,7 @@ typedef struct t_footstep_s // These are particles spawned when moving fast enough on a floor. char name[TERRAIN_NAME_LEN]; // Lookup name. + UINT32 hash; // Lookup name's hash. UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. UINT16 sfx; // Sound to play. @@ -79,6 +81,7 @@ typedef struct terrain_s // These are all of the properties that the floor gets. char name[TERRAIN_NAME_LEN]; // Lookup name. + UINT32 hash; // Lookup name's hash. size_t splashID; // Splash defintion ID. size_t footstepID; // Footstep defintion ID. @@ -93,16 +96,14 @@ typedef struct terrain_s typedef struct t_floor_s { // Terrain floor definition. - // Ties texture names to a . + // Ties a texture name to a terrain definition. - // (Could be optimized by using texture IDs instead of names, - // but was concerned because I recall sooomething about those not being netsafe? - // Someone confirm if I just hallucinated that. :V) - - char textureName[9]; // Floor texture name. + char textureName[8]; // Floor texture name. + UINT32 textureHash; // Floor texture hash. size_t terrainID; // Terrain definition ID. } t_floor_t; + /*-------------------------------------------------- size_t K_GetSplashHeapIndex(t_splash_t *splash); @@ -285,6 +286,7 @@ terrain_t *K_GetTerrainByIndex(size_t checkIndex); terrain_t *K_GetTerrainByName(const char *checkName); + /*-------------------------------------------------- terrain_t *K_GetDefaultTerrain(void); From abeec10d3243414c89755782d9f2776c3624d079 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Jan 2022 08:00:09 -0500 Subject: [PATCH 15/15] Brightmap loading uses hashes # Conflicts: # src/k_brightmap.c # src/k_brightmap.h --- src/k_brightmap.c | 10 +++++----- src/k_brightmap.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/k_brightmap.c b/src/k_brightmap.c index c62d645e6..a70d7a955 100644 --- a/src/k_brightmap.c +++ b/src/k_brightmap.c @@ -75,7 +75,7 @@ static brightmapStorage_t *K_GetBrightmapStorageByTextureName(const char *checkN { brightmapStorage_t *bms = &brightmapStorage[i]; - if (checkHash == bms->textureHash) + if (checkHash == bms->textureHash && !strncmp(checkName, bms->textureName, 8)) { // Name matches. return bms; @@ -119,8 +119,8 @@ static boolean K_BRIGHTLumpParser(UINT8 *data, size_t size) if (bms == NULL) { bms = K_NewBrightmap(); - strncpy(bms->textureName, tkn, 9); - bms->textureHash = quickncasehash(bms->textureName, 8); + strncpy(bms->textureName, tkn, 8); + bms->textureHash = quickncasehash(tkn, 8); } Z_Free(tkn); @@ -129,8 +129,8 @@ static boolean K_BRIGHTLumpParser(UINT8 *data, size_t size) if (tkn && pos < size) { - strncpy(bms->brightmapName, tkn, 9); - bms->brightmapHash = quickncasehash(bms->brightmapName, 8); + strncpy(bms->brightmapName, tkn, 8); + bms->brightmapHash = quickncasehash(tkn, 8); } else { diff --git a/src/k_brightmap.h b/src/k_brightmap.h index 72bc7e9df..33a1faec2 100644 --- a/src/k_brightmap.h +++ b/src/k_brightmap.h @@ -23,11 +23,11 @@ typedef struct brightmapStorage_s // Stores data for brightmap definitions, // before putting them into texturebrightmaps. - char textureName[9]; // The texture's name. - UINT32 textureHash; // The texture name's hash. + char textureName[8]; // The texture's name. + UINT32 textureHash; // The texture name's hash. - char brightmapName[9]; // The brightmap's name. - UINT32 brightmapHash; // The brightmap name's hash. + char brightmapName[8]; // The brightmap's name. + UINT32 brightmapHash; // The brightmap name's hash. } brightmapStorage_t; /*--------------------------------------------------