From 5cb54f29fe4820b30543235ef74b0998281dca46 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 2 Mar 2023 22:01:56 -0800 Subject: [PATCH 01/97] Sync player mobj health to bumper count Fixes players killed twice if P_DamageMobj is called with DMG_TIMEOVER and gametyperules GTR_BUMPERS. Moves death condition out of K_HandleBumperChanges and lets losing last bumper organically kill the player by decreasing their health. --- src/k_battle.c | 11 ++++++++--- src/k_kart.c | 20 +++++++++++--------- src/k_kart.h | 4 ++-- src/p_inter.c | 28 ++++++++++++++-------------- src/p_mobj.c | 5 +++++ 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index b34c7a151..37bb33f4f 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -183,10 +183,8 @@ void K_CheckEmeralds(player_t *player) continue; } - players[i].bumpers = 0; + P_DoPlayerExit(&players[i]); } - - K_CheckBumpers(); } UINT16 K_GetChaosEmeraldColor(UINT32 emeraldType) @@ -791,7 +789,14 @@ void K_BattleInit(boolean singleplayercontext) { if (!playeringame[i] || players[i].spectator) continue; + players[i].bumpers = maxbumpers; + + if (players[i].mo) + { + players[i].mo->health = maxbumpers; + } + K_SpawnPlayerBattleBumpers(players+i); } } diff --git a/src/k_kart.c b/src/k_kart.c index 4284c286d..253c79715 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4181,35 +4181,35 @@ void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers) { player->pflags |= (PF_NOCONTEST|PF_ELIMINATED); } - - P_KillMobj(player->mo, NULL, NULL, DMG_NORMAL); } K_CalculateBattleWanted(); K_CheckBumpers(); } -void K_DestroyBumpers(player_t *player, UINT8 amount) +UINT8 K_DestroyBumpers(player_t *player, UINT8 amount) { UINT8 oldBumpers = player->bumpers; if (!(gametyperules & GTR_BUMPERS)) { - return; + return 0; } amount = min(amount, player->bumpers); if (amount == 0) { - return; + return 0; } player->bumpers -= amount; K_HandleBumperChanges(player, oldBumpers); + + return amount; } -void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) +UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) { UINT8 oldPlayerBumpers = player->bumpers; UINT8 oldVictimBumpers = victim->bumpers; @@ -4218,14 +4218,14 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) if (!(gametyperules & GTR_BUMPERS)) { - return; + return 0; } amount = min(amount, victim->bumpers); if (amount == 0) { - return; + return 0; } while ((tookBumpers < amount) && (victim->bumpers > 0)) @@ -4280,7 +4280,7 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) if (tookBumpers == 0) { // No change occured. - return; + return 0; } // Play steal sound @@ -4288,6 +4288,8 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) K_HandleBumperChanges(player, oldPlayerBumpers); K_HandleBumperChanges(victim, oldVictimBumpers); + + return tookBumpers; } #define MINEQUAKEDIST 4096 diff --git a/src/k_kart.h b/src/k_kart.h index 04610be45..66ad64b50 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -103,8 +103,8 @@ void K_UpdateStumbleIndicator(player_t *player); INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers); -void K_DestroyBumpers(player_t *player, UINT8 amount); -void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); +UINT8 K_DestroyBumpers(player_t *player, UINT8 amount); +UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); void K_MineFlashScreen(mobj_t *source); void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay); void K_RunFinishLineBeam(void); diff --git a/src/p_inter.c b/src/p_inter.c index 3d69eec25..abd3c3c69 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2146,6 +2146,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da const boolean hardhit = (type == DMG_EXPLODE || type == DMG_KARMA || type == DMG_TUMBLE); // This damage type can do evil stuff like ALWAYS combo INT16 ringburst = 5; + // Do not die from damage outside of bumpers health system + damage = 0; + // Check if the player is allowed to be damaged! // If not, then spawn the instashield effect instead. if (!force) @@ -2275,12 +2278,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_TryHurtSoundExchange(target, source); K_BattleAwardHit(source->player, player, inflictor, takeBumpers); - K_TakeBumpersFromPlayer(source->player, player, takeBumpers); + damage = K_TakeBumpersFromPlayer(source->player, player, takeBumpers); if (type == DMG_KARMA) { // Destroy any remainder bumpers from the player for karma comeback damage - K_DestroyBumpers(player, player->bumpers); + damage = K_DestroyBumpers(player, player->bumpers); } else { @@ -2303,7 +2306,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } else { - K_DestroyBumpers(player, takeBumpers); + damage = K_DestroyBumpers(player, takeBumpers); } if (!(damagetype & DMG_STEAL)) @@ -2389,15 +2392,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (type != DMG_STUMBLE) { player->instashield = 15; - K_SetHitLagForObjects(target, inflictor, laglength, true); } if (inflictor && !P_MobjWasRemoved(inflictor) && inflictor->type == MT_BANANA) { player->flipDI = true; } - - return true; } } else @@ -2433,16 +2433,16 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da //K_SetHitLagForObjects(target, inflictor, laglength, true); - if (player) - P_ResetPlayer(target->player); - else + if (!player) + { P_SetMobjState(target, target->info->painstate); - if (!P_MobjWasRemoved(target)) - { - // if not intent on another player, - // chase after this one - P_SetTarget(&target->target, source); + if (!P_MobjWasRemoved(target)) + { + // if not intent on another player, + // chase after this one + P_SetTarget(&target->target, source); + } } return true; diff --git a/src/p_mobj.c b/src/p_mobj.c index 3f0481a94..0f1b6de78 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11829,6 +11829,11 @@ void P_SpawnPlayer(INT32 playernum) p->bumpers = K_StartingBumperCount(); K_SpawnPlayerBattleBumpers(p); } + + if (p->bumpers > 0) + { + mobj->health = p->bumpers; + } } // I'm not refactoring the loop at the top of this file. From fec24381dfdd683a11aaa605da72e93750424951 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 2 Mar 2023 23:40:54 -0800 Subject: [PATCH 02/97] Fix FAULT crash if no finish line waypoint --- src/k_respawn.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/k_respawn.c b/src/k_respawn.c index cb08408a2..da07b2b5f 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -150,8 +150,10 @@ void K_DoIngameRespawn(player_t *player) // FAULT if ((gametyperules & GTR_CIRCUIT) && leveltime < starttime) { - if (!(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE)) - player->respawn.wp = K_GetFinishLineWaypoint()->prevwaypoints[0]; + const waypoint_t *finish = K_GetFinishLineWaypoint(); + + if (!(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE) && finish != NULL) + player->respawn.wp = finish->prevwaypoints[0]; K_DoFault(player); } From 4a8dd02320e6ca2063add9cb3dea010a3cce1552 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 10 Dec 2022 17:28:41 -0500 Subject: [PATCH 03/97] Podium cutscene basics - Bots follow a slightly more strict path via waypoints - Bots decide their starting waypoint path depending on their position. (The 1st place winner will start by chasing waypoint ID 1, etc) - The map used for podium sequence can be decided via `PodiumMap = x` in MainCfg. TODO: - Camera work - Add Lose / Win animation states when stopping at final waypoint - Adjust physics so that the bots can follow the path more strictly in this mode - Actually go to it after GP --- src/deh_soc.c | 4 ++ src/doomstat.h | 2 + src/g_game.c | 2 + src/k_bot.c | 66 +++++++++++++++++++++++++++++++ src/k_kart.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++- src/k_kart.h | 2 + src/k_waypoint.c | 23 +++++++++++ src/k_waypoint.h | 16 ++++++++ src/lua_script.c | 3 ++ 9 files changed, 218 insertions(+), 1 deletion(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 5da9a4173..a6287b2dc 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2842,6 +2842,10 @@ void readmaincfg(MYFILE *f, boolean mainfile) { tutorialmap = Z_StrDup(word2); } + else if (fastcmp(word, "PODIUMMAP")) + { + podiummap = Z_StrDup(word2); + } else deh_warning("Maincfg: unknown word '%s'", word); } diff --git a/src/doomstat.h b/src/doomstat.h index b0375ffa2..48245ef09 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -210,6 +210,8 @@ extern char * bootmap; //bootmap for loading a map on startup extern char * tutorialmap; // map to load for tutorial extern boolean tutorialmode; // are we in a tutorial right now? +extern char * podiummap; // map to load for podium + extern boolean looptitle; // CTF colors. diff --git a/src/g_game.c b/src/g_game.c index b7fce9a96..26e7f6337 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -164,6 +164,8 @@ char * bootmap = NULL; //bootmap for loading a map on startup char * tutorialmap = NULL; // map to load for tutorial boolean tutorialmode = false; // are we in a tutorial right now? +char * podiummap = NULL; // map to load for podium + boolean looptitle = true; UINT16 skincolor_redteam = SKINCOLOR_RED; diff --git a/src/k_bot.c b/src/k_bot.c index 56b9a8c1f..89b4d2abe 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -261,6 +261,9 @@ void K_UpdateMatchRaceBots(void) --------------------------------------------------*/ boolean K_PlayerUsesBotMovement(player_t *player) { + if (K_PodiumSequence() == true) + return true; + if (player->exiting) return true; @@ -1264,6 +1267,54 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t return turnamt; } +/*-------------------------------------------------- + static void K_BotPodiumTurning(player_t *player, ticcmd_t *cmd) + + Calculates bot turning for the podium cutscene. +--------------------------------------------------*/ +static void K_BotPodiumTurning(player_t *player, ticcmd_t *cmd) +{ + const angle_t destAngle = R_PointToAngle2( + player->mo->x, player->mo->y, + player->currentwaypoint->mobj->x, player->currentwaypoint->mobj->y + ); + const INT32 delta = AngleDeltaSigned(destAngle, player->mo->angle); + const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); + fixed_t mul = FixedDiv(delta, (angle_t)(handling << TICCMD_REDUCE)); + + if (mul > FRACUNIT) + { + mul = FRACUNIT; + } + + if (mul < -FRACUNIT) + { + mul = -FRACUNIT; + } + + cmd->turning = FixedMul(mul, KART_FULLTURN); +} + +/*-------------------------------------------------- + static void K_BuildBotPodiumTiccmd(player_t *player, ticcmd_t *cmd) + + Calculates all bot movement for the podium cutscene. +--------------------------------------------------*/ +static void K_BuildBotPodiumTiccmd(player_t *player, ticcmd_t *cmd) +{ + if (player->currentwaypoint == NULL) + { + // We've reached the end of our path. + // Simply stop moving. + return; + } + + cmd->forwardmove = MAXPLMOVE; + cmd->buttons |= BT_ACCELERATE; + + K_BotPodiumTurning(player, cmd); +} + /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) @@ -1294,6 +1345,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) return; } + if (K_PodiumSequence() == true) + { + K_BuildBotPodiumTiccmd(player, cmd); + return; + } + if (!(gametyperules & GTR_BOTS) // No bot behaviors || K_GetNumWaypoints() == 0 // No waypoints || leveltime <= introtime // During intro camera @@ -1556,12 +1613,21 @@ void K_UpdateBotGameplayVars(player_t *player) { const line_t *botController; + player->botvars.controller = UINT16_MAX; + player->botvars.rubberband = FRACUNIT; + if (gamestate != GS_LEVEL || !player->mo) { // Not in the level. return; } + if (K_PodiumSequence() == true) + { + // We don't want these during podium. + return; + } + botController = K_FindBotController(player->mo); player->botvars.controller = botController ? (botController - lines) : UINT16_MAX; diff --git a/src/k_kart.c b/src/k_kart.c index ce2827cf7..dacc77fc1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -51,6 +51,19 @@ // comeback is Battle Mode's karma comeback, also bool // mapreset is set when enough players fill an empty server +boolean K_PodiumSequence(void) +{ + // FIXME: Cache so we don't have to iterate all map headers every time + INT32 podiumMapNum = nummapheaders; + + if (podiummap && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders)) + { + return (gamemap == podiumMapNum+1); + } + + return false; +} + boolean K_IsDuelItem(mobjtype_t type) { switch (type) @@ -354,6 +367,12 @@ boolean K_IsPlayerLosing(player_t *player) INT32 winningpos = 1; UINT8 i, pcount = 0; + if (K_PodiumSequence() == true) + { + // Need to be in top 3 to win. + return (player->position > 3); + } + if (player->pflags & PF_NOCONTEST) return true; @@ -8449,6 +8468,59 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) return bestwaypoint; } +static void K_UpdatePodiumWaypoint(player_t *const player, waypoint_t *const waypoint) +{ + // Set the new waypoint. + player->currentwaypoint = waypoint; + + if ((waypoint == NULL) + || (waypoint->nextwaypoints == NULL) + || (waypoint->numnextwaypoints == 0U)) + { + // No waypoint, or no next waypoint. + player->nextwaypoint = NULL; + return; + } + + // Simply use the first available next waypoint. + // No need for split paths in these cutscenes. + player->nextwaypoint = waypoint->nextwaypoints[0]; +} + +static void K_UpdatePodiumWaypoints(player_t *const player) +{ + if ((player != NULL) && (player->mo != NULL)) + { + if ((player->currentwaypoint == NULL) + && (player->position > 0 && player->position <= MAXPLAYERS) + && (leveltime <= 2)) + { + // Initialize our first waypoint to the one that + // matches our position. + K_UpdatePodiumWaypoint(player, K_GetWaypointFromID(player->position)); + } + + if (player->currentwaypoint != NULL) + { + const fixed_t xydist = P_AproxDistance( + player->mo->x - player->currentwaypoint->mobj->x, + player->mo->y - player->currentwaypoint->mobj->y + ); + const fixed_t xyzdist = P_AproxDistance( + xydist, + player->mo->z - player->currentwaypoint->mobj->z + ); + //const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); + + if (xyzdist <= player->mo->radius + player->currentwaypoint->mobj->radius) + { + // Reached waypoint, go to the next waypoint. + K_UpdatePodiumWaypoint(player, player->nextwaypoint); + } + } + } +} + /*-------------------------------------------------- void K_UpdateDistanceFromFinishLine(player_t *const player) @@ -8462,6 +8534,12 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) --------------------------------------------------*/ void K_UpdateDistanceFromFinishLine(player_t *const player) { + if (K_PodiumSequence() == true) + { + K_UpdatePodiumWaypoints(player); + return; + } + if ((player != NULL) && (player->mo != NULL)) { waypoint_t *finishline = K_GetFinishLineWaypoint(); @@ -9261,7 +9339,28 @@ void K_KartUpdatePosition(player_t *player) realplayers++; - if (gametyperules & GTR_CIRCUIT) + if (K_PodiumSequence() == true) + { + if (players[i].score > player->score) + { + // Final score is the important part. + position++; + } + else if (players[i].score == player->score) + { + if (players[i].bot == false && player->bot == true) + { + // Bots are never as important as players. + position++; + } + else if (i < player - players) + { + // Port priority is the final tie breaker. + position++; + } + } + } + else if (gametyperules & GTR_CIRCUIT) { if (player->exiting) // End of match standings { diff --git a/src/k_kart.h b/src/k_kart.h index 04610be45..f97f0ddb6 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -37,6 +37,8 @@ Make sure this matches the actual number of states #define STUMBLE_STEEP_VAL ANG60 #define STUMBLE_STEEP_VAL_AIR (ANG30 + ANG10) +boolean K_PodiumSequence(void); + player_t *K_GetItemBoxPlayer(mobj_t *mobj); angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed); diff --git a/src/k_waypoint.c b/src/k_waypoint.c index cf70c1dcd..b0ed8b39b 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -235,6 +235,29 @@ INT32 K_GetWaypointID(waypoint_t *waypoint) return waypointid; } +/*-------------------------------------------------- + waypoint_t *K_GetWaypointFromID(INT32 waypointID) + + See header file for description. +--------------------------------------------------*/ +waypoint_t *K_GetWaypointFromID(INT32 waypointID) +{ + waypoint_t *waypoint = NULL; + size_t i = SIZE_MAX; + + for (i = 0; i < numwaypoints; i++) + { + waypoint = &waypointheap[i]; + + if (K_GetWaypointID(waypoint) == waypointID) + { + return waypoint; + } + } + + return NULL; +} + /*-------------------------------------------------- UINT32 K_GetCircuitLength(void) diff --git a/src/k_waypoint.h b/src/k_waypoint.h index c5f9a3a79..051798036 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -141,9 +141,25 @@ INT32 K_GetWaypointNextID(waypoint_t *waypoint); Return:- The waypoint ID, -1 if there is no waypoint or mobj. --------------------------------------------------*/ + INT32 K_GetWaypointID(waypoint_t *waypoint); +/*-------------------------------------------------- + waypoint_t *K_GetWaypointFromID(INT32 waypointID) + + Returns the first waypoint with the specified ID. + + Input Arguments:- + waypointID - The ID of the waypoint to get + + Return:- + The first waypoint with this ID, NULL if the ID doesn't exist at all in the map +--------------------------------------------------*/ + +waypoint_t *K_GetWaypointFromID(INT32 waypointID); + + /*-------------------------------------------------- UINT32 K_GetCircuitLength(void) diff --git a/src/lua_script.c b/src/lua_script.c index 17abc7384..62756688f 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -223,6 +223,9 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word,"tutorialmode")) { lua_pushboolean(L, tutorialmode); return 1; + } else if (fastcmp(word,"podiummap")) { + lua_pushstring(L, podiummap); + return 1; // end map vars // begin CTF colors } else if (fastcmp(word,"skincolor_redteam")) { From e47f46d3bc7d3a1c5a43e6632efeb5ca69bcc876 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Feb 2023 18:33:26 -0500 Subject: [PATCH 04/97] Give podium bots more traction --- src/deh_soc.c | 6 ++++++ src/k_kart.c | 29 ++++++++++++++++++++++++++--- src/k_kart.h | 1 + 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index a6287b2dc..82c477529 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2559,6 +2559,10 @@ void readmaincfg(MYFILE *f, boolean mainfile) INT32 value; boolean doClearLevels = false; +#ifdef DEVELOP + (void)mainfile; +#endif + do { if (myfgets(s, MAXLINELEN, f)) @@ -2630,10 +2634,12 @@ void readmaincfg(MYFILE *f, boolean mainfile) //clear_levels(); doClearLevels = true; } +#ifndef DEVELOP else if (!mainfile && !gamedataadded) { deh_warning("You must define a custom gamedata to use \"%s\"", word); } +#endif else if (fastcmp(word, "CLEARLEVELS")) { doClearLevels = (UINT8)(value == 0 || word2[0] == 'F' || word2[0] == 'N'); diff --git a/src/k_kart.c b/src/k_kart.c index dacc77fc1..629eff073 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -53,9 +53,15 @@ boolean K_PodiumSequence(void) { - // FIXME: Cache so we don't have to iterate all map headers every time INT32 podiumMapNum = nummapheaders; + if (grandprixinfo.gp == false) + { + return false; + } + + // FIXME: This function is used a lot during gameplay. + // Cache so we don't have to iterate all map headers every time. if (podiummap && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders)) { return (gamemap == podiumMapNum+1); @@ -3422,7 +3428,7 @@ fixed_t K_GetNewSpeed(player_t *player) // Don't calculate the acceleration as ever being above top speed if (oldspeed > p_speed) oldspeed = p_speed; - newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), ORIG_FRICTION); + newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), K_PlayerBaseFriction(ORIG_FRICTION)); finalspeed = newspeed - oldspeed; @@ -10007,18 +10013,35 @@ static void K_AirFailsafe(player_t *player) } } +// +// K_PlayerBaseFriction +// +fixed_t K_PlayerBaseFriction(fixed_t original) +{ + fixed_t frict = original; + + if (K_PodiumSequence() == true) + { + frict -= 4096; + } + + return frict; +} + // // K_AdjustPlayerFriction // void K_AdjustPlayerFriction(player_t *player) { - fixed_t prevfriction = player->mo->friction; + const fixed_t prevfriction = K_PlayerBaseFriction(player->mo->friction); if (P_IsObjectOnGround(player->mo) == false) { return; } + player->mo->friction = prevfriction; + // Reduce friction after hitting a spring if (player->tiregrease) { diff --git a/src/k_kart.h b/src/k_kart.h index f97f0ddb6..498416c91 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -178,6 +178,7 @@ fixed_t K_3dKartMovement(player_t *player); boolean K_PlayerEBrake(player_t *player); SINT8 K_Sliptiding(player_t *player); boolean K_FastFallBounce(player_t *player); +fixed_t K_PlayerBaseFriction(fixed_t original); void K_AdjustPlayerFriction(player_t *player); void K_MoveKartPlayer(player_t *player, boolean onground); void K_CheckSpectateStatus(void); From 7760d6688bfc0481705de68afd652bebc2a78105 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Feb 2023 19:34:13 -0500 Subject: [PATCH 05/97] Ceremony gamestate Doesn't work --- src/d_main.c | 1 + src/f_finale.c | 25 +++++++++++++++++++++++++ src/f_finale.h | 1 + src/g_game.c | 12 ++++++++++-- src/g_state.h | 1 + 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 3b72cb3b2..489ad3c32 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -381,6 +381,7 @@ static void D_Display(void) } /* FALLTHRU */ case GS_LEVEL: + case GS_CEREMONY: if (!gametic) break; HU_Erase(); diff --git a/src/f_finale.c b/src/f_finale.c index acd923765..6658227a8 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -3132,3 +3132,28 @@ void F_TextPromptTicker(void) animtimer--; } } + +boolean F_StartCeremony(void) +{ + INT32 podiumMapNum = nummapheaders; + + wipegamestate = GS_CEREMONY; + + if (podiummap + && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders) + && mapheaderinfo[podiumMapNum] + && mapheaderinfo[podiumMapNum]->lumpnum != LUMPERROR) + { + gamemap = podiumMapNum+1; + + maptol = mapheaderinfo[gamemap-1]->typeoflevel; + globalweather = mapheaderinfo[gamemap-1]->weather; + + G_DoLoadLevel(false); + + G_SetGamestate(GS_CEREMONY); + return true; + } + + return false; +} diff --git a/src/f_finale.h b/src/f_finale.h index c5972f9d1..338f514e7 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -78,6 +78,7 @@ void F_StartIntro(void); void F_StartTitleScreen(void); void F_StartEnding(void); void F_StartCredits(void); +boolean F_StartCeremony(void); extern INT32 finalecount; extern INT32 titlescrollxspeed; diff --git a/src/g_game.c b/src/g_game.c index 26e7f6337..c2ef64403 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2230,6 +2230,12 @@ void G_Ticker(boolean run) F_TitleScreenTicker(run); break; + case GS_CEREMONY: + P_Ticker(run); + ST_Ticker(run); + HU_Ticker(); + break; + case GS_WAITINGPLAYERS: if (netgame) F_WaitingPlayersTicker(); @@ -4258,8 +4264,10 @@ void G_EndGame(void) { if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony { - /*F_StartEnding(); -- temporary - return;*/ + if (F_StartCeremony() == true) + { + return; + } } if (nextmap == NEXTMAP_CREDITS) // end game with credits { diff --git a/src/g_state.h b/src/g_state.h index ed526f056..4446493b5 100644 --- a/src/g_state.h +++ b/src/g_state.h @@ -36,6 +36,7 @@ typedef enum GS_CREDITS, // credit sequence GS_EVALUATION, // Evaluation at the end of a game. GS_GAMEEND, // game end sequence - "did you get all those chaos emeralds?" + GS_CEREMONY, // RR: Podium sequence // Hardcoded fades or other fading methods GS_INTRO, // introduction From f78b3f77949cab0735d1e46bb8e9707bb065d327 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Feb 2023 12:32:36 -0500 Subject: [PATCH 06/97] Fixes for CEREMONY gamestate - Made titlemap + ceremony code more straight-forward by being able to set gamestate in G_DoLoadLevel directly - Demos are only recorded on GS_LEVEL (fixes crash for ceremony + titlemaps) - Added G_GamestateUsesLevel to encapsulate all checks for using level rendering - Added GS_CEREMONY to wipedefs - K_PodiumSequence just checks for GS_CEREMONY now (optimization) The game can now properly go to the podium after GP --- src/d_clisrv.c | 2 +- src/d_main.c | 31 +++++++++++++++++++++---------- src/deh_soc.c | 8 ++++++++ src/f_finale.c | 15 ++++++--------- src/f_finale.h | 2 ++ src/f_wipe.c | 2 ++ src/g_game.c | 45 ++++++++++++++++++++++++++++++++------------- src/g_game.h | 3 +-- src/g_state.h | 5 ++++- src/k_bot.c | 4 +++- src/k_kart.c | 16 +--------------- src/lua_script.c | 2 +- src/lua_script.h | 2 +- src/m_perfstats.c | 8 +++----- src/p_setup.c | 15 +++++++++------ 15 files changed, 95 insertions(+), 65 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 76470b865..97f416c20 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1272,7 +1272,7 @@ static void CL_LoadReceivedSavegame(boolean reloading) paused = false; demo.playback = false; demo.title = false; - titlemapinaction = TITLEMAP_OFF; + titlemapinaction = false; automapactive = false; // load a base level diff --git a/src/d_main.c b/src/d_main.c index 489ad3c32..dc5db348d 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -337,8 +337,8 @@ static void D_Display(void) if (rendermode != render_none) { // Fade to black first - if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)) // fades to black on its own timing, always - && wipetypepre != UINT8_MAX) + if (G_GamestateUsesLevel() == false // fades to black on its own timing, always + && wipetypepre != UINT8_MAX) { F_WipeStartScreen(); F_WipeColorFill(31); @@ -464,7 +464,7 @@ static void D_Display(void) // clean up border stuff // see if the border needs to be initially drawn - if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction && curbghide && (!hidetitlemap))) + if (G_GamestateUsesLevel() == true || (gamestate == GS_TITLESCREEN && titlemapinaction && curbghide && (!hidetitlemap))) { if (!automapactive && !dedicated && cv_renderview.value) { @@ -560,14 +560,25 @@ static void D_Display(void) ps_uitime = I_GetPreciseTime(); - if (gamestate == GS_LEVEL) + switch (gamestate) { - ST_Drawer(); - F_TextPromptDrawer(); - HU_Drawer(); + case GS_LEVEL: + { + ST_Drawer(); + F_TextPromptDrawer(); + HU_Drawer(); + break; + } + case GS_TITLESCREEN: + { + F_TitleScreenDrawer(); + break; + } + default: + { + break; + } } - else - F_TitleScreenDrawer(); } else { @@ -577,7 +588,7 @@ static void D_Display(void) // change gamma if needed // (GS_LEVEL handles this already due to level-specific palettes) - if (forcerefresh && !(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + if (forcerefresh && G_GamestateUsesLevel() == false) V_SetPalette(0); // draw pause pic diff --git a/src/deh_soc.c b/src/deh_soc.c index 82c477529..e4eb708c2 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2990,6 +2990,14 @@ void readwipes(MYFILE *f) else if (fastcmp(pword, "FINAL")) wipeoffset = wipe_gameend_final; } + else if (fastncmp(word, "CEREMONY_", 9)) + { + pword = word + 9; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_ceremony_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_ceremony_final; + } else if (fastncmp(word, "ENCORE_", 7)) { pword = word + 7; diff --git a/src/f_finale.c b/src/f_finale.c index 6658227a8..9f7f18076 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -50,7 +50,7 @@ INT32 finalecount; INT32 titlescrollxspeed = 16; INT32 titlescrollyspeed = 0; -UINT8 titlemapinaction = TITLEMAP_OFF; +boolean titlemapinaction = false; static INT32 timetonext; // Delay between screen changes @@ -1835,14 +1835,14 @@ void F_StartTitleScreen(void) mapthing_t *startpos; gamestate_t prevwipegamestate = wipegamestate; - titlemapinaction = TITLEMAP_LOADING; + titlemapinaction = true; titlemapcameraref = NULL; gamemap = titleMapNum+1; maptol = mapheaderinfo[titleMapNum]->typeoflevel; globalweather = mapheaderinfo[titleMapNum]->weather; - G_DoLoadLevel(true); + G_DoLoadLevelEx(true, GS_TITLESCREEN); if (!titlemap) return; @@ -1878,13 +1878,12 @@ void F_StartTitleScreen(void) } else { - titlemapinaction = TITLEMAP_OFF; + G_SetGamestate(GS_TITLESCREEN); + titlemapinaction = false; gamemap = 1; // g_game.c CON_ClearHUD(); } - G_SetGamestate(GS_TITLESCREEN); - // IWAD dependent stuff. animtimer = skullAnimCounter = 0; @@ -3149,9 +3148,7 @@ boolean F_StartCeremony(void) maptol = mapheaderinfo[gamemap-1]->typeoflevel; globalweather = mapheaderinfo[gamemap-1]->weather; - G_DoLoadLevel(false); - - G_SetGamestate(GS_CEREMONY); + G_DoLoadLevelEx(false, GS_CEREMONY); return true; } diff --git a/src/f_finale.h b/src/f_finale.h index 338f514e7..8afc54aba 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -185,6 +185,7 @@ enum wipe_credits_toblack, wipe_evaluation_toblack, wipe_gameend_toblack, + wipe_ceremony_toblack, wipe_intro_toblack, wipe_ending_toblack, wipe_cutscene_toblack, @@ -203,6 +204,7 @@ enum wipe_credits_final, wipe_evaluation_final, wipe_gameend_final, + wipe_ceremony_final, wipe_intro_final, wipe_ending_final, wipe_cutscene_final, diff --git a/src/f_wipe.c b/src/f_wipe.c index 227573c46..19f365611 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -64,6 +64,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 99, // wipe_credits_toblack 0, // wipe_evaluation_toblack 0, // wipe_gameend_toblack + 0, // wipe_ceremony_toblack UINT8_MAX, // wipe_intro_toblack (hardcoded) 99, // wipe_ending_toblack (hardcoded) 99, // wipe_cutscene_toblack (hardcoded) @@ -80,6 +81,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 99, // wipe_credits_final 0, // wipe_evaluation_final 0, // wipe_gameend_final + 0, // wipe_ceremony_final 99, // wipe_intro_final (hardcoded) 99, // wipe_ending_final (hardcoded) 99 // wipe_cutscene_final (hardcoded) diff --git a/src/g_game.c b/src/g_game.c index c2ef64403..f8e0bd2a1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1442,9 +1442,9 @@ static void weaponPrefChange4(void) } // -// G_DoLoadLevel +// G_DoLoadLevelEx // -void G_DoLoadLevel(boolean resetplayer) +void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate) { boolean doAutomate = false; INT32 i; @@ -1471,27 +1471,27 @@ void G_DoLoadLevel(boolean resetplayer) Y_EndVote(); // cleanup - if (titlemapinaction == TITLEMAP_LOADING) + // Is this actually necessary? Doesn't F_StartTitleScreen already do a significantly more comprehensive check? + if (newstate == GS_TITLESCREEN) { - //if (W_CheckNumForName(G_BuildMapName(gamemap)) == LUMPERROR) if (gamemap < 1 || gamemap > nummapheaders) { + G_SetGamestate(GS_TITLESCREEN); + titlemapinaction = false; + Z_Free(titlemap); titlemap = NULL; // let's not infinite recursion ok + Command_ExitGame_f(); return; } - - titlemapinaction = TITLEMAP_RUNNING; } - else - titlemapinaction = TITLEMAP_OFF; // Doing this matches HOSTMOD behavior. // Is that desired? IDK - doAutomate = (gamestate != GS_LEVEL); + doAutomate = (gamestate != GS_LEVEL && newstate == GS_LEVEL); - G_SetGamestate(GS_LEVEL); + G_SetGamestate(newstate); if (wipegamestate == GS_MENU) M_ClearMenus(true); I_UpdateMouseGrab(); @@ -1536,6 +1536,11 @@ void G_DoLoadLevel(boolean resetplayer) } } +void G_DoLoadLevel(boolean resetplayer) +{ + G_DoLoadLevelEx(resetplayer, GS_LEVEL); +} + // // Start the title card. // @@ -1566,7 +1571,7 @@ void G_StartTitleCard(void) } // start the title card - WipeStageTitle = (!titlemapinaction); + WipeStageTitle = (gamestate == GS_LEVEL); } // @@ -2168,7 +2173,6 @@ void G_Ticker(boolean run) F_TextPromptTicker(); AM_Ticker(); HU_Ticker(); - break; case GS_INTERMISSION: @@ -5340,6 +5344,22 @@ void G_SetGamestate(gamestate_t newstate) #endif } +boolean G_GamestateUsesLevel(void) +{ + switch (gamestate) + { + case GS_TITLESCREEN: + return titlemapinaction; + + case GS_LEVEL: + case GS_CEREMONY: + return true; + + default: + return false; + } +} + /* These functions handle the exitgame flag. Before, when the user chose to end a game, it happened immediately, which could cause crashes if the game was in the middle of something. Now, a flag @@ -5423,4 +5443,3 @@ INT32 G_TicsToMilliseconds(tic_t tics) { return (INT32)((tics%TICRATE) * (1000.00f/TICRATE)); } - diff --git a/src/g_game.h b/src/g_game.h index fac245c9a..081f414e0 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -164,6 +164,7 @@ void G_SpawnPlayer(INT32 playernum); // A normal game starts at map 1, but a warp test can start elsewhere void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ssplayers, boolean FLS); +void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate); void G_DoLoadLevel(boolean resetplayer); void G_StartTitleCard(void); @@ -250,8 +251,6 @@ void G_LoadGameSettings(void); void G_SetGameModified(boolean silent, boolean major); void G_SetUsedCheats(void); -void G_SetGamestate(gamestate_t newstate); - // Gamedata record shit void G_AllocMainRecordData(INT16 i); void G_ClearRecords(void); diff --git a/src/g_state.h b/src/g_state.h index 4446493b5..b78105639 100644 --- a/src/g_state.h +++ b/src/g_state.h @@ -59,10 +59,13 @@ typedef enum } gameaction_t; extern gamestate_t gamestate; -extern UINT8 titlemapinaction; +extern boolean titlemapinaction; extern UINT8 ultimatemode; // was sk_insane extern gameaction_t gameaction; +void G_SetGamestate(gamestate_t newstate); +boolean G_GamestateUsesLevel(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_bot.c b/src/k_bot.c index 89b4d2abe..300b27404 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1333,7 +1333,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Remove any existing controls memset(cmd, 0, sizeof(ticcmd_t)); - if (gamestate != GS_LEVEL || !player->mo || player->spectator) + if (player->mo == NULL + || player->spectator == true + || G_GamestateUsesLevel() == false) { // Not in the level. return; diff --git a/src/k_kart.c b/src/k_kart.c index 629eff073..e8bb88dc4 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -53,21 +53,7 @@ boolean K_PodiumSequence(void) { - INT32 podiumMapNum = nummapheaders; - - if (grandprixinfo.gp == false) - { - return false; - } - - // FIXME: This function is used a lot during gameplay. - // Cache so we don't have to iterate all map headers every time. - if (podiummap && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders)) - { - return (gamemap == podiumMapNum+1); - } - - return false; + return (gamestate == GS_CEREMONY); } boolean K_IsDuelItem(mobjtype_t type) diff --git a/src/lua_script.c b/src/lua_script.c index 62756688f..f2642e516 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -212,7 +212,7 @@ int LUA_PushGlobals(lua_State *L, const char *word) lua_pushstring(L, titlemap); return 1; } else if (fastcmp(word,"titlemapinaction")) { - lua_pushboolean(L, (titlemapinaction != TITLEMAP_OFF)); + lua_pushboolean(L, titlemapinaction); return 1; } else if (fastcmp(word,"bootmap")) { lua_pushstring(L, bootmap); diff --git a/src/lua_script.h b/src/lua_script.h index 9bfddc875..9e59d4d6d 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -139,7 +139,7 @@ void COM_Lua_f(void); // #define HAVE_LUA_SEGS #define ISINLEVEL \ - (gamestate == GS_LEVEL || titlemapinaction) + (G_GamestateUsesLevel()) #define INLEVEL if (! ISINLEVEL)\ return luaL_error(L, "This can only be used in a level!"); diff --git a/src/m_perfstats.c b/src/m_perfstats.c index 330db5f1f..53014a5ef 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -255,10 +255,7 @@ static void M_DrawRenderStats(void) perfstatcol_t batchcalls_col = {220, 200, V_PURPLEMAP, batchcalls_row}; - boolean rendering = ( - gamestate == GS_LEVEL || - (gamestate == GS_TITLESCREEN && titlemapinaction) - ); + boolean rendering = G_GamestateUsesLevel(); draw_row = 10; M_DrawPerfTiming(&frametime_col); @@ -619,8 +616,9 @@ void M_DrawPerfStats(void) } else if (cv_perfstats.value == PS_THINKFRAME) // lua thinkframe { - if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + if (G_GamestateUsesLevel() == false) return; + if (vid.width < 640 || vid.height < 400) // low resolution { // it's not gonna fit very well.. diff --git a/src/p_setup.c b/src/p_setup.c index d97696309..8679bf278 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7419,7 +7419,7 @@ static void P_InitGametype(void) // Start recording replay in multiplayer with a temp filename //@TODO I'd like to fix dedis crashing when recording replays for the future too... - if (!demo.playback && multiplayer && !dedicated) + if (gamestate == GS_LEVEL && !demo.playback && multiplayer && !dedicated) { char buf[MAX_WADPATH]; char ver[128]; @@ -7697,7 +7697,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0. // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug. - if (!(reloadinggamestate || titlemapinaction)) + if (!(reloadinggamestate || gamestate != GS_LEVEL)) S_FadeMusic(0, FixedMul( FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)); @@ -7705,7 +7705,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) if (rendermode != render_none) V_ReloadPalette(); // Set the level palette - if (!(reloadinggamestate || titlemapinaction)) + if (!(reloadinggamestate || gamestate != GS_LEVEL)) { if (ranspecialwipe == 2) { @@ -7745,8 +7745,11 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) F_RunWipe(wipetype, wipedefs[wipetype], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false); } - /*if (!titlemapinaction) - wipegamestate = GS_LEVEL;*/ + + /* + if (!titlemapinaction) + wipegamestate = GS_LEVEL; + */ // Close text prompt before freeing the old level F_EndTextPrompt(false, true); @@ -7948,7 +7951,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) P_MapEnd(); // tm.thing is no longer needed from this point onwards // Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap... - if (!titlemapinaction) + if (gamestate == GS_LEVEL) { if (!lastmaploaded) // Start a new game? { From 4d19b127c3905007aed5a1d3963b7d59dabcb650 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Feb 2023 13:25:33 -0500 Subject: [PATCH 07/97] Got_ExitLevelcmd sets GP round won --- src/d_netcmd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 71483bc61..4da4b6754 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5356,6 +5356,12 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) return; } + if (grandprixinfo.gp == true) + { + // This is already a cheat command. + grandprixinfo.wonround = true; + } + G_ExitLevel(); } From 4011c8b2fab1dc59e6eec892af53fe9ba82a98d8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Feb 2023 13:26:01 -0500 Subject: [PATCH 08/97] M_GamestateCanOpenMenu --- src/k_menufunc.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 8898bee71..15feed841 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -201,6 +201,23 @@ boolean M_PrevOpt(void) return true; } +static boolean M_GamestateCanOpenMenu(void) +{ + switch (gamestate) + { + case GS_INTRO: + case GS_CUTSCENE: + case GS_GAMEEND: + case GS_CREDITS: + case GS_EVALUATION: + case GS_CEREMONY: + return false; + + default: + return true; + } +} + // // M_Responder // @@ -208,9 +225,9 @@ boolean M_Responder(event_t *ev) { menuKey = -1; - if (dedicated || (demo.playback && demo.title) - || gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_GAMEEND - || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) + if (dedicated + || (demo.playback && demo.title) + || M_GamestateCanOpenMenu() == false) { return false; } From 6f0b09fecd2ffc63090827bb2bb777f34ff187f9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Feb 2023 13:26:07 -0500 Subject: [PATCH 09/97] CON_GamestateDrawHudLines --- src/console.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/console.c b/src/console.c index 04a9fdce4..9c7639725 100644 --- a/src/console.c +++ b/src/console.c @@ -1837,6 +1837,25 @@ static void CON_DrawConsole(void) // Console refresh drawer, call each frame // +static boolean CON_GamestateDrawHudLines(void) +{ + switch (gamestate) + { + case GS_LEVEL: + case GS_INTERMISSION: + case GS_VOTING: + case GS_CUTSCENE: + case GS_CREDITS: + case GS_EVALUATION: + case GS_WAITINGPLAYERS: + case GS_CEREMONY: + return true; + + default: + return false; + } +} + void CON_Drawer(void) { Lock_state(); @@ -1856,8 +1875,7 @@ void CON_Drawer(void) if (con_curlines > 0) CON_DrawConsole(); - else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS - || gamestate == GS_VOTING || gamestate == GS_EVALUATION || gamestate == GS_WAITINGPLAYERS) + else if (CON_GamestateDrawHudLines() == true) CON_DrawHudlines(); Unlock_state(); From 4ff0a8358fe885b25c4319c8d7dd2d32c06bda07 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Feb 2023 13:35:09 -0500 Subject: [PATCH 10/97] Rework exitlevel conditions - Uses K_CanChangeRules instead of checking for multiplayer, to catch all of the restricted singleplayer scenarios - Checks for server admin first before cheats --- src/d_netcmd.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4da4b6754..40d020700 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5330,14 +5330,22 @@ static void Command_Mapmd5_f(void) static void Command_ExitLevel_f(void) { - if (!(netgame || multiplayer) && !CV_CheatsEnabled()) - CONS_Printf(M_GetText("This only works in a netgame.\n")); - else if (!(server || (IsPlayerAdmin(consoleplayer)))) + if (!(server || (IsPlayerAdmin(consoleplayer)))) + { CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); + } + else if (K_CanChangeRules(false) == false && CV_CheatsEnabled() == false) + { + CONS_Printf(M_GetText("This cannot be used without cheats enabled.\n")); + } else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demo.playback) + { CONS_Printf(M_GetText("You must be in a level to use this.\n")); + } else + { SendNetXCmd(XD_EXITLEVEL, NULL, 0); + } } static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) From 82313de49909589e3571c29f093c982a057a700a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 26 Feb 2023 20:27:39 -0500 Subject: [PATCH 11/97] Awayview cleanup - Moved variables to awayview_t - Arbritrary +20 is no longer added to z pos, it will use the thing position as the camera position directly. --- src/d_clisrv.c | 2 +- src/d_player.h | 11 ++++++++--- src/f_finale.c | 21 +++++++++++++-------- src/f_finale.h | 11 ++--------- src/g_game.c | 2 +- src/k_hud.c | 4 ++-- src/lua_playerlib.c | 24 ++++++++++------------- src/p_enemy.c | 4 ++-- src/p_mobj.c | 22 +++++++++++----------- src/p_saveg.c | 22 ++++++++++------------ src/p_spec.c | 46 ++++++++++++++++++++++----------------------- src/p_user.c | 16 ++++++++-------- src/r_main.c | 22 +++++++++++----------- src/s_sound.c | 12 ++++++------ src/typedef.h | 1 + 15 files changed, 109 insertions(+), 111 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 97f416c20..56cb58b47 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2557,7 +2557,7 @@ void CL_ClearPlayer(INT32 playernum) P_SetTarget(&players[playernum].skybox.viewpoint, NULL); P_SetTarget(&players[playernum].skybox.centerpoint, NULL); - P_SetTarget(&players[playernum].awayviewmobj, NULL); + P_SetTarget(&players[playernum].awayview.mobj, NULL); P_SetTarget(&players[playernum].followmobj, NULL); P_SetTarget(&players[playernum].hoverhyudoro, NULL); P_SetTarget(&players[playernum].stumbleIndicator, NULL); diff --git a/src/d_player.h b/src/d_player.h index 298c65485..f38076f6f 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -384,6 +384,13 @@ typedef struct { boolean flip; } sonicloopvars_t; +// player_t struct for all alternative viewpoint variables +struct altview_t +{ + mobj_t *mobj; + INT32 tics; +}; + // ======================================================================== // PLAYER STRUCTURE // ======================================================================== @@ -644,9 +651,7 @@ struct player_t INT32 onconveyor; // You are on a conveyor belt if nonzero - mobj_t *awayviewmobj; - INT32 awayviewtics; - angle_t awayviewaiming; // Used for cut-away view + altview_t awayview; boolean spectator; tic_t spectatewait; // reimplementable as UINT8 queue - How long have you been waiting as a spectator diff --git a/src/f_finale.c b/src/f_finale.c index 9f7f18076..54f90e07a 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -62,7 +62,7 @@ static tic_t stoptimer; static boolean keypressed = false; static INT32 menuanimtimer; // Title screen: background animation timing -mobj_t *titlemapcameraref = NULL; +altview_t titlemapcam = {0}; // menu presentation state char curbgname[9]; @@ -1836,7 +1836,7 @@ void F_StartTitleScreen(void) gamestate_t prevwipegamestate = wipegamestate; titlemapinaction = true; - titlemapcameraref = NULL; + P_SetTarget(&titlemapcam.mobj, NULL); gamemap = titleMapNum+1; maptol = mapheaderinfo[titleMapNum]->typeoflevel; @@ -2138,7 +2138,7 @@ void F_TitleScreenTicker(boolean run) mobj_t *cameraref = NULL; // If there's a Line 422 Switch Cut-Away view, don't force us. - if (!titlemapcameraref || titlemapcameraref->type != MT_ALTVIEWMAN) + if (titlemapcam.mobj == NULL || titlemapcam.mobj->type != MT_ALTVIEWMAN) { for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) { @@ -2153,14 +2153,21 @@ void F_TitleScreenTicker(boolean run) if (mo2->type != MT_ALTVIEWMAN) continue; - cameraref = titlemapcameraref = mo2; + cameraref = mo2; break; } + + if (cameraref != NULL) + { + P_SetTarget(&titlemapcam.mobj, cameraref); + } } else - cameraref = titlemapcameraref; + { + cameraref = titlemapcam.mobj; + } - if (cameraref) + if (cameraref != NULL) { camera[0].x = cameraref->x; camera[0].y = cameraref->y; @@ -3136,8 +3143,6 @@ boolean F_StartCeremony(void) { INT32 podiumMapNum = nummapheaders; - wipegamestate = GS_CEREMONY; - if (podiummap && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders) && mapheaderinfo[podiumMapNum] diff --git a/src/f_finale.h b/src/f_finale.h index 8afc54aba..9b5ef0691 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -105,17 +105,10 @@ extern INT16 ttloop; extern UINT16 tttics; extern boolean ttavailable[6]; - -typedef enum -{ - TITLEMAP_OFF = 0, - TITLEMAP_LOADING, - TITLEMAP_RUNNING -} titlemap_enum; - // Current menu parameters -extern mobj_t *titlemapcameraref; +extern altview_t titlemapcam; + extern char curbgname[9]; extern SINT8 curfadevalue; extern INT32 curbgcolor; diff --git a/src/g_game.c b/src/g_game.c index f8e0bd2a1..48692590a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2520,7 +2520,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) { follower = players[player].follower; P_SetTarget(&players[player].follower, NULL); - P_SetTarget(&players[player].awayviewmobj, NULL); + P_SetTarget(&players[player].awayview.mobj, NULL); P_SetTarget(&players[player].stumbleIndicator, NULL); P_SetTarget(&players[player].followmobj, NULL); diff --git a/src/k_hud.c b/src/k_hud.c index 8fd7607ec..bbc008520 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2989,7 +2989,7 @@ static void K_drawKartPlayerCheck(void) return; } - if (stplyr->spectator || stplyr->awayviewtics) + if (stplyr->spectator || stplyr->awayview.tics) { return; } @@ -3238,7 +3238,7 @@ static void K_drawKartNameTags(void) return; } - if (stplyr->awayviewtics) + if (stplyr->awayview.tics) { return; } diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 211f83867..5e3963402 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -482,12 +482,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->timeshitprev); else if (fastcmp(field,"onconveyor")) lua_pushinteger(L, plr->onconveyor); - else if (fastcmp(field,"awayviewmobj")) - LUA_PushUserdata(L, plr->awayviewmobj, META_MOBJ); - else if (fastcmp(field,"awayviewtics")) - lua_pushinteger(L, plr->awayviewtics); - else if (fastcmp(field,"awayviewaiming")) - lua_pushangle(L, plr->awayviewaiming); + else if (fastcmp(field,"awayviewmobj")) // FIXME: struct + LUA_PushUserdata(L, plr->awayview.mobj, META_MOBJ); + else if (fastcmp(field,"awayviewtics")) // FIXME: struct + lua_pushinteger(L, plr->awayview.tics); else if (fastcmp(field,"spectator")) lua_pushboolean(L, plr->spectator); @@ -848,21 +846,19 @@ static int player_set(lua_State *L) plr->timeshitprev = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"onconveyor")) plr->onconveyor = (INT32)luaL_checkinteger(L, 3); - else if (fastcmp(field,"awayviewmobj")) + else if (fastcmp(field,"awayviewmobj")) // FIXME: struct { mobj_t *mo = NULL; if (!lua_isnil(L, 3)) mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); - P_SetTarget(&plr->awayviewmobj, mo); + P_SetTarget(&plr->awayview.mobj, mo); } - else if (fastcmp(field,"awayviewtics")) + else if (fastcmp(field,"awayviewtics")) // FIXME: struct { - plr->awayviewtics = (INT32)luaL_checkinteger(L, 3); - if (plr->awayviewtics && !plr->awayviewmobj) // awayviewtics must ALWAYS have an awayviewmobj set!! - P_SetTarget(&plr->awayviewmobj, plr->mo); // but since the script might set awayviewmobj immediately AFTER setting awayviewtics, use player mobj as filler for now. + plr->awayview.tics = (INT32)luaL_checkinteger(L, 3); + if (plr->awayview.tics && !plr->awayview.mobj) // awayviewtics must ALWAYS have an awayviewmobj set!! + P_SetTarget(&plr->awayview.mobj, plr->mo); // but since the script might set awayviewmobj immediately AFTER setting awayviewtics, use player mobj as filler for now. } - else if (fastcmp(field,"awayviewaiming")) - plr->awayviewaiming = luaL_checkangle(L, 3); else if (fastcmp(field,"spectator")) plr->spectator = lua_toboolean(L, 3); else if (fastcmp(field,"bot")) diff --git a/src/p_enemy.c b/src/p_enemy.c index 86ae6d0c3..7c1ede87a 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -4165,8 +4165,8 @@ void A_OverlayThink(mobj_t *actor) { angle_t viewingangle; - if (players[displayplayers[0]].awayviewtics) - viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y); + if (players[displayplayers[0]].awayview.tics) + viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayers[0]].awayview.mobj->x, players[displayplayers[0]].awayview.mobj->y); else if (!camera[0].chase && players[displayplayers[0]].mo) viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y); else diff --git a/src/p_mobj.c b/src/p_mobj.c index 3f0481a94..4433e1c64 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3779,13 +3779,13 @@ void P_CalcChasePostImg(player_t *player, camera_t *thiscam) { postimg = postimg_mirror; } - else if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist + else if (player->awayview.tics && player->awayview.mobj && !P_MobjWasRemoved(player->awayview.mobj)) // Camera must obviously exist { camera_t dummycam; - dummycam.subsector = player->awayviewmobj->subsector; - dummycam.x = player->awayviewmobj->x; - dummycam.y = player->awayviewmobj->y; - dummycam.z = player->awayviewmobj->z; + dummycam.subsector = player->awayview.mobj->subsector; + dummycam.x = player->awayview.mobj->x; + dummycam.y = player->awayview.mobj->y; + dummycam.z = player->awayview.mobj->z; //dummycam.height = 40*FRACUNIT; // alt view height is 20*FRACUNIT dummycam.height = 0; // Why? Remote viewpoint cameras have no height. // Are we in water? @@ -8088,8 +8088,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) angle_t viewingangle; statenum_t curstate = ((mobj->tics == 1) ? (mobj->state->nextstate) : ((statenum_t)(mobj->state-states))); - if (players[displayplayers[0]].awayviewtics) - viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y); + if (players[displayplayers[0]].awayview.tics) + viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayview.mobj->x, players[displayplayers[0]].awayview.mobj->y); else if (!camera[0].chase && players[displayplayers[0]].mo) viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y); else @@ -8219,8 +8219,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { angle_t viewingangle; - if (players[displayplayers[0]].awayviewtics) - viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y); + if (players[displayplayers[0]].awayview.tics) + viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayview.mobj->x, players[displayplayers[0]].awayview.mobj->y); else if (!camera[0].chase && players[displayplayers[0]].mo) viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y); else @@ -8324,8 +8324,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { angle_t viewingangle; - if (players[displayplayers[0]].awayviewtics) - viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y); + if (players[displayplayers[0]].awayview.tics) + viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayview.mobj->x, players[displayplayers[0]].awayview.mobj->y); else if (!camera[0].chase && players[displayplayers[0]].mo) viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y); else diff --git a/src/p_saveg.c b/src/p_saveg.c index fc9d7daf4..4d252c64b 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -137,8 +137,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEANGLE(save->p, players[i].drawangle); WRITEANGLE(save->p, players[i].viewrollangle); WRITEANGLE(save->p, players[i].tilt); - WRITEANGLE(save->p, players[i].awayviewaiming); - WRITEINT32(save->p, players[i].awayviewtics); + WRITEINT32(save->p, players[i].awayview.tics); WRITEUINT8(save->p, players[i].playerstate); WRITEUINT32(save->p, players[i].pflags); @@ -197,7 +196,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].splitscreenindex); - if (players[i].awayviewmobj) + if (players[i].awayview.mobj) flags |= AWAYVIEW; if (players[i].followmobj) @@ -227,7 +226,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, players[i].skybox.centerpoint->mobjnum); if (flags & AWAYVIEW) - WRITEUINT32(save->p, players[i].awayviewmobj->mobjnum); + WRITEUINT32(save->p, players[i].awayview.mobj->mobjnum); if (flags & FOLLOWITEM) WRITEUINT32(save->p, players[i].followmobj->mobjnum); @@ -527,8 +526,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].drawangle = players[i].old_drawangle = READANGLE(save->p); players[i].viewrollangle = READANGLE(save->p); players[i].tilt = READANGLE(save->p); - players[i].awayviewaiming = READANGLE(save->p); - players[i].awayviewtics = READINT32(save->p); + players[i].awayview.tics = READINT32(save->p); players[i].playerstate = READUINT8(save->p); players[i].pflags = READUINT32(save->p); @@ -596,7 +594,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].skybox.centerpoint = (mobj_t *)(size_t)READUINT32(save->p); if (flags & AWAYVIEW) - players[i].awayviewmobj = (mobj_t *)(size_t)READUINT32(save->p); + players[i].awayview.mobj = (mobj_t *)(size_t)READUINT32(save->p); if (flags & FOLLOWITEM) players[i].followmobj = (mobj_t *)(size_t)READUINT32(save->p); @@ -4684,12 +4682,12 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&players[i].skybox.centerpoint, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "skybox.centerpoint not found on player %d\n", i); } - if (players[i].awayviewmobj) + if (players[i].awayview.mobj) { - temp = (UINT32)(size_t)players[i].awayviewmobj; - players[i].awayviewmobj = NULL; - if (!P_SetTarget(&players[i].awayviewmobj, P_FindNewPosition(temp))) - CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on player %d\n", i); + temp = (UINT32)(size_t)players[i].awayview.mobj; + players[i].awayview.mobj = NULL; + if (!P_SetTarget(&players[i].awayview.mobj, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "awayview.mobj not found on player %d\n", i); } if (players[i].followmobj) { diff --git a/src/p_spec.c b/src/p_spec.c index 5d52da0fb..60c261ad8 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2994,35 +2994,35 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha case 422: // Cut away to another view { - mobj_t *altview; - INT32 aim; + altview_t *modifyView = NULL; + mobj_t *newViewMobj = NULL; - if ((!mo || !mo->player) && !titlemapinaction) // only players have views, and title screens - return false; - - altview = P_FindObjectTypeFromTag(MT_ALTVIEWMAN, args[0]); - if (!altview || !altview->spawnpoint) - return false; - - // If titlemap, set the camera ref for title's thinker - // This is not revoked until overwritten; awayviewtics is ignored if (titlemapinaction) - titlemapcameraref = altview; + { + modifyView = &titlemapcam; + } + else if (mo != NULL && mo->player != NULL) + { + modifyView = &mo->player->awayview; + } else { - P_SetTarget(&mo->player->awayviewmobj, altview); - mo->player->awayviewtics = args[1]; + return false; } - aim = (backwardsCompat) ? args[2] : altview->spawnpoint->pitch; - aim = (aim + 360) % 360; - aim *= (ANGLE_90>>8); - aim /= 90; - aim <<= 8; - if (titlemapinaction) - titlemapcameraref->cusval = (angle_t)aim; - else - mo->player->awayviewaiming = (angle_t)aim; + newViewMobj = P_FindObjectTypeFromTag(MT_ALTVIEWMAN, args[0]); + if (newViewMobj == NULL || newViewMobj->spawnpoint == NULL) + { + return false; + } + + P_SetTarget(&modifyView->mobj, newViewMobj); + + // If titlemap, awayview.tics is ignored + if (titlemapinaction == false) + { + modifyView->tics = args[1]; + } } break; diff --git a/src/p_user.c b/src/p_user.c index f95d5afc1..fcf035850 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3670,10 +3670,10 @@ static void P_CalcPostImg(player_t *player) else pviewheight = player->mo->z + player->viewheight; - if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj)) + if (player->awayview.tics && player->awayview.mobj && !P_MobjWasRemoved(player->awayview.mobj)) { - sector = player->awayviewmobj->subsector->sector; - pviewheight = player->awayviewmobj->z + 20*FRACUNIT; + sector = player->awayview.mobj->subsector->sector; + pviewheight = player->awayview.mobj->z; } for (i = 0; i <= (unsigned)r_splitscreen; i++) @@ -4020,17 +4020,17 @@ void P_PlayerThink(player_t *player) player->old_drawangle = player->drawangle; - if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj)) + if (player->awayview.mobj && P_MobjWasRemoved(player->awayview.mobj)) { - P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid - player->awayviewtics = 0; // reset to zero + P_SetTarget(&player->awayview.mobj, NULL); // remove awayview.mobj asap if invalid + player->awayview.tics = 0; // reset to zero } if (player->flashcount) player->flashcount--; - if (player->awayviewtics && player->awayviewtics != -1) - player->awayviewtics--; + if (player->awayview.tics && player->awayview.tics != -1) + player->awayview.tics--; // Track airtime if (P_IsObjectOnGround(player->mo) diff --git a/src/r_main.c b/src/r_main.c index b00717810..ded540741 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1192,10 +1192,10 @@ static void R_SetupAimingFrame(int s) player_t *player = &players[displayplayers[s]]; camera_t *thiscam = &camera[s]; - if (player->awayviewtics) + if (player->awayview.tics) { - newview->aim = player->awayviewaiming; - newview->angle = player->awayviewmobj->angle; + newview->aim = player->awayview.mobj->pitch; + newview->angle = player->awayview.mobj->angle; } else if (thiscam && thiscam->chase) { @@ -1237,15 +1237,15 @@ void R_SetupFrame(int s) R_SetupAimingFrame(s); - if (player->awayviewtics) + if (player->awayview.tics) { // cut-away view stuff - r_viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN + r_viewmobj = player->awayview.mobj; // should be a MT_ALTVIEWMAN I_Assert(r_viewmobj != NULL); newview->x = r_viewmobj->x; newview->y = r_viewmobj->y; - newview->z = r_viewmobj->z + 20*FRACUNIT; + newview->z = r_viewmobj->z; R_SetupCommonFrame(player, r_viewmobj->subsector); } @@ -1306,10 +1306,10 @@ void R_SkyboxFrame(int s) vector3_t campos = {0,0,0}; // Position of player's actual view point mobj_t *center = player->skybox.centerpoint; - if (player->awayviewtics) { - campos.x = player->awayviewmobj->x; - campos.y = player->awayviewmobj->y; - campos.z = player->awayviewmobj->z + 20*FRACUNIT; + if (player->awayview.tics) { + campos.x = player->awayview.mobj->x; + campos.y = player->awayview.mobj->y; + campos.z = player->awayview.mobj->z; } else if (thiscam->chase) { campos.x = thiscam->x; campos.y = thiscam->y; @@ -1403,7 +1403,7 @@ boolean R_IsViewpointThirdPerson(player_t *player, boolean skybox) boolean chasecam = R_ViewpointHasChasecam(player); // cut-away view stuff - if (player->awayviewtics || skybox) + if (player->awayview.tics || skybox) return chasecam; // use outside cam view else if (!player->spectator && chasecam) diff --git a/src/s_sound.c b/src/s_sound.c index 6deab1f91..b45215c73 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -540,9 +540,9 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) continue; } - if (player->awayviewtics) + if (player->awayview.tics) { - listenmobj[i] = player->awayviewmobj; + listenmobj[i] = player->awayview.mobj; } else { @@ -572,7 +572,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) continue; } - if (camera[i].chase && !player->awayviewtics) + if (camera[i].chase && !player->awayview.tics) { listener[i].x = camera[i].x; listener[i].y = camera[i].y; @@ -827,9 +827,9 @@ void S_UpdateSounds(void) continue; } - if (player->awayviewtics) + if (player->awayview.tics) { - listenmobj[i] = player->awayviewmobj; + listenmobj[i] = player->awayview.mobj; } else { @@ -858,7 +858,7 @@ void S_UpdateSounds(void) continue; } - if (camera[i].chase && !player->awayviewtics) + if (camera[i].chase && !player->awayview.tics) { listener[i].x = camera[i].x; listener[i].y = camera[i].y; diff --git a/src/typedef.h b/src/typedef.h index 632684d1e..0b745eb30 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -46,6 +46,7 @@ TYPEDEF (respawnvars_t); TYPEDEF (botvars_t); TYPEDEF (skybox_t); TYPEDEF (itemroulette_t); +TYPEDEF (altview_t); TYPEDEF (player_t); // d_clisrv.h From 9501b57296a93477709c2080b84aea3d5a92dd2d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 27 Feb 2023 18:59:34 -0500 Subject: [PATCH 12/97] Extend cut-away view functionality - arg2: TID of an object to make the camera's angle/pitch follow. - arg3: Zoom tube waypoint sequence to start following. - Ceremony uses one-screen, regardless of previous splitscreen-ed-ness --- src/f_finale.c | 28 ++++++++++++++- src/f_finale.h | 2 ++ src/g_game.c | 3 +- src/info.c | 2 +- src/p_local.h | 2 ++ src/p_mobj.c | 64 +++++++++++++++++++++++++++++++--- src/p_spec.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/p_user.c | 28 ++++++++++----- 8 files changed, 202 insertions(+), 20 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 54f90e07a..be90017f8 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2173,7 +2173,7 @@ void F_TitleScreenTicker(boolean run) camera[0].y = cameraref->y; camera[0].z = cameraref->z; camera[0].angle = cameraref->angle; - camera[0].aiming = cameraref->cusval; + camera[0].aiming = cameraref->pitch; camera[0].subsector = cameraref->subsector; } else @@ -3154,8 +3154,34 @@ boolean F_StartCeremony(void) globalweather = mapheaderinfo[gamemap-1]->weather; G_DoLoadLevelEx(false, GS_CEREMONY); + + r_splitscreen = 0; // Only one screen for the ceremony + R_ExecuteSetViewSize(); return true; } return false; } + +void F_CeremonyTicker(boolean run) +{ + (void)run; + + // don't trigger if doing anything besides idling + if (gameaction != ga_nothing || gamestate != GS_CEREMONY) + { + return; + } + + P_TickAltView(&titlemapcam); + + if (titlemapcam.mobj != NULL) + { + camera[0].x = titlemapcam.mobj->x; + camera[0].y = titlemapcam.mobj->y; + camera[0].z = titlemapcam.mobj->z; + camera[0].angle = titlemapcam.mobj->angle; + camera[0].aiming = titlemapcam.mobj->pitch; + camera[0].subsector = titlemapcam.mobj->subsector; + } +} diff --git a/src/f_finale.h b/src/f_finale.h index 9b5ef0691..e86e2ce5c 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -78,7 +78,9 @@ void F_StartIntro(void); void F_StartTitleScreen(void); void F_StartEnding(void); void F_StartCredits(void); + boolean F_StartCeremony(void); +void F_CeremonyTicker(boolean run); extern INT32 finalecount; extern INT32 titlescrollxspeed; diff --git a/src/g_game.c b/src/g_game.c index 48692590a..fadc35acd 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2236,8 +2236,7 @@ void G_Ticker(boolean run) case GS_CEREMONY: P_Ticker(run); - ST_Ticker(run); - HU_Ticker(); + F_CeremonyTicker(run); break; case GS_WAITINGPLAYERS: diff --git a/src/info.c b/src/info.c index d0509e731..a400c4cc2 100644 --- a/src/info.c +++ b/src/info.c @@ -21281,7 +21281,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 10, // mass 0, // damage sfx_None, // activesound - MF_NOTHINK|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags + MF_NOBLOCKMAP|MF_NOGRAVITY, // flags S_NULL // raisestate }, diff --git a/src/p_local.h b/src/p_local.h index 12362a548..eb4633ff4 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -197,6 +197,8 @@ void P_HaltPlayerOrbit(player_t *player); void P_ExitPlayerOrbit(player_t *player); boolean P_PlayerOrbit(player_t *player); +void P_TickAltView(altview_t *view); + void P_MovePlayer(player_t *player); void P_PlayerThink(player_t *player); void P_PlayerAfterThink(player_t *player); diff --git a/src/p_mobj.c b/src/p_mobj.c index 4433e1c64..cc0faf8df 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -47,6 +47,7 @@ #include "k_objects.h" #include "k_grandprix.h" #include "k_director.h" +#include "m_easing.h" static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); @@ -3782,12 +3783,13 @@ void P_CalcChasePostImg(player_t *player, camera_t *thiscam) else if (player->awayview.tics && player->awayview.mobj && !P_MobjWasRemoved(player->awayview.mobj)) // Camera must obviously exist { camera_t dummycam; + dummycam.subsector = player->awayview.mobj->subsector; dummycam.x = player->awayview.mobj->x; dummycam.y = player->awayview.mobj->y; dummycam.z = player->awayview.mobj->z; - //dummycam.height = 40*FRACUNIT; // alt view height is 20*FRACUNIT - dummycam.height = 0; // Why? Remote viewpoint cameras have no height. + dummycam.height = 0; + // Are we in water? if (P_CameraCheckWater(&dummycam)) postimg = postimg_water; @@ -9487,6 +9489,61 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_MONITOR_PART: Obj_MonitorPartThink(mobj); break; + case MT_ALTVIEWMAN: + { + mobj->momx = mobj->momy = mobj->momz = 0; + + if (mobj->tracer != NULL && P_MobjWasRemoved(mobj->tracer) == false) + { + fixed_t dist = P_AproxDistance( + P_AproxDistance( + mobj->tracer->x - mobj->x, + mobj->tracer->y - mobj->y + ), + mobj->tracer->z - mobj->z + ); + + mobj->angle = Easing_OutSine(FRACUNIT/8, mobj->angle, mobj->tracer->angle); + mobj->pitch = Easing_OutSine(FRACUNIT/8, mobj->pitch, mobj->tracer->pitch); + + if (dist > mobj->radius) + { + fixed_t newX = Easing_OutSine(FRACUNIT/8, mobj->x, mobj->tracer->x); + fixed_t newY = Easing_OutSine(FRACUNIT/8, mobj->y, mobj->tracer->y); + fixed_t newZ = Easing_OutSine(FRACUNIT/8, mobj->z, mobj->tracer->z); + + mobj->momx = newX - mobj->x; + mobj->momy = newY - mobj->y; + mobj->momz = newZ - mobj->z; + } + else + { + P_SetTarget(&mobj->tracer, P_GetNextTubeWaypoint(mobj->tracer, false)); + } + } + + // If target is valid, then we'll focus on it. + if (mobj->target != NULL && P_MobjWasRemoved(mobj->target) == false) + { + mobj->angle = R_PointToAngle2( + mobj->x, + mobj->y, + mobj->target->x, + mobj->target->y + ); + + mobj->pitch = R_PointToAngle2( + 0, + mobj->z, + R_PointToDist2( + mobj->x, mobj->y, + mobj->target->x, mobj->target->y + ), + mobj->target->z + (mobj->target->height >> 1) + ); + } + } + break; default: // check mobj against possible water content, before movement code P_MobjCheckWater(mobj); @@ -10464,9 +10521,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) else switch (mobj->type) { - case MT_ALTVIEWMAN: - if (titlemapinaction) mobj->flags &= ~MF_NOTHINK; - break; case MT_LOCKONINF: P_SetScale(mobj, (mobj->destscale = 3*mobj->scale)); break; diff --git a/src/p_spec.c b/src/p_spec.c index 60c261ad8..74397cd8f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2997,7 +2997,7 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha altview_t *modifyView = NULL; mobj_t *newViewMobj = NULL; - if (titlemapinaction) + if (gamestate != GS_LEVEL) { modifyView = &titlemapcam; } @@ -3018,11 +3018,98 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha P_SetTarget(&modifyView->mobj, newViewMobj); - // If titlemap, awayview.tics is ignored - if (titlemapinaction == false) + if (gamestate != GS_LEVEL) + { + // If titlemap, awayview.tics is ignored + modifyView->tics = -1; + } + else { modifyView->tics = args[1]; } + + if (args[2] != 0) + { + switch (args[2]) + { + case -1: + { + mobj_t *firstPlace = NULL; + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + + if (playeringame[i] == false) + { + continue; + } + + player = &players[i]; + if (player->spectator == true) + { + continue; + } + + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + continue; + } + + if (player->position == 1) + { + firstPlace = player->mo; + break; + } + } + + P_SetTarget( + &newViewMobj->target, + firstPlace + ); + break; + } + case -2: + { + mobj_t *consoleMo = NULL; + if (playeringame[consoleplayer] == true) + { + consoleMo = players[consoleplayer].mo; + } + + P_SetTarget( + &newViewMobj->target, + consoleMo + ); + break; + } + default: + { + P_SetTarget( + &newViewMobj->target, + P_FindMobjFromTID(args[2], NULL, NULL) + ); + break; + } + } + } + else + { + P_SetTarget(&newViewMobj->target, NULL); + } + + if (args[3] > 0 && args[3] <= NUMTUBEWAYPOINTSEQUENCES) + { + P_SetTarget( + &newViewMobj->tracer, + P_GetFirstTubeWaypoint(args[3] - 1) + ); + } + else + { + P_SetTarget(&newViewMobj->tracer, NULL); + } } break; diff --git a/src/p_user.c b/src/p_user.c index fcf035850..0a2c5b548 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3997,6 +3997,25 @@ DoABarrelRoll (player_t *player) player->tilt = slope; } +void P_TickAltView(altview_t *view) +{ + if (view->mobj != NULL && P_MobjWasRemoved(view->mobj) == true) + { + P_SetTarget(&view->mobj, NULL); // remove view->mobj asap if invalid + view->tics = 0; // reset to zero + } + + if (view->tics > 0) + { + view->tics--; + + if (view->tics == 0) + { + P_SetTarget(&view->mobj, NULL); + } + } +} + // // P_PlayerThink // @@ -4020,18 +4039,11 @@ void P_PlayerThink(player_t *player) player->old_drawangle = player->drawangle; - if (player->awayview.mobj && P_MobjWasRemoved(player->awayview.mobj)) - { - P_SetTarget(&player->awayview.mobj, NULL); // remove awayview.mobj asap if invalid - player->awayview.tics = 0; // reset to zero - } + P_TickAltView(&player->awayview); if (player->flashcount) player->flashcount--; - if (player->awayview.tics && player->awayview.tics != -1) - player->awayview.tics--; - // Track airtime if (P_IsObjectOnGround(player->mo) && !P_PlayerInPain(player)) // This isn't airtime, but it's control loss all the same. From 04eefa1acceb46e9c1d4833e7cff633534ae1535 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 27 Feb 2023 19:26:18 -0500 Subject: [PATCH 13/97] Go to podium when game over on special stage --- src/g_game.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index fadc35acd..9d1183df3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3087,13 +3087,20 @@ void G_ExitLevel(void) if (i == MAXPLAYERS) { // GAME OVER, try again from the start! - - if (netgame) + if (grandprixinfo.gp == true + && grandprixinfo.eventmode == GPEVENT_SPECIAL) { - ; // restart cup here if we do online GP + // We were in a Special Stage. + // We can still progress to the podium when we game over here. + doretry = false; + } + else if (netgame) + { + ; // Restart cup here whenever we do Online GP } else { + // Back to the menu with you. D_QuitNetGame(); CL_Reset(); D_ClearState(); @@ -3102,11 +3109,14 @@ void G_ExitLevel(void) } else { - // Go redo this course. + // We have lives, just redo this one course. G_SetRetryFlag(); } - return; + if (doretry == true) + { + return; + } } gameaction = ga_completed; From 2eba4183345c19cc4cfdf75e524e8b26705fa9c0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 27 Feb 2023 19:31:20 -0500 Subject: [PATCH 14/97] Ensure players can spawn before the podium starts --- src/f_finale.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/f_finale.c b/src/f_finale.c index be90017f8..64f2a67c2 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -3142,6 +3142,7 @@ void F_TextPromptTicker(void) boolean F_StartCeremony(void) { INT32 podiumMapNum = nummapheaders; + INT32 i; if (podiummap && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders) @@ -3153,6 +3154,16 @@ boolean F_StartCeremony(void) maptol = mapheaderinfo[gamemap-1]->typeoflevel; globalweather = mapheaderinfo[gamemap-1]->weather; + // Make sure all of the GAME OVER'd players can spawn + // and be present for the podium + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator && !players[i].bot) + { + players[i].lives = max(1, players[i].lives); + } + } + G_DoLoadLevelEx(false, GS_CEREMONY); r_splitscreen = 0; // Only one screen for the ceremony From 2d84837e5ba5e935a927f7902ddf70c56818d51f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 1 Mar 2023 21:31:59 -0500 Subject: [PATCH 15/97] Use ceremony gamestate when warping to podium on startup Doesn't spawn the bots for some reason still, but I need this for quicker prototyping --- src/d_main.c | 23 +++++++++-------------- src/f_finale.c | 8 ++++++++ src/g_game.c | 24 +++++++++++++++++++++++- src/k_bot.c | 6 ------ src/p_tick.c | 4 ++-- 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index dc5db348d..b3cae9892 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -346,7 +346,7 @@ static void D_Display(void) F_RunWipe(wipedefindex, wipetypepre, gamestate != GS_MENU, "FADEMAP0", false, false); } - if (gamestate != GS_LEVEL && rendermode != render_none) + if (G_GamestateUsesLevel() == false && rendermode != render_none) { V_SetPaletteLump("PLAYPAL"); // Reset the palette R_ReInitColormaps(0, NULL, 0); @@ -381,7 +381,6 @@ static void D_Display(void) } /* FALLTHRU */ case GS_LEVEL: - case GS_CEREMONY: if (!gametic) break; HU_Erase(); @@ -400,6 +399,13 @@ static void D_Display(void) HU_Drawer(); break; + case GS_CEREMONY: + if (!gametic) + break; + HU_Erase(); + HU_Drawer(); + break; + case GS_MENU: break; @@ -464,7 +470,7 @@ static void D_Display(void) // clean up border stuff // see if the border needs to be initially drawn - if (G_GamestateUsesLevel() == true || (gamestate == GS_TITLESCREEN && titlemapinaction && curbghide && (!hidetitlemap))) + if (G_GamestateUsesLevel() == true) { if (!automapactive && !dedicated && cv_renderview.value) { @@ -757,17 +763,6 @@ void D_SRB2Loop(void) because I_FinishUpdate was called afterward */ -#if 0 - /* Smells like a hack... Don't fade Sonic's ass into the title screen. */ - if (gamestate != GS_TITLESCREEN) - { - static lumpnum_t gstartuplumpnum = W_CheckNumForName("STARTUP"); - if (gstartuplumpnum == LUMPERROR) - gstartuplumpnum = W_GetNumForName("MISSING"); - V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(gstartuplumpnum, PU_PATCH)); - } -#endif - for (;;) { // capbudget is the minimum precise_t duration of a single loop iteration diff --git a/src/f_finale.c b/src/f_finale.c index 64f2a67c2..f801067a1 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -44,6 +44,7 @@ // SRB2Kart #include "k_menu.h" +#include "k_grandprix.h" // Stage of animation: // 0 = text, 1 = art screen @@ -3144,11 +3145,18 @@ boolean F_StartCeremony(void) INT32 podiumMapNum = nummapheaders; INT32 i; + if (grandprixinfo.gp == false) + { + return false; + } + if (podiummap && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders) && mapheaderinfo[podiumMapNum] && mapheaderinfo[podiumMapNum]->lumpnum != LUMPERROR) { + P_SetTarget(&titlemapcam.mobj, NULL); + gamemap = podiumMapNum+1; maptol = mapheaderinfo[gamemap-1]->typeoflevel; diff --git a/src/g_game.c b/src/g_game.c index 9d1183df3..955855f2b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1454,7 +1454,7 @@ void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate) levelstarttic = gametic; // for time calculation - if (wipegamestate == GS_LEVEL) + if (wipegamestate == newstate) wipegamestate = -1; // force a wipe if (cv_currprofile.value == -1 && !demo.playback) @@ -1715,6 +1715,16 @@ boolean G_Responder(event_t *ev) return true; } } + else if (gamestate == GS_CEREMONY) + { + if (HU_Responder(ev)) + { + hu_keystrokes = true; + return true; // chat ate the event + } + + // todo + } else if (gamestate == GS_CONTINUING) { return true; @@ -1725,11 +1735,13 @@ boolean G_Responder(event_t *ev) return true; } else if (gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_EVALUATION) + { if (HU_Responder(ev)) { hu_keystrokes = true; return true; // chat ate the event } + } if (gamestate == GS_LEVEL && ev->type == ev_keydown && multiplayer && demo.playback && !demo.freecam) { @@ -5059,6 +5071,16 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr return; } + if (map == G_MapNumber(podiummap)+1) + { + // Didn't want to do this, but it needs to be here + // for it to work on startup. + if (F_StartCeremony() == true) + { + return; + } + } + gamemap = map; maptol = mapheaderinfo[gamemap-1]->typeoflevel; diff --git a/src/k_bot.c b/src/k_bot.c index 300b27404..b986d1d4a 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1624,12 +1624,6 @@ void K_UpdateBotGameplayVars(player_t *player) return; } - if (K_PodiumSequence() == true) - { - // We don't want these during podium. - return; - } - botController = K_FindBotController(player->mo); player->botvars.controller = botController ? (botController - lines) : UINT16_MAX; diff --git a/src/p_tick.c b/src/p_tick.c index 70c4a14f3..4d780d9a6 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -78,7 +78,7 @@ void Command_Numthinkers_f(void) thinklistnum_t end = NUM_THINKERLISTS - 1; thinklistnum_t i; - if (gamestate != GS_LEVEL) + if (G_GamestateUsesLevel() == false) { CONS_Printf(M_GetText("You must be in a level to use this.\n")); return; @@ -149,7 +149,7 @@ void Command_CountMobjs_f(void) mobjtype_t i; INT32 count; - if (gamestate != GS_LEVEL) + if (G_GamestateUsesLevel() == false) { CONS_Printf(M_GetText("You must be in a level to use this.\n")); return; From dddc992e24f5011ccdd3cbff96605496831320ab Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 1 Mar 2023 22:24:42 -0500 Subject: [PATCH 16/97] Fix camera tilting in podium --- src/p_tick.c | 10 ++++++---- src/r_main.c | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/p_tick.c b/src/p_tick.c index 4d780d9a6..9b4e987bf 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -795,10 +795,12 @@ void P_Ticker(boolean run) } } - K_UpdateDirector(); - - // Always move the camera. - P_RunChaseCameras(); + if (gamestate == GS_LEVEL) + { + // Move the camera during levels. + K_UpdateDirector(); + P_RunChaseCameras(); + } LUA_HOOK(PostThinkFrame); diff --git a/src/r_main.c b/src/r_main.c index ded540741..0dfe36d38 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -928,7 +928,22 @@ void R_ApplyViewMorph(int s) angle_t R_ViewRollAngle(const player_t *player) { - angle_t roll = player->viewrollangle; + angle_t roll = 0; + + if (gamestate != GS_LEVEL) + { + // FIXME: The way this is implemented is totally + // incompatible with cameras that aren't directly + // tied to the player. (podium, titlemap, + // MT_ALTVIEWMAN in general) + + // All of these player variables should affect their + // camera_t in P_MoveChaseCamera, and then this + // just returns that variable instead. + return 0; + } + + roll = player->viewrollangle; if (cv_tilting.value) { From 2806f1c08587aac2e48ba6fbc6ffed5261921865 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 16:16:36 -0500 Subject: [PATCH 17/97] Fix bots not being able to spawn for podium (This might break titlemaps but maybe titlemaps should do something more explicit to hide all players) --- src/g_game.c | 9 ++++++++- src/p_mobj.c | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 955855f2b..98b03ec6e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2109,7 +2109,7 @@ void G_Ticker(boolean run) marathontime++; P_MapStart(); - // do player reborns if needed + if (gamestate == GS_LEVEL) { // Or, alternatively, retry. @@ -2127,11 +2127,18 @@ void G_Ticker(boolean run) D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), false, 1, false, false); } + } + // do player reborns if needed + if (G_GamestateUsesLevel() == true) + { for (i = 0; i < MAXPLAYERS; i++) + { if (playeringame[i] && players[i].playerstate == PST_REBORN) G_DoReborn(i); + } } + P_MapEnd(); // do things to change the game state diff --git a/src/p_mobj.c b/src/p_mobj.c index cc0faf8df..66b7011cc 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11740,7 +11740,9 @@ void P_SpawnPlayer(INT32 playernum) } else if (p->bot) { - if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE) + if (K_PodiumSequence() == false + && grandprixinfo.gp == true + && grandprixinfo.eventmode != GPEVENT_NONE) { // Bots aren't supposed to be here. p->spectator = true; From 78a893666f68a1be8d3092e4a180b1df437d8862 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 16:17:29 -0500 Subject: [PATCH 18/97] Podium bots don't drop rings if they bump --- src/k_collide.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/k_collide.c b/src/k_collide.c index ee773dfa3..bf813e410 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -811,6 +811,12 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) boolean stungT1 = false; boolean stungT2 = false; + if (K_PodiumSequence() == true) + { + // Always regular bumps, no ring toss. + return false; + } + // Clash instead of damage if both parties have any of these conditions t1Condition = (K_IsBigger(t1, t2) == true) || (t1->player->invincibilitytimer > 0) From 0d12a3cb5da13ac4f944d7db390beffc6eb8dc5d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 16:18:17 -0500 Subject: [PATCH 19/97] No starttime for podium --- src/k_kart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index e8bb88dc4..22a7ddb9b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -114,6 +114,12 @@ void K_TimerInit(void) boolean domodeattack = ((modeattacking != ATTACKING_NONE) || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)); + if (K_PodiumSequence() == true) + { + // Leave it alone for podium + return; + } + // Rooooooolllling staaaaaaart if ((gametyperules & (GTR_ROLLINGSTART|GTR_CIRCUIT)) == (GTR_ROLLINGSTART|GTR_CIRCUIT)) { From 3850f2d579f4f99753e15eda66466a02abf7e315 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 16:19:27 -0500 Subject: [PATCH 20/97] Podium bot handling tweaks - Friction was made stronger - They are better at turning - Friction change is properly clamped --- src/k_kart.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 22a7ddb9b..da534d349 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8911,6 +8911,11 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) turnfixed = FixedMul(turnfixed, 5*FRACUNIT/4); // Base increase to turning } + if (K_PodiumSequence() == true) + { + turnfixed *= 2; + } + if (player->drift != 0 && P_IsObjectOnGround(player->mo)) { fixed_t countersteer = FixedDiv(turnfixed, KART_FULLTURN*FRACUNIT); @@ -10014,9 +10019,12 @@ fixed_t K_PlayerBaseFriction(fixed_t original) if (K_PodiumSequence() == true) { - frict -= 4096; + frict -= FRACUNIT >> 3; } + if (frict > FRACUNIT) { frict = FRACUNIT; } + if (frict < 0) { frict = 0; } + return frict; } From 32c6baf2fc73976b787478c78284effd39285210 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 17:25:46 -0500 Subject: [PATCH 21/97] Fix + improve podium camera movement - Record starting position properly for easing - Uses linear easing now (everything else ended up looking bad with multiple waypoints, would to calculate splines if you wanted to ease that) - arg4 is now how long the camera spends between each waypoint --- src/p_mobj.c | 33 +++++++++++++++++++-------------- src/p_spec.c | 15 +++++++++++++++ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 66b7011cc..aa9ff6974 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9493,24 +9493,23 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { mobj->momx = mobj->momy = mobj->momz = 0; + if (mobj->movefactor <= 0) + { + mobj->movefactor = FRACUNIT / TICRATE; // default speed + } + if (mobj->tracer != NULL && P_MobjWasRemoved(mobj->tracer) == false) { - fixed_t dist = P_AproxDistance( - P_AproxDistance( - mobj->tracer->x - mobj->x, - mobj->tracer->y - mobj->y - ), - mobj->tracer->z - mobj->z - ); + mobj->movecount += mobj->movefactor; - mobj->angle = Easing_OutSine(FRACUNIT/8, mobj->angle, mobj->tracer->angle); - mobj->pitch = Easing_OutSine(FRACUNIT/8, mobj->pitch, mobj->tracer->pitch); - - if (dist > mobj->radius) + if (mobj->movecount < FRACUNIT) { - fixed_t newX = Easing_OutSine(FRACUNIT/8, mobj->x, mobj->tracer->x); - fixed_t newY = Easing_OutSine(FRACUNIT/8, mobj->y, mobj->tracer->y); - fixed_t newZ = Easing_OutSine(FRACUNIT/8, mobj->z, mobj->tracer->z); + fixed_t newX = Easing_Linear(mobj->movecount, mobj->extravalue1, mobj->tracer->x); + fixed_t newY = Easing_Linear(mobj->movecount, mobj->extravalue2, mobj->tracer->y); + fixed_t newZ = Easing_Linear(mobj->movecount, mobj->cusval, mobj->tracer->z); + + mobj->angle = Easing_Linear(mobj->movecount, mobj->movedir, mobj->tracer->angle); + mobj->pitch = Easing_Linear(mobj->movecount, mobj->lastlook, mobj->tracer->pitch); mobj->momx = newX - mobj->x; mobj->momy = newY - mobj->y; @@ -9519,6 +9518,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) else { P_SetTarget(&mobj->tracer, P_GetNextTubeWaypoint(mobj->tracer, false)); + mobj->movecount = 0; // time + mobj->movedir = mobj->angle; // start angle + mobj->lastlook = mobj->pitch; // start pitch + mobj->extravalue1 = mobj->x; // start x + mobj->extravalue2 = mobj->y; // start y + mobj->cusval = mobj->z; // start z } } diff --git a/src/p_spec.c b/src/p_spec.c index 74397cd8f..ebe0ad37f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3105,6 +3105,21 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha &newViewMobj->tracer, P_GetFirstTubeWaypoint(args[3] - 1) ); + newViewMobj->movecount = 0; // time + newViewMobj->movedir = newViewMobj->angle; // start angle + newViewMobj->lastlook = newViewMobj->pitch; // start pitch + newViewMobj->extravalue1 = newViewMobj->x; // start x + newViewMobj->extravalue2 = newViewMobj->y; // start y + newViewMobj->cusval = newViewMobj->z; // start z + + if (args[4] > 0) + { + newViewMobj->movefactor = FRACUNIT / args[4]; + } + else + { + newViewMobj->movefactor = FRACUNIT / TICRATE; // default speed + } } else { From 0156eaf81f3b1e8352ba93f108a07bfb2e36c462 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 17:57:37 -0500 Subject: [PATCH 22/97] Don't fault respawn in podium --- 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 aa9ff6974..5c91e95a2 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12033,7 +12033,7 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing) mobj->angle = angle; // FAULT - if (leveltime > introtime && !p->spectator) + if (gamestate == GS_LEVEL && leveltime > introtime && !p->spectator) { K_DoIngameRespawn(p); } From 8f047d7b428771672d01363c4bf8be8f4dc81df6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 17:57:59 -0500 Subject: [PATCH 23/97] Turn off engine sounds in podium --- src/k_kart.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index da534d349..954839a77 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6983,6 +6983,13 @@ static void K_UpdateEngineSounds(player_t *player) INT32 targetsnd = 0; INT32 i; + if (leveltime < 8 || player->spectator || gamestate != GS_LEVEL) + { + // Silence the engines, and reset sound number while we're at it. + player->karthud[khud_enginesnd] = 0; + return; + } + s = (player->kartspeed - 1) / 3; w = (player->kartweight - 1) / 3; @@ -6995,13 +7002,6 @@ static void K_UpdateEngineSounds(player_t *player) class = s + (3*w); - if (leveltime < 8 || player->spectator) - { - // Silence the engines, and reset sound number while we're at it. - player->karthud[khud_enginesnd] = 0; - return; - } - #if 0 if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct! #else From a4dd2ff6ffffbb5d20ed41392da2fcc756a08c90 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 18:02:02 -0500 Subject: [PATCH 24/97] Make 1st place faster in the podium sequence --- src/k_kart.c | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 954839a77..42560fcad 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3232,27 +3232,42 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberband) { const boolean mobjValid = (player->mo != NULL && P_MobjWasRemoved(player->mo) == false); - fixed_t finalspeed = K_GetKartSpeedFromStat(player->kartspeed); + fixed_t finalspeed = 0; - if (gametyperules & GTR_BUMPERS && player->bumpers <= 0) - finalspeed = 3 * finalspeed / 2; - - if (player->spheres > 0) + if (K_PodiumSequence() == true) { - fixed_t sphereAdd = (FRACUNIT/40); // 100% at max - finalspeed = FixedMul(finalspeed, FRACUNIT + (sphereAdd * player->spheres)); + // Make 1st reach their podium faster! + finalspeed = K_GetKartSpeedFromStat(max(1, 11 - (player->position * 3))); + + // Ignore other speed boosts. + doboostpower = dorubberband = false; } - - if (K_PlayerUsesBotMovement(player)) + else { - // Increase bot speed by 1-10% depending on difficulty - fixed_t add = (player->botvars.difficulty * (FRACUNIT/10)) / DIFFICULTBOT; - finalspeed = FixedMul(finalspeed, FRACUNIT + add); + finalspeed = K_GetKartSpeedFromStat(player->kartspeed); - if (player->bot && player->botvars.rival) + if (gametyperules & GTR_BUMPERS && player->bumpers <= 0) { - // +10% top speed for the rival - finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10); + finalspeed = 3 * finalspeed / 2; + } + + if (player->spheres > 0) + { + fixed_t sphereAdd = (FRACUNIT/40); // 100% at max + finalspeed = FixedMul(finalspeed, FRACUNIT + (sphereAdd * player->spheres)); + } + + if (K_PlayerUsesBotMovement(player)) + { + // Increase bot speed by 1-10% depending on difficulty + fixed_t add = (player->botvars.difficulty * (FRACUNIT/10)) / DIFFICULTBOT; + finalspeed = FixedMul(finalspeed, FRACUNIT + add); + + if (player->bot && player->botvars.rival) + { + // +10% top speed for the rival + finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10); + } } } From 29a10ebb0fb10bb364bc3def3750cfd834223d30 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 18:21:48 -0500 Subject: [PATCH 25/97] Normalize accel stats during podium --- src/k_kart.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 42560fcad..867657ed6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3295,16 +3295,31 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberb fixed_t K_GetKartAccel(player_t *player) { fixed_t k_accel = 121; + UINT8 stat = (9 - player->kartspeed); - k_accel += 17 * (9 - player->kartspeed); // 121 - 257 + if (K_PodiumSequence() == true) + { + stat = 4; + } + + k_accel += 17 * stat; // 121 - 257 + + if (K_PodiumSequence() == true) + { + return FixedMul(k_accel, FRACUNIT / 4); + } // karma bomb gets 2x acceleration if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) + { k_accel *= 2; + } // Marble Garden Top gets 1200% accel if (player->curshield == KSHIELD_TOP) + { k_accel *= 12; + } return FixedMul(k_accel, (FRACUNIT + player->accelboost) / 4); } From 0a63476a68658b8fc388f5a1c0119492c9941ce3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 18:21:57 -0500 Subject: [PATCH 26/97] No tether during podium --- src/k_kart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 867657ed6..813fe77a7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1243,6 +1243,11 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed angle_t yourangle, theirangle, diff; #endif + if (K_PodiumSequence() == true) + { + return false; + } + #ifndef EASYDRAFTTEST // Don't draft on yourself :V if (dest->player && dest->player == player) From 170be982eadc44d5917b54f78f9dea8fc43377a5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 18:41:46 -0500 Subject: [PATCH 27/97] Force Easy game speed for Podium sequence --- src/p_setup.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index 8679bf278..22f426c9e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7083,7 +7083,11 @@ static void P_InitLevelSettings(void) gamespeed = KARTSPEED_EASY; franticitems = false; - if (grandprixinfo.gp == true) + if (K_PodiumSequence() == true) + { + ; // NOP + } + else if (grandprixinfo.gp == true) { if (gametyperules & GTR_CIRCUIT) { From 672d69c62644dd7452d873ef604b6988677d9b35 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 19:17:58 -0500 Subject: [PATCH 28/97] More podium physics tweaks for the new scale - Normalize turning stats - More friction - Less acceleration - Podium bots will go at a reduced speed when going towards a No Respawn waypoint, so they can calm down a bit before they get on their platform. --- src/k_bot.c | 11 ++++++++++- src/k_kart.c | 19 ++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index b986d1d4a..5085e3fb6 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1309,7 +1309,16 @@ static void K_BuildBotPodiumTiccmd(player_t *player, ticcmd_t *cmd) return; } - cmd->forwardmove = MAXPLMOVE; + if (K_GetWaypointIsSpawnpoint(player->currentwaypoint) == false) + { + // Hacky flag reuse: slow down before reaching your podium stand. + cmd->forwardmove = MAXPLMOVE * 3 / 4; + } + else + { + cmd->forwardmove = MAXPLMOVE; + } + cmd->buttons |= BT_ACCELERATE; K_BotPodiumTurning(player, cmd); diff --git a/src/k_kart.c b/src/k_kart.c index 813fe77a7..c5e602f2a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3304,7 +3304,8 @@ fixed_t K_GetKartAccel(player_t *player) if (K_PodiumSequence() == true) { - stat = 4; + // Normalize to Metal's accel + stat = 1; } k_accel += 17 * stat; // 121 - 257 @@ -8939,6 +8940,15 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) p_speed = min(currentSpeed, (p_maxspeed * 2)); } + if (K_PodiumSequence() == true) + { + // Normalize turning for podium + weightadjust = FixedDiv((p_maxspeed * 3), (p_maxspeed * 3) + (2 * FRACUNIT)); + turnfixed = FixedMul(turnfixed, weightadjust); + turnfixed *= 2; + return (turnfixed / FRACUNIT); + } + weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT)); if (K_PlayerUsesBotMovement(player)) @@ -8946,11 +8956,6 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) turnfixed = FixedMul(turnfixed, 5*FRACUNIT/4); // Base increase to turning } - if (K_PodiumSequence() == true) - { - turnfixed *= 2; - } - if (player->drift != 0 && P_IsObjectOnGround(player->mo)) { fixed_t countersteer = FixedDiv(turnfixed, KART_FULLTURN*FRACUNIT); @@ -10054,7 +10059,7 @@ fixed_t K_PlayerBaseFriction(fixed_t original) if (K_PodiumSequence() == true) { - frict -= FRACUNIT >> 3; + frict -= FRACUNIT >> 4; } if (frict > FRACUNIT) { frict = FRACUNIT; } From 5c6cec7b31a2541850e2aeea5968a24a62affd7c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 19:41:20 -0500 Subject: [PATCH 29/97] Cleanup UDMF thing scale code Turns out what I was trying to fix was not a bug, but I will keep this anyway. --- src/p_mobj.c | 6 ++---- src/p_setup.c | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 5c91e95a2..a99383506 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -13416,16 +13416,14 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, fixed_t z, mobjtype_t i) { - fixed_t relativise = FixedDiv(mthing->scale, mapobjectscale); - mobj_t *mobj = NULL; boolean doangle = true; mobj = P_SpawnMobj(x, y, z, i); mobj->spawnpoint = mthing; - P_SetScale(mobj, FixedMul(mobj->scale, relativise)); - mobj->destscale = FixedMul(mobj->destscale, relativise); + P_SetScale(mobj, FixedMul(mobj->scale, mthing->scale)); + mobj->destscale = FixedMul(mobj->destscale, mthing->scale); if (!P_SetupSpawnedMapThing(mthing, mobj, &doangle)) return mobj; diff --git a/src/p_setup.c b/src/p_setup.c index 22f426c9e..aaa335cef 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1278,7 +1278,7 @@ static void P_LoadThings(UINT8 *data) mt->options = READUINT16(data); mt->extrainfo = (UINT8)(mt->type >> 12); Tag_FSet(&mt->tags, 0); - mt->scale = mapobjectscale; + mt->scale = FRACUNIT; memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args)); memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs)); mt->special = 0; @@ -1783,7 +1783,7 @@ static void ParseTextmapThingParameter(UINT32 i, const char *param, const char * else if (fastcmp(param, "type")) mapthings[i].type = atol(val); else if (fastcmp(param, "scale") || fastcmp(param, "scalex") || fastcmp(param, "scaley")) - mapthings[i].scale = FixedMul(mapobjectscale, FLOAT_TO_FIXED(atof(val))); + mapthings[i].scale = FLOAT_TO_FIXED(atof(val)); // Flags else if (fastcmp(param, "flip") && fastcmp("true", val)) mapthings[i].options |= MTF_OBJECTFLIP; @@ -2731,7 +2731,7 @@ static void P_LoadTextmap(void) mt->z = 0; mt->extrainfo = 0; Tag_FSet(&mt->tags, 0); - mt->scale = mapobjectscale; + mt->scale = FRACUNIT; memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args)); memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs)); mt->special = 0; From 9b18ce7849fd49b55aa386432662a767e3db49db Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 20:13:07 -0500 Subject: [PATCH 30/97] Remove magic camera follow numbers, add 2nd and 3rd follow options --- src/p_spec.c | 8 +++++--- src/p_spec.h | 8 ++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index ebe0ad37f..267e948cf 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3032,7 +3032,9 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha { switch (args[2]) { - case -1: + case TMCAM_FIRST: + case TMCAM_SECOND: + case TMCAM_THIRD: { mobj_t *firstPlace = NULL; INT32 i; @@ -3057,7 +3059,7 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha continue; } - if (player->position == 1) + if (player->position == abs(args[2])) // a bit of a hack { firstPlace = player->mo; break; @@ -3070,7 +3072,7 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha ); break; } - case -2: + case TMCAM_CONSOLE: { mobj_t *consoleMo = NULL; if (playeringame[consoleplayer] == true) diff --git a/src/p_spec.h b/src/p_spec.h index 908046936..b9eed2e4e 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -519,6 +519,14 @@ typedef enum TMLOOP_BETA = 1, } textmaploopendpointtype_t; +typedef enum +{ + TMCAM_FIRST = -1, + TMCAM_SECOND = -2, + TMCAM_THIRD = -3, + TMCAM_CONSOLE = -4, +} textmapcamerafollow_t; + // GETSECSPECIAL (specialval, section) // // Pulls out the special # from a particular section. From 9073d5bd176fdf681cab44589be41078cb44d017 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 20:31:12 -0500 Subject: [PATCH 31/97] Add CameraWait ACS function Pauses a script until the tagged camera has no more waypoints to follow. --- src/acs/call-funcs.cpp | 19 +++++++++++++++++++ src/acs/call-funcs.hpp | 1 + src/acs/environment.cpp | 14 ++++++++++++++ src/acs/thread.hpp | 1 + src/p_spec.c | 2 +- src/p_spec.h | 2 ++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index b6e1ce3a8..cf1e87d3a 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -445,6 +445,25 @@ bool CallFunc_PolyWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wo return true; // Execution interrupted } +/*-------------------------------------------------- + bool CallFunc_CameraWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Pauses the thread until the tagged + camera is done moving. +--------------------------------------------------*/ +bool CallFunc_CameraWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + (void)argC; + + thread->state = { + ACSVM::ThreadState::WaitTag, + argV[0], + ACS_TAGTYPE_CAMERA + }; + + return true; // Execution interrupted +} + /*-------------------------------------------------- bool CallFunc_ChangeFloor(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index 4bbe3eaec..c4215b5e0 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -50,6 +50,7 @@ bool CallFunc_Random(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word bool CallFunc_ThingCount(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_TagWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_PolyWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_CameraWait(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_ChangeFloor(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_ChangeCeiling(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_LineSide(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index 9c5c5ca61..ab7a67811 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -36,6 +36,7 @@ extern "C" { #include "../p_spec.h" #include "../w_wad.h" #include "../z_zone.h" +#include "../p_local.h" } #include "environment.hpp" @@ -160,6 +161,8 @@ Environment::Environment() addFuncDataACS0( 307, addCallFunc(CallFunc_PlayerLap)); addFuncDataACS0( 308, addCallFunc(CallFunc_LowestLap)); addFuncDataACS0( 309, addCallFunc(CallFunc_EncoreMode)); + + addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait)); } ACSVM::Thread *Environment::allocThread() @@ -262,6 +265,17 @@ bool Environment::checkTag(ACSVM::Word type, ACSVM::Word tag) const polyobj_t *po = Polyobj_GetForNum(tag); return (po == nullptr || po->thinker == nullptr); } + + case ACS_TAGTYPE_CAMERA: + { + const mobj_t *camera = P_FindObjectTypeFromTag(MT_ALTVIEWMAN, tag); + if (camera == nullptr || camera->spawnpoint == nullptr) + { + return true; + } + + return (camera->tracer == nullptr || P_MobjWasRemoved(camera->tracer) == true); + } } return true; diff --git a/src/acs/thread.hpp b/src/acs/thread.hpp index 4c3c307c2..7e528608a 100644 --- a/src/acs/thread.hpp +++ b/src/acs/thread.hpp @@ -57,6 +57,7 @@ enum acs_tagType_e { ACS_TAGTYPE_POLYOBJ, ACS_TAGTYPE_SECTOR, + ACS_TAGTYPE_CAMERA, }; class ThreadInfo : public ACSVM::ThreadInfo diff --git a/src/p_spec.c b/src/p_spec.c index 267e948cf..0d844cb27 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2405,7 +2405,7 @@ static void P_SwitchSkybox(INT32 args, player_t *player, skybox_t *skybox) } } -static mobj_t* P_FindObjectTypeFromTag(mobjtype_t type, mtag_t tag) +mobj_t* P_FindObjectTypeFromTag(mobjtype_t type, mtag_t tag) { if (udmf) { diff --git a/src/p_spec.h b/src/p_spec.h index b9eed2e4e..d33cdeb3f 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -582,6 +582,8 @@ void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing); void P_PushSpecialLine(line_t *line, mobj_t *thing); void P_ActivateThingSpecial(mobj_t *mo, mobj_t *source); +mobj_t* P_FindObjectTypeFromTag(mobjtype_t type, mtag_t tag); + // // Special activation info // From 0f9f10d90fd5ecd575fba0246028cd7c796a6f04 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 20:44:16 -0500 Subject: [PATCH 32/97] Add PodiumPosition ACS function Returns the best position of all non-CPU players. --- src/acs/call-funcs.cpp | 42 +++++++++++++++++++++++++++++++++++++++++ src/acs/call-funcs.hpp | 2 ++ src/acs/environment.cpp | 1 + 3 files changed, 45 insertions(+) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index cf1e87d3a..de4e166ae 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -1351,3 +1351,45 @@ bool CallFunc_EncoreMode(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM:: thread->dataStk.push(encoremode); return false; } + +/*-------------------------------------------------- + bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Returns the best position of all non-CPU players. +--------------------------------------------------*/ +bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + UINT8 ret = MAXPLAYERS; + INT32 i; + + (void)argV; + (void)argC; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + + if (playeringame[i] == false) + { + continue; + } + + player = &players[i]; + + if (player->spectator == true) + { + continue; + } + + if (player->bot == true) + { + continue; + } + + ret = std::min(ret, player->position); + } + + thread->dataStk.push(ret); + return false; +} + diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index c4215b5e0..5ca20e915 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -85,4 +85,6 @@ bool CallFunc_PlayerLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W bool CallFunc_LowestLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_EncoreMode(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); + #endif // __SRB2_ACS_CALL_FUNCS_HPP__ diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index ab7a67811..d656a3503 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -163,6 +163,7 @@ Environment::Environment() addFuncDataACS0( 309, addCallFunc(CallFunc_EncoreMode)); addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait)); + addFuncDataACS0( 501, addCallFunc(CallFunc_PodiumPosition)); } ACSVM::Thread *Environment::allocThread() From 2dae6833c295ad41438ae9b09830f223314c7731 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 2 Mar 2023 20:51:45 -0500 Subject: [PATCH 33/97] Fix camera jitter when changing waypoints --- src/p_mobj.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index a99383506..9de016f60 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9500,30 +9500,30 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (mobj->tracer != NULL && P_MobjWasRemoved(mobj->tracer) == false) { + fixed_t newX = Easing_Linear(mobj->movecount, mobj->extravalue1, mobj->tracer->x); + fixed_t newY = Easing_Linear(mobj->movecount, mobj->extravalue2, mobj->tracer->y); + fixed_t newZ = Easing_Linear(mobj->movecount, mobj->cusval, mobj->tracer->z); + + mobj->angle = Easing_Linear(mobj->movecount, mobj->movedir, mobj->tracer->angle); + mobj->pitch = Easing_Linear(mobj->movecount, mobj->lastlook, mobj->tracer->pitch); + + mobj->momx = newX - mobj->x; + mobj->momy = newY - mobj->y; + mobj->momz = newZ - mobj->z; + mobj->movecount += mobj->movefactor; - if (mobj->movecount < FRACUNIT) + if (mobj->movecount >= FRACUNIT) { - fixed_t newX = Easing_Linear(mobj->movecount, mobj->extravalue1, mobj->tracer->x); - fixed_t newY = Easing_Linear(mobj->movecount, mobj->extravalue2, mobj->tracer->y); - fixed_t newZ = Easing_Linear(mobj->movecount, mobj->cusval, mobj->tracer->z); + mobj->movecount = mobj->movecount % FRACUNIT; // time - mobj->angle = Easing_Linear(mobj->movecount, mobj->movedir, mobj->tracer->angle); - mobj->pitch = Easing_Linear(mobj->movecount, mobj->lastlook, mobj->tracer->pitch); + mobj->movedir = mobj->tracer->angle; // start angle + mobj->lastlook = mobj->tracer->pitch; // start pitch + mobj->extravalue1 = mobj->tracer->x; // start x + mobj->extravalue2 = mobj->tracer->y; // start y + mobj->cusval = mobj->tracer->z; // start z - mobj->momx = newX - mobj->x; - mobj->momy = newY - mobj->y; - mobj->momz = newZ - mobj->z; - } - else - { P_SetTarget(&mobj->tracer, P_GetNextTubeWaypoint(mobj->tracer, false)); - mobj->movecount = 0; // time - mobj->movedir = mobj->angle; // start angle - mobj->lastlook = mobj->pitch; // start pitch - mobj->extravalue1 = mobj->x; // start x - mobj->extravalue2 = mobj->y; // start y - mobj->cusval = mobj->z; // start z } } From a1976aa43daf0faf6f310069c68b1f6d2f5d9ed9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 3 Mar 2023 15:41:31 -0500 Subject: [PATCH 34/97] Fixes for podium bots loading - Handle bot spectator on spawn purely in P_SpawnPlayer from now on - Don't give podium players flashing tics - Don't call P_MovePlayerToStarpost during podium. (Needs further fixing in another branch, since this also apparently affects bot spawning in all maps.) - Update mobj_t pointers in CL_ClearPlayer if G_GamestateUsesLevel() - Forcefully set initial podium waypoint if jointime <= 1 - Use G_SpawnPlayer consistently in P_InitPlayers for podium --- src/d_clisrv.c | 5 +---- src/g_game.c | 2 +- src/k_kart.c | 2 +- src/p_mobj.c | 5 ++--- src/p_setup.c | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 56cb58b47..807e55666 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2542,7 +2542,7 @@ void CL_ClearPlayer(INT32 playernum) int i; // Handle mobj_t pointers. - if (gamestate == GS_LEVEL) + if (G_GamestateUsesLevel() == true) { if (players[playernum].follower) { @@ -3771,9 +3771,6 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) sprintf(player_names[newplayernum], "%s", skins[skinnum].realname); SetPlayerSkinByNum(newplayernum, skinnum); - players[newplayernum].spectator = !(gametyperules & GTR_BOTS) - || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE); - if (netgame) { HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false); diff --git a/src/g_game.c b/src/g_game.c index 98b03ec6e..9db0b8955 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2742,7 +2742,7 @@ void G_MovePlayerToSpawnOrStarpost(INT32 playernum) #else // Player's first spawn should be at the "map start". // I.e. level load or join mid game. - if (leveltime > starttime && players[playernum].jointime > 0) + if (leveltime > starttime && players[playernum].jointime > 1 && K_PodiumSequence() == false) P_MovePlayerToStarpost(playernum); else P_MovePlayerToSpawn(playernum, G_FindMapStart(playernum)); diff --git a/src/k_kart.c b/src/k_kart.c index c5e602f2a..92c75b889 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8527,7 +8527,7 @@ static void K_UpdatePodiumWaypoints(player_t *const player) { if ((player->currentwaypoint == NULL) && (player->position > 0 && player->position <= MAXPLAYERS) - && (leveltime <= 2)) + && (leveltime <= introtime || player->jointime <= 1)) { // Initialize our first waypoint to the one that // matches our position. diff --git a/src/p_mobj.c b/src/p_mobj.c index 9de016f60..88f6718da 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11746,8 +11746,7 @@ void P_SpawnPlayer(INT32 playernum) else if (p->bot) { if (K_PodiumSequence() == false - && grandprixinfo.gp == true - && grandprixinfo.eventmode != GPEVENT_NONE) + && (!(gametyperules & GTR_BOTS) || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE))) { // Bots aren't supposed to be here. p->spectator = true; @@ -11811,7 +11810,7 @@ void P_SpawnPlayer(INT32 playernum) p->skincolor = skincolor_blueteam; } - if (leveltime > introtime) + if (leveltime > introtime && K_PodiumSequence() == false) p->flashing = K_GetKartFlashing(p); // Babysitting deterrent mobj = P_SpawnMobj(0, 0, 0, MT_PLAYER); diff --git a/src/p_setup.c b/src/p_setup.c index aaa335cef..5fc6058b6 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7363,7 +7363,7 @@ static void P_InitPlayers(void) players[i].mo = NULL; - if (!(gametyperules & GTR_CIRCUIT)) + if (!(gametyperules & GTR_CIRCUIT) && K_PodiumSequence() == false) { G_DoReborn(i); } From 35d0cb8eaa95331c3f32b81d2ab876d94d495cab Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 3 Mar 2023 16:05:12 -0500 Subject: [PATCH 35/97] Move podium code to its own file --- src/CMakeLists.txt | 1 + src/f_finale.c | 65 ---------------------- src/f_finale.h | 3 -- src/g_game.c | 7 +-- src/k_bot.c | 2 +- src/k_collide.c | 1 + src/k_kart.c | 6 +-- src/k_kart.h | 2 - src/k_podium.c | 132 +++++++++++++++++++++++++++++++++++++++++++++ src/k_podium.h | 79 +++++++++++++++++++++++++++ src/p_mobj.c | 1 + src/p_setup.c | 1 + 12 files changed, 221 insertions(+), 79 deletions(-) create mode 100644 src/k_podium.c create mode 100644 src/k_podium.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e68db60b..ad870cec4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -134,6 +134,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_profiles.c k_specialstage.c k_roulette.c + k_podium.c ) if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) diff --git a/src/f_finale.c b/src/f_finale.c index f801067a1..bd3adb85b 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -3139,68 +3139,3 @@ void F_TextPromptTicker(void) animtimer--; } } - -boolean F_StartCeremony(void) -{ - INT32 podiumMapNum = nummapheaders; - INT32 i; - - if (grandprixinfo.gp == false) - { - return false; - } - - if (podiummap - && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders) - && mapheaderinfo[podiumMapNum] - && mapheaderinfo[podiumMapNum]->lumpnum != LUMPERROR) - { - P_SetTarget(&titlemapcam.mobj, NULL); - - gamemap = podiumMapNum+1; - - maptol = mapheaderinfo[gamemap-1]->typeoflevel; - globalweather = mapheaderinfo[gamemap-1]->weather; - - // Make sure all of the GAME OVER'd players can spawn - // and be present for the podium - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator && !players[i].bot) - { - players[i].lives = max(1, players[i].lives); - } - } - - G_DoLoadLevelEx(false, GS_CEREMONY); - - r_splitscreen = 0; // Only one screen for the ceremony - R_ExecuteSetViewSize(); - return true; - } - - return false; -} - -void F_CeremonyTicker(boolean run) -{ - (void)run; - - // don't trigger if doing anything besides idling - if (gameaction != ga_nothing || gamestate != GS_CEREMONY) - { - return; - } - - P_TickAltView(&titlemapcam); - - if (titlemapcam.mobj != NULL) - { - camera[0].x = titlemapcam.mobj->x; - camera[0].y = titlemapcam.mobj->y; - camera[0].z = titlemapcam.mobj->z; - camera[0].angle = titlemapcam.mobj->angle; - camera[0].aiming = titlemapcam.mobj->pitch; - camera[0].subsector = titlemapcam.mobj->subsector; - } -} diff --git a/src/f_finale.h b/src/f_finale.h index e86e2ce5c..4149f111c 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -79,9 +79,6 @@ void F_StartTitleScreen(void); void F_StartEnding(void); void F_StartCredits(void); -boolean F_StartCeremony(void); -void F_CeremonyTicker(boolean run); - extern INT32 finalecount; extern INT32 titlescrollxspeed; extern INT32 titlescrollyspeed; diff --git a/src/g_game.c b/src/g_game.c index 9db0b8955..2427900c0 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -62,6 +62,7 @@ #include "k_bot.h" #include "doomstat.h" #include "k_director.h" +#include "k_podium.h" #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -2255,7 +2256,7 @@ void G_Ticker(boolean run) case GS_CEREMONY: P_Ticker(run); - F_CeremonyTicker(run); + K_CeremonyTicker(run); break; case GS_WAITINGPLAYERS: @@ -4296,7 +4297,7 @@ void G_EndGame(void) { if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony { - if (F_StartCeremony() == true) + if (K_StartCeremony() == true) { return; } @@ -5082,7 +5083,7 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr { // Didn't want to do this, but it needs to be here // for it to work on startup. - if (F_StartCeremony() == true) + if (K_StartCeremony() == true) { return; } diff --git a/src/k_bot.c b/src/k_bot.c index 5085e3fb6..cb0b57eb0 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -28,7 +28,7 @@ #include "r_things.h" // numskins #include "k_race.h" // finishBeamLine #include "m_perfstats.h" - +#include "k_podium.h" /*-------------------------------------------------- boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) diff --git a/src/k_collide.c b/src/k_collide.c index bf813e410..12757a2a3 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -13,6 +13,7 @@ #include "g_game.h" // Sink snipe print #include "k_objects.h" #include "k_roulette.h" +#include "k_podium.h" angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) { diff --git a/src/k_kart.c b/src/k_kart.c index 92c75b889..fe4e79938 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -43,6 +43,7 @@ #include "k_boss.h" #include "k_specialstage.h" #include "k_roulette.h" +#include "k_podium.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -51,11 +52,6 @@ // comeback is Battle Mode's karma comeback, also bool // mapreset is set when enough players fill an empty server -boolean K_PodiumSequence(void) -{ - return (gamestate == GS_CEREMONY); -} - boolean K_IsDuelItem(mobjtype_t type) { switch (type) diff --git a/src/k_kart.h b/src/k_kart.h index 498416c91..fdc9cb2de 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -37,8 +37,6 @@ Make sure this matches the actual number of states #define STUMBLE_STEEP_VAL ANG60 #define STUMBLE_STEEP_VAL_AIR (ANG30 + ANG10) -boolean K_PodiumSequence(void); - player_t *K_GetItemBoxPlayer(mobj_t *mobj); angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed); diff --git a/src/k_podium.c b/src/k_podium.c new file mode 100644 index 000000000..6edb86146 --- /dev/null +++ b/src/k_podium.c @@ -0,0 +1,132 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_podium.c +/// \brief Grand Prix podium cutscene + +#include "k_podium.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "d_main.h" +#include "d_netcmd.h" +#include "f_finale.h" +#include "g_game.h" +#include "hu_stuff.h" +#include "r_local.h" +#include "s_sound.h" +#include "i_time.h" +#include "i_video.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" +#include "i_system.h" +#include "i_threads.h" +#include "dehacked.h" +#include "g_input.h" +#include "console.h" +#include "m_random.h" +#include "m_misc.h" // moviemode functionality +#include "y_inter.h" +#include "m_cond.h" +#include "p_local.h" +#include "p_setup.h" +#include "st_stuff.h" // hud hiding +#include "fastcmp.h" + +#include "lua_hud.h" +#include "lua_hook.h" + +#include "k_menu.h" +#include "k_grandprix.h" + +/*-------------------------------------------------- + boolean K_PodiumSequence(void) + + See header file for description. +--------------------------------------------------*/ +boolean K_PodiumSequence(void) +{ + return (gamestate == GS_CEREMONY); +} + +/*-------------------------------------------------- + boolean K_StartCeremony(void) + + See header file for description. +--------------------------------------------------*/ +boolean K_StartCeremony(void) +{ + INT32 podiumMapNum = nummapheaders; + INT32 i; + + if (grandprixinfo.gp == false) + { + return false; + } + + if (podiummap + && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders) + && mapheaderinfo[podiumMapNum] + && mapheaderinfo[podiumMapNum]->lumpnum != LUMPERROR) + { + P_SetTarget(&titlemapcam.mobj, NULL); + + gamemap = podiumMapNum+1; + + maptol = mapheaderinfo[gamemap-1]->typeoflevel; + globalweather = mapheaderinfo[gamemap-1]->weather; + + // Make sure all of the GAME OVER'd players can spawn + // and be present for the podium + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator && !players[i].bot) + { + players[i].lives = max(1, players[i].lives); + } + } + + G_DoLoadLevelEx(false, GS_CEREMONY); + + r_splitscreen = 0; // Only one screen for the ceremony + R_ExecuteSetViewSize(); + return true; + } + + return false; +} + +/*-------------------------------------------------- + void K_CeremonyTicker(boolean run) + + See header file for description. +--------------------------------------------------*/ +void K_CeremonyTicker(boolean run) +{ + (void)run; + + // don't trigger if doing anything besides idling + if (gameaction != ga_nothing || gamestate != GS_CEREMONY) + { + return; + } + + P_TickAltView(&titlemapcam); + + if (titlemapcam.mobj != NULL) + { + camera[0].x = titlemapcam.mobj->x; + camera[0].y = titlemapcam.mobj->y; + camera[0].z = titlemapcam.mobj->z; + camera[0].angle = titlemapcam.mobj->angle; + camera[0].aiming = titlemapcam.mobj->pitch; + camera[0].subsector = titlemapcam.mobj->subsector; + } +} diff --git a/src/k_podium.h b/src/k_podium.h new file mode 100644 index 000000000..b11de1067 --- /dev/null +++ b/src/k_podium.h @@ -0,0 +1,79 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_podium.h +/// \brief Grand Prix podium cutscene + +#ifndef __K_PODIUM__ +#define __K_PODIUM__ + +#include "doomtype.h" +#include "d_event.h" +#include "p_mobj.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*-------------------------------------------------- + boolean K_PodiumSequence(void); + + Returns whenver or not we are in the podium + cutscene mode. + + Input Arguments:- + N/A + + Return:- + true if we're in GS_CEREMONY, otherwise false. +--------------------------------------------------*/ + +boolean K_PodiumSequence(void); + + +/*-------------------------------------------------- + boolean K_StartCeremony(void); + + Loads the podium map and changes the gamestate + to the podium cutscene mode. + + Input Arguments:- + N/A + + Return:- + true if successful, otherwise false. Can fail + if there is no podium map defined. +--------------------------------------------------*/ + +boolean K_StartCeremony(void); + + +/*-------------------------------------------------- + void K_CeremonyTicker(boolean run); + + Ticker function to be ran during the podium + cutscene mode gamestate. Handles updating + the camera. + + Input Arguments:- + run - Set to true when we're running a + new game frame. + + Return:- + N/A +--------------------------------------------------*/ + +void K_CeremonyTicker(boolean run); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __K_PODIUM__ diff --git a/src/p_mobj.c b/src/p_mobj.c index 88f6718da..ff7aa597a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -48,6 +48,7 @@ #include "k_grandprix.h" #include "k_director.h" #include "m_easing.h" +#include "k_podium.h" static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); diff --git a/src/p_setup.c b/src/p_setup.c index 5fc6058b6..f0d38d694 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -101,6 +101,7 @@ #include "k_specialstage.h" #include "acs/interface.h" #include "doomstat.h" // MAXMUSNAMES +#include "k_podium.h" // Replay names have time #if !defined (UNDER_CE) From c4525a61a2ac377956670abbcd92836e72def498 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 3 Mar 2023 17:30:35 -0500 Subject: [PATCH 36/97] Add results screen --- src/acs/call-funcs.cpp | 15 ++++++ src/acs/call-funcs.hpp | 1 + src/acs/environment.cpp | 1 + src/d_main.c | 6 +++ src/g_game.c | 8 ++- src/k_podium.c | 107 ++++++++++++++++++++++++++++++++++++++++ src/k_podium.h | 49 ++++++++++++++++++ 7 files changed, 186 insertions(+), 1 deletion(-) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index de4e166ae..0e131a3fb 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -50,6 +50,7 @@ extern "C" { #include "../m_cond.h" #include "../r_skins.h" #include "../k_battle.h" +#include "../k_podium.h" } #include "call-funcs.hpp" @@ -1393,3 +1394,17 @@ bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACS return false; } +/*-------------------------------------------------- + bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Ends the podium sequence. Doesn't do anything + outside of podium maps. +--------------------------------------------------*/ +bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + (void)argV; + (void)argC; + + K_FinishCeremony(); + return false; +} diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index 5ca20e915..04e4b59b7 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -86,5 +86,6 @@ bool CallFunc_LowestLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W bool CallFunc_EncoreMode(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); #endif // __SRB2_ACS_CALL_FUNCS_HPP__ diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index d656a3503..470774f22 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -164,6 +164,7 @@ Environment::Environment() addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait)); addFuncDataACS0( 501, addCallFunc(CallFunc_PodiumPosition)); + addFuncDataACS0( 502, addCallFunc(CallFunc_PodiumFinish)); } ACSVM::Thread *Environment::allocThread() diff --git a/src/d_main.c b/src/d_main.c index b3cae9892..9d1eee317 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -76,6 +76,7 @@ #include "m_random.h" // P_ClearRandom #include "k_specialstage.h" #include "acs/interface.h" +#include "k_podium.h" #ifdef HWRENDER #include "hardware/hw_main.h" // 3D View Rendering @@ -580,6 +581,11 @@ static void D_Display(void) F_TitleScreenDrawer(); break; } + case GS_CEREMONY: + { + K_CeremonyDrawer(); + break; + } default: { break; diff --git a/src/g_game.c b/src/g_game.c index 2427900c0..ae049df8d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1471,6 +1471,8 @@ void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate) if (gamestate == GS_VOTING) Y_EndVote(); + K_ResetCeremony(); + // cleanup // Is this actually necessary? Doesn't F_StartTitleScreen already do a significantly more comprehensive check? if (newstate == GS_TITLESCREEN) @@ -1724,7 +1726,11 @@ boolean G_Responder(event_t *ev) return true; // chat ate the event } - // todo + if (K_CeremonyResponder(ev)) + { + D_StartTitle(); + return true; + } } else if (gamestate == GS_CONTINUING) { diff --git a/src/k_podium.c b/src/k_podium.c index 6edb86146..9b6f82093 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -46,6 +46,8 @@ #include "k_menu.h" #include "k_grandprix.h" +static boolean s_podiumDone = false; + /*-------------------------------------------------- boolean K_PodiumSequence(void) @@ -103,6 +105,31 @@ boolean K_StartCeremony(void) return false; } +/*-------------------------------------------------- + void K_FinishCeremony(void) + + See header file for description. +--------------------------------------------------*/ +void K_FinishCeremony(void) +{ + if (K_PodiumSequence() == false) + { + return; + } + + s_podiumDone = true; +} + +/*-------------------------------------------------- + void K_ResetCeremony(void) + + See header file for description. +--------------------------------------------------*/ +void K_ResetCeremony(void) +{ + s_podiumDone = false; +} + /*-------------------------------------------------- void K_CeremonyTicker(boolean run) @@ -130,3 +157,83 @@ void K_CeremonyTicker(boolean run) camera[0].subsector = titlemapcam.mobj->subsector; } } + +/*-------------------------------------------------- + boolean K_CeremonyResponder(event_t *event) + + See header file for description. +--------------------------------------------------*/ +boolean K_CeremonyResponder(event_t *event) +{ + INT32 key = event->data1; + + if (s_podiumDone == false) + { + return false; + } + + // remap virtual keys (mouse & joystick buttons) + switch (key) + { + case KEY_MOUSE1: + key = KEY_ENTER; + break; + case KEY_MOUSE1 + 1: + key = KEY_BACKSPACE; + break; + case KEY_JOY1: + case KEY_JOY1 + 2: + key = KEY_ENTER; + break; + case KEY_JOY1 + 3: + key = 'n'; + break; + case KEY_JOY1 + 1: + key = KEY_BACKSPACE; + break; + case KEY_HAT1: + key = KEY_UPARROW; + break; + case KEY_HAT1 + 1: + key = KEY_DOWNARROW; + break; + case KEY_HAT1 + 2: + key = KEY_LEFTARROW; + break; + case KEY_HAT1 + 3: + key = KEY_RIGHTARROW; + break; + } + + if (event->type != ev_keydown) + { + return false; + } + + if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_BACKSPACE) + { + return false; + } + + return true; +} + +/*-------------------------------------------------- + void K_CeremonyDrawer(void) + + See header file for description. +--------------------------------------------------*/ +void K_CeremonyDrawer(void) +{ + if (s_podiumDone == true) + { + V_DrawFadeScreen(0xFF00, 16); + V_DrawCenteredString(BASEVIDWIDTH / 2, 64, 0, "STUFF GOES HERE"); + } + + if (timeinmap < 16) + { + // Level fade-in + V_DrawCustomFadeScreen(((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), 31-(timeinmap*2)); + } +} diff --git a/src/k_podium.h b/src/k_podium.h index b11de1067..21b31f74d 100644 --- a/src/k_podium.h +++ b/src/k_podium.h @@ -54,6 +54,27 @@ boolean K_PodiumSequence(void); boolean K_StartCeremony(void); +/*-------------------------------------------------- + void K_FinishCeremony(void); + + Called at the end of the podium cutscene, + displays the ranking screen and starts + accepting input. +--------------------------------------------------*/ + +void K_FinishCeremony(void); + + +/*-------------------------------------------------- + void K_ResetCeremony(void); + + Called on level load, to reset all of the + podium variables. +--------------------------------------------------*/ + +void K_ResetCeremony(void); + + /*-------------------------------------------------- void K_CeremonyTicker(boolean run); @@ -72,6 +93,34 @@ boolean K_StartCeremony(void); void K_CeremonyTicker(boolean run); +/*-------------------------------------------------- + void K_CeremonyResponder(event_t *ev); + + Responder function to be ran during the podium + cutscene mode gamestate. Handles key presses + ending the podium scene. + + Input Arguments:- + ev - The player input event. + + Return:- + true to end the podium cutscene and return + to the title screen, otherwise false. +--------------------------------------------------*/ + +boolean K_CeremonyResponder(event_t *ev); + + +/*-------------------------------------------------- + void K_CeremonyDrawer(void); + + Handles the ranking screen and other HUD for + the podium cutscene. +--------------------------------------------------*/ + +void K_CeremonyDrawer(void); + + #ifdef __cplusplus } // extern "C" #endif From 1f00e3fc3f4a620e3e4994f6148b4f857d51d319 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 3 Mar 2023 18:03:03 -0500 Subject: [PATCH 37/97] A_SetScale: Scale by mapthing scale --- src/p_enemy.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_enemy.c b/src/p_enemy.c index 7c1ede87a..ccf5ff47a 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -9390,6 +9390,10 @@ void A_SetScale(mobj_t *actor) } locvar1 = FixedMul(locvar1, mapobjectscale); // SRB2Kart + if (target->spawnpoint != NULL) + { + locvar1 = FixedMul(locvar1, target->spawnpoint->scale); + } target->destscale = locvar1; // destination scale if (!(locvar2 & 65535)) From ea4ff616a92df8d19fca9fe7b46efe7c3dde47c3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 3 Mar 2023 22:16:30 -0500 Subject: [PATCH 38/97] Fix broken float flag --- src/p_mobj.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index ff7aa597a..eb676f660 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12111,6 +12111,7 @@ void P_MovePlayerToStarpost(INT32 playernum) fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const fixed_t y, const fixed_t dz, const fixed_t offset, const boolean flip, const fixed_t scale) { + const fixed_t finalScale = FixedMul(scale, mapobjectscale); const subsector_t *ss = R_PointInSubsector(x, y); // Axis objects snap to the floor. @@ -12119,9 +12120,9 @@ fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const f // Establish height. if (flip) - return P_GetSectorCeilingZAt(ss->sector, x, y) - dz - FixedMul(scale, offset + mobjinfo[mobjtype].height); + return P_GetSectorCeilingZAt(ss->sector, x, y) - dz - FixedMul(finalScale, offset + mobjinfo[mobjtype].height); else - return P_GetSectorFloorZAt(ss->sector, x, y) + dz + FixedMul(scale, offset); + return P_GetSectorFloorZAt(ss->sector, x, y) + dz + FixedMul(finalScale, offset); } fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mthing, const fixed_t x, const fixed_t y) From 89276c5900691ed05edbb43821cdf9382eb5a83d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 3 Mar 2023 22:17:05 -0500 Subject: [PATCH 39/97] Fade-in the tint behind podium ranking --- src/k_podium.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/k_podium.c b/src/k_podium.c index 9b6f82093..fdd450b43 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -46,7 +46,11 @@ #include "k_menu.h" #include "k_grandprix.h" -static boolean s_podiumDone = false; +static struct podiumData_s +{ + boolean ranking; + UINT8 fade; +} podiumData; /*-------------------------------------------------- boolean K_PodiumSequence(void) @@ -117,7 +121,7 @@ void K_FinishCeremony(void) return; } - s_podiumDone = true; + podiumData.ranking = true; } /*-------------------------------------------------- @@ -127,7 +131,7 @@ void K_FinishCeremony(void) --------------------------------------------------*/ void K_ResetCeremony(void) { - s_podiumDone = false; + memset(&podiumData, 0, sizeof(struct podiumData_s)); } /*-------------------------------------------------- @@ -137,8 +141,6 @@ void K_ResetCeremony(void) --------------------------------------------------*/ void K_CeremonyTicker(boolean run) { - (void)run; - // don't trigger if doing anything besides idling if (gameaction != ga_nothing || gamestate != GS_CEREMONY) { @@ -156,6 +158,19 @@ void K_CeremonyTicker(boolean run) camera[0].aiming = titlemapcam.mobj->pitch; camera[0].subsector = titlemapcam.mobj->subsector; } + + if (podiumData.ranking == false) + { + return; + } + + if (run == true) + { + if (podiumData.fade < 16) + { + podiumData.fade++; + } + } } /*-------------------------------------------------- @@ -167,7 +182,7 @@ boolean K_CeremonyResponder(event_t *event) { INT32 key = event->data1; - if (s_podiumDone == false) + if (podiumData.ranking == false || podiumData.fade < 16) { return false; } @@ -225,9 +240,9 @@ boolean K_CeremonyResponder(event_t *event) --------------------------------------------------*/ void K_CeremonyDrawer(void) { - if (s_podiumDone == true) + if (podiumData.ranking == true) { - V_DrawFadeScreen(0xFF00, 16); + V_DrawFadeScreen(0xFF00, podiumData.fade); V_DrawCenteredString(BASEVIDWIDTH / 2, 64, 0, "STUFF GOES HERE"); } From 839650968dad8d501966f3c1c815d2104c278914 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 3 Mar 2023 22:29:20 -0500 Subject: [PATCH 40/97] Make podium end use G_EndGame --- src/g_game.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index ae049df8d..0c8530b0c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1728,7 +1728,8 @@ boolean G_Responder(event_t *ev) if (K_CeremonyResponder(ev)) { - D_StartTitle(); + nextmap = NEXTMAP_TITLE; + G_EndGame(); return true; } } From 04525f2fb5f1384b2c66a750bf65fc7287e8c09e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 01:30:49 -0500 Subject: [PATCH 41/97] G_FindPodiumStart Since G_FindRaceStart doesn't use regular position stuff, it needs to do this. Seems to fix the weird bug people were running into in the VC, but it also seemed kinda wishy-washy --- src/g_game.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/g_game.h | 1 + 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 0c8530b0c..900a27dcf 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2846,7 +2846,9 @@ mapthing_t *G_FindRaceStart(INT32 playernum) // SRB2Kart: figure out player spawn pos from points if (!playeringame[playernum] || players[playernum].spectator) + { return playerstarts[0]; // go to first spot if you're a spectator + } for (i = 0; i < MAXPLAYERS; i++) { @@ -2929,6 +2931,78 @@ mapthing_t *G_FindRaceStart(INT32 playernum) return NULL; } +mapthing_t *G_FindPodiumStart(INT32 playernum) +{ + const boolean doprints = P_IsLocalPlayer(&players[playernum]); + + if (numcoopstarts) + { + UINT8 i; + UINT8 pos = 0; + + // SRB2Kart: figure out player spawn pos from points + if (!playeringame[playernum] || players[playernum].spectator) + { + return playerstarts[0]; // go to first spot if you're a spectator + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } + + if (i == playernum) + { + continue; + } + + if (players[i].score > players[playernum].score) + { + // Final score is the important part. + pos++; + } + else if (players[i].score == players[playernum].score) + { + if (players[i].bot == false && players[playernum].bot == true) + { + // Bots are never as important as players. + pos++; + } + else if (i < playernum) + { + // Port priority is the final tie breaker. + pos++; + } + } + } + + if (G_CheckSpot(playernum, playerstarts[pos % numcoopstarts])) + { + return playerstarts[pos % numcoopstarts]; + } + + // Your spot isn't available? Find whatever you can get first. + for (i = 0; i < numcoopstarts; i++) + { + if (G_CheckSpot(playernum, playerstarts[i])) + return playerstarts[i]; + } + + if (doprints) + { + CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Podium starts!\n")); + } + + return NULL; + } + + if (doprints) + CONS_Alert(CONS_WARNING, M_GetText("No Podium starts in this map!\n")); + return NULL; +} + // Find a Co-op start, or fallback into other types of starts. static inline mapthing_t *G_FindRaceStartOrFallback(INT32 playernum) { @@ -2965,9 +3039,14 @@ mapthing_t *G_FindMapStart(INT32 playernum) if (!playeringame[playernum]) return NULL; + // -- Podium -- + // Single special behavior + if (K_PodiumSequence() == true) + spawnpoint = G_FindPodiumStart(playernum); + // -- Time Attack -- // Order: Race->DM->CTF - if (K_TimeAttackRules() == true) + else if (K_TimeAttackRules() == true) spawnpoint = G_FindRaceStartOrFallback(playernum); // -- CTF -- diff --git a/src/g_game.h b/src/g_game.h index 081f414e0..ed38b4402 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -156,6 +156,7 @@ INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep); mapthing_t *G_FindTeamStart(INT32 playernum); mapthing_t *G_FindBattleStart(INT32 playernum); mapthing_t *G_FindRaceStart(INT32 playernum); +mapthing_t *G_FindPodiumStart(INT32 playernum); mapthing_t *G_FindMapStart(INT32 playernum); void G_MovePlayerToSpawnOrStarpost(INT32 playernum); void G_SpawnPlayer(INT32 playernum); From 664fb0222910838624b3416c4bbdf1673e64a816 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 13:32:06 -0500 Subject: [PATCH 42/97] Remove exitlevel wonround Don't care enough to fix it right now. --- src/d_netcmd.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 40d020700..54ebdcebd 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5364,12 +5364,6 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) return; } - if (grandprixinfo.gp == true) - { - // This is already a cheat command. - grandprixinfo.wonround = true; - } - G_ExitLevel(); } From 4961a585a7433ffb4fa1973cb744c41ddb05a417 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 13:49:07 -0500 Subject: [PATCH 43/97] Add ceremony to twodee wipedefs tables --- src/f_wipe.c | 128 +++++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 60 deletions(-) diff --git a/src/f_wipe.c b/src/f_wipe.c index 19f365611..105358407 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -90,15 +90,16 @@ UINT8 wipedefs[NUMWIPEDEFS] = { static boolean g_wipedef_toblack[NUMWIPEDEFS] = { true, // wipe_credits_intermediate (0) - true, // wipe_level_toblack - true, // wipe_intermission_toblack - true, // wipe_voting_toblack, - true, // wipe_continuing_toblack - true, // wipe_titlescreen_toblack - true, // wipe_menu_toblack + true, // wipe_level_toblack + true, // wipe_intermission_toblack + true, // wipe_voting_toblack, + true, // wipe_continuing_toblack + true, // wipe_titlescreen_toblack + true, // wipe_menu_toblack true, // wipe_credits_toblack - true, // wipe_evaluation_toblack - true, // wipe_gameend_toblack + true, // wipe_evaluation_toblack + true, // wipe_gameend_toblack + true, // wipe_ceremony_toblack true, // wipe_intro_toblack (hardcoded) true, // wipe_ending_toblack (hardcoded) true, // wipe_cutscene_toblack (hardcoded) @@ -107,14 +108,15 @@ static boolean g_wipedef_toblack[NUMWIPEDEFS] = { false, // wipe_encore_towhite true, // wipe_level_final - true, // wipe_intermission_final - true, // wipe_voting_final - true, // wipe_continuing_final - true, // wipe_titlescreen_final - true, // wipe_menu_final + true, // wipe_intermission_final + true, // wipe_voting_final + true, // wipe_continuing_final + true, // wipe_titlescreen_final + true, // wipe_menu_final true, // wipe_credits_final - true, // wipe_evaluation_final - true, // wipe_gameend_final + true, // wipe_evaluation_final + true, // wipe_gameend_final + true, // wipe_ceremony_final true, // wipe_intro_final (hardcoded) true, // wipe_ending_final (hardcoded) true // wipe_cutscene_final (hardcoded) @@ -123,15 +125,16 @@ static boolean g_wipedef_toblack[NUMWIPEDEFS] = { static boolean g_wipedef_toinvert[NUMWIPEDEFS] = { false, // wipe_credits_intermediate (0) - false, // wipe_level_toblack - false, // wipe_intermission_toblack - false, // wipe_voting_toblack, - false, // wipe_continuing_toblack - false, // wipe_titlescreen_toblack - false, // wipe_menu_toblack + false, // wipe_level_toblack + false, // wipe_intermission_toblack + false, // wipe_voting_toblack, + false, // wipe_continuing_toblack + false, // wipe_titlescreen_toblack + false, // wipe_menu_toblack false, // wipe_credits_toblack - false, // wipe_evaluation_toblack - false, // wipe_gameend_toblack + false, // wipe_evaluation_toblack + false, // wipe_gameend_toblack + false, // wipe_ceremony_toblack false, // wipe_intro_toblack (hardcoded) false, // wipe_ending_toblack (hardcoded) false, // wipe_cutscene_toblack (hardcoded) @@ -140,14 +143,15 @@ static boolean g_wipedef_toinvert[NUMWIPEDEFS] = { false, // wipe_encore_towhite false, // wipe_level_final - false, // wipe_intermission_final - false, // wipe_voting_final - false, // wipe_continuing_final - false, // wipe_titlescreen_final - false, // wipe_menu_final + false, // wipe_intermission_final + false, // wipe_voting_final + false, // wipe_continuing_final + false, // wipe_titlescreen_final + false, // wipe_menu_final false, // wipe_credits_final - false, // wipe_evaluation_final - false, // wipe_gameend_final + false, // wipe_evaluation_final + false, // wipe_gameend_final + false, // wipe_ceremony_final false, // wipe_intro_final (hardcoded) false, // wipe_ending_final (hardcoded) false // wipe_cutscene_final (hardcoded) @@ -156,15 +160,16 @@ static boolean g_wipedef_toinvert[NUMWIPEDEFS] = { static boolean g_wipedef_towhite[NUMWIPEDEFS] = { false, // wipe_credits_intermediate (0) - false, // wipe_level_toblack - false, // wipe_intermission_toblack - false, // wipe_voting_toblack, - false, // wipe_continuing_toblack - false, // wipe_titlescreen_toblack - false, // wipe_menu_toblack + false, // wipe_level_toblack + false, // wipe_intermission_toblack + false, // wipe_voting_toblack, + false, // wipe_continuing_toblack + false, // wipe_titlescreen_toblack + false, // wipe_menu_toblack false, // wipe_credits_toblack - false, // wipe_evaluation_toblack - false, // wipe_gameend_toblack + false, // wipe_evaluation_toblack + false, // wipe_gameend_toblack + false, // wipe_ceremony_toblack false, // wipe_intro_toblack (hardcoded) false, // wipe_ending_toblack (hardcoded) false, // wipe_cutscene_toblack (hardcoded) @@ -173,14 +178,15 @@ static boolean g_wipedef_towhite[NUMWIPEDEFS] = { true, // wipe_encore_towhite false, // wipe_level_final - false, // wipe_intermission_final - false, // wipe_voting_final - false, // wipe_continuing_final - false, // wipe_titlescreen_final - false, // wipe_menu_final + false, // wipe_intermission_final + false, // wipe_voting_final + false, // wipe_continuing_final + false, // wipe_titlescreen_final + false, // wipe_menu_final false, // wipe_credits_final - false, // wipe_evaluation_final - false, // wipe_gameend_final + false, // wipe_evaluation_final + false, // wipe_gameend_final + false, // wipe_ceremony_final false, // wipe_intro_final (hardcoded) false, // wipe_ending_final (hardcoded) false // wipe_cutscene_final (hardcoded) @@ -189,15 +195,16 @@ static boolean g_wipedef_towhite[NUMWIPEDEFS] = { static boolean g_wipedef_crossfade[NUMWIPEDEFS] = { false, // wipe_credits_intermediate (0) - false, // wipe_level_toblack - false, // wipe_intermission_toblack - false, // wipe_voting_toblack, - false, // wipe_continuing_toblack - false, // wipe_titlescreen_toblack - false, // wipe_menu_toblack + false, // wipe_level_toblack + false, // wipe_intermission_toblack + false, // wipe_voting_toblack, + false, // wipe_continuing_toblack + false, // wipe_titlescreen_toblack + false, // wipe_menu_toblack false, // wipe_credits_toblack - false, // wipe_evaluation_toblack - false, // wipe_gameend_toblack + false, // wipe_evaluation_toblack + false, // wipe_gameend_toblack + false, // wipe_ceremony_toblack false, // wipe_intro_toblack (hardcoded) false, // wipe_ending_toblack (hardcoded) false, // wipe_cutscene_toblack (hardcoded) @@ -206,14 +213,15 @@ static boolean g_wipedef_crossfade[NUMWIPEDEFS] = { false, // wipe_encore_towhite true, // wipe_level_final - true, // wipe_intermission_final - true, // wipe_voting_final - true, // wipe_continuing_final - true, // wipe_titlescreen_final - true, // wipe_menu_final + true, // wipe_intermission_final + true, // wipe_voting_final + true, // wipe_continuing_final + true, // wipe_titlescreen_final + true, // wipe_menu_final true, // wipe_credits_final - true, // wipe_evaluation_final - true, // wipe_gameend_final + true, // wipe_evaluation_final + true, // wipe_gameend_final + true, // wipe_ceremony_final true, // wipe_intro_final (hardcoded) true, // wipe_ending_final (hardcoded) true // wipe_cutscene_final (hardcoded) From b84383bd8d804b7c932384dd40b3040880a9ab9d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 13:50:15 -0500 Subject: [PATCH 44/97] Free the special map strings --- src/deh_soc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/deh_soc.c b/src/deh_soc.c index e4eb708c2..5c281db50 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2747,6 +2747,7 @@ void readmaincfg(MYFILE *f, boolean mainfile) } else if (fastcmp(word, "TITLEMAP")) { + Z_Free(titlemap); titlemap = Z_StrDup(word2); titlechanged = true; } @@ -2841,15 +2842,18 @@ void readmaincfg(MYFILE *f, boolean mainfile) } else if (fastcmp(word, "BOOTMAP")) { + Z_Free(bootmap); bootmap = Z_StrDup(word2); //titlechanged = true; } else if (fastcmp(word, "TUTORIALMAP")) { + Z_Free(tutorialmap); tutorialmap = Z_StrDup(word2); } else if (fastcmp(word, "PODIUMMAP")) { + Z_Free(podiummap); podiummap = Z_StrDup(word2); } else From 2ebf8ebb8c18b9bb004146349240f57956903c60 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 5 Mar 2023 00:41:20 -0800 Subject: [PATCH 45/97] Don't skip spectators when searching for Podium starts There shouldn't be any spectators in Podium anyway. But there are if you come here from a Sealed Star because bots get spectated in those. The bots are still spectators until they spawn (after G_FindPodiumStart). --- src/g_game.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 900a27dcf..1e2a3d757 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2940,15 +2940,9 @@ mapthing_t *G_FindPodiumStart(INT32 playernum) UINT8 i; UINT8 pos = 0; - // SRB2Kart: figure out player spawn pos from points - if (!playeringame[playernum] || players[playernum].spectator) - { - return playerstarts[0]; // go to first spot if you're a spectator - } - for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator) + if (!playeringame[i]) { continue; } From 34112be38ecc96a68b3e6be13a132177c30b4674 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Mar 2023 14:12:03 -0500 Subject: [PATCH 46/97] Move bot updates before P_InitPlayers --- src/g_game.c | 7 ++++++- src/p_setup.c | 40 +++++++++++++++++++--------------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 1e2a3d757..e3c99e7fc 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2940,9 +2940,14 @@ mapthing_t *G_FindPodiumStart(INT32 playernum) UINT8 i; UINT8 pos = 0; + if (!playeringame[playernum] || players[playernum].spectator) + { + return playerstarts[0]; // go to first spot if you're a spectator + } + for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i]) + if (!playeringame[i] || players[i].spectator) { continue; } diff --git a/src/p_setup.c b/src/p_setup.c index f0d38d694..3b6dfc23f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7382,6 +7382,25 @@ static void P_InitGametype(void) spectateGriefed = 0; K_CashInPowerLevels(); // Pushes power level changes even if intermission was skipped + if (grandprixinfo.gp == true) + { + if (grandprixinfo.initalize == true) + { + K_InitGrandPrixBots(); + grandprixinfo.initalize = false; + } + else if (grandprixinfo.wonround == true) + { + K_UpdateGrandPrixBots(); + grandprixinfo.wonround = false; + } + } + else if (!modeattacking) + { + // We're in a Match Race, use simplistic randomized bots. + K_UpdateMatchRaceBots(); + } + P_InitPlayers(); if (modeattacking && !demo.playback) @@ -7969,27 +7988,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) lastmaploaded = gamemap; // HAS to be set after saving!! } - if (reloadinggamestate) - ; - else if (grandprixinfo.gp == true) - { - if (grandprixinfo.initalize == true) - { - K_InitGrandPrixBots(); - grandprixinfo.initalize = false; - } - else if (grandprixinfo.wonround == true) - { - K_UpdateGrandPrixBots(); - grandprixinfo.wonround = false; - } - } - else if (!modeattacking) - { - // We're in a Match Race, use simplistic randomized bots. - K_UpdateMatchRaceBots(); - } - if (!fromnetsave) // uglier hack { // to make a newly loaded level start on the second frame. INT32 buf = gametic % BACKUPTICS; From 28c2ff96686b4410beffd0fd65e73bc69a04b972 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Mar 2023 14:12:25 -0500 Subject: [PATCH 47/97] Force GT_RACE before podium map loads --- src/k_podium.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_podium.c b/src/k_podium.c index fdd450b43..8570fb5cb 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -99,6 +99,7 @@ boolean K_StartCeremony(void) } } + G_SetGametype(GT_RACE); G_DoLoadLevelEx(false, GS_CEREMONY); r_splitscreen = 0; // Only one screen for the ceremony From 9c264b3736d2a6143620b9882243058335e3a1c8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Mar 2023 14:59:11 -0500 Subject: [PATCH 48/97] K_UpdateAllPlayerPositions Call this whenever we need accurate player positions. In addition to before player think, is now being used for exiting as well as immediately when the player is spawned. --- src/k_kart.c | 23 +++++++++++++++++++++++ src/k_kart.h | 1 + src/p_mobj.c | 2 ++ src/p_tick.c | 36 +++++++----------------------------- src/p_user.c | 2 +- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index fe4e79938..b1035cfc5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9507,6 +9507,29 @@ void K_KartUpdatePosition(player_t *player) player->position = position; } +void K_UpdateAllPlayerPositions(void) +{ + INT32 i; + + // First loop: Ensure all players' distance to the finish line are all accurate + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) + { + K_UpdateDistanceFromFinishLine(&players[i]); + } + } + + // Second loop: Ensure all player positions reflect everyone's distances + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) + { + K_KartUpdatePosition(&players[i]); + } + } +} + // // K_StripItems // diff --git a/src/k_kart.h b/src/k_kart.h index fdc9cb2de..fc364624a 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -139,6 +139,7 @@ INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage); void K_SpawnDriftBoostExplosion(player_t *player, int stage); void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave); void K_KartUpdatePosition(player_t *player); +void K_UpdateAllPlayerPositions(void); SINT8 K_GetTotallyRandomResult(UINT8 useodds); mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount); void K_DropItems(player_t *player); diff --git a/src/p_mobj.c b/src/p_mobj.c index eb676f660..251577a20 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11951,6 +11951,8 @@ void P_AfterPlayerSpawn(INT32 playernum) if (CheckForReverseGravity) P_CheckGravity(mobj, false); + + K_UpdateAllPlayerPositions(); } // spawn it at a playerspawn mapthing diff --git a/src/p_tick.c b/src/p_tick.c index 9b4e987bf..22f9187be 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -532,10 +532,12 @@ void P_Ticker(boolean run) // Increment jointime and quittime even if paused for (i = 0; i < MAXPLAYERS; i++) + { if (playeringame[i]) { players[i].jointime++; } + } if (objectplacing) { @@ -602,32 +604,16 @@ void P_Ticker(boolean run) ps_playerthink_time = I_GetPreciseTime(); -#define PLAYERCONDITION(i) (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) - // First loop: Ensure all players' distance to the finish line are all accurate - for (i = 0; i < MAXPLAYERS; i++) - { - if (!PLAYERCONDITION(i)) - continue; - K_UpdateDistanceFromFinishLine(&players[i]); - } - - // Second loop: Ensure all player positions reflect everyone's distances - for (i = 0; i < MAXPLAYERS; i++) - { - if (!PLAYERCONDITION(i)) - continue; - K_KartUpdatePosition(&players[i]); - } + K_UpdateAllPlayerPositions(); // OK! Now that we got all of that sorted, players can think! for (i = 0; i < MAXPLAYERS; i++) { - if (!PLAYERCONDITION(i)) + if (!(playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))) continue; P_PlayerThink(&players[i]); K_KartPlayerHUDUpdate(&players[i]); } -#undef PLAYERCONDITION ps_playerthink_time = I_GetPreciseTime() - ps_playerthink_time; } @@ -869,19 +855,11 @@ void P_PreTicker(INT32 frames) R_UpdateMobjInterpolators(); - // First loop: Ensure all players' distance to the finish line are all accurate - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) - K_UpdateDistanceFromFinishLine(&players[i]); - - // Second loop: Ensure all player positions reflect everyone's distances - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) - K_KartUpdatePosition(&players[i]); - - // OK! Now that we got all of that sorted, players can think! LUA_HOOK(PreThinkFrame); + K_UpdateAllPlayerPositions(); + + // OK! Now that we got all of that sorted, players can think! for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) { diff --git a/src/p_user.c b/src/p_user.c index 0a2c5b548..f5cb57df2 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1284,7 +1284,7 @@ void P_DoPlayerExit(player_t *player) if ((gametyperules & GTR_CIRCUIT)) // If in Race Mode, allow { - K_KartUpdatePosition(player); + K_UpdateAllPlayerPositions(); if (cv_kartvoices.value) { From 2b706fb95b81e73e8eeb9a07e113bd78e7d28ae9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Mar 2023 15:00:05 -0500 Subject: [PATCH 49/97] G_PlayerReborn jointime betweenmaps Fixes newly created bots having 0 rings. --- src/p_mobj.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 251577a20..0f7d7327b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11721,7 +11721,9 @@ void P_SpawnPlayer(INT32 playernum) mobj_t *mobj; if (p->playerstate == PST_REBORN) - G_PlayerReborn(playernum, false); + { + G_PlayerReborn(playernum, (p->jointime <= 1)); + } for (i = 0; i < MAXPLAYERS; i++) { From 4bb0c3913381832c09a4a4793ec7b46eff161c64 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sun, 5 Mar 2023 16:07:17 -0600 Subject: [PATCH 50/97] Fix controller delay in menus M_UpdateMenuCMD only needs to be called once per tick per player. It was being called for every event, which really mangled the repeat delays as more attached controllers sent axis events. --- src/d_main.c | 7 +++++++ src/k_menu.h | 1 + src/k_menufunc.c | 8 +------- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 18acb191d..53ae4d86c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -178,6 +178,7 @@ boolean capslock = 0; // gee i wonder what this does. void D_ProcessEvents(void) { event_t *ev; + int i; boolean eaten; boolean menuresponse = false; @@ -251,6 +252,12 @@ void D_ProcessEvents(void) { M_MapMenuControls(NULL); } + + // Update menu CMD + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + M_UpdateMenuCMD(i); + } } // diff --git a/src/k_menu.h b/src/k_menu.h index fb41a64a2..2271ab7b1 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -571,6 +571,7 @@ void Addons_option_Onchange(void); void M_SortServerList(void); void M_MapMenuControls(event_t *ev); +void M_UpdateMenuCMD(UINT8 i); boolean M_Responder(event_t *ev); boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt); boolean M_MenuButtonHeld(UINT8 pid, UINT32 bt); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index ce9b07e2f..899099f12 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -691,7 +691,7 @@ void M_SetMenuDelay(UINT8 i) } } -static void M_UpdateMenuCMD(UINT8 i) +void M_UpdateMenuCMD(UINT8 i) { UINT8 mp = max(1, setup_numplayers); @@ -734,12 +734,6 @@ void M_MapMenuControls(event_t *ev) // update keys current state G_MapEventsToControls(ev); } - - // Update menu CMD - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - M_UpdateMenuCMD(i); - } } boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt) From 28f2f4a51ae9a98f9e4eb8852b8a91687f885ac3 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 5 Mar 2023 21:02:32 -0800 Subject: [PATCH 51/97] k_menufunc.c: fix -Wunused-variable --- src/k_menufunc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 899099f12..51bc3a2a7 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -727,8 +727,6 @@ void M_UpdateMenuCMD(UINT8 i) void M_MapMenuControls(event_t *ev) { - INT32 i; - if (ev) { // update keys current state From 1dccf5dfea20f327b8aa3f2b85a6a9fae9a63a9d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Mar 2023 01:21:32 -0500 Subject: [PATCH 52/97] Remove spectator crap again welp... --- src/g_game.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index e3c99e7fc..1e2a3d757 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2940,14 +2940,9 @@ mapthing_t *G_FindPodiumStart(INT32 playernum) UINT8 i; UINT8 pos = 0; - if (!playeringame[playernum] || players[playernum].spectator) - { - return playerstarts[0]; // go to first spot if you're a spectator - } - for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator) + if (!playeringame[i]) { continue; } From 87d8945f9721aadb84dd48cfa6616dcd6bd71883 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sun, 5 Mar 2023 23:57:47 -0700 Subject: [PATCH 53/97] Make sliptiding actually good (no visuals) 50% increased effect of handling boosts, 75% reduced penalty for handling boost stacking, grants a boost proportional to your sliptide length when you straighten out --- src/d_player.h | 4 ++++ src/k_kart.c | 37 +++++++++++++++++++++++++++++++++++-- src/lua_playerlib.c | 12 ++++++++++++ src/p_saveg.c | 8 ++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 98dee8e9a..c2ad5050b 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -675,6 +675,10 @@ struct player_t UINT8 tripwireReboundDelay; // When failing Tripwire, brieftly lock out speed-based tripwire pass (anti-cheese) + UINT16 sliptideZip; // How long is our chained sliptide? Grant a proportional boost when it's over. + UINT8 sliptideZipDelay; // How long since the last sliptide? Only boost once you've been straightened out for a bit. + UINT16 sliptideZipBoost; // The actual boost granted from sliptideZip. + mobj_t *stumbleIndicator; #ifdef HWRENDER diff --git a/src/k_kart.c b/src/k_kart.c index 5e9982648..eb8962fbc 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1809,6 +1809,11 @@ static void K_SpawnGenericSpeedLines(player_t *player, boolean top) fast->colorized = true; fast->renderflags |= RF_ADD; } + else if (player->sliptideZipBoost) + { + fast->color = SKINCOLOR_WHITE; + fast->colorized = true; + } } void K_SpawnNormalSpeedLines(player_t *player) @@ -3046,7 +3051,7 @@ static void K_GetKartBoostPower(player_t *player) speedboost += FixedDiv(s, FRACUNIT + (metabolism * (numboosts-1))); \ accelboost += FixedDiv(a, FRACUNIT + (metabolism * (numboosts-1))); \ if (player->aizdriftstrat) \ - handleboost += FixedDiv(h, FRACUNIT + (metabolism * (numboosts-1))); \ + handleboost += FixedDiv(3*h/2, FRACUNIT + (metabolism * (numboosts-1))/4); \ else \ handleboost = max(h, handleboost); \ } @@ -3080,6 +3085,11 @@ static void K_GetKartBoostPower(player_t *player) ); } + if (player->sliptideZipBoost) + { + ADDBOOST(3*FRACUNIT/4, 4*FRACUNIT, sliptidehandling/2); + } + if (player->spindashboost) // Spindash boost { const fixed_t MAXCHARGESPEED = K_GetSpindashChargeSpeed(player); @@ -7483,7 +7493,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->sneakertimer || player->ringboost || player->driftboost || player->startboost || player->eggmanexplode || player->trickboost - || player->gateBoost) + || player->gateBoost || player->sliptideZipBoost) { #if 0 if (player->invincibilitytimer) @@ -7745,6 +7755,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->startboost--; } + if (player->sliptideZipBoost > 0 && onground == true) + { + player->sliptideZipBoost--; + } + if (player->spindashboost) { player->spindashboost--; @@ -9205,6 +9220,8 @@ static void K_KartDrift(player_t *player, boolean onground) { K_SpawnAIZDust(player); + player->sliptideZip++; + if (abs(player->aizdrifttilt) < ANGLE_22h) { player->aizdrifttilt = @@ -9222,6 +9239,18 @@ static void K_KartDrift(player_t *player, boolean onground) if (!K_Sliptiding(player)) { + if (player->sliptideZip > 0) + { + player->sliptideZipDelay++; + if (player->sliptideZipDelay > TICRATE && player->drift == 0) + { + S_StartSound(player->mo, sfx_s3kb6); + player->sliptideZipBoost += player->sliptideZip; + player->sliptideZip = 0; + player->sliptideZipDelay = 0; + } + } + player->aizdrifttilt -= player->aizdrifttilt / 4; player->aizdriftturn -= player->aizdriftturn / 4; @@ -9230,6 +9259,10 @@ static void K_KartDrift(player_t *player, boolean onground) if (abs(player->aizdriftturn) < ANGLE_11hh) player->aizdriftturn = 0; } + else + { + player->sliptideZipDelay = 0; + } if (player->drift && ((buttons & BT_BRAKE) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 211f83867..caa44ce58 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -308,6 +308,12 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->tripwireLeniency); else if (fastcmp(field,"tripwireReboundDelay")) lua_pushinteger(L, plr->tripwireReboundDelay); + else if (fastcmp(field,"sliptideZip")) + lua_pushinteger(L, plr->sliptideZip); + else if (fastcmp(field,"sliptideZipDelay")) + lua_pushinteger(L, plr->sliptideZipDelay); + else if (fastcmp(field,"sliptideZipBoost")) + lua_pushinteger(L, plr->sliptideZipDelay); /* else if (fastcmp(field,"itemroulette")) lua_pushinteger(L, plr->itemroulette); @@ -688,6 +694,12 @@ static int player_set(lua_State *L) plr->tripwireLeniency = luaL_checkinteger(L, 3); else if (fastcmp(field,"tripwireReboundDelay")) plr->tripwireReboundDelay = luaL_checkinteger(L, 3); + else if (fastcmp(field,"sliptideZip")) + plr->sliptideZip = luaL_checkinteger(L, 3); + else if (fastcmp(field,"sliptideZipDelay")) + plr->sliptideZipDelay = luaL_checkinteger(L, 3); + else if (fastcmp(field,"sliptideZipBoost")) + plr->sliptideZipDelay = luaL_checkinteger(L, 3); /* else if (fastcmp(field,"itemroulette")) plr->itemroulette = luaL_checkinteger(L, 3); diff --git a/src/p_saveg.c b/src/p_saveg.c index fc9d7daf4..49d31a230 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -397,6 +397,10 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].tripwireReboundDelay); + WRITEUINT16(save->p, players[i].sliptideZip); + WRITEUINT8(save->p, players[i].sliptideZipDelay); + WRITEUINT16(save->p, players[i].sliptideZipBoost); + // respawnvars_t WRITEUINT8(save->p, players[i].respawn.state); WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].respawn.wp)); @@ -767,6 +771,10 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].tripwireReboundDelay = READUINT8(save->p); + players[i].sliptideZip = READUINT16(save->p); + players[i].sliptideZipDelay = READUINT8(save->p); + players[i].sliptideZipBoost = READUINT16(save->p); + // respawnvars_t players[i].respawn.state = READUINT8(save->p); players[i].respawn.wp = (waypoint_t *)(size_t)READUINT32(save->p); From d79e53a0e392b576a2d43adcfe1ef2dcaf2bdc96 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 3 Mar 2023 03:41:02 -0800 Subject: [PATCH 54/97] G_SaveGameData: check for NULL This fixes I_Error if used before M_NewDataStruct is called. --- 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 a0d187790..9c9487339 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4582,7 +4582,7 @@ void G_SaveGameData(void) UINT8 btemp; savebuffer_t save = {0}; - if (!gamedata->loaded) + if (gamedata == NULL || !gamedata->loaded) return; // If never loaded (-nodata), don't save if (usedCheats) From f4fbd1e65480e44f366611a3d75b76dca298fe17 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 05:00:59 -0700 Subject: [PATCH 55/97] Sliptide boost VFX first pass --- src/d_clisrv.c | 1 + src/d_player.h | 1 + src/deh_tables.c | 4 ++ src/info.c | 31 ++++++++++++++ src/info.h | 5 +++ src/k_kart.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++- src/k_kart.h | 1 + src/p_mobj.c | 2 + 8 files changed, 147 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 76470b865..af2dfe0cd 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2561,6 +2561,7 @@ void CL_ClearPlayer(INT32 playernum) P_SetTarget(&players[playernum].followmobj, NULL); P_SetTarget(&players[playernum].hoverhyudoro, NULL); P_SetTarget(&players[playernum].stumbleIndicator, NULL); + P_SetTarget(&players[playernum].sliptideZipIndicator, NULL); } // Handle parties. diff --git a/src/d_player.h b/src/d_player.h index c2ad5050b..23f792c1b 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -680,6 +680,7 @@ struct player_t UINT16 sliptideZipBoost; // The actual boost granted from sliptideZip. mobj_t *stumbleIndicator; + mobj_t *sliptideZipIndicator; #ifdef HWRENDER fixed_t fovadd; // adjust FOV for hw rendering diff --git a/src/deh_tables.c b/src/deh_tables.c index de88639e8..1745d9a7d 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3304,6 +3304,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_MAGICIANBOXTOP", "S_MAGICIANBOXBOTTOM", + "S_SLIPTIDEZIP", + // Signpost sparkles "S_SIGNSPARK1", "S_SIGNSPARK2", @@ -5323,6 +5325,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_MONITOR_PART", "MT_MONITOR_SHARD", "MT_MAGICIANBOX", + + "MT_SLIPTIDEZIP", "MT_SIGNSPARKLE", diff --git a/src/info.c b/src/info.c index d0509e731..4d799efe9 100644 --- a/src/info.c +++ b/src/info.c @@ -552,6 +552,8 @@ char sprnames[NUMSPRITES + 1][5] = "IMDB", // Item Monitor Small Shard (Debris) "MTWK", // Item Monitor Glass Twinkle + "SLPT", // Sliptide zip indicator + "WIPD", // Wipeout dust trail "DRIF", // Drift Sparks "BDRF", // Brake drift sparks @@ -3930,6 +3932,8 @@ state_t states[NUMSTATES] = {SPR_MGBT, FF_FLOORSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_MAGICIANBOX_TOP {SPR_MGBB, FF_FLOORSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_MAGICIANBOX_BOTTOM + {SPR_SLPT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_SLIPTIDEZIP + {SPR_SGNS, FF_ADD|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_SIGNSPARK2}, // S_SIGNSPARK1 {SPR_SGNS, FF_ADD|FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_SIGNSPARK3}, // S_SIGNSPARK2 {SPR_SGNS, FF_ADD|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_SIGNSPARK4}, // S_SIGNSPARK3 @@ -22583,6 +22587,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, + + { // MT_SLIPTIDEZIP + -1, // doomednum + S_SLIPTIDEZIP, // spawnstate + 1000, // 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 + 20*FRACUNIT, // radius + 20*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING, // flags + S_NULL // raisestate + }, { // MT_SIGNSPARKLE -1, // doomednum diff --git a/src/info.h b/src/info.h index 02e194cb6..3a1921b3c 100644 --- a/src/info.h +++ b/src/info.h @@ -1103,6 +1103,8 @@ typedef enum sprite SPR_IMDB, // Item Monitor Small Shard (Debris) SPR_MTWK, // Item Monitor Glass Twinkle + SPR_SLPT, // Sliptide zip indicator + SPR_WIPD, // Wipeout dust trail SPR_DRIF, // Drift Sparks SPR_BDRF, // Brake drift sparks @@ -4339,6 +4341,8 @@ typedef enum state S_MAGICIANBOX_TOP, S_MAGICIANBOX_BOTTOM, + S_SLIPTIDEZIP, + // Signpost sparkles S_SIGNSPARK1, S_SIGNSPARK2, @@ -6394,6 +6398,7 @@ typedef enum mobj_type MT_MONITOR_PART, MT_MONITOR_SHARD, MT_MAGICIANBOX, + MT_SLIPTIDEZIP, MT_SIGNSPARKLE, diff --git a/src/k_kart.c b/src/k_kart.c index eb8962fbc..b4b12b7e1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3883,6 +3883,31 @@ void K_InitStumbleIndicator(player_t *player) P_SetTarget(&new->target, player->mo); } +void K_InitSliptideZipIndicator(player_t *player) +{ + mobj_t *new = NULL; + + if (player == NULL) + { + return; + } + + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + return; + } + + if (player->stumbleIndicator != NULL && P_MobjWasRemoved(player->sliptideZipIndicator) == false) + { + P_RemoveMobj(player->sliptideZipIndicator); + } + + new = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SLIPTIDEZIP); + + P_SetTarget(&player->sliptideZipIndicator, new); + P_SetTarget(&new->target, player->mo); +} + void K_UpdateStumbleIndicator(player_t *player) { const angle_t fudge = ANG15; @@ -3985,6 +4010,71 @@ void K_UpdateStumbleIndicator(player_t *player) } } +void K_UpdateSliptideZipIndicator(player_t *player) +{ + mobj_t *mobj = NULL; + + if (player == NULL) + { + return; + } + + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + return; + } + + if (player->stumbleIndicator == NULL || P_MobjWasRemoved(player->stumbleIndicator) == true) + { + K_InitSliptideZipIndicator(player); + return; + } + + mobj = player->sliptideZipIndicator; + angle_t momentumAngle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + + P_MoveOrigin(mobj, player->mo->x - FixedMul(40*mapobjectscale, FINECOSINE(momentumAngle >> ANGLETOFINESHIFT)), + player->mo->y - FixedMul(40*mapobjectscale, FINESINE(momentumAngle >> ANGLETOFINESHIFT)), + player->mo->z + (player->mo->height / 2)); + mobj->angle = momentumAngle + ANGLE_90; + P_SetScale(mobj, 3 * player->mo->scale / 2); + + mobj->renderflags &= ~RF_DONTDRAW; + + // No stored boost + if (player->sliptideZip == 0) + { + mobj->renderflags |= RF_DONTDRAW; + mobj->frame = 7; + return; + } + + UINT32 chargeFrame = 7 - min(7, player->sliptideZip / 10); + UINT32 decayFrame = min(7, player->sliptideZipDelay / 5); + if (max(chargeFrame, decayFrame) > mobj->frame) + mobj->frame++; + else if (max(chargeFrame, decayFrame) < mobj->frame) + mobj->frame--; + + CONS_Printf("%d/%d\n", chargeFrame, decayFrame); + + mobj->renderflags &= ~RF_TRANSMASK; + mobj->renderflags |= RF_PAPERSPRITE; + + if (!K_Sliptiding(player) && player->drift == 0) + { + // Decay timer's ticking + mobj->rollangle += 3*ANG30/4; + if (leveltime % 2 == 0) + mobj->renderflags |= RF_TRANS50; + } + else + { + // Storing boost + mobj->rollangle += 3*ANG15/4; + } +} + static boolean K_LastTumbleBounceCondition(player_t *player) { return (player->tumbleBounces > TUMBLEBOUNCES && player->tumbleHeight < 60); @@ -8129,6 +8219,8 @@ void K_KartPlayerAfterThink(player_t *player) K_UpdateStumbleIndicator(player); + K_UpdateSliptideZipIndicator(player); + // Move held objects (Bananas, Orbinaut, etc) K_MoveHeldObjects(player); @@ -9239,13 +9331,14 @@ static void K_KartDrift(player_t *player, boolean onground) if (!K_Sliptiding(player)) { - if (player->sliptideZip > 0) + if (player->sliptideZip > 0 && player->drift == 0) { player->sliptideZipDelay++; if (player->sliptideZipDelay > TICRATE && player->drift == 0) { S_StartSound(player->mo, sfx_s3kb6); player->sliptideZipBoost += player->sliptideZip; + K_SpawnDriftBoostExplosion(player, 0); player->sliptideZip = 0; player->sliptideZipDelay = 0; } @@ -9703,6 +9796,9 @@ static void K_KartSpindashWind(mobj_t *parent) P_SetTarget(&wind->target, parent); + if (parent->player && parent->player->sliptideZipBoost) + P_SetScale(wind, wind->scale * 2); + if (parent->momx || parent->momy) wind->angle = R_PointToAngle2(0, 0, parent->momx, parent->momy); else @@ -9778,6 +9874,11 @@ static void K_KartSpindash(player_t *player) K_KartSpindashWind(player->mo); } + if ((player->sliptideZipBoost > 0) && (spawnWind == true)) + { + K_KartSpindashWind(player->mo); + } + if (player->spindashboost > (TICRATE/2)) { K_KartSpindashDust(player->mo); diff --git a/src/k_kart.h b/src/k_kart.h index 04610be45..3409bb5d2 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -99,6 +99,7 @@ angle_t K_StumbleSlope(angle_t angle, angle_t pitch, angle_t roll); void K_StumblePlayer(player_t *player); boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir); void K_InitStumbleIndicator(player_t *player); +void K_InitSliptideZipIndicator(player_t *player); void K_UpdateStumbleIndicator(player_t *player); INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); diff --git a/src/p_mobj.c b/src/p_mobj.c index 8fe24ffed..00bd35fa5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11794,6 +11794,8 @@ void P_SpawnPlayer(INT32 playernum) K_InitStumbleIndicator(p); + K_InitSliptideZipIndicator(p); + if (gametyperules & GTR_ITEMARROWS) { mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height + 16*FRACUNIT, MT_PLAYERARROW); From 2e418628023d9ce329cfbba90f7a604ef45cab0e Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 05:03:39 -0700 Subject: [PATCH 56/97] Please fucking stage the files I tell you to stage --- src/k_kart.c | 2 -- src/k_kart.h | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index b4b12b7e1..7ecb517b8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4056,8 +4056,6 @@ void K_UpdateSliptideZipIndicator(player_t *player) else if (max(chargeFrame, decayFrame) < mobj->frame) mobj->frame--; - CONS_Printf("%d/%d\n", chargeFrame, decayFrame); - mobj->renderflags &= ~RF_TRANSMASK; mobj->renderflags |= RF_PAPERSPRITE; diff --git a/src/k_kart.h b/src/k_kart.h index 3409bb5d2..6ab2fbfb0 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -101,6 +101,7 @@ boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, bool void K_InitStumbleIndicator(player_t *player); void K_InitSliptideZipIndicator(player_t *player); void K_UpdateStumbleIndicator(player_t *player); +void K_UpdateSliptideZipIndicator(player_t *player); INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers); From c2c2c71f0a01aec9a220e4dc5756ae84a214efca Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 05:17:26 -0700 Subject: [PATCH 57/97] Freeze sliptide zip delay while airborne --- src/k_kart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 7ecb517b8..906b519a8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4059,7 +4059,7 @@ void K_UpdateSliptideZipIndicator(player_t *player) mobj->renderflags &= ~RF_TRANSMASK; mobj->renderflags |= RF_PAPERSPRITE; - if (!K_Sliptiding(player) && player->drift == 0) + if (!K_Sliptiding(player) && player->drift == 0 && P_IsObjectOnGround(player->mo)) { // Decay timer's ticking mobj->rollangle += 3*ANG30/4; @@ -9329,10 +9329,10 @@ static void K_KartDrift(player_t *player, boolean onground) if (!K_Sliptiding(player)) { - if (player->sliptideZip > 0 && player->drift == 0) + if (player->sliptideZip > 0 && player->drift == 0 && P_IsObjectOnGround(player->mo)) { player->sliptideZipDelay++; - if (player->sliptideZipDelay > TICRATE && player->drift == 0) + if (player->sliptideZipDelay > TICRATE) { S_StartSound(player->mo, sfx_s3kb6); player->sliptideZipBoost += player->sliptideZip; From 8951ccf61dd7a2b9059cc98c1c7cfcfb64d7e760 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 05:43:02 -0700 Subject: [PATCH 58/97] Buff sliptide zip --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 906b519a8..a428d4936 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3087,7 +3087,7 @@ static void K_GetKartBoostPower(player_t *player) if (player->sliptideZipBoost) { - ADDBOOST(3*FRACUNIT/4, 4*FRACUNIT, sliptidehandling/2); + ADDBOOST(FRACUNIT, 4*FRACUNIT, sliptidehandling/2); // + 100% top speed, + 400% acceleration, +25% handling } if (player->spindashboost) // Spindash boost From 5dea8f9bbabfe15445c99cf2c7cd31ad6201ccce Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 05:43:14 -0700 Subject: [PATCH 59/97] Break zip combo when dying, idiot --- src/k_respawn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_respawn.c b/src/k_respawn.c index cb08408a2..936216f8f 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -159,6 +159,7 @@ void K_DoIngameRespawn(player_t *player) player->ringboost = 0; player->driftboost = player->strongdriftboost = 0; player->gateBoost = 0; + player->sliptideZip = player->sliptideZipBoost = player->sliptideZipDelay = 0; K_TumbleInterrupt(player); P_ResetPlayer(player); From ff838a08a5783cfc114c1f66b5131e1e1ce7b3ef Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 04:43:57 -0800 Subject: [PATCH 60/97] cxxutil.hpp: fix std::hash include --- src/cxxutil.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cxxutil.hpp b/src/cxxutil.hpp index 56b85c79b..835d5e74f 100644 --- a/src/cxxutil.hpp +++ b/src/cxxutil.hpp @@ -2,6 +2,7 @@ #define __SRB2_CXXUTIL_HPP__ #include +#include #include #include From 1de5046623e3b97e580ebdf2fc88d573ece0cda6 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 04:52:43 -0800 Subject: [PATCH 61/97] Add cv_debugrender_contrast, adjusts contrast of level geometry debugrender_contrast -1.0 to 1.0, default 0.0 (no change). Higher values add more contrast (darkens the level), lower values add less (brighten). Does not affect thing drawing. Software mode only. Adds r_debug.cpp --- src/CMakeLists.txt | 1 + src/r_debug.cpp | 38 ++++++++++++++++++++++++++++++++++++++ src/r_main.c | 4 ++++ src/r_main.h | 7 +++++++ src/r_plane.c | 2 ++ src/r_segs.c | 18 ++++++++++++++---- 6 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 src/r_debug.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e68db60b..8e6233429 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,6 +64,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 tables.c r_bsp.c r_data.c + r_debug.cpp r_draw.c r_fps.c r_main.c diff --git a/src/r_debug.cpp b/src/r_debug.cpp new file mode 100644 index 000000000..d090d230e --- /dev/null +++ b/src/r_debug.cpp @@ -0,0 +1,38 @@ +// 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. +//----------------------------------------------------------------------------- +/// \file r_debug.cpp +/// \brief Software renderer debugging + +#include // std::clamp + +#include "cxxutil.hpp" + +#include "command.h" +#include "m_fixed.h" +#include "r_main.h" + +namespace +{ + +CV_PossibleValue_t contrast_cons_t[] = {{-FRACUNIT, "MIN"}, {FRACUNIT, "MAX"}, {}}; + +}; // namespace + +consvar_t cv_debugrender_contrast = + CVAR_INIT("debugrender_contrast", "0.0", CV_CHEAT | CV_FLOAT, contrast_cons_t, nullptr); + +INT32 R_AdjustLightLevel(INT32 light) +{ + constexpr fixed_t kRange = (LIGHTLEVELS - 1) * FRACUNIT; + const fixed_t adjust = FixedMul(cv_debugrender_contrast.value, kRange); + + light = std::clamp((light * FRACUNIT) - adjust, 0, kRange); + + return light / FRACUNIT; +} diff --git a/src/r_main.c b/src/r_main.c index b00717810..1e5e7a430 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1656,4 +1656,8 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_fpscap); CV_RegisterVar(&cv_drawpickups); + + // debugging + + CV_RegisterVar(&cv_debugrender_contrast); } diff --git a/src/r_main.h b/src/r_main.h index 01a0d9b31..df17342a2 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -123,6 +123,13 @@ extern consvar_t cv_skybox; extern consvar_t cv_tailspickup; extern consvar_t cv_drawpickups; +// debugging + +INT32 R_AdjustLightLevel(INT32 light); + +extern consvar_t + cv_debugrender_contrast; + // Called by startup code. void R_Init(void); diff --git a/src/r_plane.c b/src/r_plane.c index 4472c9fc3..4638a4081 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -1030,6 +1030,8 @@ void R_DrawSinglePlane(visplane_t *pl) if (light < 0) light = 0; + light = R_AdjustLightLevel(light); + if (pl->slope) { mapfunc = R_MapTiltedPlane; diff --git a/src/r_segs.c b/src/r_segs.c index c00330a4d..b7a25daa0 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -298,6 +298,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) else if (P_ApplyLightOffset(lightnum)) lightnum += curline->lightOffset; + lightnum = R_AdjustLightLevel(lightnum); + if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) @@ -413,12 +415,14 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) if ((rlight->flags & FOF_NOSHADE)) continue; - if (rlight->lightnum < 0) + lightnum = R_AdjustLightLevel(rlight->lightnum); + + if (lightnum < 0) xwalllights = scalelight[0]; - else if (rlight->lightnum >= LIGHTLEVELS) + else if (lightnum >= LIGHTLEVELS) xwalllights = scalelight[LIGHTLEVELS-1]; else - xwalllights = scalelight[rlight->lightnum]; + xwalllights = scalelight[lightnum]; pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; @@ -789,6 +793,8 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) else if (P_ApplyLightOffset(lightnum)) lightnum += curline->lightOffset; + lightnum = R_AdjustLightLevel(lightnum); + if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) @@ -963,7 +969,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) lighteffect = !(dc_lightlist[i].flags & FOF_NOSHADE); if (lighteffect) { - lightnum = rlight->lightnum; + lightnum = R_AdjustLightLevel(rlight->lightnum); if (lightnum < 0) xwalllights = scalelight[0]; @@ -1379,6 +1385,8 @@ static void R_RenderSegLoop (void) else if (P_ApplyLightOffset(lightnum)) lightnum += curline->lightOffset; + lightnum = R_AdjustLightLevel(lightnum); + if (lightnum < 0) xwalllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) @@ -2425,6 +2433,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (P_ApplyLightOffset(lightnum)) lightnum += curline->lightOffset; + lightnum = R_AdjustLightLevel(lightnum); + if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) From cae8cd5100070e7554fc0a179e3c2a9d8e36c98e Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 06:07:55 -0700 Subject: [PATCH 62/97] Don't encore remap sliptide zip indicator --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index 4d799efe9..f0bbf1187 100644 --- a/src/info.c +++ b/src/info.c @@ -22611,7 +22611,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING, // flags + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, From 08ddc324c307d8f75274b10db30a03bb8b697c3c Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 06:11:13 -0700 Subject: [PATCH 63/97] Fix potential ADDBOOST macro footgun --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index a428d4936..57df9242b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3051,7 +3051,7 @@ static void K_GetKartBoostPower(player_t *player) speedboost += FixedDiv(s, FRACUNIT + (metabolism * (numboosts-1))); \ accelboost += FixedDiv(a, FRACUNIT + (metabolism * (numboosts-1))); \ if (player->aizdriftstrat) \ - handleboost += FixedDiv(3*h/2, FRACUNIT + (metabolism * (numboosts-1))/4); \ + handleboost += FixedDiv((3*h)/2, FRACUNIT + (metabolism * (numboosts-1))/4); \ else \ handleboost = max(h, handleboost); \ } From 948dea83043b16520e9223638f5acd6041bfabba Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 06:17:34 -0700 Subject: [PATCH 64/97] Sliptide zip readability --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 57df9242b..931064b83 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4039,8 +4039,6 @@ void K_UpdateSliptideZipIndicator(player_t *player) mobj->angle = momentumAngle + ANGLE_90; P_SetScale(mobj, 3 * player->mo->scale / 2); - mobj->renderflags &= ~RF_DONTDRAW; - // No stored boost if (player->sliptideZip == 0) { @@ -4049,6 +4047,8 @@ void K_UpdateSliptideZipIndicator(player_t *player) return; } + mobj->renderflags &= ~RF_DONTDRAW; + UINT32 chargeFrame = 7 - min(7, player->sliptideZip / 10); UINT32 decayFrame = min(7, player->sliptideZipDelay / 5); if (max(chargeFrame, decayFrame) > mobj->frame) From 53b2922fbbc128319415a1f0f238c0693f97b6fe Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 05:10:58 -0800 Subject: [PATCH 65/97] Add the debugrender_highlight command, highlight specific rendering in flat colors Software mode only. The command is used like this: debugrender_highlight planes sprites debugrender_highlight pl spr debugrender_highlight none (Abbreviations work.) Supported rendering to flag: planes - sector floor/ceiling fofplanes - FOF top/bottom fofsides - FOF sides midtextures - pegged midtexture walls - sector upper/lower texture, one-sided linedefs sprites - sprites sky - skybox --- src/CMakeLists.txt | 1 + src/r_debug.cpp | 62 ++++++++++++++++++++++++++- src/r_debug_detail.hpp | 42 +++++++++++++++++++ src/r_debug_parser.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++ src/r_draw.c | 4 ++ src/r_draw.h | 9 ++++ src/r_draw8_flat.c | 79 +++++++++++++++++++++++++++++++++++ src/r_main.c | 2 + src/r_main.h | 17 ++++++++ src/r_plane.c | 29 +++++++++---- src/r_segs.c | 13 ++++++ src/r_things.c | 6 +++ src/screen.c | 31 ++++++++++++-- src/screen.h | 1 + 14 files changed, 380 insertions(+), 11 deletions(-) create mode 100644 src/r_debug_detail.hpp create mode 100644 src/r_debug_parser.cpp create mode 100644 src/r_draw8_flat.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e6233429..2817abb01 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,6 +65,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 r_bsp.c r_data.c r_debug.cpp + r_debug_parser.cpp r_draw.c r_fps.c r_main.c diff --git a/src/r_debug.cpp b/src/r_debug.cpp index d090d230e..e12eb33b1 100644 --- a/src/r_debug.cpp +++ b/src/r_debug.cpp @@ -12,11 +12,15 @@ #include // std::clamp #include "cxxutil.hpp" +#include "r_debug_detail.hpp" #include "command.h" #include "m_fixed.h" +#include "r_draw.h" #include "r_main.h" +using namespace srb2::r_debug; + namespace { @@ -27,12 +31,68 @@ CV_PossibleValue_t contrast_cons_t[] = {{-FRACUNIT, "MIN"}, {FRACUNIT, "MAX"}, { consvar_t cv_debugrender_contrast = CVAR_INIT("debugrender_contrast", "0.0", CV_CHEAT | CV_FLOAT, contrast_cons_t, nullptr); +UINT32 debugrender_highlight; + +void R_CheckDebugHighlight(debugrender_highlight_t k) +{ + // If highlighting is enabled for anything, surfaces + // must be highlighted in one of two colors, depending on + // whether they fall under focus of the debug. + + if (debugrender_highlight) + { + r8_flatcolor = (debugrender_highlight & (1 << k)) ? detail::kHighlightOptions[k].color : 0x1F; + } +} + INT32 R_AdjustLightLevel(INT32 light) { constexpr fixed_t kRange = (LIGHTLEVELS - 1) * FRACUNIT; const fixed_t adjust = FixedMul(cv_debugrender_contrast.value, kRange); - light = std::clamp((light * FRACUNIT) - adjust, 0, kRange); + if (debugrender_highlight) + { + light = (kRange / 2) - (adjust / 2); + + SRB2_ASSERT(light >= 0); + SRB2_ASSERT(light <= kRange); + } + else + { + light = std::clamp((light * FRACUNIT) - adjust, 0, kRange); + } return light / FRACUNIT; } + +void Command_Debugrender_highlight(void) +{ + const bool changes = COM_Argc() > 1; + + if (!CV_CheatsEnabled()) + { + CONS_Printf("Cheats must be enabled.\n"); + return; + } + + if (changes) + { + const char* arg = COM_Argv(1); + + debugrender_highlight = 0; // always reset + + if (COM_Argc() > 2 || + // approximate match "none" + strncasecmp(arg, "none", strlen(arg))) + { + char* p = COM_Args(); + + while (p) + { + p = detail::parse_highlight_arg(p); + } + } + } + + detail::highlight_help(changes); +} diff --git a/src/r_debug_detail.hpp b/src/r_debug_detail.hpp new file mode 100644 index 000000000..7d495db47 --- /dev/null +++ b/src/r_debug_detail.hpp @@ -0,0 +1,42 @@ +// 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. +//----------------------------------------------------------------------------- +/// \file r_debug_detail.cpp +/// \brief Software renderer debugging, internal header + +#ifndef __R_DEBUG_DETAIL__ +#define __R_DEBUG_DETAIL__ + +#include "r_main.h" + +namespace srb2::r_debug::detail +{ + +struct HighlightDesc +{ + uint8_t color; + const char* label; + const char* description; +}; + +constexpr HighlightDesc kHighlightOptions[NUM_SW_HI] = { + {0x96, "planes", "Sector floor/ceiling"}, + {0x49, "fofplanes", "FOF top/bottom"}, + {0xB6, "fofsides", "FOF sides"}, + {0x7A, "midtextures", "Two-sided midtexture"}, + {0xC9, "walls", "Sector upper/lower texture, one-sided midtexture"}, + {0x23, "sprites", "Sprites"}, + {0x0F, "sky", "Sky texture"}}; + +char* skip_alnum(char* p, int mode); +char* parse_highlight_arg(char* p); +void highlight_help(bool only_on); + +}; // srb2::r_debug::detail + +#endif // __R_DEBUG_DETAIL__ diff --git a/src/r_debug_parser.cpp b/src/r_debug_parser.cpp new file mode 100644 index 000000000..8d1d30080 --- /dev/null +++ b/src/r_debug_parser.cpp @@ -0,0 +1,95 @@ +// 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. +//----------------------------------------------------------------------------- +/// \file r_debug_parser.cpp +/// \brief Helper functions for the debugrender_highlight command + +#include "r_debug_detail.hpp" + +#include "doomdef.h" +#include "r_main.h" + +using namespace srb2::r_debug; +using namespace srb2::r_debug::detail; + +char* detail::skip_alnum(char* p, int mode) +{ + while (*p != '\0' && !isalnum(*p) == !mode) + { + p++; + } + + return p; +} + +char* detail::parse_highlight_arg(char* p) +{ + INT32 k; + const HighlightDesc* key; + + const auto old = static_cast(debugrender_highlight); + + char* t; + int c; + + p = skip_alnum(p, 0); // skip "whitespace" + + if (*p == '\0') + { + return NULL; + } + + t = skip_alnum(p, 1); // find end of word + + c = *t; // save to restore afterward + *t = '\0'; // isolate word string + + for (k = 0; (key = &kHighlightOptions[k]), k < NUM_SW_HI; ++k) + { + // allow an approximate match + if (!strncasecmp(p, key->label, (t - p))) + { + debugrender_highlight |= (1 << k); + // keep going to match multiple with same + // prefix + } + } + + if (debugrender_highlight == old) + { + // no change? Foolish user + CONS_Alert(CONS_WARNING, "\"%s\" makes no sense\n", p); + } + + *t = c; // restore + + return t; // skip to end of word +} + +void detail::highlight_help(bool only_on) +{ + int32_t k; + const HighlightDesc* key; + + for (k = 0; (key = &kHighlightOptions[k]), k < NUM_SW_HI; ++k) + { + const bool on = (debugrender_highlight & (1 << k)) != 0; + + if (!only_on || on) + { + CONS_Printf("%s\x80 \x87%s\x80 - %s\n", on ? "\x83 ON" : "\x85OFF", key->label, key->description); + } + } + + if (!only_on) + { + CONS_Printf("\nYou can change the highlights by using a command like:\n\n" + "\x87 debugrender_highlight planes sprites\n" + "\x87 debugrender_highlight none\n"); + } +} diff --git a/src/r_draw.c b/src/r_draw.c index 6eb5d6cfb..44959c9d7 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -68,6 +68,8 @@ INT32 columnofs[MAXVIDWIDTH*4]; UINT8 *topleft; +UINT8 r8_flatcolor; + // ========================================================================= // COLUMN DRAWING CODE STUFF // ========================================================================= @@ -81,6 +83,7 @@ UINT8 dc_hires; // under MSVC boolean is a byte, while on other systems, it a bi // soo lets make it a byte on all system for the ASM code UINT8 *dc_source; UINT8 *dc_brightmap; +UINT8 *dc_lightmap; // ----------------------- // translucency stuff here @@ -638,6 +641,7 @@ void R_DrawViewBorder(void) #include "r_draw8.c" #include "r_draw8_npo2.c" +#include "r_draw8_flat.c" // ========================================================================== // INCLUDE 16bpp DRAWING CODE HERE diff --git a/src/r_draw.h b/src/r_draw.h index beb19b739..200ad5183 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -30,6 +30,7 @@ extern UINT8 *ylookup3[MAXVIDHEIGHT*4]; extern UINT8 *ylookup4[MAXVIDHEIGHT*4]; extern INT32 columnofs[MAXVIDWIDTH*4]; extern UINT8 *topleft; +extern UINT8 r8_flatcolor; // ------------------------- // COLUMN DRAWING CODE STUFF @@ -43,6 +44,7 @@ extern UINT8 dc_hires; extern UINT8 *dc_source; // first pixel in a column extern UINT8 *dc_brightmap; // brightmap texture column, can be NULL +extern UINT8 *dc_lightmap; // lighting only // translucency stuff here extern UINT8 *dc_transmap; @@ -76,6 +78,8 @@ extern UINT8 *ds_source; extern UINT8 *ds_brightmap; extern UINT8 *ds_transmap; +extern UINT8 ds_flatcolor; + struct floatv3_t { float x, y, z; }; @@ -232,6 +236,11 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void); void R_DrawTranslucentWaterSpan_NPO2_8(void); void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void); +// Debugging - highlight surfaces in flat colors +void R_DrawColumn_Flat_8(void); +void R_DrawSpan_Flat_8(void); +void R_DrawTiltedSpan_Flat_8(void); + #ifdef USEASM void ASMCALL R_DrawColumn_8_ASM(void); void ASMCALL R_DrawShadeColumn_8_ASM(void); diff --git a/src/r_draw8_flat.c b/src/r_draw8_flat.c new file mode 100644 index 000000000..20372d2c4 --- /dev/null +++ b/src/r_draw8_flat.c @@ -0,0 +1,79 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 2023 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_draw8_flat.c +/// \brief 8bpp span/column drawer functions for debugging (draws in flat colors only) +/// \note no includes because this is included as part of r_draw.c + +void R_DrawColumn_Flat_8 (void) +{ + INT32 count; + UINT8 color = dc_lightmap[r8_flatcolor]; + register UINT8 *dest; + + count = dc_yh - dc_yl; + + if (count < 0) // Zero length, column does not exceed a pixel. + return; + +#ifdef RANGECHECK + if ((unsigned)dc_x >= (unsigned)vid.width || dc_yl < 0 || dc_yh >= vid.height) + return; +#endif + + // Framebuffer destination address. + // Use ylookup LUT to avoid multiply with ScreenWidth. + // Use columnofs LUT for subwindows? + + //dest = ylookup[dc_yl] + columnofs[dc_x]; + dest = &topleft[dc_yl*vid.width + dc_x]; + + count++; + + do + { + *dest = color; + dest += vid.width; + } while (--count); +} + +void R_DrawSpan_Flat_8 (void) +{ + UINT8 *dest = ylookup[ds_y] + columnofs[ds_x1]; + + memset(dest, ds_colormap[r8_flatcolor], (ds_x2 - ds_x1) + 1); +} + +void R_DrawTiltedSpan_Flat_8 (void) +{ + // x1, x2 = ds_x1, ds_x2 + int width = ds_x2 - ds_x1; + double iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); + + UINT8 *dest = ylookup[ds_y]; + + // Lighting is simple. It's just linear interpolation from start to end + { + float planelightfloat = PLANELIGHTFLOAT; + float lightstart, lightend; + + lightend = (iz + ds_szp->x*width) * planelightfloat; + lightstart = iz * planelightfloat; + + R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend)); + //CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf); + } + + while (ds_x1 <= ds_x2) + { + dest[ds_x1] = planezlight[tiltlighting[ds_x1]][r8_flatcolor]; + ds_x1++; + } +} diff --git a/src/r_main.c b/src/r_main.c index 1e5e7a430..e380b7d28 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1660,4 +1660,6 @@ void R_RegisterEngineStuff(void) // debugging CV_RegisterVar(&cv_debugrender_contrast); + + COM_AddCommand("debugrender_highlight", Command_Debugrender_highlight); } diff --git a/src/r_main.h b/src/r_main.h index df17342a2..07bda7e02 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -125,8 +125,25 @@ extern consvar_t cv_drawpickups; // debugging +typedef enum { + SW_HI_PLANES, + SW_HI_FOFPLANES, + SW_HI_FOFSIDES, + SW_HI_MIDTEXTURES, + SW_HI_WALLS, + SW_HI_THINGS, + SW_HI_SKY, + + NUM_SW_HI +} debugrender_highlight_t; + +extern UINT32 debugrender_highlight; + +void R_CheckDebugHighlight(debugrender_highlight_t type); INT32 R_AdjustLightLevel(INT32 light); +void Command_Debugrender_highlight(void); + extern consvar_t cv_debugrender_contrast; diff --git a/src/r_plane.c b/src/r_plane.c index 4638a4081..be4ecd02d 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -208,14 +208,17 @@ static void R_MapPlane(INT32 y, INT32 x1, INT32 x2) pindex = MAXLIGHTZ - 1; ds_colormap = planezlight[pindex]; - if (currentplane->extra_colormap) - ds_colormap = currentplane->extra_colormap->colormap + (ds_colormap - colormaps); - - ds_fullbright = colormaps; - if (encoremap && !currentplane->noencore) + if (!debugrender_highlight) { - ds_colormap += COLORMAP_REMAPOFFSET; - ds_fullbright += COLORMAP_REMAPOFFSET; + if (currentplane->extra_colormap) + ds_colormap = currentplane->extra_colormap->colormap + (ds_colormap - colormaps); + + ds_fullbright = colormaps; + if (encoremap && !currentplane->noencore) + { + ds_colormap += COLORMAP_REMAPOFFSET; + ds_fullbright += COLORMAP_REMAPOFFSET; + } } ds_y = y; @@ -613,6 +616,8 @@ static void R_DrawSkyPlane(visplane_t *pl) INT32 x; INT32 angle; + R_CheckDebugHighlight(SW_HI_SKY); + // Reset column drawer function (note: couldn't we just call walldrawerfunc directly?) // (that is, unless we'll need to switch drawers in future for some reason) R_SetColumnFunc(BASEDRAWFUNC, false); @@ -631,6 +636,7 @@ static void R_DrawSkyPlane(visplane_t *pl) dc_colormap += COLORMAP_REMAPOFFSET; dc_fullbright += COLORMAP_REMAPOFFSET; } + dc_lightmap = colormaps; dc_texturemid = skytexturemid; dc_texheight = textureheight[skytexture] >>FRACBITS; @@ -831,6 +837,7 @@ void R_DrawSinglePlane(visplane_t *pl) INT32 x, stop; ffloor_t *rover; INT32 type, spanfunctype = BASEDRAWFUNC; + debugrender_highlight_t debug = 0; void (*mapfunc)(INT32, INT32, INT32) = R_MapPlane; if (!(pl->minx <= pl->maxx)) @@ -911,10 +918,16 @@ void R_DrawSinglePlane(visplane_t *pl) light = (pl->lightlevel >> LIGHTSEGSHIFT); } else light = (pl->lightlevel >> LIGHTSEGSHIFT); + + debug = SW_HI_FOFPLANES; } else + { light = (pl->lightlevel >> LIGHTSEGSHIFT); + debug = SW_HI_PLANES; + } + #ifndef NOWATER if (pl->ripple) { @@ -1085,6 +1098,8 @@ void R_DrawSinglePlane(visplane_t *pl) } + R_CheckDebugHighlight(debug); + // Use the correct span drawer depending on the powers-of-twoness R_SetSpanFunc(spanfunctype, !ds_powersoftwo, ds_brightmap != NULL); diff --git a/src/r_segs.c b/src/r_segs.c index b7a25daa0..0dee4fae3 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -185,6 +185,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) transtable = 0; } + R_CheckDebugHighlight(SW_HI_MIDTEXTURES); + if (blendmode == AST_FOG) { R_SetColumnFunc(COLDRAWFUNC_FOG, bmnum != 0); @@ -440,6 +442,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) if (height <= windowtop) { dc_colormap = rlight->rcolormap; + dc_lightmap = xwalllights[pindex]; dc_fullbright = colormaps; if (encoremap && !(ldef->flags & ML_TFERLINE)) { @@ -465,6 +468,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) colfunc_2s(col, bmCol, -1); windowtop = windowbottom + 1; dc_colormap = rlight->rcolormap; + dc_lightmap = xwalllights[pindex]; dc_fullbright = colormaps; if (encoremap && !(ldef->flags & ML_TFERLINE)) { @@ -487,6 +491,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) pindex = MAXLIGHTSCALE - 1; dc_colormap = walllights[pindex]; + dc_lightmap = walllights[pindex]; dc_fullbright = colormaps; if (encoremap && !(ldef->flags & ML_TFERLINE)) { @@ -642,6 +647,8 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) texnum = R_GetTextureNum(sides[pfloor->master->sidenum[0]].midtexture); bmnum = R_GetTextureBrightmap(texnum); + R_CheckDebugHighlight(SW_HI_FOFSIDES); + R_SetColumnFunc(BASEDRAWFUNC, bmnum != 0); if (pfloor->master->flags & ML_TFERLINE) @@ -1033,6 +1040,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) if (lighteffect) { dc_colormap = rlight->rcolormap; + dc_lightmap = xwalllights[pindex]; dc_fullbright = colormaps; if (encoremap && !(curline->linedef->flags & ML_TFERLINE)) { @@ -1069,6 +1077,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) if (lighteffect) { dc_colormap = rlight->rcolormap; + dc_lightmap = xwalllights[pindex]; dc_fullbright = colormaps; if (encoremap && !(curline->linedef->flags & ML_TFERLINE)) { @@ -1093,6 +1102,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) pindex = MAXLIGHTSCALE - 1; dc_colormap = walllights[pindex]; + dc_lightmap = walllights[pindex]; dc_fullbright = colormaps; if (encoremap && !(curline->linedef->flags & ML_TFERLINE)) @@ -1359,6 +1369,7 @@ static void R_RenderSegLoop (void) pindex = MAXLIGHTSCALE-1; dc_colormap = walllights[pindex]; + dc_lightmap = walllights[pindex]; dc_fullbright = colormaps; if (encoremap && !(curline->linedef->flags & ML_TFERLINE)) { @@ -1626,6 +1637,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) memset(&segleft, 0x00, sizeof(segleft)); memset(&segright, 0x00, sizeof(segright)); + R_CheckDebugHighlight(SW_HI_WALLS); + R_SetColumnFunc(BASEDRAWFUNC, false); if (ds_p == drawsegs+maxdrawsegs) diff --git a/src/r_things.c b/src/r_things.c index 92f9148ee..a1e32c3b2 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -939,6 +939,8 @@ static void R_DrawVisSprite(vissprite_t *vis) if (!dc_colormap) dc_colormap = colormaps; + dc_lightmap = colormaps; + dc_fullbright = colormaps; if (encoremap && !vis->mobj->color && !(vis->mobj->flags & MF_DONTENCOREMAP)) @@ -1143,6 +1145,8 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis) dc_fullbright += COLORMAP_REMAPOFFSET; } + dc_lightmap = colormaps; + dc_iscale = FixedDiv(FRACUNIT, vis->scale); dc_texturemid = FixedDiv(vis->texturemid, this_scale); dc_texheight = 0; @@ -3210,6 +3214,8 @@ static void R_DrawSprite(vissprite_t *spr) mfloorclip = spr->clipbot; mceilingclip = spr->cliptop; + R_CheckDebugHighlight(SW_HI_THINGS); + if (spr->cut & SC_BBOX) R_DrawThingBoundingBox(spr); else if (spr->cut & SC_SPLAT) diff --git a/src/screen.c b/src/screen.c index 2b78d03f1..90ccd4bac 100644 --- a/src/screen.c +++ b/src/screen.c @@ -64,6 +64,7 @@ void (*spanfuncs_npo2[SPANDRAWFUNC_MAX])(void); #ifdef USE_COL_SPAN_ASM void (*spanfuncs_asm[SPANDRAWFUNC_MAX])(void); #endif +void (*spanfuncs_flat[SPANDRAWFUNC_MAX])(void); // ------------------ // global video state @@ -170,6 +171,22 @@ void SCR_SetDrawFuncs(void) spanfuncs_npo2[SPANDRAWFUNC_TILTEDWATER] = R_DrawTiltedTranslucentWaterSpan_NPO2_8; spanfuncs_npo2[SPANDRAWFUNC_FOG] = NULL; // Not needed + // Debugging - highlight surfaces in flat colors + spanfuncs_flat[BASEDRAWFUNC] = R_DrawSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_TRANS] = R_DrawSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_TILTED] = R_DrawTiltedSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_TILTEDTRANS] = R_DrawTiltedSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_SPLAT] = R_DrawSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_TRANSSPLAT] = R_DrawSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_TILTEDSPLAT] = R_DrawTiltedSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_SPRITE] = R_DrawSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_TRANSSPRITE] = R_DrawSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_TILTEDSPRITE] = R_DrawTiltedSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_TILTEDTRANSSPRITE] = R_DrawTiltedSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_WATER] = R_DrawSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_TILTEDWATER] = R_DrawTiltedSpan_Flat_8; + spanfuncs_flat[SPANDRAWFUNC_FOG] = R_DrawSpan_Flat_8; // Not needed + #if (defined(RUSEASM) && defined(USE_COL_SPAN_ASM)) if (R_ASM) { @@ -220,13 +237,17 @@ void R_SetColumnFunc(size_t id, boolean brightmapped) colfunctype = id; + if (debugrender_highlight != 0) + { + colfunc = R_DrawColumn_Flat_8; + } #ifdef USE_COL_SPAN_ASM - if (colfuncs_asm[id] != NULL && brightmapped == false) + else if (colfuncs_asm[id] != NULL && brightmapped == false) { colfunc = colfuncs_asm[id]; } - else #endif + else { colfunc = colfuncs[id]; } @@ -236,7 +257,11 @@ void R_SetSpanFunc(size_t id, boolean npo2, boolean brightmapped) { I_Assert(id < SPANDRAWFUNC_MAX); - if (spanfuncs_npo2[id] != NULL && npo2 == true) + if (spanfuncs_flat[id] != NULL && debugrender_highlight != 0) + { + spanfunc = spanfuncs_flat[id]; + } + else if (spanfuncs_npo2[id] != NULL && npo2 == true) { spanfunc = spanfuncs_npo2[id]; } diff --git a/src/screen.h b/src/screen.h index f76fe05a3..e9cfc7d59 100644 --- a/src/screen.h +++ b/src/screen.h @@ -179,6 +179,7 @@ extern void (*spanfuncs_npo2[SPANDRAWFUNC_MAX])(void); #ifdef USE_COL_SPAN_ASM extern void (*spanfuncs_asm[SPANDRAWFUNC_MAX])(void); #endif +extern void (*spanfuncs_flat[SPANDRAWFUNC_MAX])(void); // ----- // CPUID From 15a0eb6c48547534239fe0f384d5cee56ba01b87 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 06:26:21 -0700 Subject: [PATCH 66/97] Add sliptideZipIndicator to netsave --- src/p_saveg.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index 49d31a230..c420646b4 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -67,6 +67,7 @@ typedef enum SKYBOXCENTER = 0x10, HOVERHYUDORO = 0x20, STUMBLE = 0x40, + SLIPTIDEZIP = 0x80 } player_saveflags; static inline void P_ArchivePlayer(savebuffer_t *save) @@ -218,6 +219,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (players[i].stumbleIndicator) flags |= STUMBLE; + if (players[i].sliptideZipIndicator) + flags |= SLIPTIDEZIP; + WRITEUINT16(save->p, flags); if (flags & SKYBOXVIEW) @@ -238,6 +242,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (flags & STUMBLE) WRITEUINT32(save->p, players[i].stumbleIndicator->mobjnum); + if (flags & SLIPTIDEZIP) + WRITEUINT32(save->p, players[i].sliptideZipIndicator->mobjnum); + WRITEUINT32(save->p, (UINT32)players[i].followitem); WRITEUINT32(save->p, players[i].charflags); @@ -611,6 +618,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) if (flags & STUMBLE) players[i].stumbleIndicator = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & SLIPTIDEZIP) + players[i].sliptideZipIndicator = (mobj_t *)(size_t)READUINT32(save->p); + players[i].followitem = (mobjtype_t)READUINT32(save->p); //SetPlayerSkinByNum(i, players[i].skin); @@ -4754,6 +4764,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&players[i].stumbleIndicator, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "stumbleIndicator not found on player %d\n", i); } + if (players[i].sliptideZipIndicator) + { + temp = (UINT32)(size_t)players[i].sliptideZipIndicator; + players[i].sliptideZipIndicator = NULL; + if (!P_SetTarget(&players[i].sliptideZipIndicator, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "sliptideZipIndicator not found on player %d\n", i); + } } } From aed9fe996c9c577b139b18e11e804faeff4f2500 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 05:14:57 -0800 Subject: [PATCH 67/97] Add cv_debugrender_spriteclip, toggle sprite clipping This cvar can be used to reveal which sprites are technically drawn but clipped completely by level geometry. --- src/r_debug.cpp | 2 ++ src/r_main.c | 1 + src/r_main.h | 3 ++- src/r_things.c | 20 ++++++++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/r_debug.cpp b/src/r_debug.cpp index e12eb33b1..570cc3754 100644 --- a/src/r_debug.cpp +++ b/src/r_debug.cpp @@ -31,6 +31,8 @@ CV_PossibleValue_t contrast_cons_t[] = {{-FRACUNIT, "MIN"}, {FRACUNIT, "MAX"}, { consvar_t cv_debugrender_contrast = CVAR_INIT("debugrender_contrast", "0.0", CV_CHEAT | CV_FLOAT, contrast_cons_t, nullptr); +consvar_t cv_debugrender_spriteclip = CVAR_INIT("debugrender_spriteclip", "Off", CV_CHEAT, CV_OnOff, nullptr); + UINT32 debugrender_highlight; void R_CheckDebugHighlight(debugrender_highlight_t k) diff --git a/src/r_main.c b/src/r_main.c index e380b7d28..bf5fd78fe 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1660,6 +1660,7 @@ void R_RegisterEngineStuff(void) // debugging CV_RegisterVar(&cv_debugrender_contrast); + CV_RegisterVar(&cv_debugrender_spriteclip); COM_AddCommand("debugrender_highlight", Command_Debugrender_highlight); } diff --git a/src/r_main.h b/src/r_main.h index 07bda7e02..208d846a9 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -145,7 +145,8 @@ INT32 R_AdjustLightLevel(INT32 light); void Command_Debugrender_highlight(void); extern consvar_t - cv_debugrender_contrast; + cv_debugrender_contrast, + cv_debugrender_spriteclip; // Called by startup code. void R_Init(void); diff --git a/src/r_things.c b/src/r_things.c index a1e32c3b2..5f29920a5 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2982,6 +2982,16 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps for (r2 = head->next; r2 != head; r2 = r2->next) { + if (cv_debugrender_spriteclip.value) + { + // Only sort behind other sprites; sorts in + // front of everything else. + if (!r2->sprite) + { + continue; + } + } + if (r2->plane) { fixed_t planeobjectz, planecameraz; @@ -3244,6 +3254,16 @@ void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* portal) fixed_t lowscale; INT32 silhouette; + if (cv_debugrender_spriteclip.value) + { + for (x = x1; x <= x2; x++) + { + spr->clipbot[x] = (INT16)viewheight; + spr->cliptop[x] = (INT16)con_clipviewtop; + } + return; + } + for (x = x1; x <= x2; x++) { spr->clipbot[x] = spr->cliptop[x] = CLIP_UNDEF; From f71ba63cd3f1217a2b2f7112f270f96bea4e5680 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 05:56:00 -0800 Subject: [PATCH 68/97] Add RF_ALWAYSONTOP, renders sprite through walls Specifically, it avoids sorting or clipping the sprite against stuff like FOF planes, which are notorious for messing up sprites. Useful for debug stuff which absolutely must be visible. --- src/deh_tables.c | 1 + src/r_defs.h | 2 ++ src/r_things.c | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index de88639e8..14d4696f8 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6303,6 +6303,7 @@ struct int_const_s const INT_CONST[] = { {"RF_FULLDARK",RF_FULLDARK}, {"RF_SEMIBRIGHT",RF_SEMIBRIGHT}, {"RF_NOCOLORMAPS",RF_NOCOLORMAPS}, + {"RF_ALWAYSONTOP",RF_ALWAYSONTOP}, {"RF_SPRITETYPEMASK",RF_SPRITETYPEMASK}, {"RF_PAPERSPRITE",RF_PAPERSPRITE}, {"RF_FLOORSPRITE",RF_FLOORSPRITE}, diff --git a/src/r_defs.h b/src/r_defs.h index d4606c8ad..4e947616b 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -932,6 +932,8 @@ typedef enum RF_NOCOLORMAPS = 0x00000400, // Sprite is not drawn with colormaps + RF_ALWAYSONTOP = 0x00000800, // Sprite is drawn on top of level geometry + RF_SPRITETYPEMASK = 0x00003000, // --Different sprite types RF_PAPERSPRITE = 0x00001000, // Paper sprite RF_FLOORSPRITE = 0x00002000, // Floor sprite diff --git a/src/r_things.c b/src/r_things.c index 5f29920a5..458eaac4e 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2975,6 +2975,9 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps for (rover = vsprsortedhead.prev; rover != &vsprsortedhead; rover = rover->prev) { + const boolean alwaysontop = cv_debugrender_spriteclip.value || (rover->renderflags & RF_ALWAYSONTOP); + const INT32 ontopflag = cv_debugrender_spriteclip.value ? 0 : (rover->renderflags & RF_ALWAYSONTOP); + if (rover->szt > vid.height || rover->sz < 0) continue; @@ -2982,7 +2985,7 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps for (r2 = head->next; r2 != head; r2 = r2->next) { - if (cv_debugrender_spriteclip.value) + if (alwaysontop) { // Only sort behind other sprites; sorts in // front of everything else. @@ -2990,6 +2993,15 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps { continue; } + + // Only sort behind other RF_ALWAYSONTOP sprites. + // This avoids sorting behind a sprite that is + // behind level geometry and thus sorting this + // one behind level geometry too. + if (r2->sprite->renderflags ^ ontopflag) + { + continue; + } } if (r2->plane) @@ -3254,7 +3266,7 @@ void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* portal) fixed_t lowscale; INT32 silhouette; - if (cv_debugrender_spriteclip.value) + if ((spr->renderflags & RF_ALWAYSONTOP) || cv_debugrender_spriteclip.value) { for (x = x1; x <= x2; x++) { From 99362bc9b283d0b6a5cdf31d8dae1be42572a9dc Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 05:58:07 -0800 Subject: [PATCH 69/97] Give debugwaypoints orb and radius RF_ALWAYSONTOP Makes the radius always visible in full. --- src/k_waypoint.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index cf70c1dcd..27465856a 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -578,7 +578,7 @@ static void K_DebugWaypointDrawRadius(waypoint_t *const waypoint) spawnX = waypointmobj->x; spawnY = waypointmobj->y; - spawnZ = waypointmobj->z + 16*mapobjectscale; + spawnZ = waypointmobj->z; radiusOrb = P_SpawnMobj(spawnX, spawnY, spawnZ, MT_SPARK); @@ -588,6 +588,7 @@ static void K_DebugWaypointDrawRadius(waypoint_t *const waypoint) radiusOrb->frame &= ~FF_TRANSMASK; radiusOrb->frame |= FF_FULLBRIGHT; radiusOrb->color = SKINCOLOR_PURPLE; + radiusOrb->renderflags |= RF_ALWAYSONTOP; radiusOrb->destscale = FixedDiv(waypointmobj->radius, spriteRadius); P_SetScale(radiusOrb, radiusOrb->destscale); @@ -627,6 +628,7 @@ void K_DebugWaypointsVisualise(void) debugmobj->frame &= ~FF_TRANSMASK; debugmobj->frame |= FF_FULLBRIGHT; //FF_TRANS20 + debugmobj->renderflags |= RF_ALWAYSONTOP; // There's a waypoint setup for this mobj! So draw that it's a valid waypoint and draw lines to its connections if (waypoint != NULL) From c1fabe8e355cdbac5b433cf8cabacfd8add35f86 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 06:22:47 -0800 Subject: [PATCH 70/97] Make debugwaypoints radius reverse subtractive --- src/k_waypoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 27465856a..ca4746357 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -586,7 +586,7 @@ static void K_DebugWaypointDrawRadius(waypoint_t *const waypoint) radiusOrb->tics = 1; radiusOrb->frame &= ~FF_TRANSMASK; - radiusOrb->frame |= FF_FULLBRIGHT; + radiusOrb->frame |= FF_FULLBRIGHT|FF_REVERSESUBTRACT; radiusOrb->color = SKINCOLOR_PURPLE; radiusOrb->renderflags |= RF_ALWAYSONTOP; From 154cc7718667fa546f85005626d920dbd22ee723 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 07:05:42 -0800 Subject: [PATCH 71/97] Return quickly from R_AdjustLightLevel if possible --- src/r_debug.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/r_debug.cpp b/src/r_debug.cpp index 570cc3754..8fbe9390d 100644 --- a/src/r_debug.cpp +++ b/src/r_debug.cpp @@ -49,6 +49,11 @@ void R_CheckDebugHighlight(debugrender_highlight_t k) INT32 R_AdjustLightLevel(INT32 light) { + if (!debugrender_highlight && cv_debugrender_contrast.value == 0) + { + return light; + } + constexpr fixed_t kRange = (LIGHTLEVELS - 1) * FRACUNIT; const fixed_t adjust = FixedMul(cv_debugrender_contrast.value, kRange); From 20dedb6602ed952a87c382b39aca311e2d7a3428 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Mar 2023 18:24:51 -0500 Subject: [PATCH 72/97] Try to fix the podium bot spawning (again) - Podium waypoint initialize is done always directly after spawning the player, instead of trying to detect it in K_UpdatePodiumWaypoints. - Position is manually calculated for K_InitializePodiumWaypoint, instead of needing K_UpdateAllPlayerPositions to be called. - Instead of calling K_UpdateAllPlayerPositions every time a player spawns, do it at the end of the spawning loops. - Prioritize spawning bots in further away spots instead of 1st available, as spots 1st to 3rd spots are more important. --- src/g_game.c | 50 ++++++------------ src/k_kart.c | 53 ------------------- src/k_podium.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++ src/k_podium.h | 50 ++++++++++++++++++ src/p_mobj.c | 5 +- src/p_setup.c | 2 + 6 files changed, 205 insertions(+), 89 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 1e2a3d757..5468cf505 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2140,10 +2140,20 @@ void G_Ticker(boolean run) // do player reborns if needed if (G_GamestateUsesLevel() == true) { + boolean changed = false; + for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && players[i].playerstate == PST_REBORN) + { G_DoReborn(i); + changed = true; + } + } + + if (changed == true) + { + K_UpdateAllPlayerPositions(); } } @@ -2937,40 +2947,8 @@ mapthing_t *G_FindPodiumStart(INT32 playernum) if (numcoopstarts) { + UINT8 pos = K_GetPodiumPosition(&players[playernum]) - 1; UINT8 i; - UINT8 pos = 0; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - { - continue; - } - - if (i == playernum) - { - continue; - } - - if (players[i].score > players[playernum].score) - { - // Final score is the important part. - pos++; - } - else if (players[i].score == players[playernum].score) - { - if (players[i].bot == false && players[playernum].bot == true) - { - // Bots are never as important as players. - pos++; - } - else if (i < playernum) - { - // Port priority is the final tie breaker. - pos++; - } - } - } if (G_CheckSpot(playernum, playerstarts[pos % numcoopstarts])) { @@ -2980,8 +2958,10 @@ mapthing_t *G_FindPodiumStart(INT32 playernum) // Your spot isn't available? Find whatever you can get first. for (i = 0; i < numcoopstarts; i++) { - if (G_CheckSpot(playernum, playerstarts[i])) - return playerstarts[i]; + if (G_CheckSpot(playernum, playerstarts[(pos + i) % numcoopstarts])) + { + return playerstarts[(pos + i) % numcoopstarts]; + } } if (doprints) diff --git a/src/k_kart.c b/src/k_kart.c index b1035cfc5..6b1a54739 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8498,59 +8498,6 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) return bestwaypoint; } -static void K_UpdatePodiumWaypoint(player_t *const player, waypoint_t *const waypoint) -{ - // Set the new waypoint. - player->currentwaypoint = waypoint; - - if ((waypoint == NULL) - || (waypoint->nextwaypoints == NULL) - || (waypoint->numnextwaypoints == 0U)) - { - // No waypoint, or no next waypoint. - player->nextwaypoint = NULL; - return; - } - - // Simply use the first available next waypoint. - // No need for split paths in these cutscenes. - player->nextwaypoint = waypoint->nextwaypoints[0]; -} - -static void K_UpdatePodiumWaypoints(player_t *const player) -{ - if ((player != NULL) && (player->mo != NULL)) - { - if ((player->currentwaypoint == NULL) - && (player->position > 0 && player->position <= MAXPLAYERS) - && (leveltime <= introtime || player->jointime <= 1)) - { - // Initialize our first waypoint to the one that - // matches our position. - K_UpdatePodiumWaypoint(player, K_GetWaypointFromID(player->position)); - } - - if (player->currentwaypoint != NULL) - { - const fixed_t xydist = P_AproxDistance( - player->mo->x - player->currentwaypoint->mobj->x, - player->mo->y - player->currentwaypoint->mobj->y - ); - const fixed_t xyzdist = P_AproxDistance( - xydist, - player->mo->z - player->currentwaypoint->mobj->z - ); - //const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); - - if (xyzdist <= player->mo->radius + player->currentwaypoint->mobj->radius) - { - // Reached waypoint, go to the next waypoint. - K_UpdatePodiumWaypoint(player, player->nextwaypoint); - } - } - } -} - /*-------------------------------------------------- void K_UpdateDistanceFromFinishLine(player_t *const player) diff --git a/src/k_podium.c b/src/k_podium.c index 8570fb5cb..c7d5a4af1 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -62,6 +62,140 @@ boolean K_PodiumSequence(void) return (gamestate == GS_CEREMONY); } +/*-------------------------------------------------- + UINT8 K_GetPodiumPosition(player_t *player) + + See header file for description. +--------------------------------------------------*/ +UINT8 K_GetPodiumPosition(player_t *player) +{ + UINT8 position = 1; + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *other = NULL; + if (playeringame[i] == false) + { + continue; + } + + other = &players[i]; + if (other->spectator == true) + { + continue; + } + + if (other->score > player->score) + { + // Final score is the important part. + position++; + } + else if (other->score == player->score) + { + if (other->bot == false && player->bot == true) + { + // Bots are never as important as players. + position++; + } + else if (i < player - players) + { + // Port priority is the final tie breaker. + position++; + } + } + } + + return position; +} + +/*-------------------------------------------------- + static void K_SetPodiumWaypoint(player_t *const player, waypoint_t *const waypoint) + + Changes the player's current and next waypoints, for + use during the podium sequence. + + Input Arguments:- + player - The player to update the waypoints of. + waypoint - The new current waypoint. + + Return:- + None +--------------------------------------------------*/ +static void K_SetPodiumWaypoint(player_t *const player, waypoint_t *const waypoint) +{ + // Set the new waypoint. + player->currentwaypoint = waypoint; + + if ((waypoint == NULL) + || (waypoint->nextwaypoints == NULL) + || (waypoint->numnextwaypoints == 0U)) + { + // No waypoint, or no next waypoint. + player->nextwaypoint = NULL; + return; + } + + // Simply use the first available next waypoint. + // No need for split paths in these cutscenes. + player->nextwaypoint = waypoint->nextwaypoints[0]; +} + +/*-------------------------------------------------- + void K_InitializePodiumWaypoint(player_t *const player) + + See header file for description. +--------------------------------------------------*/ +void K_InitializePodiumWaypoint(player_t *const player) +{ + if ((player != NULL) && (player->mo != NULL)) + { + player->position = K_GetPodiumPosition(player); + + if (player->position > 0 && player->position <= MAXPLAYERS) + { + // Initialize our first waypoint to the one that + // matches our position. + K_SetPodiumWaypoint(player, K_GetWaypointFromID(player->position)); + } + else + { + // None does, so remove it if we happen to have one. + K_SetPodiumWaypoint(player, NULL); + } + } +} + +/*-------------------------------------------------- + void K_UpdatePodiumWaypoints(player_t *const player) + + See header file for description. +--------------------------------------------------*/ +void K_UpdatePodiumWaypoints(player_t *const player) +{ + if ((player != NULL) && (player->mo != NULL)) + { + if (player->currentwaypoint != NULL) + { + const fixed_t xydist = P_AproxDistance( + player->mo->x - player->currentwaypoint->mobj->x, + player->mo->y - player->currentwaypoint->mobj->y + ); + const fixed_t xyzdist = P_AproxDistance( + xydist, + player->mo->z - player->currentwaypoint->mobj->z + ); + //const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); + + if (xyzdist <= player->mo->radius + player->currentwaypoint->mobj->radius) + { + // Reached waypoint, go to the next waypoint. + K_SetPodiumWaypoint(player, player->nextwaypoint); + } + } + } +} + /*-------------------------------------------------- boolean K_StartCeremony(void) diff --git a/src/k_podium.h b/src/k_podium.h index 21b31f74d..08357dd02 100644 --- a/src/k_podium.h +++ b/src/k_podium.h @@ -37,6 +37,56 @@ extern "C" { boolean K_PodiumSequence(void); +/*-------------------------------------------------- + UINT8 K_GetPodiumPosition(player_t *player); + + Calculates what the player's position would + be at the final standings. + + Input Arguments:- + player - The player to do the calculation for. + + Return:- + The player's final position, as a number + between 1 and MAXPLAYERS. +--------------------------------------------------*/ + +UINT8 K_GetPodiumPosition(player_t *player); + + +/*-------------------------------------------------- + void K_InitializePodiumWaypoint(player_t *const player); + + Sets a bot's current waypoint to one matching + their final podium position. + + Input Arguments:- + player - The podium bot to update. + + Return:- + N/A +--------------------------------------------------*/ + +void K_InitializePodiumWaypoint(player_t *const player); + + +/*-------------------------------------------------- + void K_UpdatePodiumWaypoints(player_t *const player); + + Helps a bot move along a predetermined path by + updating their current and next waypoints as + they move. Intended for the podium sequence. + + Input Arguments:- + player - The podium bot to update. + + Return:- + N/A +--------------------------------------------------*/ + +void K_UpdatePodiumWaypoints(player_t *const player); + + /*-------------------------------------------------- boolean K_StartCeremony(void); diff --git a/src/p_mobj.c b/src/p_mobj.c index 0f7d7327b..21054e4d5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11954,7 +11954,10 @@ void P_AfterPlayerSpawn(INT32 playernum) if (CheckForReverseGravity) P_CheckGravity(mobj, false); - K_UpdateAllPlayerPositions(); + if (K_PodiumSequence() == true) + { + K_InitializePodiumWaypoint(p); + } } // spawn it at a playerspawn mapthing diff --git a/src/p_setup.c b/src/p_setup.c index 3b6dfc23f..77261176e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7373,6 +7373,8 @@ static void P_InitPlayers(void) G_SpawnPlayer(i); } } + + K_UpdateAllPlayerPositions(); } static void P_InitGametype(void) From 55de982fa380dc74329ee4a72c6682500981141a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 22 Feb 2023 04:31:15 -0500 Subject: [PATCH 73/97] GP ranking Needs balancing + intermission, but mostly functional --- src/d_player.h | 1 + src/g_game.c | 18 ++++++ src/k_battle.c | 1 + src/k_grandprix.c | 160 ++++++++++++++++++++++++++++++++++++++++++++-- src/k_grandprix.h | 54 ++++++++++++++++ src/k_hud.c | 35 ++++++++++ src/p_inter.c | 2 + src/p_saveg.c | 2 + src/p_setup.c | 1 + src/p_spec.c | 13 +++- src/p_user.c | 11 ++++ 11 files changed, 291 insertions(+), 7 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index f38076f6f..40bfcc95a 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -634,6 +634,7 @@ struct player_t tic_t realtime; // integer replacement for leveltime UINT8 laps; // Number of laps (optional) UINT8 latestlap; + UINT32 lapPoints; // Points given from laps INT32 starpostnum; // The number of the last starpost you hit UINT8 ctfteam; // 0 == Spectator, 1 == Red, 2 == Blue diff --git a/src/g_game.c b/src/g_game.c index 5468cf505..601834123 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2388,6 +2388,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT16 totalring; UINT8 laps; UINT8 latestlap; + UINT32 lapPoints; UINT16 skincolor; INT32 skin; UINT8 availabilities[MAXAVAILABILITY]; @@ -2497,6 +2498,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) nocontrol = 0; laps = 0; latestlap = 0; + lapPoints = 0; roundscore = 0; exiting = 0; khudfinish = 0; @@ -2532,6 +2534,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) laps = players[player].laps; latestlap = players[player].latestlap; + lapPoints = players[player].lapPoints; roundscore = players[player].roundscore; @@ -2604,6 +2607,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->laps = laps; p->latestlap = latestlap; + p->lapPoints = lapPoints; p->totalring = totalring; p->bot = bot; @@ -4123,6 +4127,8 @@ static void G_DoCompleted(void) G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; + UINT8 bestDifficulty = 0; + for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) @@ -4148,9 +4154,16 @@ static void G_DoCompleted(void) } G_PlayerFinishLevel(i); // take away cards and stuff + + if (players[i].bot) + { + bestDifficulty = max(bestDifficulty, players[i].botvars.difficulty); + } } } + gpRank.difficulty = bestDifficulty; + // See Y_StartIntermission timer handling if ((gametyperules & GTR_CIRCUIT) && ((multiplayer && demo.playback) || j == r_splitscreen+1) && (!K_CanChangeRules(false) || cv_inttime.value > 0)) // play some generic music if there's no win/cool/lose music going on (for exitlevel commands) @@ -5485,6 +5498,11 @@ boolean G_GetExitGameFlag(void) // Same deal with retrying. void G_SetRetryFlag(void) { + if (retrying == false) + { + gpRank.continuesUsed++; + } + retrying = true; } diff --git a/src/k_battle.c b/src/k_battle.c index b34c7a151..8e5db972b 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -780,6 +780,7 @@ void K_BattleInit(boolean singleplayercontext) P_SpawnMapThing(mt); } + gpRank.totalCapsules += maptargets; battlecapsules = true; } diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 6d879054b..c061d271f 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -21,6 +21,7 @@ #include "r_things.h" struct grandprixinfo grandprixinfo; +struct gpRank gpRank; /*-------------------------------------------------- UINT8 K_BotStartingDifficulty(SINT8 value) @@ -114,6 +115,86 @@ UINT8 K_BotDefaultSkin(void) return (UINT8)defaultbotskin; } +/*-------------------------------------------------- + static UINT8 K_GetGPPlayerCount(UINT8 humans) + + Counts the number of total players, + including humans and bots, to put into + a GP session. +--------------------------------------------------*/ +static UINT8 K_GetGPPlayerCount(UINT8 humans) +{ + UINT8 playerCount = 8; + + if (humans > 2) + { + // Add 3 bots per player beyond 2P + playerCount += (humans - 2) * 3; + } + + return playerCount; +} + +/*-------------------------------------------------- + void K_InitGrandPrixRank(void) + + See header file for description. +--------------------------------------------------*/ +void K_InitGrandPrixRank(void) +{ + UINT8 numHumans = 0; + INT32 i; + + memset(&gpRank, 0, sizeof(gpRank)); + + if (grandprixinfo.cup == NULL) + { + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (numHumans < MAXSPLITSCREENPLAYERS && players[i].spectator == false) + { + numHumans++; + } + } + } + + // Calculate players + gpRank.players = numHumans; + gpRank.totalPlayers = K_GetGPPlayerCount(numHumans); + + // Calculate total of points + // (Should this account for all coop players?) + for (i = 0; i < numHumans; i++) + { + gpRank.totalPoints += grandprixinfo.cup->numlevels * K_CalculateGPRankPoints(i + 1, gpRank.totalPlayers); + } + + gpRank.totalRings = grandprixinfo.cup->numlevels * numHumans * 20; + + UINT32 laps = 0; + for (i = 0; i < grandprixinfo.cup->numlevels; i++) + { + const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[i]; + if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] != NULL) + { + laps += mapheaderinfo[cupLevelNum]->numlaps; + } + } + + // +1, since 1st place laps are worth 2 pts. + for (i = 0; i < numHumans+1; i++) + { + gpRank.totalLaps += laps; + } + + // Total capsules will need to be calculated as you enter the bonus stages... +} + /*-------------------------------------------------- void K_InitGrandPrixBots(void) @@ -198,12 +279,7 @@ void K_InitGrandPrixBots(void) } } - if (numplayers > 2) - { - // Add 3 bots per player beyond 2P - playercount += (numplayers-2) * 3; - } - + playercount = K_GetGPPlayerCount(numplayers); wantedbots = playercount - numplayers; // Create rival list @@ -728,3 +804,75 @@ boolean K_CanChangeRules(boolean allowdemos) return true; } + +/*-------------------------------------------------- + gp_rank_e K_CalculateGPGrade(void) + + See header file for description. +--------------------------------------------------*/ +gp_rank_e K_CalculateGPGrade(void) +{ + static const fixed_t gradePercents[GRADE_A] = { + 9*FRACUNIT/20, // GRADE_E -> GRADE_D + 12*FRACUNIT/20, // GRADE_D -> GRADE_C + 15*FRACUNIT/20, // GRADE_C -> GRADE_B + 18*FRACUNIT/20 // GRADE_B -> GRADE_A + }; + + gp_rank_e retGrade = GRADE_E; + + // TODO: + const INT32 pointsWeight = 100; + const INT32 lapsWeight = 100; + const INT32 capsulesWeight = 100; + const INT32 ringsWeight = 50; + const INT32 difficultyWeight = 20; + const INT32 total = pointsWeight + lapsWeight + capsulesWeight + ringsWeight + difficultyWeight; + const INT32 continuesPenalty = 20; + + INT32 ours = 0; + fixed_t percent = 0; + + if (gpRank.totalPoints > 0) + { + ours += (gpRank.winPoints * pointsWeight) / gpRank.totalPoints; + } + + if (gpRank.totalLaps > 0) + { + ours += (gpRank.laps * lapsWeight) / gpRank.totalLaps; + } + + if (gpRank.totalCapsules > 0) + { + ours += (gpRank.capsules * capsulesWeight) / gpRank.totalCapsules; + } + + if (gpRank.totalRings > 0) + { + ours += (gpRank.rings * ringsWeight) / gpRank.totalRings; + } + + ours += (gpRank.difficulty * difficultyWeight) / MAXBOTDIFFICULTY; + + ours -= gpRank.continuesUsed * continuesPenalty; + + percent = FixedDiv(ours, total); + + for (retGrade = 0; retGrade < GRADE_A; retGrade++) + { + if (percent < gradePercents[retGrade]) + { + break; + } + } + + if (gpRank.specialWon == true) + { + // Winning the Special Stage gives you + // a free grade increase. + retGrade++; + } + + return retGrade; +} diff --git a/src/k_grandprix.h b/src/k_grandprix.h index a862245c7..9103b047f 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -37,6 +37,38 @@ extern struct grandprixinfo UINT8 eventmode; ///< See GPEVENT_ constants } grandprixinfo; +extern struct gpRank +{ + UINT8 players; + UINT8 totalPlayers; + + UINT32 winPoints; + UINT32 totalPoints; + + UINT32 laps; + UINT32 totalLaps; + + UINT32 continuesUsed; + + UINT32 capsules; + UINT32 totalCapsules; + + UINT32 rings; + UINT32 totalRings; + + boolean specialWon; + UINT8 difficulty; +} gpRank; + +typedef enum +{ + GRADE_E, + GRADE_D, + GRADE_C, + GRADE_B, + GRADE_A, + GRADE_S +} gp_rank_e; /*-------------------------------------------------- UINT8 K_BotStartingDifficulty(SINT8 value); @@ -80,6 +112,13 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); UINT8 K_BotDefaultSkin(void); +/*-------------------------------------------------- + void K_InitGrandPrixRank(void); + + Calculates rank requirements for a GP session. +--------------------------------------------------*/ + +void K_InitGrandPrixRank(void); /*-------------------------------------------------- void K_InitGrandPrixBots(void); @@ -170,6 +209,21 @@ void K_PlayerLoseLife(player_t *player); boolean K_CanChangeRules(boolean allowdemos); +/*-------------------------------------------------- + gp_rank_e K_CalculateGPGrade(void); + + Calculates the player's grade using the + variables from gpRank. + + Input Arguments:- + N/A + + Return:- + gp_rank_e representing the total grade. +--------------------------------------------------*/ + +gp_rank_e K_CalculateGPGrade(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_hud.c b/src/k_hud.c index bbc008520..947032b41 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5067,4 +5067,39 @@ void K_drawKartHUD(void) K_DrawWaypointDebugger(); K_DrawDirectorDebugger(); + + if (grandprixinfo.gp == true) + { + gp_rank_e grade = K_CalculateGPGrade(); + char gradeChar = '?'; + + V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("PTS: %d / %d", gpRank.winPoints, gpRank.totalPoints)); + V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("LAPS: %d / %d", gpRank.laps, gpRank.totalLaps)); + V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("CONTINUES: %d", gpRank.continuesUsed)); + V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("CAPSULES: %d / %d", gpRank.capsules, gpRank.totalCapsules)); + V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("RINGS: %d / %d", gpRank.rings, gpRank.totalRings)); + V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("SPECIAL: %d", gpRank.specialWon)); + V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("DIFFICULTY: %d", gpRank.difficulty)); + + switch (grade) + { + case GRADE_E: { gradeChar = 'E'; break; } + case GRADE_D: { gradeChar = 'D'; break; } + case GRADE_C: { gradeChar = 'C'; break; } + case GRADE_B: { gradeChar = 'B'; break; } + case GRADE_A: { gradeChar = 'A'; break; } + case GRADE_S: { gradeChar = 'S'; break; } + default: { break; } + } + + V_DrawThinString(0, 80, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP, + va(" ** FINAL GRADE: %c", gradeChar)); + } } diff --git a/src/p_inter.c b/src/p_inter.c index 3d69eec25..be0de4620 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1595,6 +1595,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget K_SpawnBattlePoints(source->player, NULL, 1); } + gpRank.capsules++; + // All targets busted! if (++numtargets >= maptargets) { diff --git a/src/p_saveg.c b/src/p_saveg.c index 4d252c64b..8e6a4dd15 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -178,6 +178,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, players[i].realtime); WRITEUINT8(save->p, players[i].laps); WRITEUINT8(save->p, players[i].latestlap); + WRITEUINT32(save->p, players[i].lapPoints); WRITEINT32(save->p, players[i].starpostnum); WRITEUINT8(save->p, players[i].ctfteam); @@ -567,6 +568,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].realtime = READUINT32(save->p); // integer replacement for leveltime players[i].laps = READUINT8(save->p); // Number of laps (optional) players[i].latestlap = READUINT8(save->p); + players[i].lapPoints = READUINT32(save->p); players[i].starpostnum = READINT32(save->p); players[i].ctfteam = READUINT8(save->p); // 1 == Red, 2 == Blue diff --git a/src/p_setup.c b/src/p_setup.c index 77261176e..e8043d56e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7388,6 +7388,7 @@ static void P_InitGametype(void) { if (grandprixinfo.initalize == true) { + K_InitGrandPrixRank(); K_InitGrandPrixBots(); grandprixinfo.initalize = false; } diff --git a/src/p_spec.c b/src/p_spec.c index 0d844cb27..e81620426 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2027,7 +2027,6 @@ static void K_HandleLapIncrement(player_t *player) SetRandomFakePlayerSkin(player, true); } } - if (player->laps > player->latestlap) { @@ -2046,6 +2045,18 @@ static void K_HandleLapIncrement(player_t *player) // Update power levels for this lap. K_UpdatePowerLevels(player, player->laps, false); + + if (nump > 1 && K_IsPlayerLosing(player) == false) + { + if (nump > 2 && player->position == 1) // 1st place in 1v1 uses thumbs up + { + player->lapPoints += 2; + } + else + { + player->lapPoints++; + } + } } player->latestlap = player->laps; diff --git a/src/p_user.c b/src/p_user.c index f5cb57df2..140e2c7de 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1353,6 +1353,7 @@ void P_DoPlayerExit(player_t *player) if (RINGTOTAL(player) > 0) { player->totalring += RINGTOTAL(player); + gpRank.rings += player->totalring; extra = player->totalring / lifethreshold; @@ -1363,6 +1364,16 @@ void P_DoPlayerExit(player_t *player) player->xtralife = extra; } } + + if (grandprixinfo.eventmode == GPEVENT_NONE) + { + gpRank.winPoints += K_CalculateGPRankPoints(player->position, gpRank.totalPlayers); + gpRank.laps += player->lapPoints; + } + else if (grandprixinfo.eventmode == GPEVENT_SPECIAL) + { + gpRank.specialWon = true; + } } } } From c61e017c38e4ba66e06eb3c4fd1a601cc2e56ff2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Feb 2023 12:05:01 -0500 Subject: [PATCH 74/97] GP rank cleanup - Move ranking to its own file. - gpRank (the variable) -> g_gpRank - gpRank (the struct) -> gpRank_t - Functions that worked on the global directly now take a pointer to a struct - Fixed total ring increment - Fixed final lap's lapPoints being discarded - Capsules are now added when exiting with the rest of the stuff --- src/CMakeLists.txt | 1 + src/g_game.c | 9 ++- src/k_battle.c | 3 +- src/k_grandprix.c | 141 +--------------------------------------- src/k_grandprix.h | 65 ++++--------------- src/k_hud.c | 18 +++--- src/k_rank.c | 156 +++++++++++++++++++++++++++++++++++++++++++++ src/k_rank.h | 95 +++++++++++++++++++++++++++ src/p_inter.c | 2 - src/p_setup.c | 3 +- src/p_spec.c | 57 ++++++++--------- src/p_user.c | 12 ++-- src/typedef.h | 3 + 13 files changed, 326 insertions(+), 239 deletions(-) create mode 100644 src/k_rank.c create mode 100644 src/k_rank.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ad870cec4..04d1c6040 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -135,6 +135,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_specialstage.c k_roulette.c k_podium.c + k_rank.c ) if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) diff --git a/src/g_game.c b/src/g_game.c index 601834123..3eae4f03b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -63,6 +63,7 @@ #include "doomstat.h" #include "k_director.h" #include "k_podium.h" +#include "k_rank.h" #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -4127,7 +4128,7 @@ static void G_DoCompleted(void) G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; - UINT8 bestDifficulty = 0; + g_gpRank.difficulty = 0; for (i = 0; i < MAXPLAYERS; i++) { @@ -4157,13 +4158,11 @@ static void G_DoCompleted(void) if (players[i].bot) { - bestDifficulty = max(bestDifficulty, players[i].botvars.difficulty); + g_gpRank.difficulty = max(g_gpRank.difficulty, players[i].botvars.difficulty); } } } - gpRank.difficulty = bestDifficulty; - // See Y_StartIntermission timer handling if ((gametyperules & GTR_CIRCUIT) && ((multiplayer && demo.playback) || j == r_splitscreen+1) && (!K_CanChangeRules(false) || cv_inttime.value > 0)) // play some generic music if there's no win/cool/lose music going on (for exitlevel commands) @@ -5500,7 +5499,7 @@ void G_SetRetryFlag(void) { if (retrying == false) { - gpRank.continuesUsed++; + g_gpRank.continuesUsed++; } retrying = true; diff --git a/src/k_battle.c b/src/k_battle.c index 8e5db972b..4c5c01b54 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -20,6 +20,7 @@ #include "k_boss.h" // bossinfo.valid #include "p_spec.h" #include "k_objects.h" +#include "k_rank.h" // Battle overtime info struct battleovertime battleovertime; @@ -780,7 +781,7 @@ void K_BattleInit(boolean singleplayercontext) P_SpawnMapThing(mt); } - gpRank.totalCapsules += maptargets; + g_gpRank.totalCapsules += maptargets; battlecapsules = true; } diff --git a/src/k_grandprix.c b/src/k_grandprix.c index c061d271f..bc4779386 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -21,7 +21,6 @@ #include "r_things.h" struct grandprixinfo grandprixinfo; -struct gpRank gpRank; /*-------------------------------------------------- UINT8 K_BotStartingDifficulty(SINT8 value) @@ -116,13 +115,11 @@ UINT8 K_BotDefaultSkin(void) } /*-------------------------------------------------- - static UINT8 K_GetGPPlayerCount(UINT8 humans) + UINT8 K_GetGPPlayerCount(UINT8 humans) - Counts the number of total players, - including humans and bots, to put into - a GP session. + See header file for description. --------------------------------------------------*/ -static UINT8 K_GetGPPlayerCount(UINT8 humans) +UINT8 K_GetGPPlayerCount(UINT8 humans) { UINT8 playerCount = 8; @@ -135,66 +132,6 @@ static UINT8 K_GetGPPlayerCount(UINT8 humans) return playerCount; } -/*-------------------------------------------------- - void K_InitGrandPrixRank(void) - - See header file for description. ---------------------------------------------------*/ -void K_InitGrandPrixRank(void) -{ - UINT8 numHumans = 0; - INT32 i; - - memset(&gpRank, 0, sizeof(gpRank)); - - if (grandprixinfo.cup == NULL) - { - return; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - if (numHumans < MAXSPLITSCREENPLAYERS && players[i].spectator == false) - { - numHumans++; - } - } - } - - // Calculate players - gpRank.players = numHumans; - gpRank.totalPlayers = K_GetGPPlayerCount(numHumans); - - // Calculate total of points - // (Should this account for all coop players?) - for (i = 0; i < numHumans; i++) - { - gpRank.totalPoints += grandprixinfo.cup->numlevels * K_CalculateGPRankPoints(i + 1, gpRank.totalPlayers); - } - - gpRank.totalRings = grandprixinfo.cup->numlevels * numHumans * 20; - - UINT32 laps = 0; - for (i = 0; i < grandprixinfo.cup->numlevels; i++) - { - const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[i]; - if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] != NULL) - { - laps += mapheaderinfo[cupLevelNum]->numlaps; - } - } - - // +1, since 1st place laps are worth 2 pts. - for (i = 0; i < numHumans+1; i++) - { - gpRank.totalLaps += laps; - } - - // Total capsules will need to be calculated as you enter the bonus stages... -} - /*-------------------------------------------------- void K_InitGrandPrixBots(void) @@ -804,75 +741,3 @@ boolean K_CanChangeRules(boolean allowdemos) return true; } - -/*-------------------------------------------------- - gp_rank_e K_CalculateGPGrade(void) - - See header file for description. ---------------------------------------------------*/ -gp_rank_e K_CalculateGPGrade(void) -{ - static const fixed_t gradePercents[GRADE_A] = { - 9*FRACUNIT/20, // GRADE_E -> GRADE_D - 12*FRACUNIT/20, // GRADE_D -> GRADE_C - 15*FRACUNIT/20, // GRADE_C -> GRADE_B - 18*FRACUNIT/20 // GRADE_B -> GRADE_A - }; - - gp_rank_e retGrade = GRADE_E; - - // TODO: - const INT32 pointsWeight = 100; - const INT32 lapsWeight = 100; - const INT32 capsulesWeight = 100; - const INT32 ringsWeight = 50; - const INT32 difficultyWeight = 20; - const INT32 total = pointsWeight + lapsWeight + capsulesWeight + ringsWeight + difficultyWeight; - const INT32 continuesPenalty = 20; - - INT32 ours = 0; - fixed_t percent = 0; - - if (gpRank.totalPoints > 0) - { - ours += (gpRank.winPoints * pointsWeight) / gpRank.totalPoints; - } - - if (gpRank.totalLaps > 0) - { - ours += (gpRank.laps * lapsWeight) / gpRank.totalLaps; - } - - if (gpRank.totalCapsules > 0) - { - ours += (gpRank.capsules * capsulesWeight) / gpRank.totalCapsules; - } - - if (gpRank.totalRings > 0) - { - ours += (gpRank.rings * ringsWeight) / gpRank.totalRings; - } - - ours += (gpRank.difficulty * difficultyWeight) / MAXBOTDIFFICULTY; - - ours -= gpRank.continuesUsed * continuesPenalty; - - percent = FixedDiv(ours, total); - - for (retGrade = 0; retGrade < GRADE_A; retGrade++) - { - if (percent < gradePercents[retGrade]) - { - break; - } - } - - if (gpRank.specialWon == true) - { - // Winning the Special Stage gives you - // a free grade increase. - retGrade++; - } - - return retGrade; -} diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 9103b047f..2bd3be61c 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -37,39 +37,6 @@ extern struct grandprixinfo UINT8 eventmode; ///< See GPEVENT_ constants } grandprixinfo; -extern struct gpRank -{ - UINT8 players; - UINT8 totalPlayers; - - UINT32 winPoints; - UINT32 totalPoints; - - UINT32 laps; - UINT32 totalLaps; - - UINT32 continuesUsed; - - UINT32 capsules; - UINT32 totalCapsules; - - UINT32 rings; - UINT32 totalRings; - - boolean specialWon; - UINT8 difficulty; -} gpRank; - -typedef enum -{ - GRADE_E, - GRADE_D, - GRADE_C, - GRADE_B, - GRADE_A, - GRADE_S -} gp_rank_e; - /*-------------------------------------------------- UINT8 K_BotStartingDifficulty(SINT8 value); @@ -112,13 +79,23 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); UINT8 K_BotDefaultSkin(void); -/*-------------------------------------------------- - void K_InitGrandPrixRank(void); - Calculates rank requirements for a GP session. +/*-------------------------------------------------- + UINT8 K_GetGPPlayerCount(UINT8 humans) + + Counts the number of total players, + including humans and bots, to put into + a GP session. + + Input Arguments:- + humans - Number of human players. + + Return:- + Number of both human players and CPU. --------------------------------------------------*/ -void K_InitGrandPrixRank(void); +UINT8 K_GetGPPlayerCount(UINT8 humans); + /*-------------------------------------------------- void K_InitGrandPrixBots(void); @@ -209,20 +186,6 @@ void K_PlayerLoseLife(player_t *player); boolean K_CanChangeRules(boolean allowdemos); -/*-------------------------------------------------- - gp_rank_e K_CalculateGPGrade(void); - - Calculates the player's grade using the - variables from gpRank. - - Input Arguments:- - N/A - - Return:- - gp_rank_e representing the total grade. ---------------------------------------------------*/ - -gp_rank_e K_CalculateGPGrade(void); #ifdef __cplusplus } // extern "C" diff --git a/src/k_hud.c b/src/k_hud.c index 947032b41..27bed0e83 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -37,6 +37,8 @@ #include "r_fps.h" #include "m_random.h" #include "k_roulette.h" +#include "k_bot.h" +#include "k_rank.h" //{ Patch Definitions static patch_t *kp_nodraw; @@ -5070,23 +5072,23 @@ void K_drawKartHUD(void) if (grandprixinfo.gp == true) { - gp_rank_e grade = K_CalculateGPGrade(); + gp_rank_e grade = K_CalculateGPGrade(&g_gpRank); char gradeChar = '?'; V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("PTS: %d / %d", gpRank.winPoints, gpRank.totalPoints)); + va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints)); V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("LAPS: %d / %d", gpRank.laps, gpRank.totalLaps)); + va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps)); V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CONTINUES: %d", gpRank.continuesUsed)); + va("CONTINUES: %d", g_gpRank.continuesUsed)); V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CAPSULES: %d / %d", gpRank.capsules, gpRank.totalCapsules)); + va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules)); V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("RINGS: %d / %d", gpRank.rings, gpRank.totalRings)); + va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("SPECIAL: %d", gpRank.specialWon)); + va("DIFFICULTY: %d / %d", g_gpRank.difficulty, MAXBOTDIFFICULTY)); V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("DIFFICULTY: %d", gpRank.difficulty)); + va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); switch (grade) { diff --git a/src/k_rank.c b/src/k_rank.c new file mode 100644 index 000000000..629bf1640 --- /dev/null +++ b/src/k_rank.c @@ -0,0 +1,156 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_rank.c +/// \brief Grand Prix mode ranking + +#include "k_rank.h" +#include "k_grandprix.h" +#include "k_specialstage.h" +#include "doomdef.h" +#include "d_player.h" +#include "g_game.h" +#include "k_bot.h" +#include "k_kart.h" +#include "m_random.h" +#include "r_things.h" + +gpRank_t g_gpRank = {0}; + +/*-------------------------------------------------- + void K_InitGrandPrixRank(gpRank_t *rankData) + + See header file for description. +--------------------------------------------------*/ +void K_InitGrandPrixRank(gpRank_t *rankData) +{ + UINT8 numHumans = 0; + INT32 i; + + memset(rankData, 0, sizeof(gpRank_t)); + + if (grandprixinfo.cup == NULL) + { + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (numHumans < MAXSPLITSCREENPLAYERS && players[i].spectator == false) + { + numHumans++; + } + } + } + + // Calculate players + rankData->players = numHumans; + rankData->totalPlayers = K_GetGPPlayerCount(numHumans); + + // Calculate total of points + // (Should this account for all coop players?) + for (i = 0; i < numHumans; i++) + { + rankData->totalPoints += grandprixinfo.cup->numlevels * K_CalculateGPRankPoints(i + 1, rankData->totalPlayers); + } + + rankData->totalRings = grandprixinfo.cup->numlevels * numHumans * 20; + + UINT32 laps = 0; + for (i = 0; i < grandprixinfo.cup->numlevels; i++) + { + const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[i]; + if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] != NULL) + { + laps += mapheaderinfo[cupLevelNum]->numlaps; + } + } + + // +1, since 1st place laps are worth 2 pts. + for (i = 0; i < numHumans+1; i++) + { + rankData->totalLaps += laps; + } + + // Total capsules will need to be calculated as you enter the bonus stages... +} + +/*-------------------------------------------------- + gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) + + See header file for description. +--------------------------------------------------*/ +gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) +{ + static const fixed_t gradePercents[GRADE_A] = { + 9*FRACUNIT/20, // GRADE_E -> GRADE_D + 12*FRACUNIT/20, // GRADE_D -> GRADE_C + 15*FRACUNIT/20, // GRADE_C -> GRADE_B + 18*FRACUNIT/20 // GRADE_B -> GRADE_A + }; + + gp_rank_e retGrade = GRADE_E; + + // TODO: Balance requirements + const INT32 pointsWeight = 100; + const INT32 lapsWeight = 100; + const INT32 capsulesWeight = 100; + const INT32 ringsWeight = 50; + const INT32 difficultyWeight = 20; + const INT32 total = pointsWeight + lapsWeight + capsulesWeight + ringsWeight + difficultyWeight; + const INT32 continuesPenalty = 20; + + INT32 ours = 0; + fixed_t percent = 0; + + if (rankData->totalPoints > 0) + { + ours += (rankData->winPoints * pointsWeight) / rankData->totalPoints; + } + + if (rankData->totalLaps > 0) + { + ours += (rankData->laps * lapsWeight) / rankData->totalLaps; + } + + if (rankData->totalCapsules > 0) + { + ours += (rankData->capsules * capsulesWeight) / rankData->totalCapsules; + } + + if (rankData->totalRings > 0) + { + ours += (rankData->rings * ringsWeight) / rankData->totalRings; + } + + ours += (rankData->difficulty * difficultyWeight) / MAXBOTDIFFICULTY; + + ours -= rankData->continuesUsed * continuesPenalty; + + percent = FixedDiv(ours, total); + + for (retGrade = 0; retGrade < GRADE_A; retGrade++) + { + if (percent < gradePercents[retGrade]) + { + break; + } + } + + if (rankData->specialWon == true) + { + // Winning the Special Stage gives you + // a free grade increase. + retGrade++; + } + + return retGrade; +} diff --git a/src/k_rank.h b/src/k_rank.h new file mode 100644 index 000000000..6f417ce82 --- /dev/null +++ b/src/k_rank.h @@ -0,0 +1,95 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_rank.h +/// \brief Grand Prix mode ranking + +#ifndef __K_RANK__ +#define __K_RANK__ + +#include "doomdef.h" +#include "doomstat.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct gpRank_t +{ + UINT8 players; + UINT8 totalPlayers; + + UINT32 winPoints; + UINT32 totalPoints; + + UINT32 laps; + UINT32 totalLaps; + + UINT32 continuesUsed; + + UINT32 capsules; + UINT32 totalCapsules; + + UINT32 rings; + UINT32 totalRings; + + boolean specialWon; + UINT8 difficulty; +}; + +extern gpRank_t g_gpRank; + +typedef enum +{ + GRADE_E, + GRADE_D, + GRADE_C, + GRADE_B, + GRADE_A, + GRADE_S +} gp_rank_e; + + +/*-------------------------------------------------- + void K_InitGrandPrixRank(gpRank_t *rankData); + + Calculates rank requirements for a GP session. + + Input Arguments:- + rankData - Pointer to struct that contains all + of the information required to calculate GP rank. + + Return:- + N/A +--------------------------------------------------*/ + +void K_InitGrandPrixRank(gpRank_t *rankData); + + +/*-------------------------------------------------- + gp_rank_e K_CalculateGPGrade(gpRank_t *rankData); + + Calculates the player's grade using the + variables from gpRank. + + Input Arguments:- + rankData - struct containing existing rank data. + + Return:- + gp_rank_e representing the total grade. +--------------------------------------------------*/ + +gp_rank_e K_CalculateGPGrade(gpRank_t *rankData); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/src/p_inter.c b/src/p_inter.c index be0de4620..3d69eec25 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1595,8 +1595,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget K_SpawnBattlePoints(source->player, NULL, 1); } - gpRank.capsules++; - // All targets busted! if (++numtargets >= maptargets) { diff --git a/src/p_setup.c b/src/p_setup.c index e8043d56e..fe769e1e5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -102,6 +102,7 @@ #include "acs/interface.h" #include "doomstat.h" // MAXMUSNAMES #include "k_podium.h" +#include "k_rank.h" // Replay names have time #if !defined (UNDER_CE) @@ -7388,7 +7389,7 @@ static void P_InitGametype(void) { if (grandprixinfo.initalize == true) { - K_InitGrandPrixRank(); + K_InitGrandPrixRank(&g_gpRank); K_InitGrandPrixBots(); grandprixinfo.initalize = false; } diff --git a/src/p_spec.c b/src/p_spec.c index e81620426..94506b429 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2000,34 +2000,6 @@ static void K_HandleLapIncrement(player_t *player) } } - // finished race exit setup - if (player->laps > numlaps) - { - if (specialstageinfo.valid == true) - { - // Don't permit a win just by sneaking ahead of the UFO/emerald. - if (!(specialstageinfo.ufo == NULL || P_MobjWasRemoved(specialstageinfo.ufo))) - { - player->pflags |= PF_NOCONTEST; - } - } - - P_DoPlayerExit(player); - - if (!(player->pflags & PF_NOCONTEST)) - P_SetupSignExit(player); - } - else - { - UINT32 skinflags = (demo.playback) - ? demo.skinlist[demo.currentskinid[(player-players)]].flags - : skins[player->skin].flags; - if (skinflags & SF_IRONMAN) - { - SetRandomFakePlayerSkin(player, true); - } - } - if (player->laps > player->latestlap) { if (player->laps > 1) @@ -2062,8 +2034,35 @@ static void K_HandleLapIncrement(player_t *player) player->latestlap = player->laps; } - thwompsactive = true; // Lap 2 effects + // finished race exit setup + if (player->laps > numlaps) + { + if (specialstageinfo.valid == true) + { + // Don't permit a win just by sneaking ahead of the UFO/emerald. + if (!(specialstageinfo.ufo == NULL || P_MobjWasRemoved(specialstageinfo.ufo))) + { + player->pflags |= PF_NOCONTEST; + } + } + P_DoPlayerExit(player); + + if (!(player->pflags & PF_NOCONTEST)) + P_SetupSignExit(player); + } + else + { + UINT32 skinflags = (demo.playback) + ? demo.skinlist[demo.currentskinid[(player-players)]].flags + : skins[player->skin].flags; + if (skinflags & SF_IRONMAN) + { + SetRandomFakePlayerSkin(player, true); + } + } + + thwompsactive = true; // Lap 2 effects lowestLap = P_FindLowestLap(); for (i = 0; i < numlines; i++) diff --git a/src/p_user.c b/src/p_user.c index 140e2c7de..7c0a9f13a 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -58,6 +58,8 @@ #include "k_terrain.h" // K_SpawnSplashForMobj #include "k_color.h" #include "k_follower.h" +#include "k_battle.h" +#include "k_rank.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -1353,7 +1355,7 @@ void P_DoPlayerExit(player_t *player) if (RINGTOTAL(player) > 0) { player->totalring += RINGTOTAL(player); - gpRank.rings += player->totalring; + g_gpRank.rings += RINGTOTAL(player); extra = player->totalring / lifethreshold; @@ -1367,13 +1369,15 @@ void P_DoPlayerExit(player_t *player) if (grandprixinfo.eventmode == GPEVENT_NONE) { - gpRank.winPoints += K_CalculateGPRankPoints(player->position, gpRank.totalPlayers); - gpRank.laps += player->lapPoints; + g_gpRank.winPoints += K_CalculateGPRankPoints(player->position, g_gpRank.totalPlayers); + g_gpRank.laps += player->lapPoints; } else if (grandprixinfo.eventmode == GPEVENT_SPECIAL) { - gpRank.specialWon = true; + g_gpRank.specialWon = true; } + + g_gpRank.capsules += numtargets; } } } diff --git a/src/typedef.h b/src/typedef.h index 0b745eb30..ae61dea9a 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -198,6 +198,9 @@ TYPEDEF (t_floor_t); // k_waypoint.h TYPEDEF (waypoint_t); +// k_rank.h +TYPEDEF (gpRank_t); + // lua_hudlib_drawlist.h typedef struct huddrawlist_s *huddrawlist_h; From f7851623ff979d8d82b57034a6201c4510039508 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Feb 2023 12:43:08 -0500 Subject: [PATCH 75/97] Calculate a bot difficulty to reach 13 is very unlikely for Normal, likely impossible for Easy. (If we want a bonus game speeds, it should be explicitly defined) --- src/k_hud.c | 2 +- src/k_rank.c | 19 +++++++++++++++++-- src/k_rank.h | 4 +++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 27bed0e83..7fe62a8a6 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5086,7 +5086,7 @@ void K_drawKartHUD(void) V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("DIFFICULTY: %d / %d", g_gpRank.difficulty, MAXBOTDIFFICULTY)); + va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget)); V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); diff --git a/src/k_rank.c b/src/k_rank.c index 629bf1640..bf548b1ed 100644 --- a/src/k_rank.c +++ b/src/k_rank.c @@ -31,6 +31,7 @@ gpRank_t g_gpRank = {0}; void K_InitGrandPrixRank(gpRank_t *rankData) { UINT8 numHumans = 0; + UINT32 laps = 0; INT32 i; memset(rankData, 0, sizeof(gpRank_t)); @@ -64,7 +65,18 @@ void K_InitGrandPrixRank(gpRank_t *rankData) rankData->totalRings = grandprixinfo.cup->numlevels * numHumans * 20; - UINT32 laps = 0; + if (grandprixinfo.masterbots == true) + { + rankData->difficultyTarget = MAXBOTDIFFICULTY; + } + else + { + rankData->difficultyTarget = min( + MAXBOTDIFFICULTY, + K_BotStartingDifficulty(grandprixinfo.gamespeed) + ((grandprixinfo.cup->numlevels + 1) / 2) + ); + } + for (i = 0; i < grandprixinfo.cup->numlevels; i++) { const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[i]; @@ -131,7 +143,10 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) ours += (rankData->rings * ringsWeight) / rankData->totalRings; } - ours += (rankData->difficulty * difficultyWeight) / MAXBOTDIFFICULTY; + if (rankData->difficultyTarget > 0) + { + ours += (rankData->difficulty * difficultyWeight) / rankData->difficultyTarget; + } ours -= rankData->continuesUsed * continuesPenalty; diff --git a/src/k_rank.h b/src/k_rank.h index 6f417ce82..93c139df0 100644 --- a/src/k_rank.h +++ b/src/k_rank.h @@ -39,8 +39,10 @@ struct gpRank_t UINT32 rings; UINT32 totalRings; - boolean specialWon; UINT8 difficulty; + UINT8 difficultyTarget; + + boolean specialWon; }; extern gpRank_t g_gpRank; From 6a4b4d8b70e0327f116565cc444152854eec8f9e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Feb 2023 13:22:12 -0500 Subject: [PATCH 76/97] Balance rank requirements Old values were taken from Snap, which has a small number of very straight-forward and relatively easy to 100% requirements, so it has very high standards. RR has lots of requirements, they're slightly more nuanced, and it's extremely improbable to have them all 100% in one run even if you're good. - Toned A rank down very slightly. (90% -> 85%) - Rest of the ranks are more evenly distributed. (D: 45% -> 35%, C: 60% -> 50%, B: 75% -> 70%) --- src/k_rank.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/k_rank.c b/src/k_rank.c index bf548b1ed..b4d2b90df 100644 --- a/src/k_rank.c +++ b/src/k_rank.c @@ -103,15 +103,14 @@ void K_InitGrandPrixRank(gpRank_t *rankData) gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) { static const fixed_t gradePercents[GRADE_A] = { - 9*FRACUNIT/20, // GRADE_E -> GRADE_D - 12*FRACUNIT/20, // GRADE_D -> GRADE_C - 15*FRACUNIT/20, // GRADE_C -> GRADE_B - 18*FRACUNIT/20 // GRADE_B -> GRADE_A + 7*FRACUNIT/20, // D: 35% or higher + 10*FRACUNIT/20, // C: 50% or higher + 14*FRACUNIT/20, // B: 70% or higher + 17*FRACUNIT/20 // A: 85% or higher }; gp_rank_e retGrade = GRADE_E; - // TODO: Balance requirements const INT32 pointsWeight = 100; const INT32 lapsWeight = 100; const INT32 capsulesWeight = 100; From 3d1070c2f219663d74772a2e976c0b439d51db50 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 00:37:51 -0500 Subject: [PATCH 77/97] Show ranking data on the screen --- src/d_netcmd.c | 1 + src/d_netcmd.h | 1 + src/k_hud.c | 88 ++++++++++++++++++++++---------------- src/k_kart.c | 1 + src/k_podium.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 166 insertions(+), 37 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 54ebdcebd..a1fae65f2 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -453,6 +453,7 @@ consvar_t cv_kartdebugcolorize = CVAR_INIT ("debugcolorize", "Off", CV_CHEAT, CV consvar_t cv_kartdebugdirector = CVAR_INIT ("debugdirector", "Off", CV_CHEAT, CV_OnOff, NULL); consvar_t cv_spbtest = CVAR_INIT ("spbtest", "Off", CV_CHEAT|CV_NETVAR, CV_OnOff, NULL); consvar_t cv_gptest = CVAR_INIT ("gptest", "Off", CV_CHEAT|CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_debugrank = CVAR_INIT ("debugrank", "Off", CV_CHEAT, CV_OnOff, NULL); static CV_PossibleValue_t capsuletest_cons_t[] = { {CV_CAPSULETEST_OFF, "Off"}, diff --git a/src/d_netcmd.h b/src/d_netcmd.h index a126de023..e6c10fa11 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -96,6 +96,7 @@ extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugdistribution, extern consvar_t cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector; extern consvar_t cv_spbtest, cv_gptest, cv_reducevfx; extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbotpredict; +extern consvar_t cv_debugrank; typedef enum { CV_CAPSULETEST_OFF, diff --git a/src/k_hud.c b/src/k_hud.c index 7fe62a8a6..af93c6ef0 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4798,6 +4798,58 @@ static void K_DrawWaypointDebugger(void) } } +static void K_DrawGPRankDebugger(void) +{ + gp_rank_e grade = GRADE_E; + char gradeChar = '?'; + + if (cv_debugrank.value == 0) + { + return; + } + + if (stplyr != &players[displayplayers[0]]) // only for p1 + { + return; + } + + if (grandprixinfo.gp == false) + { + return; + } + + grade = K_CalculateGPGrade(&g_gpRank); + + V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints)); + V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps)); + V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("CONTINUES: %d", g_gpRank.continuesUsed)); + V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules)); + V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); + V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget)); + V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); + + switch (grade) + { + case GRADE_E: { gradeChar = 'E'; break; } + case GRADE_D: { gradeChar = 'D'; break; } + case GRADE_C: { gradeChar = 'C'; break; } + case GRADE_B: { gradeChar = 'B'; break; } + case GRADE_A: { gradeChar = 'A'; break; } + case GRADE_S: { gradeChar = 'S'; break; } + default: { break; } + } + + V_DrawThinString(0, 80, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP, + va(" ** FINAL GRADE: %c", gradeChar)); +} + void K_drawKartHUD(void) { boolean islonesome = false; @@ -5069,39 +5121,5 @@ void K_drawKartHUD(void) K_DrawWaypointDebugger(); K_DrawDirectorDebugger(); - - if (grandprixinfo.gp == true) - { - gp_rank_e grade = K_CalculateGPGrade(&g_gpRank); - char gradeChar = '?'; - - V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints)); - V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps)); - V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CONTINUES: %d", g_gpRank.continuesUsed)); - V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules)); - V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); - V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget)); - V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); - - switch (grade) - { - case GRADE_E: { gradeChar = 'E'; break; } - case GRADE_D: { gradeChar = 'D'; break; } - case GRADE_C: { gradeChar = 'C'; break; } - case GRADE_B: { gradeChar = 'B'; break; } - case GRADE_A: { gradeChar = 'A'; break; } - case GRADE_S: { gradeChar = 'S'; break; } - default: { break; } - } - - V_DrawThinString(0, 80, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP, - va(" ** FINAL GRADE: %c", gradeChar)); - } + K_DrawGPRankDebugger(); } diff --git a/src/k_kart.c b/src/k_kart.c index 6b1a54739..10ab61c8d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -347,6 +347,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartdebugnodes); CV_RegisterVar(&cv_kartdebugcolorize); CV_RegisterVar(&cv_kartdebugdirector); + CV_RegisterVar(&cv_debugrank); CV_RegisterVar(&cv_spbtest); CV_RegisterVar(&cv_gptest); CV_RegisterVar(&cv_capsuletest); diff --git a/src/k_podium.c b/src/k_podium.c index c7d5a4af1..0ff0e23a5 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -45,13 +45,20 @@ #include "k_menu.h" #include "k_grandprix.h" +#include "k_rank.h" static struct podiumData_s { boolean ranking; + gpRank_t rankData; + gp_rank_e grade; + UINT8 state; + UINT8 delay; UINT8 fade; } podiumData; +#define PODIUM_STATES (9) // TODO: enum + /*-------------------------------------------------- boolean K_PodiumSequence(void) @@ -267,6 +274,14 @@ void K_FinishCeremony(void) void K_ResetCeremony(void) { memset(&podiumData, 0, sizeof(struct podiumData_s)); + + if (K_PodiumSequence() == false) + { + return; + } + + podiumData.rankData = g_gpRank; + podiumData.grade = K_CalculateGPGrade(&podiumData.rankData); } /*-------------------------------------------------- @@ -305,6 +320,19 @@ void K_CeremonyTicker(boolean run) { podiumData.fade++; } + else + { + if (podiumData.state < PODIUM_STATES) + { + podiumData.delay++; + + if (podiumData.delay > TICRATE) + { + podiumData.state++; + podiumData.delay = 0; + } + } + } } } @@ -317,7 +345,7 @@ boolean K_CeremonyResponder(event_t *event) { INT32 key = event->data1; - if (podiumData.ranking == false || podiumData.fade < 16) + if (podiumData.ranking == false || podiumData.state < PODIUM_STATES) { return false; } @@ -377,8 +405,88 @@ void K_CeremonyDrawer(void) { if (podiumData.ranking == true) { + char gradeChar = '?'; + INT32 x = 64; + INT32 y = 48; + INT32 i; + + switch (podiumData.grade) + { + case GRADE_E: { gradeChar = 'E'; break; } + case GRADE_D: { gradeChar = 'D'; break; } + case GRADE_C: { gradeChar = 'C'; break; } + case GRADE_B: { gradeChar = 'B'; break; } + case GRADE_A: { gradeChar = 'A'; break; } + case GRADE_S: { gradeChar = 'S'; break; } + default: { break; } + } + V_DrawFadeScreen(0xFF00, podiumData.fade); - V_DrawCenteredString(BASEVIDWIDTH / 2, 64, 0, "STUFF GOES HERE"); + + for (i = 0; i <= podiumData.state; i++) + { + switch (i) + { + case 1: + { + V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, + va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints) + ); + break; + } + case 2: + { + V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, + va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps) + ); + break; + } + case 3: + { + V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, + va("CONTINUES: %d", g_gpRank.continuesUsed) + ); + break; + } + case 4: + { + V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, + va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules) + ); + break; + } + case 5: + { + V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, + va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings) + ); + break; + } + case 6: + { + V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, + va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget) + ); + break; + } + case 7: + { + V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, + va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO") + ); + break; + } + case 8: + { + V_DrawString(x, y + 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, + va(" ** FINAL GRADE: %c", gradeChar) + ); + break; + } + } + + y += 10; + } } if (timeinmap < 16) From 3c076419383e970bc8b57e243820646873f6cb9f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 16:16:37 -0500 Subject: [PATCH 78/97] Add final position as a ranking requirement 1st is a large bonus, 2nd is a medium bonus, 3rd place is no bonus, and everything below is a penalty. This will help make Loser Valley grades never be above a C at most. --- src/g_game.c | 5 +++ src/k_hud.c | 18 ++++---- src/k_kart.c | 120 +++++++++++++++++++++++-------------------------- src/k_podium.c | 48 +++++++++++++------- src/k_rank.c | 25 ++++++++--- src/k_rank.h | 4 ++ 6 files changed, 125 insertions(+), 95 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 3eae4f03b..22760c3e0 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4128,6 +4128,7 @@ static void G_DoCompleted(void) G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; + g_gpRank.position = MAXPLAYERS; g_gpRank.difficulty = 0; for (i = 0; i < MAXPLAYERS; i++) @@ -4160,6 +4161,10 @@ static void G_DoCompleted(void) { g_gpRank.difficulty = max(g_gpRank.difficulty, players[i].botvars.difficulty); } + else + { + g_gpRank.position = min(g_gpRank.position, K_GetPodiumPosition(&players[i])); + } } } diff --git a/src/k_hud.c b/src/k_hud.c index af93c6ef0..db1464ceb 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4821,19 +4821,21 @@ static void K_DrawGPRankDebugger(void) grade = K_CalculateGPGrade(&g_gpRank); V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints)); + va("POS: %d / %d", g_gpRank.position, RANK_NEUTRAL_POSITION)); V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps)); + va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints)); V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CONTINUES: %d", g_gpRank.continuesUsed)); + va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps)); V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules)); + va("CONTINUES: %d", g_gpRank.continuesUsed)); V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); + va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules)); V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget)); + va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); + va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget)); + V_DrawThinString(0, 70, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("EMERALD: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); switch (grade) { @@ -4846,7 +4848,7 @@ static void K_DrawGPRankDebugger(void) default: { break; } } - V_DrawThinString(0, 80, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP, + V_DrawThinString(0, 90, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP, va(" ** FINAL GRADE: %c", gradeChar)); } diff --git a/src/k_kart.c b/src/k_kart.c index 10ab61c8d..3231f47cd 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9319,85 +9319,79 @@ void K_KartUpdatePosition(player_t *player) return; } - for (i = 0; i < MAXPLAYERS; i++) + if (K_PodiumSequence() == true) { - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; + position = K_GetPodiumPosition(player); - realplayers++; - - if (K_PodiumSequence() == true) + for (i = 0; i < MAXPLAYERS; i++) { - if (players[i].score > player->score) - { - // Final score is the important part. - position++; - } - else if (players[i].score == player->score) - { - if (players[i].bot == false && player->bot == true) - { - // Bots are never as important as players. - position++; - } - else if (i < player - players) - { - // Port priority is the final tie breaker. - position++; - } - } + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + realplayers++; } - else if (gametyperules & GTR_CIRCUIT) + } + else + { + for (i = 0; i < MAXPLAYERS; i++) { - if (player->exiting) // End of match standings - { - // Only time matters - if (players[i].realtime < player->realtime) - position++; - } - else - { - // I'm a lap behind this player OR - // My distance to the finish line is higher, so I'm behind - if ((players[i].laps > player->laps) - || (players[i].distancetofinish < player->distancetofinish)) - { - position++; - } - } - } - else - { - if (player->exiting) // End of match standings - { - // Only score matters - if (players[i].roundscore > player->roundscore) - position++; - } - else - { - UINT8 myEmeralds = K_NumEmeralds(player); - UINT8 yourEmeralds = K_NumEmeralds(&players[i]); + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; - // First compare all points - if (players[i].roundscore > player->roundscore) + realplayers++; + + if (gametyperules & GTR_CIRCUIT) + { + if (player->exiting) // End of match standings { - position++; + // Only time matters + if (players[i].realtime < player->realtime) + position++; } - else if (players[i].roundscore == player->roundscore) + else { - // Emeralds are a tie breaker - if (yourEmeralds > myEmeralds) + // I'm a lap behind this player OR + // My distance to the finish line is higher, so I'm behind + if ((players[i].laps > player->laps) + || (players[i].distancetofinish < player->distancetofinish)) { position++; } - else if (yourEmeralds == myEmeralds) + } + } + else + { + if (player->exiting) // End of match standings + { + // Only score matters + if (players[i].roundscore > player->roundscore) + position++; + } + else + { + UINT8 myEmeralds = K_NumEmeralds(player); + UINT8 yourEmeralds = K_NumEmeralds(&players[i]); + + // First compare all points + if (players[i].roundscore > player->roundscore) { - // Bumpers are the second tier tie breaker - if (players[i].bumpers > player->bumpers) + position++; + } + else if (players[i].roundscore == player->roundscore) + { + // Emeralds are a tie breaker + if (yourEmeralds > myEmeralds) { position++; } + else if (yourEmeralds == myEmeralds) + { + // Bumpers are the second tier tie breaker + if (players[i].bumpers > player->bumpers) + { + position++; + } + } } } } diff --git a/src/k_podium.c b/src/k_podium.c index 0ff0e23a5..2e610db1b 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -57,7 +57,7 @@ static struct podiumData_s UINT8 fade; } podiumData; -#define PODIUM_STATES (9) // TODO: enum +#define PODIUM_STATES (10) // TODO: enum when this actually gets made /*-------------------------------------------------- boolean K_PodiumSequence(void) @@ -326,7 +326,7 @@ void K_CeremonyTicker(boolean run) { podiumData.delay++; - if (podiumData.delay > TICRATE) + if (podiumData.delay > TICRATE/2) { podiumData.state++; podiumData.delay = 0; @@ -429,60 +429,74 @@ void K_CeremonyDrawer(void) { case 1: { - V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, - va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints) + V_DrawString(x, y, V_ALLOWLOWERCASE, + va("POS: %d / %d", podiumData.rankData.position, RANK_NEUTRAL_POSITION) ); break; } case 2: { - V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, - va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps) + V_DrawString(x, y, V_ALLOWLOWERCASE, + va("PTS: %d / %d", podiumData.rankData.winPoints, podiumData.rankData.totalPoints) ); break; } case 3: { - V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, - va("CONTINUES: %d", g_gpRank.continuesUsed) + V_DrawString(x, y, V_ALLOWLOWERCASE, + va("LAPS: %d / %d", podiumData.rankData.laps, podiumData.rankData.totalLaps) ); break; } case 4: { - V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, - va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules) + V_DrawString(x, y, V_ALLOWLOWERCASE, + va("CONTINUES: %d", podiumData.rankData.continuesUsed) ); break; } case 5: { - V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, - va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings) + V_DrawString(x, y, V_ALLOWLOWERCASE, + va("CAPSULES: %d / %d", podiumData.rankData.capsules, podiumData.rankData.totalCapsules) ); break; } case 6: { - V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, - va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget) + V_DrawString(x, y, V_ALLOWLOWERCASE, + va("RINGS: %d / %d", podiumData.rankData.rings, podiumData.rankData.totalRings) ); break; } case 7: { - V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, - va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO") + V_DrawString(x, y, V_ALLOWLOWERCASE, + va("DIFFICULTY: %d / %d", podiumData.rankData.difficulty, podiumData.rankData.difficultyTarget) ); break; } case 8: { - V_DrawString(x, y + 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, + V_DrawString(x, y, V_ALLOWLOWERCASE, + va("EMERALD: %s", (podiumData.rankData.specialWon == true) ? "YES" : "NO") + ); + break; + } + case 9: + { + V_DrawString(x, y + 10, V_YELLOWMAP|V_ALLOWLOWERCASE, va(" ** FINAL GRADE: %c", gradeChar) ); break; } + case 10: + { + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, BASEVIDHEIGHT - 10, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + "Press some button type deal to continue" + ); + break; + } } y += 10; diff --git a/src/k_rank.c b/src/k_rank.c index b4d2b90df..08bf19c80 100644 --- a/src/k_rank.c +++ b/src/k_rank.c @@ -56,6 +56,9 @@ void K_InitGrandPrixRank(gpRank_t *rankData) rankData->players = numHumans; rankData->totalPlayers = K_GetGPPlayerCount(numHumans); + // Initialize to the neutral value. + rankData->position = RANK_NEUTRAL_POSITION; + // Calculate total of points // (Should this account for all coop players?) for (i = 0; i < numHumans; i++) @@ -111,17 +114,25 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) gp_rank_e retGrade = GRADE_E; - const INT32 pointsWeight = 100; - const INT32 lapsWeight = 100; - const INT32 capsulesWeight = 100; - const INT32 ringsWeight = 50; - const INT32 difficultyWeight = 20; - const INT32 total = pointsWeight + lapsWeight + capsulesWeight + ringsWeight + difficultyWeight; - const INT32 continuesPenalty = 20; + const INT32 positionWeight = 1500; + const INT32 pointsWeight = 1000; + const INT32 lapsWeight = 1000; + const INT32 capsulesWeight = 1000; + const INT32 ringsWeight = 500; + const INT32 difficultyWeight = 200; + const INT32 total = positionWeight + pointsWeight + lapsWeight + capsulesWeight + ringsWeight + difficultyWeight; + const INT32 continuesPenalty = 200; INT32 ours = 0; fixed_t percent = 0; + if (rankData->position > 0) + { + const INT32 sc = (rankData->position - 1); + const INT32 loser = (RANK_NEUTRAL_POSITION - 1); + ours += ((loser - sc) * positionWeight) / loser; + } + if (rankData->totalPoints > 0) { ours += (rankData->winPoints * pointsWeight) / rankData->totalPoints; diff --git a/src/k_rank.h b/src/k_rank.h index 93c139df0..b86744c7e 100644 --- a/src/k_rank.h +++ b/src/k_rank.h @@ -25,6 +25,8 @@ struct gpRank_t UINT8 players; UINT8 totalPlayers; + UINT8 position; + UINT32 winPoints; UINT32 totalPoints; @@ -57,6 +59,8 @@ typedef enum GRADE_S } gp_rank_e; +// 3rd place is neutral, anything below is a penalty +#define RANK_NEUTRAL_POSITION (3) /*-------------------------------------------------- void K_InitGrandPrixRank(gpRank_t *rankData); From d5d0647ae6b858030fde139e30990c83c25c0c8f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 16:27:22 -0500 Subject: [PATCH 79/97] Remove difficulty as a ranking requirement Was a cute idea, but removing it since it's not something you can 100% guarantee every time, which was the same reasoning for getting rid of times getting damaged / dealing damage from the requirements. --- src/g_game.c | 7 +------ src/k_hud.c | 4 +--- src/k_podium.c | 15 ++++----------- src/k_rank.c | 32 +++++++------------------------- src/k_rank.h | 3 --- 5 files changed, 13 insertions(+), 48 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 22760c3e0..aae7d02a3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4129,7 +4129,6 @@ static void G_DoCompleted(void) wipegamestate = GS_NULL; g_gpRank.position = MAXPLAYERS; - g_gpRank.difficulty = 0; for (i = 0; i < MAXPLAYERS; i++) { @@ -4157,11 +4156,7 @@ static void G_DoCompleted(void) G_PlayerFinishLevel(i); // take away cards and stuff - if (players[i].bot) - { - g_gpRank.difficulty = max(g_gpRank.difficulty, players[i].botvars.difficulty); - } - else + if (players[i].bot == false) { g_gpRank.position = min(g_gpRank.position, K_GetPodiumPosition(&players[i])); } diff --git a/src/k_hud.c b/src/k_hud.c index db1464ceb..630baca43 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4833,8 +4833,6 @@ static void K_DrawGPRankDebugger(void) V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget)); - V_DrawThinString(0, 70, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, va("EMERALD: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); switch (grade) @@ -4848,7 +4846,7 @@ static void K_DrawGPRankDebugger(void) default: { break; } } - V_DrawThinString(0, 90, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP, + V_DrawThinString(0, 80, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP, va(" ** FINAL GRADE: %c", gradeChar)); } diff --git a/src/k_podium.c b/src/k_podium.c index 2e610db1b..cf8c3cc85 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -57,7 +57,7 @@ static struct podiumData_s UINT8 fade; } podiumData; -#define PODIUM_STATES (10) // TODO: enum when this actually gets made +#define PODIUM_STATES (9) // TODO: enum when this actually gets made /*-------------------------------------------------- boolean K_PodiumSequence(void) @@ -470,29 +470,22 @@ void K_CeremonyDrawer(void) break; } case 7: - { - V_DrawString(x, y, V_ALLOWLOWERCASE, - va("DIFFICULTY: %d / %d", podiumData.rankData.difficulty, podiumData.rankData.difficultyTarget) - ); - break; - } - case 8: { V_DrawString(x, y, V_ALLOWLOWERCASE, va("EMERALD: %s", (podiumData.rankData.specialWon == true) ? "YES" : "NO") ); break; } - case 9: + case 8: { V_DrawString(x, y + 10, V_YELLOWMAP|V_ALLOWLOWERCASE, va(" ** FINAL GRADE: %c", gradeChar) ); break; } - case 10: + case 9: { - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, BASEVIDHEIGHT - 10, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + V_DrawThinString(2, BASEVIDHEIGHT - 10, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, "Press some button type deal to continue" ); break; diff --git a/src/k_rank.c b/src/k_rank.c index 08bf19c80..369b940e1 100644 --- a/src/k_rank.c +++ b/src/k_rank.c @@ -68,18 +68,6 @@ void K_InitGrandPrixRank(gpRank_t *rankData) rankData->totalRings = grandprixinfo.cup->numlevels * numHumans * 20; - if (grandprixinfo.masterbots == true) - { - rankData->difficultyTarget = MAXBOTDIFFICULTY; - } - else - { - rankData->difficultyTarget = min( - MAXBOTDIFFICULTY, - K_BotStartingDifficulty(grandprixinfo.gamespeed) + ((grandprixinfo.cup->numlevels + 1) / 2) - ); - } - for (i = 0; i < grandprixinfo.cup->numlevels; i++) { const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[i]; @@ -114,14 +102,13 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) gp_rank_e retGrade = GRADE_E; - const INT32 positionWeight = 1500; - const INT32 pointsWeight = 1000; - const INT32 lapsWeight = 1000; - const INT32 capsulesWeight = 1000; - const INT32 ringsWeight = 500; - const INT32 difficultyWeight = 200; - const INT32 total = positionWeight + pointsWeight + lapsWeight + capsulesWeight + ringsWeight + difficultyWeight; - const INT32 continuesPenalty = 200; + const INT32 positionWeight = 150; + const INT32 pointsWeight = 100; + const INT32 lapsWeight = 100; + const INT32 capsulesWeight = 100; + const INT32 ringsWeight = 50; + const INT32 total = positionWeight + pointsWeight + lapsWeight + capsulesWeight + ringsWeight; + const INT32 continuesPenalty = 20; INT32 ours = 0; fixed_t percent = 0; @@ -153,11 +140,6 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) ours += (rankData->rings * ringsWeight) / rankData->totalRings; } - if (rankData->difficultyTarget > 0) - { - ours += (rankData->difficulty * difficultyWeight) / rankData->difficultyTarget; - } - ours -= rankData->continuesUsed * continuesPenalty; percent = FixedDiv(ours, total); diff --git a/src/k_rank.h b/src/k_rank.h index b86744c7e..093bb178e 100644 --- a/src/k_rank.h +++ b/src/k_rank.h @@ -41,9 +41,6 @@ struct gpRank_t UINT32 rings; UINT32 totalRings; - UINT8 difficulty; - UINT8 difficultyTarget; - boolean specialWon; }; From cf20e2e8311d7886838777cd1986af030f7754fd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 16:35:50 -0500 Subject: [PATCH 80/97] SS requirement is now getting A rank --- src/g_game.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index aae7d02a3..c7c423d03 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3887,20 +3887,9 @@ static void G_GetNextMap(void) // Special stage else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) { - INT16 totaltotalring = 0; + gp_rank_e grade = K_CalculateGPGrade(&g_gpRank); - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - if (players[i].spectator) - continue; - if (players[i].bot) - continue; - totaltotalring += players[i].totalring; - } - - if (totaltotalring >= 50) + if (grade >= GRADE_A) // On A rank pace? Then you get a chance for S rank! { const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL]; if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]) From 57f53a5531f638229b898e476bae7a21cd7eb1af Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 19:02:40 -0500 Subject: [PATCH 81/97] Search bonus maps to calculate total capsules No more moving goalpost! This means we can put it on the intermission screen and not just the final rankings screen. --- src/k_battle.c | 1 - src/k_rank.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 255 insertions(+), 2 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 4c5c01b54..a49883f6f 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -781,7 +781,6 @@ void K_BattleInit(boolean singleplayercontext) P_SpawnMapThing(mt); } - g_gpRank.totalCapsules += maptargets; battlecapsules = true; } diff --git a/src/k_rank.c b/src/k_rank.c index 369b940e1..09651015a 100644 --- a/src/k_rank.c +++ b/src/k_rank.c @@ -20,9 +20,240 @@ #include "k_kart.h" #include "m_random.h" #include "r_things.h" +#include "fastcmp.h" +#include "byteptr.h" gpRank_t g_gpRank = {0}; +// I was ALMOST tempted to start tearing apart all +// of the map loading code and turning it into C++ +// and making it properly split between read-only +// and true level loading and clean up all of the +// global variable garbage it uses ... but I stopped +// myself. So here's code duplication hell instead. +static UINT32 g_rankCapsules_mapthingsPos[UINT16_MAX]; +static size_t g_rankCapsules_nummapthings = 0; +static boolean g_rankCapsules_udmf = false; +static UINT32 g_rankCapsules_count = 0; + +/*-------------------------------------------------- + static void RankCapsules_TextmapCount(size_t size) + + Counts the number of map things and records + the structure positions, for the result of + RankCapsules_CountFromMap. + + Input Arguments:- + size - Length of the TEXTMAP lump. + + Return:- + N/A +--------------------------------------------------*/ +static UINT32 RankCapsules_TextmapCount(size_t size) +{ + const char *tkn = M_TokenizerRead(0); + UINT8 brackets = 0; + + g_rankCapsules_nummapthings = 0; + + // Look for namespace at the beginning. + if (!fastcmp(tkn, "namespace")) + { + return false; + } + + // Check if namespace is valid. + tkn = M_TokenizerRead(0); + + while ((tkn = M_TokenizerRead(0)) && M_TokenizerGetEndPos() < size) + { + // Avoid anything inside bracketed stuff, only look for external keywords. + if (brackets) + { + if (fastcmp(tkn, "}")) + brackets--; + } + else if (fastcmp(tkn, "{")) + brackets++; + // Check for valid fields. + else if (fastcmp(tkn, "thing")) + g_rankCapsules_mapthingsPos[g_rankCapsules_nummapthings++] = M_TokenizerGetEndPos(); + } + + if (brackets) + { + return false; + } + + return true; +} + +/*-------------------------------------------------- + static void RankCapsules_LoadTextmap(void) + + Loads UDMF map data for the result of + RankCapsules_CountFromMap. +--------------------------------------------------*/ +static void RankCapsules_LoadTextmap(void) +{ + size_t i; + + for (i = 0; i < g_rankCapsules_nummapthings; i++) + { + const char *param, *val; + + M_TokenizerSetEndPos(g_rankCapsules_mapthingsPos[i]); + param = M_TokenizerRead(0); + + if (!fastcmp(param, "{")) + { + continue; + } + + while (true) + { + param = M_TokenizerRead(0); + + if (fastcmp(param, "}")) + { + break; + } + + val = M_TokenizerRead(1); + + if (fastcmp(param, "type")) + { + UINT16 type = atol(val); + + if (type == mobjinfo[MT_BATTLECAPSULE].doomednum) + { + g_rankCapsules_count++; + } + + break; + } + } + } +} + +/*-------------------------------------------------- + static void RankCapsules_LoadThingsLump(UINT8 *data) + + Loads binary map data for the result of + RankCapsules_CountFromMap. + + Input Arguments:- + data - Pointer to a THINGS lump. + + Return:- + N/A +--------------------------------------------------*/ +static void RankCapsules_LoadThingsLump(UINT8 *data) +{ + size_t i; + + for (i = 0; i < g_rankCapsules_nummapthings; i++) + { + UINT16 type = 0; + + data += 2; // x + data += 2; // y + + data += 2; // angle + type = READUINT16(data); // type + type &= 4095; + + data += 2; // options + + if (type == mobjinfo[MT_BATTLECAPSULE].doomednum) + { + g_rankCapsules_count++; + } + } +} + +/*-------------------------------------------------- + static boolean RankCapsules_LoadMapData(const virtres_t *virt) + + Loads either UDMF or binary map data, for the + result of RankCapsules_CountFromMap. + + Input Arguments:- + virt - Pointer to the map's virtual resource. + + Return:- + true if we could successfully load the map data, + otherwise false. +--------------------------------------------------*/ +static boolean RankCapsules_LoadMapData(const virtres_t *virt) +{ + virtlump_t *virtthings = NULL; + + // Count map data. + if (g_rankCapsules_udmf) // Count how many entries for each type we got in textmap. + { + virtlump_t *textmap = vres_Find(virt, "TEXTMAP"); + M_TokenizerOpen((char *)textmap->data); + if (!RankCapsules_TextmapCount(textmap->size)) + { + M_TokenizerClose(); + return false; + } + } + else + { + virtthings = vres_Find(virt, "THINGS"); + + if (!virtthings) + { + return false; + } + + // Traditional doom map format just assumes the number of elements from the lump sizes. + g_rankCapsules_nummapthings = virtthings->size / (5 * sizeof (INT16)); + } + + // Load map data. + if (g_rankCapsules_udmf) + { + RankCapsules_LoadTextmap(); + M_TokenizerClose(); + } + else + { + RankCapsules_LoadThingsLump(virtthings->data); + } + + return true; +} + +/*-------------------------------------------------- + static UINT32 RankCapsules_CountFromMap(const virtres_t *virt) + + Counts the number of capsules in a map, without + needing to fully load it. + + Input Arguments:- + virt - Pointer to the map's virtual resource. + + Return:- + Number of MT_BATTLECAPSULE instances found. +--------------------------------------------------*/ +static UINT32 RankCapsules_CountFromMap(const virtres_t *virt) +{ + virtlump_t *textmap = vres_Find(virt, "TEXTMAP"); + + g_rankCapsules_udmf = (textmap != NULL); + g_rankCapsules_count = 0; + + if (RankCapsules_LoadMapData(virt) == true) + { + return g_rankCapsules_count; + } + + return 0; +} + /*-------------------------------------------------- void K_InitGrandPrixRank(gpRank_t *rankData) @@ -83,7 +314,30 @@ void K_InitGrandPrixRank(gpRank_t *rankData) rankData->totalLaps += laps; } - // Total capsules will need to be calculated as you enter the bonus stages... + // Search through all of the cup's bonus levels + // for an accurate count of how many capsules they have. + for (i = 0; i < grandprixinfo.cup->numbonus; i++) + { + const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS + i]; + if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] != NULL) + { + lumpnum_t lp = mapheaderinfo[cupLevelNum]->lumpnum; + virtres_t *virt = NULL; + + if (lp == LUMPERROR) + { + continue; + } + + virt = vres_GetMap(lp); + if (virt == NULL) + { + continue; + } + + rankData->totalCapsules += RankCapsules_CountFromMap(virt); + } + } } /*-------------------------------------------------- From 96477caa88ec7c84d32e3c194588626df69d021c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 4 Mar 2023 19:28:31 -0500 Subject: [PATCH 82/97] Move K_ResetCeremony Properly carry over GP ranking data to the ceremony --- src/g_game.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index c7c423d03..103255e78 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1472,8 +1472,6 @@ void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate) if (gamestate == GS_VOTING) Y_EndVote(); - K_ResetCeremony(); - // cleanup // Is this actually necessary? Doesn't F_StartTitleScreen already do a significantly more comprehensive check? if (newstate == GS_TITLESCREEN) @@ -1500,6 +1498,8 @@ void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate) M_ClearMenus(true); I_UpdateMouseGrab(); + K_ResetCeremony(); + for (i = 0; i < MAXPLAYERS; i++) { if (resetplayer || (playeringame[i] && players[i].playerstate == PST_DEAD)) From ca178324de6c8fee9e2bc38913cbb47ac963126c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Mar 2023 19:10:23 -0500 Subject: [PATCH 83/97] Free capsules counting virtual resources --- src/k_rank.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_rank.c b/src/k_rank.c index 09651015a..648f3a0c8 100644 --- a/src/k_rank.c +++ b/src/k_rank.c @@ -336,6 +336,7 @@ void K_InitGrandPrixRank(gpRank_t *rankData) } rankData->totalCapsules += RankCapsules_CountFromMap(virt); + vres_Free(virt); } } } From 23a00b1d00cc64194e70d326ef3670b52829d49a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Mar 2023 19:33:43 -0500 Subject: [PATCH 84/97] Move GP ranking data to grandprixinfo --- src/g_game.c | 8 ++++---- src/k_grandprix.h | 13 +++++++++---- src/k_hud.c | 16 ++++++++-------- src/k_podium.c | 20 ++++++++++---------- src/k_rank.c | 2 -- src/k_rank.h | 2 -- src/p_setup.c | 2 +- src/p_user.c | 10 +++++----- 8 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 103255e78..497c54320 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3887,7 +3887,7 @@ static void G_GetNextMap(void) // Special stage else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) { - gp_rank_e grade = K_CalculateGPGrade(&g_gpRank); + gp_rank_e grade = K_CalculateGPGrade(&grandprixinfo.rank); if (grade >= GRADE_A) // On A rank pace? Then you get a chance for S rank! { @@ -4117,7 +4117,7 @@ static void G_DoCompleted(void) G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; - g_gpRank.position = MAXPLAYERS; + grandprixinfo.rank.position = MAXPLAYERS; for (i = 0; i < MAXPLAYERS; i++) { @@ -4147,7 +4147,7 @@ static void G_DoCompleted(void) if (players[i].bot == false) { - g_gpRank.position = min(g_gpRank.position, K_GetPodiumPosition(&players[i])); + grandprixinfo.rank.position = min(grandprixinfo.rank.position, K_GetPodiumPosition(&players[i])); } } } @@ -5488,7 +5488,7 @@ void G_SetRetryFlag(void) { if (retrying == false) { - g_gpRank.continuesUsed++; + grandprixinfo.rank.continuesUsed++; } retrying = true; diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 2bd3be61c..efa251246 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -15,14 +15,18 @@ #include "doomdef.h" #include "doomstat.h" +#include "k_rank.h" // gpRank_t #ifdef __cplusplus extern "C" { #endif -#define GPEVENT_NONE 0 -#define GPEVENT_BONUS 1 -#define GPEVENT_SPECIAL 2 +typedef enum +{ + GPEVENT_NONE = 0, + GPEVENT_BONUS, + GPEVENT_SPECIAL, +} gpEvent_e; extern struct grandprixinfo { @@ -34,7 +38,8 @@ extern struct grandprixinfo boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode) boolean initalize; ///< If true, we need to initialize a new session. boolean wonround; ///< If false, then we retry the map instead of going to the next. - UINT8 eventmode; ///< See GPEVENT_ constants + gpEvent_e eventmode; ///< Special event mode, bots are set to spectate and a special gametype is played + gpRank_t rank; ///< Struct containing grading information. (See also: k_rank.h) } grandprixinfo; /*-------------------------------------------------- diff --git a/src/k_hud.c b/src/k_hud.c index 630baca43..2ee8fa7e0 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4818,22 +4818,22 @@ static void K_DrawGPRankDebugger(void) return; } - grade = K_CalculateGPGrade(&g_gpRank); + grade = K_CalculateGPGrade(&grandprixinfo.rank); V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("POS: %d / %d", g_gpRank.position, RANK_NEUTRAL_POSITION)); + va("POS: %d / %d", grandprixinfo.rank.position, RANK_NEUTRAL_POSITION)); V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints)); + va("PTS: %d / %d", grandprixinfo.rank.winPoints, grandprixinfo.rank.totalPoints)); V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps)); + va("LAPS: %d / %d", grandprixinfo.rank.laps, grandprixinfo.rank.totalLaps)); V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CONTINUES: %d", g_gpRank.continuesUsed)); + va("CONTINUES: %d", grandprixinfo.rank.continuesUsed)); V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules)); + va("CAPSULES: %d / %d", grandprixinfo.rank.capsules, grandprixinfo.rank.totalCapsules)); V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); + va("RINGS: %d / %d", grandprixinfo.rank.rings, grandprixinfo.rank.totalRings)); V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("EMERALD: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); + va("EMERALD: %s", (grandprixinfo.rank.specialWon == true) ? "YES" : "NO")); switch (grade) { diff --git a/src/k_podium.c b/src/k_podium.c index cf8c3cc85..7f00834d9 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -50,7 +50,7 @@ static struct podiumData_s { boolean ranking; - gpRank_t rankData; + gpRank_t rank; gp_rank_e grade; UINT8 state; UINT8 delay; @@ -280,8 +280,8 @@ void K_ResetCeremony(void) return; } - podiumData.rankData = g_gpRank; - podiumData.grade = K_CalculateGPGrade(&podiumData.rankData); + podiumData.rank = grandprixinfo.rank; + podiumData.grade = K_CalculateGPGrade(&podiumData.rank); } /*-------------------------------------------------- @@ -430,49 +430,49 @@ void K_CeremonyDrawer(void) case 1: { V_DrawString(x, y, V_ALLOWLOWERCASE, - va("POS: %d / %d", podiumData.rankData.position, RANK_NEUTRAL_POSITION) + va("POS: %d / %d", podiumData.rank.position, RANK_NEUTRAL_POSITION) ); break; } case 2: { V_DrawString(x, y, V_ALLOWLOWERCASE, - va("PTS: %d / %d", podiumData.rankData.winPoints, podiumData.rankData.totalPoints) + va("PTS: %d / %d", podiumData.rank.winPoints, podiumData.rank.totalPoints) ); break; } case 3: { V_DrawString(x, y, V_ALLOWLOWERCASE, - va("LAPS: %d / %d", podiumData.rankData.laps, podiumData.rankData.totalLaps) + va("LAPS: %d / %d", podiumData.rank.laps, podiumData.rank.totalLaps) ); break; } case 4: { V_DrawString(x, y, V_ALLOWLOWERCASE, - va("CONTINUES: %d", podiumData.rankData.continuesUsed) + va("CONTINUES: %d", podiumData.rank.continuesUsed) ); break; } case 5: { V_DrawString(x, y, V_ALLOWLOWERCASE, - va("CAPSULES: %d / %d", podiumData.rankData.capsules, podiumData.rankData.totalCapsules) + va("CAPSULES: %d / %d", podiumData.rank.capsules, podiumData.rank.totalCapsules) ); break; } case 6: { V_DrawString(x, y, V_ALLOWLOWERCASE, - va("RINGS: %d / %d", podiumData.rankData.rings, podiumData.rankData.totalRings) + va("RINGS: %d / %d", podiumData.rank.rings, podiumData.rank.totalRings) ); break; } case 7: { V_DrawString(x, y, V_ALLOWLOWERCASE, - va("EMERALD: %s", (podiumData.rankData.specialWon == true) ? "YES" : "NO") + va("EMERALD: %s", (podiumData.rank.specialWon == true) ? "YES" : "NO") ); break; } diff --git a/src/k_rank.c b/src/k_rank.c index 648f3a0c8..0e07c33ec 100644 --- a/src/k_rank.c +++ b/src/k_rank.c @@ -23,8 +23,6 @@ #include "fastcmp.h" #include "byteptr.h" -gpRank_t g_gpRank = {0}; - // I was ALMOST tempted to start tearing apart all // of the map loading code and turning it into C++ // and making it properly split between read-only diff --git a/src/k_rank.h b/src/k_rank.h index 093bb178e..cc675db17 100644 --- a/src/k_rank.h +++ b/src/k_rank.h @@ -44,8 +44,6 @@ struct gpRank_t boolean specialWon; }; -extern gpRank_t g_gpRank; - typedef enum { GRADE_E, diff --git a/src/p_setup.c b/src/p_setup.c index fe769e1e5..d0cc73d6f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7389,7 +7389,7 @@ static void P_InitGametype(void) { if (grandprixinfo.initalize == true) { - K_InitGrandPrixRank(&g_gpRank); + K_InitGrandPrixRank(&grandprixinfo.rank); K_InitGrandPrixBots(); grandprixinfo.initalize = false; } diff --git a/src/p_user.c b/src/p_user.c index 7c0a9f13a..44a8c58de 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1355,7 +1355,7 @@ void P_DoPlayerExit(player_t *player) if (RINGTOTAL(player) > 0) { player->totalring += RINGTOTAL(player); - g_gpRank.rings += RINGTOTAL(player); + grandprixinfo.rank.rings += RINGTOTAL(player); extra = player->totalring / lifethreshold; @@ -1369,15 +1369,15 @@ void P_DoPlayerExit(player_t *player) if (grandprixinfo.eventmode == GPEVENT_NONE) { - g_gpRank.winPoints += K_CalculateGPRankPoints(player->position, g_gpRank.totalPlayers); - g_gpRank.laps += player->lapPoints; + grandprixinfo.rank.winPoints += K_CalculateGPRankPoints(player->position, grandprixinfo.rank.totalPlayers); + grandprixinfo.rank.laps += player->lapPoints; } else if (grandprixinfo.eventmode == GPEVENT_SPECIAL) { - g_gpRank.specialWon = true; + grandprixinfo.rank.specialWon = true; } - g_gpRank.capsules += numtargets; + grandprixinfo.rank.capsules += numtargets; } } } From 0f87e177b9605de4039f3c523608fb58d03d48dd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Mar 2023 19:58:16 -0500 Subject: [PATCH 85/97] Be able to get partial credit for capsules again --- src/g_game.c | 1 + src/p_user.c | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 497c54320..fed0efd34 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4117,6 +4117,7 @@ static void G_DoCompleted(void) G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; + grandprixinfo.rank.capsules += numtargets; grandprixinfo.rank.position = MAXPLAYERS; for (i = 0; i < MAXPLAYERS; i++) diff --git a/src/p_user.c b/src/p_user.c index 44a8c58de..3d290a678 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1376,8 +1376,6 @@ void P_DoPlayerExit(player_t *player) { grandprixinfo.rank.specialWon = true; } - - grandprixinfo.rank.capsules += numtargets; } } } From 2fb6bc5f46918e952a2bc3cd2a857b1f11c03774 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 16:51:41 -0700 Subject: [PATCH 86/97] Require 25% minimum handleboost to sliptide, change sliptide zip to 24% --- src/k_kart.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 931064b83..beae28100 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3045,12 +3045,15 @@ static void K_GetKartBoostPower(player_t *player) boostpower = (4*boostpower)/5; // Note: Handling will ONLY stack when sliptiding! + // > (NB 2023-03-06: This was previously unintentionally applied while drifting as well.) + // > (This only affected drifts where you were under the effect of multiple handling boosts.) + // > (Revisit if Growvinciblity or sneaker-panels + power items feel too heavy while drifting!) // When you're not, it just uses the best instead of adding together, like the old behavior. #define ADDBOOST(s,a,h) { \ numboosts++; \ speedboost += FixedDiv(s, FRACUNIT + (metabolism * (numboosts-1))); \ accelboost += FixedDiv(a, FRACUNIT + (metabolism * (numboosts-1))); \ - if (player->aizdriftstrat) \ + if (K_Sliptiding(player)) \ handleboost += FixedDiv((3*h)/2, FRACUNIT + (metabolism * (numboosts-1))/4); \ else \ handleboost = max(h, handleboost); \ @@ -3087,7 +3090,8 @@ static void K_GetKartBoostPower(player_t *player) if (player->sliptideZipBoost) { - ADDBOOST(FRACUNIT, 4*FRACUNIT, sliptidehandling/2); // + 100% top speed, + 400% acceleration, +25% handling + // NB: This is intentionally under the 25% threshold required to initiate a sliptide + ADDBOOST(FRACUNIT, 4*FRACUNIT, 2*sliptidehandling/5); // + 100% top speed, + 400% acceleration, +20% handling } if (player->spindashboost) // Spindash boost @@ -9296,7 +9300,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->pflags &= ~PF_DRIFTEND; } - if ((player->handleboost == 0) + if ((player->handleboost < FRACUNIT/4) || (!player->steering) || (!player->aizdriftstrat) || (player->steering > 0) != (player->aizdriftstrat > 0)) From a7cc5aac788ecd1c377dd84a21fcc133d6afc7d6 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 16:53:14 -0700 Subject: [PATCH 87/97] Fix Lua interface mangling writes to sliptideZipBoost --- src/lua_playerlib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index caa44ce58..1edf0cdd8 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -313,7 +313,7 @@ static int player_get(lua_State *L) else if (fastcmp(field,"sliptideZipDelay")) lua_pushinteger(L, plr->sliptideZipDelay); else if (fastcmp(field,"sliptideZipBoost")) - lua_pushinteger(L, plr->sliptideZipDelay); + lua_pushinteger(L, plr->sliptideZipBoost); /* else if (fastcmp(field,"itemroulette")) lua_pushinteger(L, plr->itemroulette); @@ -699,7 +699,7 @@ static int player_set(lua_State *L) else if (fastcmp(field,"sliptideZipDelay")) plr->sliptideZipDelay = luaL_checkinteger(L, 3); else if (fastcmp(field,"sliptideZipBoost")) - plr->sliptideZipDelay = luaL_checkinteger(L, 3); + plr->sliptideZipBoost = luaL_checkinteger(L, 3); /* else if (fastcmp(field,"itemroulette")) plr->itemroulette = luaL_checkinteger(L, 3); From 20fe13deb771bbc2c3066226dfd31be7f3cf64a5 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 3 Mar 2023 21:31:39 -0800 Subject: [PATCH 88/97] HUD TARGET for Battle Kiosk, Monitors and Emeralds Except in Break the Capsules, Boss mode or DUEL mode: - When Overtime begins, all TARGETs are hidden and the Kiosk is targeted instead. Blue colored TARGET. - TARGET player themself sees TARGETs on emeralds and monitors with emeralds inside. TARGET color matches emerald. --- src/k_hud_track.cpp | 56 +++++++++++++++++++++++++++++++++++++++++---- src/p_mobj.c | 5 ++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index dbb5ce8d0..62bb752b4 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -5,9 +5,11 @@ #include "k_battle.h" #include "k_boss.h" #include "k_hud.h" +#include "k_objects.h" #include "m_fixed.h" #include "p_local.h" #include "p_mobj.h" +#include "r_draw.h" #include "r_fps.h" #include "r_main.h" #include "st_stuff.h" @@ -21,10 +23,40 @@ struct TargetTracking mobj_t* mobj; vector3_t point; fixed_t camDist; + + skincolornum_t color() const + { + switch (mobj->type) + { + case MT_OVERTIME_CENTER: + return SKINCOLOR_BLUE; + + case MT_MONITOR: + case MT_EMERALD: + return static_cast(mobj->color); + + default: + return SKINCOLOR_NONE; + } + } + + const uint8_t* colormap() const + { + const skincolornum_t clr = color(); + + if (clr != SKINCOLOR_NONE) + { + return R_GetTranslationColormap(TC_RAINBOW, clr, GTC_CACHE); + } + + return nullptr; + } }; void K_DrawTargetTracking(const TargetTracking& target) { + const uint8_t* colormap = target.colormap(); + trackingResult_t result = {}; int32_t timer = 0; @@ -175,10 +207,10 @@ void K_DrawTargetTracking(const TargetTracking& target) if (targetPatch) { - V_DrawFixedPatch(targetPos.x, targetPos.y, FRACUNIT, V_SPLITSCREEN, targetPatch, nullptr); + V_DrawFixedPatch(targetPos.x, targetPos.y, FRACUNIT, V_SPLITSCREEN, targetPatch, colormap); } - V_DrawFixedPatch(arrowPos.x, arrowPos.y, FRACUNIT, V_SPLITSCREEN | arrowFlags, arrowPatch, nullptr); + V_DrawFixedPatch(arrowPos.x, arrowPos.y, FRACUNIT, V_SPLITSCREEN | arrowFlags, arrowPatch, colormap); } else { @@ -207,7 +239,7 @@ void K_DrawTargetTracking(const TargetTracking& target) FRACUNIT, V_SPLITSCREEN, patch, - nullptr + colormap ); }; @@ -229,7 +261,7 @@ void K_DrawTargetTracking(const TargetTracking& target) } } -bool is_player_tracking_target(const player_t *player) +bool is_player_tracking_target(const player_t *player = stplyr) { if ((gametyperules & (GTR_BUMPERS|GTR_CLOSERPLAYERS)) != (GTR_BUMPERS|GTR_CLOSERPLAYERS)) { @@ -253,6 +285,13 @@ bool is_player_tracking_target(const player_t *player) return player != stplyr; } + // Except for DUEL mode, Overtime hides all TARGETs except + // the kiosk. + if (battleovertime.enabled) + { + return false; + } + if (g_hiscore < 1) // SOMEONE should be scoring { return false; @@ -277,6 +316,15 @@ bool is_object_tracking_target(const mobj_t* mobj) case MT_PLAYER: return is_player_tracking_target(mobj->player); + case MT_OVERTIME_CENTER: + return inDuel == false && battleovertime.enabled; + + case MT_EMERALD: + return is_player_tracking_target(); + + case MT_MONITOR: + return is_player_tracking_target() && Obj_MonitorGetEmerald(mobj) != 0; + default: return false; } diff --git a/src/p_mobj.c b/src/p_mobj.c index eb2e18498..5768ddf38 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5265,6 +5265,11 @@ static boolean P_IsTrackerType(INT32 type) case MT_PLAYER: return true; + case MT_OVERTIME_CENTER: + case MT_MONITOR: + case MT_EMERALD: + return true; + default: return false; } From b023b165614c62c0b6fd26a330f437c60891754a Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 3 Mar 2023 21:51:59 -0800 Subject: [PATCH 89/97] Remove WANTED over players' heads, TARGET wanted player --- src/k_hud_track.cpp | 14 ++------------ src/p_mobj.c | 2 ++ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 62bb752b4..ca7bf19e4 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -261,7 +261,7 @@ void K_DrawTargetTracking(const TargetTracking& target) } } -bool is_player_tracking_target(const player_t *player = stplyr) +bool is_player_tracking_target(player_t *player = stplyr) { if ((gametyperules & (GTR_BUMPERS|GTR_CLOSERPLAYERS)) != (GTR_BUMPERS|GTR_CLOSERPLAYERS)) { @@ -292,17 +292,7 @@ bool is_player_tracking_target(const player_t *player = stplyr) return false; } - if (g_hiscore < 1) // SOMEONE should be scoring - { - return false; - } - - if (player->roundscore < g_hiscore) - { - return false; - } - - return true; + return K_IsPlayerWanted(player); } bool is_object_tracking_target(const mobj_t* mobj) diff --git a/src/p_mobj.c b/src/p_mobj.c index 5768ddf38..80f413216 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6391,6 +6391,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) numx->destscale = scale; } +#if 0 if (K_IsPlayerWanted(mobj->target->player) && mobj->movecount != 1) { mobj_t *wanted = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PLAYERWANTED); @@ -6401,6 +6402,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->movecount = 1; } else if (!K_IsPlayerWanted(mobj->target->player)) +#endif mobj->movecount = 0; } else From 18ccc3db272fd2414947e5f48003dee468672fda Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 3 Mar 2023 21:54:28 -0800 Subject: [PATCH 90/97] Remove g_hiscore since it's not used anymore --- src/doomstat.h | 1 - src/g_game.c | 1 - src/k_battle.c | 10 ---------- src/p_saveg.c | 4 ---- src/p_setup.c | 2 -- 5 files changed, 18 deletions(-) diff --git a/src/doomstat.h b/src/doomstat.h index 88e9bd66d..608fe98cd 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -697,7 +697,6 @@ extern UINT8 gamespeed; extern boolean franticitems; extern boolean encoremode, prevencoremode; -extern UINT32 g_hiscore; extern tic_t wantedcalcdelay; extern tic_t itemCooldowns[NUMKARTITEMS - 1]; extern tic_t mapreset; diff --git a/src/g_game.c b/src/g_game.c index a0d187790..70401a62c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -298,7 +298,6 @@ SINT8 votes[MAXPLAYERS]; // Each player's vote SINT8 pickedvote; // What vote the host rolls // Server-sided, synched variables -UINT32 g_hiscore; // Highest score (points) achieved by anyone in game tic_t wantedcalcdelay; // Time before it recalculates WANTED tic_t itemCooldowns[NUMKARTITEMS - 1]; // Cooldowns to prevent item spawning tic_t mapreset; // Map reset delay when enough players have joined an empty game diff --git a/src/k_battle.c b/src/k_battle.c index 37bb33f4f..d2a2175dd 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -91,7 +91,6 @@ void K_CheckBumpers(void) { UINT8 i; UINT8 numingame = 0; - UINT32 toproundscore = 0; UINT8 nobumpers = 0; if (!(gametyperules & GTR_BUMPERS)) @@ -110,11 +109,6 @@ void K_CheckBumpers(void) numingame++; - if (players[i].roundscore > toproundscore) - { - toproundscore = players[i].roundscore; - } - if (players[i].bumpers <= 0) // if you don't have any bumpers, you're probably not a winner { nobumpers++; @@ -137,10 +131,6 @@ void K_CheckBumpers(void) } return; } - else - { - g_hiscore = toproundscore; - } if (numingame <= 1) { diff --git a/src/p_saveg.c b/src/p_saveg.c index fc9d7daf4..15c084200 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4983,8 +4983,6 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) WRITESINT8(save->p, speedscramble); WRITESINT8(save->p, encorescramble); - WRITEUINT32(save->p, g_hiscore); - // battleovertime_t WRITEUINT16(save->p, battleovertime.enabled); WRITEFIXED(save->p, battleovertime.radius); @@ -5154,8 +5152,6 @@ static inline boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading) speedscramble = READSINT8(save->p); encorescramble = READSINT8(save->p); - g_hiscore = READUINT32(save->p); - // battleovertime_t battleovertime.enabled = READUINT16(save->p); battleovertime.radius = READFIXED(save->p); diff --git a/src/p_setup.c b/src/p_setup.c index d97696309..4f3f4bc7f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7109,8 +7109,6 @@ static void P_InitLevelSettings(void) franticitems = (boolean)cv_kartfrantic.value; } - g_hiscore = 0; - memset(&battleovertime, 0, sizeof(struct battleovertime)); speedscramble = encorescramble = -1; From f2c806185e67ece222d8c9724342cab84f419035 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 4 Mar 2023 18:04:02 -0800 Subject: [PATCH 91/97] Cycle color of player TARGET between held emeralds --- src/k_hud_track.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index ca7bf19e4..d16de57fd 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -2,6 +2,8 @@ #include #include +#include "core/static_vec.hpp" + #include "k_battle.h" #include "k_boss.h" #include "k_hud.h" @@ -15,6 +17,8 @@ #include "st_stuff.h" #include "v_video.h" +using namespace srb2; + namespace { @@ -35,11 +39,53 @@ struct TargetTracking case MT_EMERALD: return static_cast(mobj->color); + case MT_PLAYER: + return player_emeralds_color(); + default: return SKINCOLOR_NONE; } } + StaticVec player_emeralds_vec() const + { + StaticVec emeralds; + + const player_t* player = mobj->player; + + if (player == nullptr) + { + return emeralds; + } + + for (int i = 0; i < 7; ++i) + { + const uint32_t emeraldFlag = (1U << i); + + if (player->emeralds & emeraldFlag) + { + emeralds.push_back(emeraldFlag); + } + } + + return emeralds; + } + + skincolornum_t player_emeralds_color() const + { + const StaticVec emeralds = player_emeralds_vec(); + + if (emeralds.empty()) + { + return SKINCOLOR_NONE; + } + + constexpr tic_t kPeriod = TICRATE / 2; + const int idx = (leveltime / kPeriod) % emeralds.size(); + + return static_cast(K_GetChaosEmeraldColor(emeralds[idx])); + } + const uint8_t* colormap() const { const skincolornum_t clr = color(); From d56be63249b47c558e39825cd0a850bfdecc01b5 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 27 Feb 2023 20:54:31 -0800 Subject: [PATCH 92/97] Battle: eliminate players killed by the Overtime Barrier - Players killed this way become invisible and unable to move. - Ends the match when one player is remaining. --- src/k_battle.c | 30 ++++++++++++++++++++---------- src/p_user.c | 5 +++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index d2a2175dd..bad1f1694 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -92,6 +92,9 @@ void K_CheckBumpers(void) UINT8 i; UINT8 numingame = 0; UINT8 nobumpers = 0; + UINT8 eliminated = 0; + + const boolean singleplayer = (battlecapsules || bossinfo.valid); if (!(gametyperules & GTR_BUMPERS)) return; @@ -113,21 +116,28 @@ void K_CheckBumpers(void) { nobumpers++; } + + if (players[i].pflags & PF_ELIMINATED) + { + eliminated++; + } } - if (battlecapsules || bossinfo.valid) + if (singleplayer + ? nobumpers > 0 && nobumpers >= numingame + : eliminated >= numingame - 1) { - if (nobumpers > 0 && nobumpers >= numingame) + for (i = 0; i < MAXPLAYERS; i++) { - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - if (players[i].spectator) - continue; + if (!playeringame[i]) + continue; + if (players[i].spectator) + continue; + + if (singleplayer) players[i].pflags |= PF_NOCONTEST; - P_DoPlayerExit(&players[i]); - } + + P_DoPlayerExit(&players[i]); } return; } diff --git a/src/p_user.c b/src/p_user.c index f95d5afc1..6eb0417ef 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2773,6 +2773,11 @@ static void P_DeathThink(player_t *player) } } + if ((player->pflags & PF_ELIMINATED) && (gametyperules & GTR_BUMPERS)) + { + playerGone = true; + } + if (playerGone == false && player->deadtimer > TICRATE) { player->playerstate = PST_REBORN; From ccd933556dabaa8b87f4722aaf6af4ce93b74dc6 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 27 Feb 2023 20:56:12 -0800 Subject: [PATCH 93/97] Battle: activate director after dying to Overtime Barrier --- src/p_user.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index 6eb0417ef..ba8741adf 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -58,6 +58,7 @@ #include "k_terrain.h" // K_SpawnSplashForMobj #include "k_color.h" #include "k_follower.h" +#include "k_director.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -2783,6 +2784,13 @@ static void P_DeathThink(player_t *player) player->playerstate = PST_REBORN; } + // TODO: support splitscreen + // Spectate another player after 2 seconds + if (player == &players[consoleplayer] && playerGone == true && (gametyperules & GTR_BUMPERS) && player->deadtimer == 2*TICRATE) + { + K_ToggleDirector(true); + } + // Keep time rolling if (!(player->exiting || mapreset) && !(player->pflags & PF_NOCONTEST) && !stoppedclock) { From 5d79ecd065896e13d6d1a505c918feda8a6fbf62 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 6 Mar 2023 20:01:09 -0700 Subject: [PATCH 94/97] Oni feedback fixup Sneaker boosts now pause decay timer, boost strength reduced to prevent tripwire memes, boost lengthened based on proximity to bottom left, further improvements to handling boosts --- src/k_kart.c | 64 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index beae28100..def6c4be3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3016,6 +3016,10 @@ fixed_t K_GetSpindashChargeSpeed(player_t *player) return val; } +// v2 almost broke sliptiding when it fixed turning bugs! +// This value is fine-tuned to feel like v1 again without reverting any of those changes. +#define SLIPTIDEHANDLING 7*FRACUNIT/8 + // sets boostpower, speedboost, accelboost, and handleboost to whatever we need it to be static void K_GetKartBoostPower(player_t *player) { @@ -3023,10 +3027,6 @@ static void K_GetKartBoostPower(player_t *player) const fixed_t maxmetabolismincrease = FRACUNIT/2; const fixed_t metabolism = FRACUNIT - ((9-player->kartweight) * maxmetabolismincrease / 8); - // v2 almost broke sliptiding when it fixed turning bugs! - // This value is fine-tuned to feel like v1 again without reverting any of those changes. - const fixed_t sliptidehandling = FRACUNIT/2; - fixed_t boostpower = FRACUNIT; fixed_t speedboost = 0, accelboost = 0, handleboost = 0; UINT8 numboosts = 0; @@ -3054,7 +3054,7 @@ static void K_GetKartBoostPower(player_t *player) speedboost += FixedDiv(s, FRACUNIT + (metabolism * (numboosts-1))); \ accelboost += FixedDiv(a, FRACUNIT + (metabolism * (numboosts-1))); \ if (K_Sliptiding(player)) \ - handleboost += FixedDiv((3*h)/2, FRACUNIT + (metabolism * (numboosts-1))/4); \ + handleboost += FixedDiv(h, FRACUNIT + (metabolism * (numboosts-1))/4); \ else \ handleboost = max(h, handleboost); \ } @@ -3064,18 +3064,18 @@ static void K_GetKartBoostPower(player_t *player) UINT8 i; for (i = 0; i < player->numsneakers; i++) { - ADDBOOST(FRACUNIT/2, 8*FRACUNIT, sliptidehandling); // + 50% top speed, + 800% acceleration, +50% handling + ADDBOOST(FRACUNIT/2, 8*FRACUNIT, SLIPTIDEHANDLING); // + 50% top speed, + 800% acceleration, +50% handling } } if (player->invincibilitytimer) // Invincibility { - ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT, sliptidehandling/2); // + 37.5% top speed, + 300% acceleration, +25% handling + ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT, SLIPTIDEHANDLING/2); // + 37.5% top speed, + 300% acceleration, +25% handling } if (player->growshrinktimer > 0) // Grow { - ADDBOOST(0, 0, sliptidehandling/2); // + 0% top speed, + 0% acceleration, +25% handling + ADDBOOST(0, 0, SLIPTIDEHANDLING/2); // + 0% top speed, + 0% acceleration, +25% handling } if (player->flamedash) // Flame Shield dash @@ -3084,14 +3084,14 @@ static void K_GetKartBoostPower(player_t *player) ADDBOOST( dash, // + infinite top speed 3*FRACUNIT, // + 300% acceleration - FixedMul(FixedDiv(dash, FRACUNIT/2), sliptidehandling/2) // + infinite handling + FixedMul(FixedDiv(dash, FRACUNIT/2), SLIPTIDEHANDLING/2) // + infinite handling ); } if (player->sliptideZipBoost) { // NB: This is intentionally under the 25% threshold required to initiate a sliptide - ADDBOOST(FRACUNIT, 4*FRACUNIT, 2*sliptidehandling/5); // + 100% top speed, + 400% acceleration, +20% handling + ADDBOOST(13*FRACUNIT/20, 4*FRACUNIT, 2*SLIPTIDEHANDLING/5); // + 65% top speed, + 400% acceleration, +20% handling } if (player->spindashboost) // Spindash boost @@ -3109,7 +3109,7 @@ static void K_GetKartBoostPower(player_t *player) if (player->startboost) // Startup Boost { - ADDBOOST(FRACUNIT, 4*FRACUNIT, sliptidehandling); // + 100% top speed, + 400% acceleration, +50% handling + ADDBOOST(FRACUNIT, 4*FRACUNIT, SLIPTIDEHANDLING); // + 100% top speed, + 400% acceleration, +50% handling } if (player->driftboost) // Drift Boost @@ -3139,7 +3139,7 @@ static void K_GetKartBoostPower(player_t *player) if (player->gateBoost) // SPB Juicebox boost { - ADDBOOST(3*FRACUNIT/4, 4*FRACUNIT, sliptidehandling/2); // + 75% top speed, + 400% acceleration, +25% handling + ADDBOOST(3*FRACUNIT/4, 4*FRACUNIT, SLIPTIDEHANDLING/2); // + 75% top speed, + 400% acceleration, +25% handling } if (player->ringboost) // Ring Boost @@ -4014,6 +4014,15 @@ void K_UpdateStumbleIndicator(player_t *player) } } +static boolean K_IsLosingSliptideZip(player_t *player) +{ + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + return true; + if (!K_Sliptiding(player) && player->drift == 0 && P_IsObjectOnGround(player->mo) && player->sneakertimer == 0) + return true; + return false; +} + void K_UpdateSliptideZipIndicator(player_t *player) { mobj_t *mobj = NULL; @@ -4063,7 +4072,7 @@ void K_UpdateSliptideZipIndicator(player_t *player) mobj->renderflags &= ~RF_TRANSMASK; mobj->renderflags |= RF_PAPERSPRITE; - if (!K_Sliptiding(player) && player->drift == 0 && P_IsObjectOnGround(player->mo)) + if (K_IsLosingSliptideZip(player)) { // Decay timer's ticking mobj->rollangle += 3*ANG30/4; @@ -9300,7 +9309,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->pflags &= ~PF_DRIFTEND; } - if ((player->handleboost < FRACUNIT/4) + if ((player->handleboost < (SLIPTIDEHANDLING/2)) || (!player->steering) || (!player->aizdriftstrat) || (player->steering > 0) != (player->aizdriftstrat > 0)) @@ -9333,16 +9342,37 @@ static void K_KartDrift(player_t *player, boolean onground) if (!K_Sliptiding(player)) { - if (player->sliptideZip > 0 && player->drift == 0 && P_IsObjectOnGround(player->mo)) + if (K_IsLosingSliptideZip(player) && player->sliptideZip > 0) { player->sliptideZipDelay++; if (player->sliptideZipDelay > TICRATE) { - S_StartSound(player->mo, sfx_s3kb6); - player->sliptideZipBoost += player->sliptideZip; + fixed_t maxZipPower = 2*FRACUNIT; + fixed_t minZipPower = 1*FRACUNIT; + fixed_t powerSpread = maxZipPower - minZipPower; + + int minPenalty = 2*1 + (9-9); // Kinda doing a similar thing to driftspark stage timers here. + int maxPenalty = 2*9 + (9-1); // 1/9 gets max, 9/1 gets min, everyone else gets something in between. + int penaltySpread = maxPenalty - minPenalty; + int yourPenalty = 2*player->kartspeed + (9 - player->kartweight); // And like driftsparks, speed hurts double. + + yourPenalty -= minPenalty; // Normalize; minimum penalty should take away 0 power. + + fixed_t yourPowerReduction = FixedDiv(yourPenalty * FRACUNIT, penaltySpread * FRACUNIT); + fixed_t yourPower = maxZipPower - FixedMul(yourPowerReduction, powerSpread); + int yourBoost = FixedInt(FixedMul(yourPower, player->sliptideZip * FRACUNIT)); + + /* + CONS_Printf("SZ %d MZ %d mZ %d pwS %d mP %d MP %d peS %d yPe %d yPR %d yPw %d yB %d\n", + player->sliptideZip, maxZipPower, minZipPower, powerSpread, minPenalty, maxPenalty, penaltySpread, yourPenalty, yourPowerReduction, yourPower, yourBoost); + */ + + player->sliptideZipBoost += yourBoost; + K_SpawnDriftBoostExplosion(player, 0); player->sliptideZip = 0; player->sliptideZipDelay = 0; + S_StartSound(player->mo, sfx_s3kb6); } } From a55ddef52813dc459eb9afa7370deab0273340c2 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 22:55:01 -0800 Subject: [PATCH 95/97] Add cv_scrambleremoved, disable scramble on P_RemoveMobj Because Monitors and players leaving leak references, disable this crashing to be able to test other stuff at least. --- src/p_local.h | 7 +++++++ src/p_mobj.c | 10 ++++++---- src/r_main.c | 3 +++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index 12362a548..799f81a01 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -66,6 +66,10 @@ extern "C" { #define P_GetPlayerViewHeight(player) (41*player->mo->height/48) +#ifdef PARANOIA +#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed +#endif + typedef enum { THINK_POLYOBJ, @@ -282,6 +286,9 @@ extern mapthing_t *itemrespawnque[ITEMQUESIZE]; extern tic_t itemrespawntime[ITEMQUESIZE]; extern size_t iquehead, iquetail; extern consvar_t cv_gravity, cv_movebob; +#ifdef SCRAMBLE_REMOVED +extern consvar_t cv_scrambleremoved; +#endif void P_RespawnBattleBoxes(void); mobjtype_t P_GetMobjtype(UINT16 mthingtype); diff --git a/src/p_mobj.c b/src/p_mobj.c index 80f413216..6e1c7b111 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -51,6 +51,8 @@ static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); +consvar_t cv_scrambleremoved = CVAR_INIT ("scrambleremoved", "On", CV_NETVAR, CV_OnOff, NULL); + actioncache_t actioncachehead; static mobj_t *overlaycap = NULL; @@ -11059,9 +11061,6 @@ mapthing_t *itemrespawnque[ITEMQUESIZE]; tic_t itemrespawntime[ITEMQUESIZE]; size_t iquehead, iquetail; -#ifdef PARANOIA -#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed -#endif void P_RemoveMobj(mobj_t *mobj) { I_Assert(mobj != NULL); @@ -11197,7 +11196,10 @@ void P_RemoveMobj(mobj_t *mobj) // DBG: set everything in mobj_t to 0xFF instead of leaving it. debug memory error. #ifdef SCRAMBLE_REMOVED // Invalidate mobj_t data to cause crashes if accessed! - memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t)); + if (cv_scrambleremoved.value) + { + memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t)); + } #endif } diff --git a/src/r_main.c b/src/r_main.c index b00717810..901942510 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1651,6 +1651,9 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_maxportals); CV_RegisterVar(&cv_movebob); +#ifdef SCRAMBLE_REMOVED + CV_RegisterVar(&cv_scrambleremoved); +#endif // Frame interpolation/uncapped CV_RegisterVar(&cv_fpscap); From b8a503f56a66215ad6da5007dc0fd6f29a841389 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 6 Mar 2023 23:37:41 -0800 Subject: [PATCH 96/97] Always sync health to bumpers in P_KillPlayer Fixes Death Pit respawning in Battle. --- src/p_inter.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 7204741e2..ff4b9f916 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1934,13 +1934,12 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, return false; } - K_DestroyBumpers(player, 1); - switch (type) { case DMG_DEATHPIT: // Respawn kill types K_DoIngameRespawn(player); + player->mo->health -= K_DestroyBumpers(player, 1); return false; case DMG_SPECTATOR: // disappearifies, but still gotta put items back in play @@ -1997,10 +1996,11 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, P_SetTarget(&boom->target, player->mo); } - K_DestroyBumpers(player, player->bumpers); player->pflags |= PF_ELIMINATED; } + K_DestroyBumpers(player, player->bumpers); + return true; } From 1ccd41c3dbb3132ce7467edf0c12c9d420b189f9 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 7 Mar 2023 19:32:52 +0000 Subject: [PATCH 97/97] R_RenderThickSideRange: Compilation fix It seems `lighteffect` was not a comprehensive enough blockade to referencing an uninitialised `xwalllights` for my strictly-warning compiler. --- src/r_segs.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/r_segs.c b/src/r_segs.c index 0dee4fae3..11bac628c 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -967,14 +967,13 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) fixed_t height; fixed_t bheight = 0; INT32 solid = 0; - INT32 lighteffect = 0; for (i = 0; i < dc_numlights; i++) { // Check if the current light effects the colormap/lightlevel rlight = &dc_lightlist[i]; - lighteffect = !(dc_lightlist[i].flags & FOF_NOSHADE); - if (lighteffect) + xwalllights = NULL; + if (!(dc_lightlist[i].flags & FOF_NOSHADE)) { lightnum = R_AdjustLightLevel(rlight->lightnum); @@ -1037,7 +1036,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) if (height <= windowtop) { - if (lighteffect) + if (xwalllights) { dc_colormap = rlight->rcolormap; dc_lightmap = xwalllights[pindex]; @@ -1074,7 +1073,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) windowtop = bheight; else windowtop = windowbottom + 1; - if (lighteffect) + if (xwalllights) { dc_colormap = rlight->rcolormap; dc_lightmap = xwalllights[pindex];