diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1f3d29e6..55c445453 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,6 +117,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 lua_blockmaplib.c lua_hudlib.c lua_hudlib_drawlist.c + lua_profile.cpp k_kart.c k_respawn.c k_collide.cpp diff --git a/src/cvars.cpp b/src/cvars.cpp index 1c0812598..cd160f6e7 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -460,7 +460,14 @@ consvar_t cv_usemouse = Player("use_mouse", "Off").values({{0, "Off"}, {1, "On"} consvar_t cv_vhseffect = Player("vhspause", "On").on_off(); // synchronize page flipping with screen refresh -consvar_t cv_vidwait = GraphicsDriver("vid_wait", "Off").on_off(); +extern "C++" +{ +namespace srb2::cvarhandler +{ +void on_set_vid_wait(); +} +} +consvar_t cv_vidwait = GraphicsDriver("vid_wait", "Off").on_off().onchange(srb2::cvarhandler::on_set_vid_wait); // if true, all sounds are loaded at game startup consvar_t precachesound = Player("precachesound", "Off").on_off(); @@ -855,6 +862,9 @@ consvar_t cv_devmode_screen = PlayerCheat("devmode_screen", "1").min_max(1, 4).d consvar_t cv_drawpickups = PlayerCheat("drawpickups", "Yes").yes_no().description("Hide rings, spheres, item capsules, prison capsules (visual only)"); consvar_t cv_drawinput = PlayerCheat("drawinput", "No").yes_no().description("Draw turn inputs outside of Record Attack (turn solver debugging)"); +void lua_profile_OnChange(void); +consvar_t cv_lua_profile = PlayerCheat("lua_profile", "0").values(CV_Unsigned).onchange(lua_profile_OnChange).description("Show hook timings over an average of N tics"); + void CV_palette_OnChange(void); consvar_t cv_palette = PlayerCheat("palette", "").onchange_noinit(CV_palette_OnChange).description("Force palette to a different lump"); consvar_t cv_palettenum = PlayerCheat("palettenum", "0").values(CV_Unsigned).onchange_noinit(CV_palette_OnChange).description("Use a different sub-palette by default"); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 9d39bb81d..1fa557757 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2565,6 +2565,7 @@ void CL_ClearPlayer(INT32 playernum) // TODO: Any better handling in store? P_SetTarget(&players[playernum].flickyAttacker, NULL); P_SetTarget(&players[playernum].powerup.flickyController, NULL); + P_SetTarget(&players[playernum].powerup.barrier, NULL); // These are camera items and possibly belong to multiple players. P_SetTarget(&players[playernum].skybox.viewpoint, NULL); @@ -6146,6 +6147,7 @@ boolean TryRunTics(tic_t realtics) DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); + ps_prevtictime = ps_tictime; ps_tictime = I_GetPreciseTime(); dontRun = ExtraDataTicker(); diff --git a/src/d_main.cpp b/src/d_main.cpp index 524a2225e..d1814813b 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -96,6 +96,10 @@ #include "lua_script.h" +#include "lua_profile.h" + +extern "C" consvar_t cv_lua_profile; + /* Manually defined asset hashes * Last updated 2019 / 01 / 18 - Kart v1.0.2 - Main assets * Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart @@ -765,6 +769,11 @@ static bool D_Display(void) M_DrawPerfStats(); } + if (cv_lua_profile.value > 0) + { + LUA_RenderTimers(); + } + ps_swaptime = I_GetPreciseTime(); I_FinishUpdate(); // page flip or blit buffer ps_swaptime = I_GetPreciseTime() - ps_swaptime; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3b38b163b..8edba171b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -70,6 +70,7 @@ #include "k_powerup.h" #include "k_roulette.h" #include "k_bans.h" +#include "k_director.h" #ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES #include "m_avrecorder.h" @@ -1461,6 +1462,33 @@ void D_SendPlayerConfig(UINT8 n) WeaponPref_Send(n); } +static camera_t *LocalMutableCamera(INT32 playernum) +{ + if (demo.playback) + { + // TODO: could have splitscreen support + if (!camera[0].freecam) + { + return NULL; + } + + return &camera[0]; + } + + if (G_IsPartyLocal(playernum)) + { + UINT8 viewnum = G_PartyPosition(playernum); + camera_t *cam = &camera[viewnum]; + + if (cam->freecam || (players[playernum].spectator && !K_DirectorIsAvailable(viewnum))) + { + return cam; + } + } + + return NULL; +} + void D_Cheat(INT32 playernum, INT32 cheat, ...) { va_list ap; @@ -1468,12 +1496,62 @@ void D_Cheat(INT32 playernum, INT32 cheat, ...) UINT8 buf[64]; UINT8 *p = buf; + camera_t *cam; + if ((cam = LocalMutableCamera(playernum))) + { + switch (cheat) + { + case CHEAT_RELATIVE_TELEPORT: + va_start(ap, cheat); + cam->x += va_arg(ap, fixed_t); + cam->y += va_arg(ap, fixed_t); + cam->z += va_arg(ap, fixed_t); + va_end(ap); + return; + + case CHEAT_TELEPORT: + va_start(ap, cheat); + cam->x = va_arg(ap, fixed_t); + cam->y = va_arg(ap, fixed_t); + cam->z = va_arg(ap, fixed_t); + va_end(ap); + return; + + case CHEAT_ANGLE: + va_start(ap, cheat); + cam->angle = va_arg(ap, angle_t); + va_end(ap); + return; + + default: + break; + } + } + if (!CV_CheatsEnabled()) { CONS_Printf("This cannot be used without cheats enabled.\n"); return; } + if (demo.playback && cheat == CHEAT_DEVMODE) + { + // There is no networking in demos, but devmode is + // too useful to be inaccessible! + // TODO: maybe allow everything, even though it would + // desync replays? May be useful for debugging. + va_start(ap, cheat); + cht_debug = va_arg(ap, UINT32); + va_end(ap); + return; + } + + // sanity check + if (demo.playback) + { + return; + } + WRITEUINT8(p, playernum); WRITEUINT8(p, cheat); diff --git a/src/d_player.h b/src/d_player.h index 3f96e7148..85d30355f 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -212,6 +212,8 @@ typedef enum NUMPOWERUPS = ENDOFPOWERUPS - FIRSTPOWERUP, } kartitems_t; +#define POWERUP_BIT(x) (1 << ((x) - FIRSTPOWERUP)) + typedef enum { KSHIELD_NONE = 0, @@ -546,6 +548,7 @@ struct powerupvars_t { UINT16 barrierTimer; UINT16 rhythmBadgeTimer; mobj_t *flickyController; + mobj_t *barrier; }; // player_t struct for all alternative viewpoint variables diff --git a/src/deh_tables.c b/src/deh_tables.c index 7175b7a35..7fbf62af3 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4787,6 +4787,10 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_BLENDEYE_PUYO_SHOCK", "S_BLENDEYE_PUYO_DIE", "S_BLENDEYE_PUYO_DUST", + + "S_MEGABARRIER1", + "S_MEGABARRIER2", + "S_MEGABARRIER3", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -5994,6 +5998,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BLENDEYE_PUYO", "MT_BLENDEYE_PUYO_DUST", "MT_BLENDEYE_PUYO_DUST_COFFEE", + "MT_MEGABARRIER", }; const char *const MOBJFLAG_LIST[] = { @@ -6653,6 +6658,7 @@ struct int_const_s const INT_CONST[] = { {"RF_SHADOWEFFECTS",RF_SHADOWEFFECTS}, {"RF_DROPSHADOW",RF_DROPSHADOW}, {"RF_ABSOLUTELIGHTLEVEL",RF_ABSOLUTELIGHTLEVEL}, + {"RF_REDUCEVFX",RF_REDUCEVFX}, {"RF_DONTDRAW",RF_DONTDRAW}, {"RF_DONTDRAWP1",RF_DONTDRAWP1}, {"RF_DONTDRAWP2",RF_DONTDRAWP2}, diff --git a/src/g_game.c b/src/g_game.c index 83a8c7563..e5c42647c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2262,6 +2262,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) P_SetTarget(&players[player].awayview.mobj, NULL); P_SetTarget(&players[player].flickyAttacker, NULL); P_SetTarget(&players[player].powerup.flickyController, NULL); + P_SetTarget(&players[player].powerup.barrier, NULL); // The following pointers are safe to set directly, because the end goal should be refcount consistency before and after remanifestation. ringShooter = players[player].ringShooter; @@ -5627,6 +5628,10 @@ void G_SaveGameData(void) // Also save profiles here. PR_SaveProfiles(); + + #ifdef DEVELOP + CONS_Alert(CONS_NOTICE, M_GetText("Gamedata saved.\n")); + #endif } #define VERSIONSIZE 16 diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index b8cfc5784..5bb4c2990 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3383,7 +3383,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) } if (R_ThingIsSemiBright(spr->mobj)) - lightlevel = 128 + (lightlevel>>1); + lightlevel = 192 + (lightlevel>>1); for (i = 0; i < sector->numlights; i++) { @@ -3398,7 +3398,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) lightlevel = *list[i].lightlevel > 255 ? 255 : *list[i].lightlevel; if (R_ThingIsSemiBright(spr->mobj)) - lightlevel = 128 + (lightlevel>>1); + lightlevel = 192 + (lightlevel>>1); } if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS)) diff --git a/src/info.c b/src/info.c index 71644da80..49ca2f610 100644 --- a/src/info.c +++ b/src/info.c @@ -966,6 +966,8 @@ char sprnames[NUMSPRITES + 1][5] = "PUYD", "PUYE", + "MGSH", // Mega Barrier + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later "VIEW", }; @@ -2025,23 +2027,23 @@ state_t states[NUMSTATES] = {SPR_TFLM, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_TEAM_SPINFIRE1}, // S_TEAM_SPINFIRE6 // Floor Spike - {SPR_USPK, 0,-1, {A_SpikeRetract}, 1, 0, S_SPIKE2}, // S_SPIKE1 -- Fully extended - {SPR_USPK, 1, 2, {A_Pain}, 0, 0, S_SPIKE3}, // S_SPIKE2 - {SPR_USPK, 2, 2, {NULL}, 0, 0, S_SPIKE4}, // S_SPIKE3 - {SPR_USPK, 3,-1, {A_SpikeRetract}, 0, 0, S_SPIKE5}, // S_SPIKE4 -- Fully retracted - {SPR_USPK, 2, 2, {A_Pain}, 0, 0, S_SPIKE6}, // S_SPIKE5 - {SPR_USPK, 1, 2, {NULL}, 0, 0, S_SPIKE1}, // S_SPIKE6 - {SPR_USPK, 4,-1, {NULL}, 0, 0, S_NULL}, // S_SPIKED1 -- Busted spike particles - {SPR_USPK, 5,-1, {NULL}, 0, 0, S_NULL}, // S_SPIKED2 + {SPR_USPK, FF_SEMIBRIGHT|0,-1, {A_SpikeRetract}, 1, 0, S_SPIKE2}, // S_SPIKE1 -- Fully extended + {SPR_USPK, FF_SEMIBRIGHT|1, 2, {A_Pain}, 0, 0, S_SPIKE3}, // S_SPIKE2 + {SPR_USPK, FF_SEMIBRIGHT|0, 2, {NULL}, 0, 0, S_SPIKE4}, // S_SPIKE3 + {SPR_USPK, FF_SEMIBRIGHT|3,-1, {A_SpikeRetract}, 0, 0, S_SPIKE5}, // S_SPIKE4 -- Fully retracted + {SPR_USPK, FF_SEMIBRIGHT|2, 2, {A_Pain}, 0, 0, S_SPIKE6}, // S_SPIKE5 + {SPR_USPK, FF_SEMIBRIGHT|1, 2, {NULL}, 0, 0, S_SPIKE1}, // S_SPIKE6 + {SPR_USPK, FF_SEMIBRIGHT|4,-1, {NULL}, 0, 0, S_NULL}, // S_SPIKED1 -- Busted spike particles + {SPR_USPK, FF_SEMIBRIGHT|5,-1, {NULL}, 0, 0, S_NULL}, // S_SPIKED2 // Wall Spike - {SPR_WSPK, 0|FF_PAPERSPRITE,-1, {A_SpikeRetract}, 1, 0, S_WALLSPIKE2}, // S_WALLSPIKE1 -- Fully extended - {SPR_WSPK, 1|FF_PAPERSPRITE, 2, {A_Pain}, 0, 0, S_WALLSPIKE3}, // S_WALLSPIKE2 - {SPR_WSPK, 2|FF_PAPERSPRITE, 2, {NULL}, 0, 0, S_WALLSPIKE4}, // S_WALLSPIKE3 - {SPR_WSPK, 3|FF_PAPERSPRITE,-1, {A_SpikeRetract}, 0, 0, S_WALLSPIKE5}, // S_WALLSPIKE4 -- Fully retracted - {SPR_WSPK, 2|FF_PAPERSPRITE, 2, {A_Pain}, 0, 0, S_WALLSPIKE6}, // S_WALLSPIKE5 - {SPR_WSPK, 1|FF_PAPERSPRITE, 2, {NULL}, 0, 0, S_WALLSPIKE1}, // S_WALLSPIKE6 - {SPR_WSPB, 0|FF_PAPERSPRITE,-1, {NULL}, 0, 0, S_NULL}, // S_WALLSPIKEBASE -- Base + {SPR_WSPK, 0|FF_SEMIBRIGHT|FF_PAPERSPRITE,-1, {A_SpikeRetract}, 1, 0, S_WALLSPIKE2}, // S_WALLSPIKE1 -- Fully extended + {SPR_WSPK, 1|FF_SEMIBRIGHT|FF_PAPERSPRITE, 2, {A_Pain}, 0, 0, S_WALLSPIKE3}, // S_WALLSPIKE2 + {SPR_WSPK, 2|FF_SEMIBRIGHT|FF_PAPERSPRITE, 2, {NULL}, 0, 0, S_WALLSPIKE4}, // S_WALLSPIKE3 + {SPR_WSPK, 3|FF_SEMIBRIGHT|FF_PAPERSPRITE,-1, {A_SpikeRetract}, 0, 0, S_WALLSPIKE5}, // S_WALLSPIKE4 -- Fully retracted + {SPR_WSPK, 2|FF_SEMIBRIGHT|FF_PAPERSPRITE, 2, {A_Pain}, 0, 0, S_WALLSPIKE6}, // S_WALLSPIKE5 + {SPR_WSPK, 1|FF_SEMIBRIGHT|FF_PAPERSPRITE, 2, {NULL}, 0, 0, S_WALLSPIKE1}, // S_WALLSPIKE6 + {SPR_WSPB, 0|FF_SEMIBRIGHT|FF_PAPERSPRITE,-1, {NULL}, 0, 0, S_NULL}, // S_WALLSPIKEBASE -- Base {SPR_WSPK, 4,-1, {NULL}, 0, 0, S_NULL}, // S_WALLSPIKED1 -- Busted spike particles {SPR_WSPK, 5,-1, {NULL}, 0, 0, S_NULL}, // S_WALLSPIKED2 @@ -5613,6 +5615,10 @@ state_t states[NUMSTATES] = {SPR_PUYA, 3, -1, {A_BlendEyePuyoHack}, 0, 0, S_NULL}, // S_BLENDEYE_PUYO_SHOCK, {SPR_PUYA, 4|FF_ANIMATE, 5, {A_BlendEyePuyoHack}, 2, 2, S_NULL}, // S_BLENDEYE_PUYO_DIE, {SPR_PUYA, 5, 2, {A_BlendEyePuyoHack}, 0, 0, S_BLENDEYE_PUYO_DIE}, // S_BLENDEYE_PUYO_DUST, + + {SPR_MGSH, 2|FF_PAPERSPRITE|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_MEGABARRIER1, + {SPR_MGSH, 1|FF_PAPERSPRITE|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_MEGABARRIER2, + {SPR_MGSH, 0|FF_PAPERSPRITE|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_MEGABARRIER3, }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -5802,7 +5808,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 1000, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SOLID|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9365,7 +9371,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4, // mass 0, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPHEIGHT|MF_NOHITLAGFORME, // flags + MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPHEIGHT|MF_NOHITLAGFORME|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -9392,7 +9398,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4, // mass 0, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY|MF_NOCLIPHEIGHT|MF_PAPERCOLLISION|MF_NOHITLAGFORME, // flags + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY|MF_NOCLIPHEIGHT|MF_PAPERCOLLISION|MF_NOHITLAGFORME|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -24786,7 +24792,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 8, // speed 28*FRACUNIT, // radius - 56*FRACUNIT, // height + 0*FRACUNIT, // height 1, // display offset 16, // mass 0, // damage @@ -24840,7 +24846,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 8, // speed 28*FRACUNIT, // radius - 56*FRACUNIT, // height + 0*FRACUNIT, // height 1, // display offset 16, // mass 0, // damage @@ -24867,7 +24873,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 0, // speed 8<colorized = true; } - K_ReduceVFX(spark, NULL); + K_ReduceVFXForEveryone(spark); } } diff --git a/src/k_kart.c b/src/k_kart.c index e4c531884..362ab9519 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -320,7 +320,7 @@ UINT32 K_GetPlayerDontDrawFlag(player_t *player) return flag; } -void K_ReduceVFX(mobj_t *mo, player_t *owner) +void K_ReduceVFXForEveryone(mobj_t *mo) { if (cv_reducevfx.value == 0) { @@ -329,11 +329,6 @@ void K_ReduceVFX(mobj_t *mo, player_t *owner) } mo->renderflags |= RF_DONTDRAW; - - if (owner != NULL) - { - mo->renderflags &= ~K_GetPlayerDontDrawFlag(owner); - } } // Angle reflection used by springs & speed pads @@ -735,6 +730,11 @@ fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) static void K_SpawnBumpForObjs(mobj_t *mobj1, mobj_t *mobj2) { + if (mobj1->type == MT_KART_LEFTOVER && mobj1->health == 0) + { + return; + } + mobj_t *fx = P_SpawnMobj( mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, @@ -1739,7 +1739,8 @@ spawn_brake_dust P_SetScale(spark, (spark->destscale = FixedMul(scale, spark->scale))); - K_ReduceVFX(spark, master->player); + P_SetTarget(&spark->owner, master); + spark->renderflags |= RF_REDUCEVFX; } static void K_SpawnBrakeVisuals(player_t *player) @@ -1816,7 +1817,8 @@ void K_SpawnDriftBoostClip(player_t *player) P_RandomFlip(P_RandomRange(PR_DECORATION, FRACUNIT/2, FRACUNIT)), FixedMul(scale, player->speed)); - K_ReduceVFX(clip, player); + P_SetTarget(&clip->owner, player->mo); + clip->renderflags |= RF_REDUCEVFX; } void K_SpawnDriftBoostClipSpark(mobj_t *clip) @@ -1873,7 +1875,8 @@ static void K_SpawnGenericSpeedLines(player_t *player, boolean top) } K_MatchGenericExtraFlags(fast, player->mo); - K_ReduceVFX(fast, player); + P_SetTarget(&fast->owner, player->mo); + fast->renderflags |= RF_REDUCEVFX; if (top) { @@ -1952,7 +1955,8 @@ void K_SpawnInvincibilitySpeedLines(mobj_t *mo) fast->momz = 3*P_GetMobjZMovement(mo)/4; K_MatchGenericExtraFlags(fast, mo); - K_ReduceVFX(fast, mo->player); + P_SetTarget(&fast->owner, mo); + fast->renderflags |= RF_REDUCEVFX; fast->color = mo->color; fast->colorized = true; @@ -3066,7 +3070,8 @@ void K_SpawnWaterRunParticles(mobj_t *mobj) water->momz = mobj->momz; P_SetScale(water, trailScale); P_SetMobjState(water, curUnderlayFrame); - K_ReduceVFX(water, mobj->player); + P_SetTarget(&water->owner, mobj); + water->renderflags |= RF_REDUCEVFX; // overlay water = P_SpawnMobj(x1, y1, @@ -3078,7 +3083,8 @@ void K_SpawnWaterRunParticles(mobj_t *mobj) water->momz = mobj->momz; P_SetScale(water, trailScale); P_SetMobjState(water, curOverlayFrame); - K_ReduceVFX(water, mobj->player); + P_SetTarget(&water->owner, mobj); + water->renderflags |= RF_REDUCEVFX; // Right // Underlay @@ -3091,7 +3097,8 @@ void K_SpawnWaterRunParticles(mobj_t *mobj) water->momz = mobj->momz; P_SetScale(water, trailScale); P_SetMobjState(water, curUnderlayFrame); - K_ReduceVFX(water, mobj->player); + P_SetTarget(&water->owner, mobj); + water->renderflags |= RF_REDUCEVFX; // Overlay water = P_SpawnMobj(x2, y2, @@ -3103,7 +3110,8 @@ void K_SpawnWaterRunParticles(mobj_t *mobj) water->momz = mobj->momz; P_SetScale(water, trailScale); P_SetMobjState(water, curOverlayFrame); - K_ReduceVFX(water, mobj->player); + P_SetTarget(&water->owner, mobj); + water->renderflags |= RF_REDUCEVFX; if (!S_SoundPlaying(mobj, sfx_s3kdbs)) { @@ -4105,6 +4113,7 @@ void K_InitStumbleIndicator(player_t *player) P_SetTarget(&player->stumbleIndicator, new); P_SetTarget(&new->target, player->mo); + new->renderflags |= RF_DONTDRAW; } void K_InitWavedashIndicator(player_t *player) @@ -4130,6 +4139,7 @@ void K_InitWavedashIndicator(player_t *player) P_SetTarget(&player->wavedashIndicator, new); P_SetTarget(&new->target, player->mo); + new->renderflags |= RF_DONTDRAW; } void K_InitTrickIndicator(player_t *player) @@ -5218,7 +5228,8 @@ static void K_SpawnDriftElectricity(player_t *player) spark->momz = mo->momz; spark->color = color; K_GenericExtraFlagsNoZAdjust(spark, mo); - K_ReduceVFX(spark, player); + P_SetTarget(&spark->owner, mo); + spark->renderflags |= RF_REDUCEVFX; spark->spritexscale += scalefactor/3; spark->spriteyscale += scalefactor/8; @@ -5289,7 +5300,8 @@ void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave) sparkangle += ANGLE_90; - K_ReduceVFX(spark, player); + P_SetTarget(&spark->owner, mo); + spark->renderflags |= RF_REDUCEVFX; } } } @@ -5440,7 +5452,8 @@ static void K_SpawnDriftSparks(player_t *player) spark->tics += trail; K_MatchGenericExtraFlags(spark, player->mo); - K_ReduceVFX(spark, player); + P_SetTarget(&spark->owner, player->mo); + spark->renderflags |= RF_REDUCEVFX; } if (player->driftcharge >= dsthree) @@ -10152,6 +10165,13 @@ static void K_KartDrift(player_t *player, boolean onground) } } + if (player->airtime > 2) // Arbitrary number. Small discontinuities due to Super Jank shouldn't thrash your handling properties. + { + player->aizdriftstrat = 0; + keepsliptide = false; + } + + if ((player->aizdriftstrat && !player->drift) || (keepsliptide)) { @@ -10630,7 +10650,8 @@ void K_KartEbrakeVisuals(player_t *p) wave = P_SpawnMobj(p->mo->x, p->mo->y, p->mo->floorz, MT_SOFTLANDING); P_InstaScale(wave, p->mo->scale); P_SetTarget(&wave->target, p->mo); - K_ReduceVFX(wave, p); + P_SetTarget(&wave->owner, p->mo); + wave->renderflags |= RF_REDUCEVFX; } // sound @@ -10663,7 +10684,8 @@ void K_KartEbrakeVisuals(player_t *p) p->mo->hprev->angle = p->mo->angle; p->mo->hprev->fuse = TICRATE/2; // When we leave spindash for any reason, make sure this bubble goes away soon after. K_FlipFromObject(p->mo->hprev, p->mo); - K_ReduceVFX(p->mo->hprev, p); + P_SetTarget(&p->mo->hprev->owner, p->mo); + p->mo->hprev->renderflags |= RF_REDUCEVFX; p->mo->hprev->sprzoff = p->mo->sprzoff; p->mo->hprev->colorized = false; @@ -10681,7 +10703,8 @@ void K_KartEbrakeVisuals(player_t *p) spdl->colorized = true; spdl->color = SKINCOLOR_WHITE; K_MatchGenericExtraFlags(spdl, p->mo); - K_ReduceVFX(spdl, p); + P_SetTarget(&spdl->owner, p->mo); + spdl->renderflags |= RF_REDUCEVFX; P_SetScale(spdl, p->mo->scale); // squish the player a little bit. @@ -10800,8 +10823,6 @@ static void K_KartSpindashDust(mobj_t *parent) dust->momx = FixedMul(hmomentum, FINECOSINE(ang >> ANGLETOFINESHIFT)); dust->momy = FixedMul(hmomentum, FINESINE(ang >> ANGLETOFINESHIFT)); dust->momz = vmomentum * flip; - - //K_ReduceVFX(dust, parent->player); } } @@ -10829,7 +10850,8 @@ static void K_KartSpindashWind(mobj_t *parent) wind->momz = 3 * P_GetMobjZMovement(parent) / 4; K_MatchGenericExtraFlags(wind, parent); - K_ReduceVFX(wind, parent->player); + P_SetTarget(&wind->owner, parent); + wind->renderflags |= RF_REDUCEVFX; } // Time after which you get a thrust for releasing spindash @@ -13078,7 +13100,8 @@ void K_SetTireGrease(player_t *player, tic_t tics) P_SetTarget(&grease->target, player->mo); grease->angle = K_MomentumAngle(player->mo); grease->extravalue1 = i; - K_ReduceVFX(grease, player); + P_SetTarget(&grease->owner, player->mo); + grease->renderflags |= RF_REDUCEVFX; } } diff --git a/src/k_kart.h b/src/k_kart.h index 44b7283dc..cb5e52474 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -67,7 +67,7 @@ void K_TimerReset(void); void K_TimerInit(void); UINT32 K_GetPlayerDontDrawFlag(player_t *player); -void K_ReduceVFX(mobj_t *mo, player_t *owner); +void K_ReduceVFXForEveryone(mobj_t *mo); boolean K_IsPlayerLosing(player_t *player); fixed_t K_GetKartGameSpeedScalar(SINT8 value); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 1ec3d6598..ca030b7a1 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -2089,6 +2089,8 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) void M_DrawCharacterSelect(void) { + const UINT8 pid = 0; + UINT8 i, j, k; UINT8 priority = 0; INT16 quadx, quady; @@ -2101,6 +2103,19 @@ void M_DrawCharacterSelect(void) priority = setup_animcounter % setup_numplayers; } + { + const int kLeft = 80; + const int kTop = 6; + const int kButtonWidth = 16; + INT32 x = basex + kLeft; + + K_drawButton((x += 18) * FRACUNIT, (kTop - 3) * FRACUNIT, 0, kp_button_r, M_MenuButtonPressed(pid, MBT_R)); + V_DrawThinString((x += kButtonWidth), kTop, 0, "Info"); + + K_drawButton((x += 58) * FRACUNIT, (kTop - 1) * FRACUNIT, 0, kp_button_c[1], M_MenuButtonPressed(pid, MBT_C)); + V_DrawThinString((x += kButtonWidth), kTop, 0, "Default"); + } + // We have to loop twice -- first time to draw the drop shadows, a second time to draw the icons. if (forceskin == false) { diff --git a/src/k_objects.h b/src/k_objects.h index 619b8f81c..8a95e414d 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -249,6 +249,7 @@ void Obj_getPlayerOffRideroid(mobj_t *mo); // used in p_map.c to get off of em w /* LSZ Bungee */ void Obj_BungeeSpecial(mobj_t *mo, player_t *p); // used when the player touches the bungee, to be used in p_inter.c void Obj_playerBungeeThink(player_t *p); // player interaction with the bungee. The bungee is to be stored in p->mo->tracer. +void Obj_EndBungee(player_t *p); /* LSZ Balls */ void Obj_EggBallSpawnerThink(mobj_t *mo); @@ -293,6 +294,9 @@ void Obj_BallSwitchThink(mobj_t *mobj); void Obj_BallSwitchTouched(mobj_t *mobj, mobj_t *toucher); void Obj_BallSwitchDamaged(mobj_t *mobj, mobj_t *inflictor, mobj_t *source); +/* Barrier Power-Up */ +void Obj_SpawnMegaBarrier(player_t *player); +boolean Obj_MegaBarrierThink(mobj_t *mobj); #ifdef __cplusplus } // extern "C" diff --git a/src/k_powerup.cpp b/src/k_powerup.cpp index 3d1fcb312..51b9f96c7 100644 --- a/src/k_powerup.cpp +++ b/src/k_powerup.cpp @@ -27,17 +27,19 @@ tic_t K_PowerUpRemaining(const player_t* player, kartitems_t powerup) } } -boolean K_AnyPowerUpRemaining(const player_t* player) +UINT32 K_AnyPowerUpRemaining(const player_t* player) { + UINT32 mask = 0; + for (int k = FIRSTPOWERUP; k < ENDOFPOWERUPS; ++k) { if (K_PowerUpRemaining(player, static_cast(k))) { - return true; + mask |= POWERUP_BIT(k); } } - return false; + return mask; } void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time) @@ -56,6 +58,7 @@ void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time) case POWERUP_BARRIER: player->powerup.barrierTimer += time; + Obj_SpawnMegaBarrier(player); break; case POWERUP_BUMPER: diff --git a/src/k_powerup.h b/src/k_powerup.h index 0598e45ce..0a94edf39 100644 --- a/src/k_powerup.h +++ b/src/k_powerup.h @@ -9,7 +9,7 @@ extern "C" { #endif tic_t K_PowerUpRemaining(const player_t *player, kartitems_t powerup); -boolean K_AnyPowerUpRemaining(const player_t *player); +UINT32 K_AnyPowerUpRemaining(const player_t *player); // returns POWERUP_BIT mask void K_GivePowerUp(player_t *player, kartitems_t powerup, tic_t timer); void K_DropPowerUps(player_t *player); diff --git a/src/k_terrain.c b/src/k_terrain.c index d2d9a3f8a..1b1e4bb72 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -628,7 +628,8 @@ void K_ProcessTerrainEffect(mobj_t *mo) spark->fuse = 9; spark->cusval = K_StairJankFlip(ANGLE_90); P_SetTarget(&spark->target, mo); - K_ReduceVFX(spark, player); + P_SetTarget(&spark->owner, mo); + spark->renderflags |= RF_REDUCEVFX; } player->stairjank = 17; diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 65083e0e0..f73578a81 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -22,7 +22,9 @@ #include "lua_libs.h" #include "lua_hook.h" #include "lua_hud.h" // hud_running errors +#include "lua_profile.h" +#include "command.h" #include "m_perfstats.h" #include "d_netcmd.h" // for cv_perfstats #include "i_system.h" // I_GetPreciseTime @@ -365,6 +367,16 @@ static boolean prepare_string_hook return false; } +static boolean prepare_hud_hook +( + Hook_State * hook, + int hook_type +){ + return init_hook_type(hook, 0, + hook_type, 0, NULL, + hudHookIds[hook_type].numHooks); +} + static void init_hook_call ( Hook_State * hook, @@ -392,9 +404,52 @@ static void get_hook_from_table(Hook_State *hook, int n) lua_getref(gL, hookRefs[hook->id]); } +static int pcall(Hook_State *hook) +{ + return lua_pcall(gL, hook->values, hook->results, EINDEX); +} + +static const char *hook_name(Hook_State *hook) +{ + if (hud_running) + { + return hudHookNames[hook->hook_type]; + } + else if (hook->string) + { + return stringHookNames[hook->hook_type]; + } + else if (hook->mobj_type > 0) + { + return mobjHookNames[hook->hook_type]; + } + else + { + return hookNames[hook->hook_type]; + } +} + +static int pcall_timed_or_untimed(Hook_State *hook) +{ + extern consvar_t cv_lua_profile; + + if (!hud_running && cv_lua_profile.value > 0) + { + lua_timer_t *timer = LUA_BeginFunctionTimer(gL, -1 - hook->values, hook_name(hook)); + int k = pcall(hook); + LUA_EndFunctionTimer(timer); + + return k; + } + else + { + return pcall(hook); + } +} + static int call_single_hook_no_copy(Hook_State *hook) { - if (lua_pcall(gL, hook->values, hook->results, EINDEX) == 0) + if (pcall_timed_or_untimed(hook) == 0) { if (hook->results > 0) { @@ -625,13 +680,9 @@ int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) void LUA_HookHUD(huddrawlist_h list, int hook_type) { - const hook_t * map = &hudHookIds[hook_type]; Hook_State hook; - if (map->numHooks > 0) + if (prepare_hud_hook(&hook, hook_type)) { - start_hook_stack(); - begin_hook_values(&hook); - LUA_SetHudHook(hook_type, list); hud_running = true; // local hook @@ -640,7 +691,7 @@ void LUA_HookHUD(huddrawlist_h list, int hook_type) V_ClearClipRect(); init_hook_call(&hook, 0, res_none); - call_mapped(&hook, map); + call_mapped(&hook, &hudHookIds[hook_type]); hud_running = false; } diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 51751f996..7ce40beb0 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -109,6 +109,7 @@ enum mobj_e { mobj_stringargs, mobj_reappear, mobj_punt_ref, + mobj_owner, }; static const char *const mobj_opt[] = { @@ -198,6 +199,7 @@ static const char *const mobj_opt[] = { "stringargs", "reappear", "punt_ref", + "owner", NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field]) @@ -508,6 +510,14 @@ static int mobj_get(lua_State *L) } LUA_PushUserdata(L, mo->punt_ref, META_MOBJ); break; + case mobj_owner: + if (mo->owner && P_MobjWasRemoved(mo->owner)) + { // don't put invalid mobj back into Lua. + P_SetTarget(&mo->owner, NULL); + return 0; + } + LUA_PushUserdata(L, mo->owner, META_MOBJ); + break; default: // extra custom variables in Lua memory lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); @@ -910,6 +920,15 @@ static int mobj_set(lua_State *L) P_SetTarget(&mo->punt_ref, punt_ref); } break; + case mobj_owner: + if (lua_isnil(L, 3)) + P_SetTarget(&mo->owner, NULL); + else + { + mobj_t *owner = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); + P_SetTarget(&mo->owner, owner); + } + break; default: lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/lua_profile.cpp b/src/lua_profile.cpp new file mode 100644 index 000000000..ee2a15ed0 --- /dev/null +++ b/src/lua_profile.cpp @@ -0,0 +1,242 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { +#include "blua/lua.h" +}; + +#include "v_draw.hpp" + +#include "command.h" +#include "doomtype.h" +#include "i_system.h" +#include "lua_profile.h" +#include "m_perfstats.h" + +extern "C" consvar_t cv_lua_profile; + +namespace +{ + +precise_t g_time_reference; +int g_tics_counted; + +double g_running_tic_time; +double g_avg_tic_time; + +bool g_invalid; + +}; // namespace + +struct lua_timer_t +{ + struct Stat + { + double calls = 0; + double time = 0.0; + }; + + Stat running, avg; +}; + +namespace +{ + +std::unordered_map g_tic_timers; + +}; // namespace + +lua_timer_t* LUA_BeginFunctionTimer(lua_State* L, int fn_idx, const char* name) +{ + lua_Debug ar; + + lua_pushvalue(L, fn_idx); + lua_getinfo(L, ">S", &ar); + + auto label = [&] + { + using sv = std::string_view; + sv view{ar.source}; + + switch (view.front()) + { + case '@': // file + if (std::size_t p = view.rfind('/'); p != sv::npos) // lump or base + { + return view.substr(p + 1); + } + break; + + case '=': // ?? + view.remove_prefix(1); + break; + } + + return view; + }; + + auto [it, ins] = g_tic_timers.try_emplace(fmt::format("{}:{} ({})", label(), ar.linedefined, name)); + auto& [key, timer] = *it; + + g_time_reference = I_GetPreciseTime(); + + return &timer; +} + +void LUA_EndFunctionTimer(lua_timer_t* timer) +{ + precise_t t = I_GetPreciseTime() - g_time_reference; + double precision = I_GetPrecisePrecision(); + + timer->running.time += t / precision; + timer->running.calls += 1.0; +} + +void LUA_ResetTicTimers(void) +{ + if (cv_lua_profile.value <= 0) + { + return; + } + + if (g_tics_counted < cv_lua_profile.value) + { + g_tics_counted++; + + double precision = I_GetPrecisePrecision(); + g_running_tic_time += ps_prevtictime / precision; + } + else + { + double counted = g_tics_counted; + + for (auto& [key, timer] : g_tic_timers) + { + timer.avg = {timer.running.calls / counted, timer.running.time / counted}; + timer.running = {}; + } + + g_avg_tic_time = g_running_tic_time / counted; + g_running_tic_time = 0.0; + + g_tics_counted = 1; + + g_invalid = false; + } +} + +void LUA_RenderTimers(void) +{ + using srb2::Draw; + + constexpr int kRowHeight = 4; + + Draw row = Draw(0, kRowHeight).font(Draw::Font::kConsole).align(Draw::Align::kLeft).scale(0.5).flags(V_MONOSPACE); + row.y(-kRowHeight).text("-- AVERAGES PER TIC (over {} tics) --", cv_lua_profile.value); + + if (g_invalid) + { + row.flags(V_GRAYMAP).text(" "); + return; + } + + std::vector view; + view.reserve(g_tic_timers.size()); + + auto color_flag = [](double t) + { + if (t < 10.0) + { + return V_GRAYMAP; + } + else if (t >= 100.0) + { + return V_YELLOWMAP; + } + else + { + return 0; + } + }; + + { + double cum = 0.0; + + for (auto it = g_tic_timers.begin(); it != g_tic_timers.end(); ++it) + { + auto& [key, timer] = *it; + + cum += timer.avg.time; + + if (timer.avg.time > 0.0) + { + view.push_back(it); + } + } + + Draw tally = row.flags(color_flag(cum * 1'000'000.0)); + + tally.text("{:8.2f} us - TOTAL", cum * 1'000'000.0); + tally.y(kRowHeight).text("{:8.2f} ms", cum * 1000.0); + tally.y(kRowHeight * 2).text( + "{:8.2f}% overhead ({:.2f} / {:.2f}) <-- not counting rendering time", + (cum / g_avg_tic_time) * 100.0, + cum * TICRATE, + g_avg_tic_time * TICRATE + ); + + row = row.y(kRowHeight * 4); + } + + std::sort( + view.rbegin(), + view.rend(), + [](auto a, auto b) + { + auto& [k1, t1] = *a; + auto& [k2, t2] = *b; + return t1.avg.time < t2.avg.time; + } + ); + + for (auto it : view) + { + auto& [key, timer] = *it; + + double t = timer.avg.time * 1'000'000.0; + + row.flags(color_flag(t)).text( + "{:>8.2f} us {:>8.2f} calls - {}", + t, + timer.avg.calls, + key + ); + + row = row.y(kRowHeight); + } +} + +extern "C" void lua_profile_OnChange(void) +{ + g_invalid = true; + + if (cv_lua_profile.value == 0) + { + g_tic_timers = {}; + } +} diff --git a/src/lua_profile.h b/src/lua_profile.h new file mode 100644 index 000000000..cd772509c --- /dev/null +++ b/src/lua_profile.h @@ -0,0 +1,34 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef lua_profile_h +#define lua_profile_h + +#include "blua/lua.h" +#include "doomtype.h" +#include "typedef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct lua_timer_t; + +void LUA_ResetTicTimers(void); + +lua_timer_t *LUA_BeginFunctionTimer(lua_State *L, int fn_idx, const char *name); +void LUA_EndFunctionTimer(lua_timer_t *timer); + +void LUA_RenderTimers(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif/*lua_profile_h*/ diff --git a/src/m_cheat.c b/src/m_cheat.c index e91e445fd..39ccfd16b 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -80,13 +80,17 @@ static UINT8 cheatf_warp(void) } } + // Goofy, but this call needs to be before M_ClearMenus because that path + // calls G_LoadLevel, which will trigger a gamedata save. Garbage factory + if (success) + G_SetUsedCheats(); + M_ClearMenus(true); const char *text; if (success) { - G_SetUsedCheats(); S_StartSound(0, sfx_kc42); text = M_GetText( @@ -281,6 +285,9 @@ boolean cht_Interpret(const char *password) { CONS_Printf(M_GetText("OBJECTPLACE must be enabled.\n")); return; } #define REQUIRE_INLEVEL if (gamestate != GS_LEVEL || demo.playback)\ +{ CONS_Printf(M_GetText("You must be in a level (and not a replay) to use this.\n")); return; } + +#define REQUIRE_INLEVEL_OR_REPLAY if (gamestate != GS_LEVEL)\ { CONS_Printf(M_GetText("You must be in a level to use this.\n")); return; } #define REQUIRE_SINGLEPLAYER if (netgame)\ @@ -357,7 +364,7 @@ void Command_RTeleport_f(void) float z = atof(COM_Argv(3)); REQUIRE_CHEATS; - REQUIRE_INLEVEL; + REQUIRE_INLEVEL_OR_REPLAY; if (COM_Argc() != 4) { @@ -376,7 +383,7 @@ void Command_Teleport_f(void) float z = atof(COM_Argv(3)); REQUIRE_CHEATS; - REQUIRE_INLEVEL; + REQUIRE_INLEVEL_OR_REPLAY; if (COM_Argc() != 4) { @@ -611,7 +618,7 @@ void Command_Goto_f(void) const waypoint_t *wayp = K_GetWaypointFromID(id); REQUIRE_CHEATS; - REQUIRE_INLEVEL; + REQUIRE_INLEVEL_OR_REPLAY; if (COM_Argc() != 2) { @@ -635,7 +642,7 @@ void Command_Angle_f(void) const angle_t angle = FixedAngle(FLOAT_TO_FIXED(anglef)); REQUIRE_CHEATS; - REQUIRE_INLEVEL; + REQUIRE_INLEVEL_OR_REPLAY; D_Cheat(consoleplayer, CHEAT_ANGLE, angle); } @@ -657,7 +664,7 @@ void Command_RespawnAt_f(void) void Command_GotoSkybox_f(void) { REQUIRE_CHEATS; - REQUIRE_INLEVEL; + REQUIRE_INLEVEL_OR_REPLAY; mobj_t *skybox = players[consoleplayer].skybox.viewpoint; diff --git a/src/m_perfstats.c b/src/m_perfstats.c index 37697052c..81de6f527 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -46,6 +46,7 @@ struct perfstatrow { static precise_t ps_frametime = 0; precise_t ps_tictime = 0; +precise_t ps_prevtictime = 0; precise_t ps_playerthink_time = 0; precise_t ps_botticcmd_time = 0; diff --git a/src/m_perfstats.h b/src/m_perfstats.h index f95776e1a..bfc995f55 100644 --- a/src/m_perfstats.h +++ b/src/m_perfstats.h @@ -30,6 +30,7 @@ typedef enum } ps_types_t; extern precise_t ps_tictime; +extern precise_t ps_prevtictime; extern precise_t ps_playerthink_time; extern precise_t ps_botticcmd_time; diff --git a/src/menus/options-video-1.c b/src/menus/options-video-1.c index 35d937da8..e6a2e34be 100644 --- a/src/menus/options-video-1.c +++ b/src/menus/options-video-1.c @@ -19,6 +19,8 @@ menuitem_t OPTIONS_Video[] = {IT_STRING | IT_CVAR, "Fullscreen", "Set whether you want to use fullscreen or windowed mode.", NULL, {.cvar = &cv_fullscreen}, 0, 0}, #endif + {IT_STRING | IT_CVAR, "Vertical Sync", "Works with your screen to reduce image tearing and judder.", + NULL, {.cvar = &cv_vidwait}, 0, 0}, {IT_NOTHING|IT_SPACE, NULL, "Kanade best waifu! I promise!", NULL, {NULL}, 0, 0}, @@ -27,13 +29,13 @@ menuitem_t OPTIONS_Video[] = {IT_STRING | IT_CVAR | IT_CV_SLIDER, "Gamma", "Adjusts the overall brightness of the game.", NULL, {.cvar = &cv_globalgamma}, 0, 0}, - {IT_STRING | IT_CVAR, "FPS Cap", "Handles the refresh rate of the game (does not affect gamelogic).", + {IT_STRING | IT_CVAR, "FPS Cap", "Handles the frame rate of the game (35 to match game logic)", NULL, {.cvar = &cv_fpscap}, 0, 0}, {IT_STRING | IT_CVAR, "Enable Skyboxes", "Turning this off will improve performance at the detriment of visuals for many maps.", NULL, {.cvar = &cv_skybox}, 0, 0}, - {IT_STRING | IT_CVAR, "Draw Distance", "How far objects can be drawn. Lower values may improve performance at the cost of visibility.", + {IT_STRING | IT_CVAR, "Draw Distance", "How far objects can be drawn. A tradeoff between performance & visibility.", NULL, {.cvar = &cv_drawdist}, 0, 0}, {IT_STRING | IT_CVAR, "Weather Draw Distance", "Affects how far weather visuals can be drawn. Lower values improve performance.", diff --git a/src/music.cpp b/src/music.cpp index a2aef3070..b95d74b4a 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -57,6 +57,13 @@ void Music_Init(void) tune.priority = 21; } + { + Tune& tune = g_tunes.insert("finish_silence"); + + tune.song = ""; + tune.priority = 30; + } + { Tune& tune = g_tunes.insert("finish"); diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 8abbd34ee..2bd4a38b3 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -40,6 +40,7 @@ target_sources(SRB2SDL2 PRIVATE shadow.cpp ball-switch.cpp charge.c + mega-barrier.cpp ) add_subdirectory(versus) diff --git a/src/objects/block.c b/src/objects/block.c index d9f1dbd82..d5682d8a5 100644 --- a/src/objects/block.c +++ b/src/objects/block.c @@ -5,11 +5,6 @@ #include "../k_kart.h" #include "../k_powerup.h" -static INT16 guard_upscale (player_t *player) -{ - return K_PowerUpRemaining(player, POWERUP_BARRIER) ? 40 : player->spheres; -} - void Obj_BlockRingThink (mobj_t *ring) { if (P_MobjWasRemoved(ring->target) || !ring->target->player) @@ -28,7 +23,7 @@ void Obj_BlockRingThink (mobj_t *ring) ring->color = mo->color; fixed_t baseScale = mo->scale / 2; - baseScale += (mo->scale / 30) * guard_upscale(player); + baseScale += (mo->scale / 30) * player->spheres; P_SetScale(ring, baseScale); // Twirl @@ -41,7 +36,7 @@ void Obj_BlockRingThink (mobj_t *ring) else ring->renderflags |= RF_DONTDRAW; - if (!K_PlayerGuard(player)) + if (K_PowerUpRemaining(player, POWERUP_BARRIER) || !K_PlayerGuard(player)) ring->renderflags |= RF_DONTDRAW; } } @@ -61,7 +56,7 @@ void Obj_BlockBodyThink (mobj_t *body) body->flags &= ~(MF_NOCLIPTHING); fixed_t baseScale = mo->scale / 2; - baseScale += (mo->scale / 30) * guard_upscale(player); + baseScale += (mo->scale / 30) * player->spheres; P_SetScale(body, baseScale); P_MoveOrigin(body, mo->x, mo->y, mo->z + mo->height/2); @@ -76,7 +71,7 @@ void Obj_BlockBodyThink (mobj_t *body) else body->renderflags |= RF_DONTDRAW; - if (!K_PlayerGuard(player)) + if (K_PowerUpRemaining(player, POWERUP_BARRIER) || !K_PlayerGuard(player)) body->renderflags |= RF_DONTDRAW; } } diff --git a/src/objects/broly.c b/src/objects/broly.c index f67847ad1..2419fac10 100644 --- a/src/objects/broly.c +++ b/src/objects/broly.c @@ -51,7 +51,7 @@ Obj_SpawnBrolyKi x->tics = (duration + BUFFER_TICS); - K_ReduceVFX(x, NULL); + K_ReduceVFXForEveryone(x); S_StartSound(x, sfx_cdfm74); diff --git a/src/objects/bungee.c b/src/objects/bungee.c index a5f9519aa..270595fc0 100644 --- a/src/objects/bungee.c +++ b/src/objects/bungee.c @@ -85,19 +85,13 @@ void Obj_playerBungeeThink(player_t *p) if ((p->mo->eflags & MFE_VERTICALFLIP && p->mo->z < bungee->z) || (!(p->mo->eflags & MFE_VERTICALFLIP) && p->mo->z > bungee->z )) { - - p->mo->flags &= ~MF_NOGRAVITY; - p->mo->flags &= ~MF_NOCLIPTHING; - p->pflags &= ~PF_NOFASTFALL; - p->bungee = BUNGEE_NONE; P_InstaThrust(p->mo, bungee->angle, p->mo->momz/8); p->mo->momz = (p->mo->momz*3)/4; p->springstars = TICRATE; // these are used as a buffer not to latch to vines again. p->springcolor = SKINCOLOR_EMERALD; - P_RemoveMobj(bungee); - P_SetTarget(&p->mo->tracer, NULL); + Obj_EndBungee(p); return; } } @@ -117,3 +111,26 @@ void Obj_playerBungeeThink(player_t *p) seg->fuse = 2; } } + +void Obj_EndBungee(player_t *p) +{ + if (p->bungee == BUNGEE_NONE) + { + return; + } + + p->pflags &= ~PF_NOFASTFALL; + p->bungee = BUNGEE_NONE; + + if (!P_MobjWasRemoved(p->mo)) + { + p->mo->flags &= ~MF_NOGRAVITY; + p->mo->flags &= ~MF_NOCLIPTHING; + + if (!P_MobjWasRemoved(p->mo->tracer)) + { + P_RemoveMobj(p->mo->tracer); + } + P_SetTarget(&p->mo->tracer, NULL); + } +} diff --git a/src/objects/mega-barrier.cpp b/src/objects/mega-barrier.cpp new file mode 100644 index 000000000..cfdd4f077 --- /dev/null +++ b/src/objects/mega-barrier.cpp @@ -0,0 +1,160 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include + +#include "../doomdef.h" +#include "../d_player.h" +#include "../g_game.h" +#include "../info.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../k_powerup.h" +#include "../m_fixed.h" +#include "../p_local.h" +#include "../p_mobj.h" +#include "../r_defs.h" + +#define barrier_player(o) ((o)->extravalue1) + +namespace +{ + +struct Barrier; + +// TODO: header +struct Mobj : mobj_t +{ + struct PosArg + { + fixed_t x, y, z; + + PosArg(fixed_t x_, fixed_t y_, fixed_t z_) : x(x_), y(y_), z(z_) {} + PosArg(const mobj_t* mobj) : x(mobj->x), y(mobj->y), z(mobj->z) {} + }; + + static bool valid(const Mobj* mobj) { return !P_MobjWasRemoved(mobj); } + + PosArg center() const { return {x, y, z + (height / 2)}; } + + template + T* spawn_offset(mobjtype_t type) { return static_cast(P_SpawnMobjFromMobj(this, 0, 0, 0, type)); } + + void state(statenum_t state) { P_SetMobjState(this, state); } + statenum_t statenum() const { return static_cast(mobj_t::state - states); } + + fixed_t scale() const { return mobj_t::scale; } + + void scale(fixed_t n) + { + mobj_t::scale = n; + mobj_t::destscale = n; + } + + void move_origin(const PosArg& p) { P_MoveOrigin(this, p.x, p.y, p.z); } + + void remove() { P_RemoveMobj(this); } +}; + +struct Player : player_t +{ + struct Powerups : powerupvars_t + { + Barrier* barrier() const { return reinterpret_cast(powerupvars_t::barrier); } + void barrier(Barrier* n) { P_SetTarget(&this->powerupvars_t::barrier, reinterpret_cast(n)); } + }; + + static bool valid(std::size_t i) { return i < MAXPLAYERS && playeringame[i]; } + static Player* at(std::size_t i) { return static_cast(&players[i]); } + + std::size_t num() const { return this - Player::at(0); } + Mobj* mobj() const { return static_cast(mo); } + + Powerups& powerups() { return static_cast(player_t::powerup); } + const Powerups& powerups() const { return static_cast(player_t::powerup); } +}; + +struct Barrier : Mobj +{ + static constexpr angle_t kSpinSpeed = ANGLE_22h; + static constexpr angle_t kSpinGap = 20*ANG1; + + static Barrier* spawn(Player* player, statenum_t state, int idx) + { + Barrier* child = player->mobj()->spawn_offset(MT_MEGABARRIER); + + child->angle = player->mobj()->angle + (idx * kSpinGap); + child->player(player); + child->renderflags |= RF_DONTDRAW; + child->state(state); + + return child; + } + + static void spawn_chain(Player* player) + { + player->powerups().barrier(spawn(player, S_MEGABARRIER1, 0)); + spawn(player, S_MEGABARRIER2, 1); + spawn(player, S_MEGABARRIER2, 2); + spawn(player, S_MEGABARRIER2, 3); + spawn(player, S_MEGABARRIER2, 4); + spawn(player, S_MEGABARRIER3, 5); + } + + int playernum() const { return barrier_player(this); } + Player* player() const { return Player::at(playernum()); } + void player(player_t* n) { barrier_player(this) = n - players; } + + bool valid() const { return Mobj::valid(this) && Player::valid(playernum()) && Mobj::valid(player()->mobj()); } + + bool think() + { + if (!valid() || !K_PowerUpRemaining(player(), POWERUP_BARRIER)) + { + remove(); + return false; + } + + Mobj* source = player()->mobj(); + color = source->color; + scale(8 * source->scale() / 9); + move_origin(source->center()); + angle += kSpinSpeed; + eflags = (eflags & ~MFE_VERTICALFLIP) | (source->eflags & MFE_VERTICALFLIP); + + if (K_PlayerGuard(player())) + { + renderflags &= ~RF_DONTDRAW; + renderflags ^= RF_ADD | RF_TRANS90; + } + else + { + renderflags |= RF_DONTDRAW; + } + + return true; + } +}; + +}; // namespace + +void Obj_SpawnMegaBarrier(player_t* p) +{ + Player* player = static_cast(p); + + if (!Mobj::valid(player->powerups().barrier())) + { + Barrier::spawn_chain(player); + } +} + +boolean Obj_MegaBarrierThink(mobj_t* mobj) +{ + return static_cast(mobj)->think(); +} diff --git a/src/objects/powerup-aura.cpp b/src/objects/powerup-aura.cpp index 80620010d..6bfa47096 100644 --- a/src/objects/powerup-aura.cpp +++ b/src/objects/powerup-aura.cpp @@ -1,3 +1,4 @@ +#include "../doomtype.h" #include "../info.h" #include "../g_game.h" #include "../m_fixed.h" @@ -85,6 +86,15 @@ struct Aura : mobj_t P_InstaScale(this, 11 * origin()->scale / 10); translate(); + + if (K_AnyPowerUpRemaining(&players[seek()]) & ~POWERUP_BIT(POWERUP_BARRIER)) + { + renderflags &= ~RF_DONTDRAW; + } + else + { + renderflags |= RF_DONTDRAW; + } } void translate() diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 7ad363775..3ed9614f4 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -112,7 +112,8 @@ static void SpawnEmeraldSpeedLines(mobj_t *mo) fast->momz = 3*P_GetMobjZMovement(mo)/4; K_MatchGenericExtraFlags(fast, mo); - K_ReduceVFX(fast, mo->player); + P_SetTarget(&fast->owner, mo); + fast->renderflags |= RF_REDUCEVFX; fast->color = mo->color; fast->colorized = true; diff --git a/src/p_enemy.c b/src/p_enemy.c index 2f7700a30..f76bf88b7 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3828,7 +3828,8 @@ void A_AttractChase(mobj_t *actor) P_SetTarget(&sparkle->target, actor->target); sparkle->angle = (actor->target->angle + (offset>>1)) + (offset * actor->target->player->sparkleanim); actor->target->player->sparkleanim = (actor->target->player->sparkleanim+1) % 20; - K_ReduceVFX(sparkle, actor->target->player); + P_SetTarget(&sparkle->owner, actor->target); + sparkle->renderflags |= RF_REDUCEVFX; P_KillMobj(actor, actor->target, actor->target, DMG_NORMAL); return; diff --git a/src/p_inter.c b/src/p_inter.c index afca8d8ca..433dd5803 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1545,7 +1545,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget || target->type == MT_DROPTARGET || target->type == MT_DROPTARGET_SHIELD || target->type == MT_EGGMANITEM || target->type == MT_EGGMANITEM_SHIELD || target->type == MT_BALLHOG || target->type == MT_SPB - || target->type == MT_GACHABOM)) // kart dead items + || target->type == MT_GACHABOM || target->type == MT_KART_LEFTOVER)) // kart dead items target->flags |= MF_NOGRAVITY; // Don't drop Tails 03-08-2000 else target->flags &= ~MF_NOGRAVITY; // lose it if you for whatever reason have it, I'm looking at you shields @@ -1876,8 +1876,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (target->player->pflags & PF_NOCONTEST) P_SetTarget(&target->tracer, kart); - - kart->fuse = 5*TICRATE; } if (source && !P_MobjWasRemoved(source)) @@ -1889,18 +1887,22 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget } else { - flingAngle = target->angle + ANGLE_180; + flingAngle = target->angle; if (P_RandomByte(PR_ITEM_RINGS) & 1) { - flingAngle -= ANGLE_45; + flingAngle -= ANGLE_45/2; } else { - flingAngle += ANGLE_45; + flingAngle += ANGLE_45/2; } } + // On -20 ring deaths, you're guaranteed to be hitting the ground from Tumble, + // so make sure that this draws at the correct angle. + target->rollangle = 0; + P_InstaThrust(target, flingAngle, 14 * target->scale); P_SetObjectMomZ(target, 14*FRACUNIT, false); @@ -1923,6 +1925,16 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget } break; + case MT_KART_LEFTOVER: + if (!P_MobjWasRemoved(inflictor)) + { + K_KartSolidBounce(target, inflictor); + target->momz = 20 * inflictor->scale * P_MobjFlip(inflictor); + } + target->z += P_MobjFlip(target); + target->tics = 175; + return; + case MT_METALSONIC_RACE: target->fuse = TICRATE*3; target->momx = target->momy = target->momz = 0; @@ -3126,6 +3138,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da player->glanceDir = 0; player->preventfailsafe = TICRATE*3; player->pflags &= ~PF_GAINAX; + Obj_EndBungee(player); if (player->spectator == false && !(player->charflags & SF_IRONMAN)) { diff --git a/src/p_map.c b/src/p_map.c index 7f1b3bf84..5e68afc21 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -962,6 +962,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) || tm.thing->type == MT_SSMINE || tm.thing->type == MT_LANDMINE || tm.thing->type == MT_SINK || tm.thing->type == MT_GARDENTOP || tm.thing->type == MT_DROPTARGET + || tm.thing->type == MT_KART_LEFTOVER || (tm.thing->type == MT_PLAYER && thing->target != tm.thing))) { // see if it went over / under @@ -979,6 +980,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) || thing->type == MT_SSMINE || thing->type == MT_LANDMINE || thing->type == MT_SINK || thing->type == MT_GARDENTOP || thing->type == MT_DROPTARGET + || thing->type == MT_KART_LEFTOVER || (thing->type == MT_PLAYER && tm.thing->target != thing))) { // see if it went over / under @@ -1002,6 +1004,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) || tm.thing->type == MT_GARDENTOP || tm.thing->type == MT_MONITOR || tm.thing->type == MT_BATTLECAPSULE + || tm.thing->type == MT_KART_LEFTOVER || (tm.thing->type == MT_PLAYER))) { // see if it went over / under @@ -1019,6 +1022,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) || thing->type == MT_GARDENTOP || thing->type == MT_MONITOR || thing->type == MT_BATTLECAPSULE + || thing->type == MT_KART_LEFTOVER || (thing->type == MT_PLAYER))) { // see if it went over / under @@ -1572,7 +1576,14 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) if (tm.thing->z + tm.thing->height < thing->z) return BMIT_CONTINUE; // underneath - K_KartBouncing(tm.thing, thing); + if (K_PlayerCanPunt(tm.thing->player)) + { + P_DamageMobj(thing, tm.thing, tm.thing, 1, DMG_NORMAL); + } + else + { + K_KartBouncing(tm.thing, thing); + } return BMIT_CONTINUE; } else if (thing->type == MT_MONITOR) @@ -3137,7 +3148,8 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff, Try spark->fuse = 9; spark->cusval = K_StairJankFlip(ANGLE_90); P_SetTarget(&spark->target, thing); - K_ReduceVFX(spark, thing->player); + P_SetTarget(&spark->owner, thing); + spark->renderflags |= RF_REDUCEVFX; } thing->player->stairjank = 17; diff --git a/src/p_mobj.c b/src/p_mobj.c index f797a51f5..990758b8b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2592,6 +2592,8 @@ boolean P_ZMovement(mobj_t *mo) tireAngle += (aOffset * 2); } } + + mom.z = 0; } else if (mo->type == MT_KART_TIRE) { @@ -6807,6 +6809,14 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->angle += ANG2; break; } + case MT_MEGABARRIER: + { + if (!Obj_MegaBarrierThink(mobj)) + { + return; + } + break; + } case MT_VWREF: case MT_VWREB: { @@ -6953,6 +6963,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj) //case MT_DROPTARGET: case MT_SPB: case MT_GACHABOM: + case MT_KART_LEFTOVER: if (P_IsObjectOnGround(mobj)) { P_RemoveMobj(mobj); @@ -8025,7 +8036,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } } - K_ReduceVFX(mobj, mobj->target->player); + P_SetTarget(&mobj->owner, mobj->target); + mobj->renderflags |= RF_REDUCEVFX; break; } case MT_BOOSTFLAME: @@ -8114,7 +8126,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) S_StartSound(mobj, sfx_cdfm17); K_MatchGenericExtraFlags(mobj, mobj->target); - K_ReduceVFX(mobj, mobj->target->player); + P_SetTarget(&mobj->owner, mobj->target); + mobj->renderflags |= RF_REDUCEVFX; if (leveltime & 1) mobj->renderflags |= RF_DONTDRAW; } @@ -8319,7 +8332,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->renderflags = (mobj->renderflags & ~RF_TRANSMASK)|(trans << RF_TRANSSHIFT); } - K_ReduceVFX(mobj, mobj->target->player); + P_SetTarget(&mobj->owner, mobj->target); + mobj->renderflags |= RF_REDUCEVFX; break; case MT_MAGICIANBOX: { @@ -8796,9 +8810,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } } - P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + mobj->target->height/2); + P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (mobj->eflags & MFE_VERTICALFLIP ? 1 : 1) * mobj->target->height/2); mobj->angle = K_MomentumAngle(mobj->target); + K_FlipFromObject(mobj, mobj->target); + if (underlayst != S_NULL) { mobj_t *underlay = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_FLAMESHIELDUNDERLAY); @@ -10258,7 +10274,6 @@ static boolean P_CanFlickerFuse(mobj_t *mobj) case MT_FALLINGROCK: case MT_FLOATINGITEM: case MT_POGOSPRING: - case MT_KART_LEFTOVER: case MT_EMERALD: case MT_BLENDEYE_PUYO: if (mobj->fuse <= TICRATE) @@ -10435,6 +10450,8 @@ void P_MobjThinker(mobj_t *mobj) P_SetTarget(&mobj->itnext, NULL); if (mobj->punt_ref && P_MobjWasRemoved(mobj->punt_ref)) P_SetTarget(&mobj->punt_ref, NULL); + if (mobj->owner && P_MobjWasRemoved(mobj->owner)) + P_SetTarget(&mobj->owner, NULL); if (mobj->flags & MF_NOTHINK) return; @@ -11990,6 +12007,7 @@ void P_RemoveMobj(mobj_t *mobj) P_SetTarget(&mobj->itnext, NULL); P_SetTarget(&mobj->punt_ref, NULL); + P_SetTarget(&mobj->owner, NULL); P_RemoveThingTID(mobj); P_DeleteMobjStringArgs(mobj); @@ -12639,13 +12657,13 @@ void P_SpawnPlayer(INT32 playernum) } } - boolean director = p->spectator && pcount > 0; - if (G_IsPartyLocal(playernum)) { - // Spectating when there is literally any other - // player in the level enables director cam. - K_ToggleDirector(G_PartyPosition(playernum), director); + // Spectating always enables director cam. If there + // is no one to view, this will do nothing. If + // someone enters the game later, it will + // automatically switch to that player. + K_ToggleDirector(G_PartyPosition(playernum), p->spectator); // Spectators can switch to freecam. This should be // disabled when they enter the race, or when the level diff --git a/src/p_mobj.h b/src/p_mobj.h index 1fbb676bf..2570acda9 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -445,6 +445,8 @@ struct mobj_t // If punt_ref, set punt_ref->reappear, treat as if this->reappear mobj_t *punt_ref; + mobj_t *owner; + // WARNING: New fields must be added separately to savegame and Lua. }; diff --git a/src/p_saveg.c b/src/p_saveg.c index 6fa7a9363..f837a73c4 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -82,6 +82,7 @@ typedef enum FLICKYATTACKER = 0x0800, FLICKYCONTROLLER = 0x1000, TRICKINDICATOR = 0x2000, + BARRIER = 0x4000, } player_saveflags; static inline void P_ArchivePlayer(savebuffer_t *save) @@ -332,6 +333,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (players[i].powerup.flickyController) flags |= FLICKYCONTROLLER; + if (players[i].powerup.barrier) + flags |= BARRIER; + WRITEUINT16(save->p, flags); if (flags & SKYBOXVIEW) @@ -373,6 +377,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (flags & FLICKYCONTROLLER) WRITEUINT32(save->p, players[i].powerup.flickyController->mobjnum); + if (flags & BARRIER) + WRITEUINT32(save->p, players[i].powerup.barrier->mobjnum); + WRITEUINT32(save->p, (UINT32)players[i].followitem); WRITEUINT32(save->p, players[i].charflags); @@ -911,6 +918,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) if (flags & FLICKYCONTROLLER) players[i].powerup.flickyController = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & BARRIER) + players[i].powerup.barrier = (mobj_t *)(size_t)READUINT32(save->p); + players[i].followitem = (mobjtype_t)READUINT32(save->p); //SetPlayerSkinByNum(i, players[i].skin); @@ -2698,6 +2708,7 @@ typedef enum MD3_LIGHTLEVEL = 1, MD3_REAPPEAR = 1<<1, MD3_PUNT_REF = 1<<2, + MD3_OWNER = 1<<3, } mobj_diff3_t; typedef enum @@ -3022,6 +3033,8 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 diff3 |= MD3_REAPPEAR; if (mobj->punt_ref) diff3 |= MD3_PUNT_REF; + if (mobj->owner) + diff3 |= MD3_OWNER; if (diff3 != 0) diff2 |= MD2_MORE; @@ -3310,6 +3323,10 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 { WRITEUINT32(save->p, mobj->punt_ref->mobjnum); } + if (diff3 & MD3_OWNER) + { + WRITEUINT32(save->p, mobj->owner->mobjnum); + } WRITEUINT32(save->p, mobj->mobjnum); } @@ -4564,6 +4581,10 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) { mobj->punt_ref = (mobj_t *)(size_t)READUINT32(save->p); } + if (diff3 & MD3_OWNER) + { + mobj->owner = (mobj_t *)(size_t)READUINT32(save->p); + } // set sprev, snext, bprev, bnext, subsector P_SetThingPosition(mobj); @@ -5611,6 +5632,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->punt_ref, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "punt_ref not found on %d\n", mobj->type); } + if (mobj->owner) + { + temp = (UINT32)(size_t)mobj->owner; + mobj->owner = NULL; + if (!P_SetTarget(&mobj->owner, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "owner not found on %d\n", mobj->type); + } } for (i = 0; i < MAXPLAYERS; i++) @@ -5743,6 +5771,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&players[i].powerup.flickyController, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "powerup.flickyController not found on player %d\n", i); } + if (players[i].powerup.barrier) + { + temp = (UINT32)(size_t)players[i].powerup.barrier; + players[i].powerup.barrier = NULL; + if (!P_SetTarget(&players[i].powerup.barrier, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "powerup.barrier not found on player %d\n", i); + } } } diff --git a/src/p_tick.c b/src/p_tick.c index 1ed8efcdd..c61fed92f 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -46,6 +46,8 @@ #include "music.h" #include "k_dialogue.h" +#include "lua_profile.h" + #ifdef PARANOIA #include "deh_tables.h" // MOBJTYPE_LIST #endif @@ -844,6 +846,8 @@ void P_Ticker(boolean run) G_ReadDemoTiccmd(&players[i].cmd, i); } + LUA_ResetTicTimers(); + ps_lua_mobjhooks = 0; ps_checkposition_calls = 0; diff --git a/src/p_user.c b/src/p_user.c index edea23608..2bf8b8193 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -66,6 +66,7 @@ #include "k_profiles.h" #include "music.h" #include "k_tally.h" +#include "k_objects.h" #ifdef HWRENDER #include "hardware/hw_light.h" @@ -481,6 +482,7 @@ void P_ResetPlayer(player_t *player) player->trickpanel = TRICKSTATE_NONE; player->glanceDir = 0; player->fastfall = 0; + Obj_EndBungee(player); if (player->mo != NULL && P_MobjWasRemoved(player->mo) == false) { @@ -1166,7 +1168,8 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost->old_roll = mobj->old_roll2; ghost->old_scale = mobj->old_scale2; - K_ReduceVFX(ghost, mobj->player); + P_SetTarget(&ghost->owner, mobj); + ghost->renderflags |= RF_REDUCEVFX; ghost->reappear = mobj->reappear; P_SetTarget(&ghost->punt_ref, mobj->punt_ref); @@ -1284,7 +1287,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) if (P_IsLocalPlayer(player) && !specialout && musiccountdown == 0) { - Music_StopAll(); + Music_Play("finish_silence"); musiccountdown = MUSIC_COUNTDOWN_MAX; } @@ -3787,7 +3790,7 @@ void P_DoTimeOver(player_t *player) if (P_IsLocalPlayer(player) && musiccountdown == 0) { - Music_StopAll(); + Music_Play("finish_silence"); musiccountdown = MUSIC_COUNTDOWN_MAX; } @@ -4069,6 +4072,7 @@ void P_PlayerThink(player_t *player) PlayerPointerErase(player->hoverhyudoro); PlayerPointerErase(player->flickyAttacker); PlayerPointerErase(player->powerup.flickyController); + PlayerPointerErase(player->powerup.barrier); #undef PlayerPointerErase } diff --git a/src/r_defs.h b/src/r_defs.h index 8d84fbd6e..33933bed4 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -973,6 +973,7 @@ typedef enum RF_DROPSHADOW = (RF_SHADOWDRAW | RF_SHADOWEFFECTS | RF_FULLDARK), RF_ABSOLUTELIGHTLEVEL = 0x00010000, // mobj_t.lightlevel is absolute instead of relative + RF_REDUCEVFX = 0x00020000, // only mobj_t.owner can see this object RF_DONTDRAW = 0x00F00000, // --Don't generate a vissprite RF_DONTDRAWP1 = 0x00100000, // No P1 diff --git a/src/r_things.cpp b/src/r_things.cpp index 37dcc3bf4..a1187b803 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -3775,6 +3775,9 @@ boolean R_ThingVisible (mobj_t *thing) || (viewssnum == 3 && (thing->renderflags & RF_DONTDRAWP4))) return false; + if ((thing->renderflags & RF_REDUCEVFX) && cv_reducevfx.value && thing->owner != players[displayplayers[viewssnum]].mo) + return false; + return true; } diff --git a/src/sdl/i_video.cpp b/src/sdl/i_video.cpp index 88dd01eff..c2d42f009 100644 --- a/src/sdl/i_video.cpp +++ b/src/sdl/i_video.cpp @@ -1810,3 +1810,38 @@ UINT32 I_GetRefreshRate(void) // trouble querying mode over and over again. return refresh_rate; } + +namespace srb2::cvarhandler +{ +void on_set_vid_wait(); +} + +void srb2::cvarhandler::on_set_vid_wait() +{ + int interval = 0; + if (cv_vidwait.value > 0) + { + interval = 1; + } + + switch (rendermode) + { + case render_soft: + if (sdlglcontext == nullptr || SDL_GL_GetCurrentContext() != sdlglcontext) + { + return; + } + SDL_GL_SetSwapInterval(interval); + break; +#ifdef HWRENDER + case render_opengl: + if (g_legacy_gl_context == nullptr || SDL_GL_GetCurrentContext() != g_legacy_gl_context) + { + return; + } + SDL_GL_SetSwapInterval(interval); +#endif + default: + break; + } +} diff --git a/src/st_stuff.c b/src/st_stuff.c index e25222f19..ac670d5c6 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -452,10 +452,10 @@ void ST_drawDebugInfo(void) { INT32 height = 192; - const UINT8 screen = cv_devmode_screen.value - 1; + const UINT8 screen = min(r_splitscreen, cv_devmode_screen.value - 1); // devmode_screen = 1..4 - stplyr = &players[displayplayers[min(r_splitscreen, screen)]]; + stplyr = &players[displayplayers[screen]]; if (!stplyr->mo) return; @@ -472,11 +472,23 @@ void ST_drawDebugInfo(void) if (cht_debug & DBG_BASIC) { - const fixed_t d = AngleFixed(stplyr->mo->angle); - V_DrawRightAlignedString(320, height - 24, V_MONOSPACE, va("X: %6d", stplyr->mo->x>>FRACBITS)); - V_DrawRightAlignedString(320, height - 16, V_MONOSPACE, va("Y: %6d", stplyr->mo->y>>FRACBITS)); - V_DrawRightAlignedString(320, height - 8, V_MONOSPACE, va("Z: %6d", stplyr->mo->z>>FRACBITS)); - V_DrawRightAlignedString(320, height, V_MONOSPACE, va("A: %6d", FixedInt(d))); + camera_t *cam = &camera[screen]; + if (stplyr->spectator || cam->freecam) + { + const fixed_t d = AngleFixed(cam->angle); + V_DrawRightAlignedString(320, height - 24, V_MONOSPACE, va("X: %6d", cam->x>>FRACBITS)); + V_DrawRightAlignedString(320, height - 16, V_MONOSPACE, va("Y: %6d", cam->y>>FRACBITS)); + V_DrawRightAlignedString(320, height - 8, V_MONOSPACE, va("Z: %6d", cam->z>>FRACBITS)); + V_DrawRightAlignedString(320, height, V_MONOSPACE, va("A: %6d", FixedInt(d))); + } + else + { + const fixed_t d = AngleFixed(stplyr->mo->angle); + V_DrawRightAlignedString(320, height - 24, V_MONOSPACE, va("X: %6d", stplyr->mo->x>>FRACBITS)); + V_DrawRightAlignedString(320, height - 16, V_MONOSPACE, va("Y: %6d", stplyr->mo->y>>FRACBITS)); + V_DrawRightAlignedString(320, height - 8, V_MONOSPACE, va("Z: %6d", stplyr->mo->z>>FRACBITS)); + V_DrawRightAlignedString(320, height, V_MONOSPACE, va("A: %6d", FixedInt(d))); + } height -= 40; } diff --git a/src/typedef.h b/src/typedef.h index 248acc03b..b32d4b974 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -247,6 +247,9 @@ TYPEDEF (mapUserProperties_t); // lua_hudlib_drawlist.h typedef struct huddrawlist_s *huddrawlist_h; +// lua_profile.h +TYPEDEF (lua_timer_t); + // m_aatree.h TYPEDEF (aatree_t);