From dca887e23e519aec983cbd0bcb63164f084893b9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Nov 2021 14:36:58 -0500 Subject: [PATCH 01/36] Do not adjust friction on bots for tire grease --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 29aa4f177..96c488e0f 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -482,7 +482,7 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; fixed_t origFrict, newFrict; - if (rubberband <= 0) + if (rubberband <= 0 || player->tiregrease > 0) { // Never get weaker than normal friction return frict; From c7caf7ab441424cd216015434295dac764005c7b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Nov 2021 14:37:21 -0500 Subject: [PATCH 02/36] Pull the prediction a lot more forward by default --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 96c488e0f..375b1794d 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -571,7 +571,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict - const fixed_t speed = max(P_AproxDistance(player->mo->momx, player->mo->momy), K_GetKartSpeed(player, false) / 4); + const fixed_t speed = max(P_AproxDistance(player->rmomx, player->rmomy), K_GetKartSpeed(player, false)); const INT32 startDist = (768 * mapobjectscale) / FRACUNIT; const INT32 distance = ((FixedMul(speed, distreduce) / FRACUNIT) * futuresight) + startDist; From 40958073e39f17a8063b455b1022e2a6e1fa1e6d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Nov 2021 14:42:44 -0500 Subject: [PATCH 03/36] Increase bot max top speed now that everything around it is fixed lol --- src/k_bot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 375b1794d..44269cf58 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -430,8 +430,8 @@ fixed_t K_BotTopSpeedRubberband(player_t *player) } else { - // Max at +10% for level 9 bots - rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 10); + // Max at +20% for level 9 bots + rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 5); } // Only allow you to go faster than your regular top speed if you're facing the right direction From 6be351a3043c72f38e6dfab5e6b6de5b1ad9fb72 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Nov 2021 14:55:03 -0500 Subject: [PATCH 04/36] Improved item throwing - Bots attempt to do fast item snipes (they will throw more often when above top speed) - They will not waste double jawz anymore (they check for a jawz already targetting who they want to shoot before they decide to shoot it) --- src/k_botitem.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/k_botitem.c b/src/k_botitem.c index 212a84929..a7acea642 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -690,17 +690,19 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) const fixed_t topspeed = K_GetKartSpeed(player, false); fixed_t radius = (player->mo->radius * 32); SINT8 throwdir = -1; + UINT8 snipeMul = 2; if (player->speed > topspeed) { radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! } player->botvars.itemconfirm++; if (K_PlayerInCone(player, radius, 10, false)) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; + player->botvars.itemconfirm += player->botvars.difficulty * snipeMul; throwdir = 1; } else if (K_PlayerInCone(player, radius, 10, true)) @@ -732,10 +734,13 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) const fixed_t topspeed = K_GetKartSpeed(player, false); fixed_t radius = (player->mo->radius * 32); SINT8 throwdir = 1; + UINT8 snipeMul = 2; + INT32 lastTarg = player->lastjawztarget; if (player->speed > topspeed) { radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! } player->botvars.itemconfirm++; @@ -746,10 +751,33 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) throwdir = -1; } - if (player->lastjawztarget != -1) + if (lastTarg != -1 + && playeringame[lastTarg] == true + && players[lastTarg].spectator == false + && players[lastTarg].mo != NULL + && P_MobjWasRemoved(players[lastTarg].mo) == false) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 1; + mobj_t *targ = players[lastTarg].mo; + mobj_t *mobj = NULL, *next = NULL; + boolean targettedAlready = false; + + // Make sure no other Jawz are targetting this player. + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + + if (mobj->type == MT_JAWZ && mobj->target == targ) + { + targettedAlready = true; + break; + } + } + + if (targettedAlready == false) + { + player->botvars.itemconfirm += player->botvars.difficulty * snipeMul; + throwdir = 1; + } } if (player->botvars.itemconfirm > 5*TICRATE) From 3536c73b6634d7a66f459c17f32728c9055a9c39 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Nov 2021 15:08:54 -0500 Subject: [PATCH 05/36] Fix eggbox stealth being bugged and never being used... --- src/k_botsearch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 55533d02a..80650030d 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -122,7 +122,7 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y) } } - return (globalsmuggle.randomitems * globalsmuggle.eggboxes); + return (globalsmuggle.randomitems * (globalsmuggle.eggboxes + 1)); } /*-------------------------------------------------- From e4b27933f2e75e8879c2de267584c73deb5632aa Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Nov 2021 15:13:54 -0500 Subject: [PATCH 06/36] Comment the tire grease change --- src/k_bot.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 44269cf58..435eb1c6b 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -482,12 +482,20 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; fixed_t origFrict, newFrict; - if (rubberband <= 0 || player->tiregrease > 0) + if (rubberband <= 0) { // Never get weaker than normal friction return frict; } + if (player->tiregrease > 0) + { + // This isn't great -- it means rubberbanding will slow down when they hit a spring + // But it's better than the opposite where they accelerate into hyperspace :V + // (would appreciate an actual fix though ... could try being additive instead of multiplicative) + return frict; + } + origFrict = FixedDiv(ORIG_FRICTION, FRACUNIT + (rubberband / 2)); if (frict == ORIG_FRICTION) From fbaa57a39c50f3991730523f47951ad426cb0888 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Nov 2021 15:20:57 -0500 Subject: [PATCH 07/36] Give Landmines its own bot usage function Prevents them from attempting to use landmines tossed forward. (Not really notable at all, since there's no one in front of you when you get landmine, at most means they may hold onto it longer, but it was just a weird thing I always meant to fix) --- src/k_botitem.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/k_botitem.c b/src/k_botitem.c index a7acea642..67d858ea4 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -546,6 +546,39 @@ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) } } +/*-------------------------------------------------- + static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) + + Item usage for landmine tossing. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + turnamt - How hard they currently are turning. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + player->botvars.itemconfirm++; + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + } + + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + } + + if (player->botvars.itemconfirm > 2*TICRATE) + { + K_BotGenericPressItem(player, cmd, -1); + } +} + /*-------------------------------------------------- static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) @@ -1064,7 +1097,6 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) K_BotItemSneaker(player, cmd); break; case KITEM_BANANA: - case KITEM_LANDMINE: if (!(player->pflags & PF_ITEMOUT)) { K_BotItemGenericTrapShield(player, cmd, turnamt, false); @@ -1109,6 +1141,9 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) K_BotItemMine(player, cmd, turnamt); } break; + case KITEM_LANDMINE: + K_BotItemLandmine(player, cmd, turnamt); + break; case KITEM_THUNDERSHIELD: K_BotItemThunder(player, cmd); break; From b716e142a8c715edfc1035a38e8aa3a3aebd7fdf Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 30 Nov 2021 21:24:00 +0000 Subject: [PATCH 08/36] Some fixes for bad player info on joining servers. * Don't overwrite the first player's name when joining with multiple local splitscreen players. (resolves #151) * The defaults for unprovided names are now consistently "Player A/B/C/D" as opposed to some being letter-based and some being number-based. * Send local player info once per local splitscreen player, rather than n^2 times (where n is the number of local splitscreen players). * Since the packet *requires* four names to be sent every time you join a server, send dummy names for local splitscreen players who aren't connecting, instead of your actual cvars (no data leakage you didn't ask for!) --- src/d_clisrv.c | 37 ++++++++++++++-------- src/d_netcmd.c | 86 +++++++++++++++++++++++++++----------------------- src/d_netcmd.h | 3 +- src/g_game.c | 1 - src/r_main.c | 4 --- 5 files changed, 72 insertions(+), 59 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2148804df..e6fcb6643 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -795,10 +795,14 @@ static boolean CL_SendJoin(void) sizeof netbuffer->u.clientcfg.application); for (i = 0; i <= splitscreen; i++) - CleanupPlayerName(g_localplayers[i], cv_playername[i].zstring); - - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + // the MAXPLAYERS addition is necessary to communicate that g_localplayers is not yet safe to reference + CleanupPlayerName(MAXPLAYERS+i, cv_playername[i].zstring); strncpy(netbuffer->u.clientcfg.names[i], cv_playername[i].zstring, MAXPLAYERNAME); + } + // privacy shield for the local players not joining this session + for (; i < MAXSPLITSCREENPLAYERS; i++) + strncpy(netbuffer->u.clientcfg.names[i], va("Player %c", 'A' + i), MAXPLAYERNAME); return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak)); } @@ -1334,7 +1338,7 @@ static void CL_ReloadReceivedSavegame(void) for (i = 0; i < MAXPLAYERS; i++) { LUA_InvalidatePlayer(&players[i]); - sprintf(player_names[i], "Player %d", i + 1); + sprintf(player_names[i], "Player %c", 'A' + i); } CL_LoadReceivedSavegame(true); @@ -2401,7 +2405,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) doomcom->numslots--; // Reset the name - sprintf(player_names[playernum], "Player %d", playernum+1); + sprintf(player_names[playernum], "Player %c", 'A' + playernum); player_name_changes[playernum] = 0; @@ -3213,7 +3217,7 @@ void SV_ResetServer(void) playeringame[i] = false; playernode[i] = UINT8_MAX; memset(playeraddress[i], 0, sizeof(*playeraddress)); - sprintf(player_names[i], "Player %d", i + 1); + sprintf(player_names[i], "Player %c", 'A' + i); adminplayers[i] = -1; // Populate the entire adminplayers array with -1. K_ClearClientPowerLevels(); splitscreen_invitations[i] = -1; @@ -3412,7 +3416,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) P_ForceLocalAngle(newplayer, newplayer->angleturn); - D_SendPlayerConfig(); + D_SendPlayerConfig(splitscreenplayer); addedtogame = true; if (rejoined) @@ -3558,14 +3562,13 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) LUAh_PlayerJoin(newplayernum); } -static boolean SV_AddWaitingPlayers(const char *name, const char *name2, const char *name3, const char *name4) +static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *name2, const char *name3, const char *name4) { - INT32 node, n, newplayer = false; + INT32 n, newplayernum; UINT8 buf[4 + MAXPLAYERNAME]; UINT8 *buf_p = buf; - INT32 newplayernum; + boolean newplayer = false; - for (node = 0; node < MAXNETNODES; node++) { // splitscreen can allow 2+ players in one node for (; nodewaiting[node] > 0; nodewaiting[node]--) @@ -3684,6 +3687,7 @@ boolean SV_SpawnServer(void) I_Error("What do you think you're doing?"); return false; #else + boolean result = false; if (demo.playback) G_StopDemo(); // reset engine parameter if (metalplayback) @@ -3710,7 +3714,14 @@ boolean SV_SpawnServer(void) else doomcom->numslots = 1; } - return SV_AddWaitingPlayers(cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring); + // strictly speaking, i'm not convinced the following is necessary + // but I'm not confident enough to remove it entirely in case it breaks something + { + SINT8 node = 0; + for (; node < MAXNETNODES; node++) + result |= SV_AddWaitingPlayers(node, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring); + } + return result; #endif } @@ -3882,7 +3893,7 @@ static void HandleConnect(SINT8 node) SV_SendSaveGame(node, false); // send a complete game state DEBFILE("send savegame\n"); } - SV_AddWaitingPlayers(names[0], names[1], names[2], names[3]); + SV_AddWaitingPlayers(node, names[0], names[1], names[2], names[3]); joindelay += cv_joindelay.value * TICRATE; player_joining = true; } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4ed110f35..3524790f6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -222,8 +222,6 @@ static void Command_KartGiveItem_f(void); // CLIENT VARIABLES // ========================================================================= -void SendWeaponPref(UINT8 n); - static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force"}, {0, NULL}}; #ifdef LJOYSTICK @@ -1110,6 +1108,7 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) * is restored to what it was before. * * We assume that if playernum is in ::g_localplayers + * (unless clientjoin is true, a necessary evil) * the console variable ::cv_playername[n] is * already set to newname. However, the player name table is assumed to * contain the old name. @@ -1128,6 +1127,10 @@ void CleanupPlayerName(INT32 playernum, const char *newname) char *tmpname = NULL; INT32 i; boolean namefailed = true; + boolean clientjoin = !!(playernum >= MAXPLAYERS); + + if (clientjoin) + playernum -= MAXPLAYERS; buf = Z_StrDup(newname); @@ -1185,17 +1188,20 @@ void CleanupPlayerName(INT32 playernum, const char *newname) } // no stealing another player's name - for (i = 0; i < MAXPLAYERS; i++) + if (!clientjoin) { - if (i != playernum && playeringame[i] - && strcasecmp(tmpname, player_names[i]) == 0) + for (i = 0; i < MAXPLAYERS; i++) { - break; + if (i != playernum && playeringame[i] + && strcasecmp(tmpname, player_names[i]) == 0) + { + break; + } } - } - if (i < MAXPLAYERS) - break; + if (i < MAXPLAYERS) + break; + } // name is okay then namefailed = false; @@ -1206,18 +1212,23 @@ void CleanupPlayerName(INT32 playernum, const char *newname) // set consvars whether namefailed or not, because even if it succeeded, // spaces may have been removed - for (i = 0; i <= splitscreen; i++) + if (clientjoin) + CV_StealthSet(&cv_playername[playernum], tmpname); + else { - if (playernum == g_localplayers[i]) + for (i = 0; i <= splitscreen; i++) { - CV_StealthSet(&cv_playername[i], tmpname); - break; + if (playernum == g_localplayers[i]) + { + CV_StealthSet(&cv_playername[i], tmpname); + break; + } } - } - if (i > splitscreen) - { - I_Assert(((void)"CleanupPlayerName used on non-local player", 0)); + if (i > splitscreen) + { + I_Assert(((void)"CleanupPlayerName used on non-local player", 0)); + } } Z_Free(buf); @@ -1811,33 +1822,28 @@ static void Got_LeaveParty(UINT8 **cp,INT32 playernum) } } -void D_SendPlayerConfig(void) +void D_SendPlayerConfig(UINT8 n) { - UINT8 i; + UINT8 buf[4]; + UINT8 *p = buf; - for (i = 0; i <= splitscreen; i++) + SendNameAndColor(n); + SendWeaponPref(n); + + if (n == 0) { - UINT8 buf[4]; - UINT8 *p = buf; - - SendNameAndColor(i); - SendWeaponPref(i); - - if (i == 0) - { - // Send it over - WRITEUINT16(p, vspowerlevel[PWRLV_RACE]); - WRITEUINT16(p, vspowerlevel[PWRLV_BATTLE]); - } - else - { - // Splitscreen players have invalid powerlevel - WRITEUINT16(p, 0); - WRITEUINT16(p, 0); - } - - SendNetXCmdForPlayer(i, XD_POWERLEVEL, buf, p-buf); + // Send it over + WRITEUINT16(p, vspowerlevel[PWRLV_RACE]); + WRITEUINT16(p, vspowerlevel[PWRLV_BATTLE]); } + else + { + // Splitscreen players have invalid powerlevel + WRITEUINT16(p, 0); + WRITEUINT16(p, 0); + } + + SendNetXCmdForPlayer(n, XD_POWERLEVEL, buf, p-buf); } // Only works for displayplayer, sorry! diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 41dc3fdf7..0e8ef25e2 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -207,7 +207,8 @@ void D_RegisterServerCommands(void); void D_RegisterClientCommands(void); void CleanupPlayerName(INT32 playernum, const char *newname); boolean EnsurePlayerNameIsGood(char *name, INT32 playernum); -void D_SendPlayerConfig(void); +void SendWeaponPref(UINT8 n); +void D_SendPlayerConfig(UINT8 n); void Command_ExitGame_f(void); void Command_Retry_f(void); void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore diff --git a/src/g_game.c b/src/g_game.c index 000be94cd..e4fd7d209 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -346,7 +346,6 @@ static void kickstartaccel_OnChange(void); static void kickstartaccel2_OnChange(void); static void kickstartaccel3_OnChange(void); static void kickstartaccel4_OnChange(void); -void SendWeaponPref(UINT8 n); static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, {1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"}, diff --git a/src/r_main.c b/src/r_main.c index 231bde280..cd9a6bf22 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -149,10 +149,6 @@ static void ChaseCam_OnChange(void); static void ChaseCam2_OnChange(void); static void ChaseCam3_OnChange(void); static void ChaseCam4_OnChange(void); -void SendWeaponPref(void); -void SendWeaponPref2(void); -void SendWeaponPref3(void); -void SendWeaponPref4(void); consvar_t cv_tailspickup = CVAR_INIT ("tailspickup", "On", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_chasecam[MAXSPLITSCREENPLAYERS] = { From a617563bd88c762f1579ad23fcf95c0a4d1eafb9 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 9 Dec 2021 17:44:23 -0800 Subject: [PATCH 09/36] Add gasp sfx when exiting water, but not with bubble shield --- src/p_mobj.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 5a9f0925e..9550b0c41 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3059,7 +3059,16 @@ void P_MobjCheckWater(mobj_t *mobj) return; // The rest of this code only executes on a water state change. - if (waterwasnotset || !!(mobj->eflags & MFE_UNDERWATER) == wasinwater) + if (!!(mobj->eflags & MFE_UNDERWATER) == wasinwater) + return; + + if (p && !p->waterskip && + p->curshield != KSHIELD_BUBBLE && wasinwater) + { + S_StartSound(mobj, sfx_gasp); + } + + if (waterwasnotset) return; if ((p) // Players From a90520ebdcad4f7485d53b86df8b057809bbddcd Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 10 Dec 2021 00:32:46 -0800 Subject: [PATCH 10/36] Do not factor roll and pitch into model rollangle Those transformations are applied separately so the model tilts in 3d space. --- src/hardware/hw_md2.c | 2 +- src/r_patch.h | 1 + src/r_patchrotation.c | 11 ++++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 259770fee..95077a54a 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1650,7 +1650,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } { - fixed_t anglef = AngleFixed(R_SpriteRotationAngle(spr->mobj, NULL)); + fixed_t anglef = AngleFixed(R_ModelRotationAngle(spr->mobj, NULL)); p.rollangle = 0.0f; diff --git a/src/r_patch.h b/src/r_patch.h index 601144a1d..b86908281 100644 --- a/src/r_patch.h +++ b/src/r_patch.h @@ -42,6 +42,7 @@ patch_t *Patch_GetRotatedSprite( INT32 R_GetRollAngle(angle_t rollangle); angle_t R_GetPitchRollAngle(mobj_t *mobj, player_t *viewPlayer); +angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer); angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer); #endif diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c index 24c1de5d2..74afeb396 100644 --- a/src/r_patchrotation.c +++ b/src/r_patchrotation.c @@ -64,10 +64,9 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer) return rollAngle; } -angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer) +angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer) { - angle_t rollOrPitch = R_GetPitchRollAngle(mobj, viewPlayer); - angle_t rollAngle = (rollOrPitch + mobj->rollangle); + angle_t rollAngle = mobj->rollangle; if (mobj->player) { @@ -77,6 +76,12 @@ angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer) return rollAngle; } +angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer) +{ + angle_t rollOrPitch = R_GetPitchRollAngle(mobj, viewPlayer); + return (rollOrPitch + R_ModelRotationAngle(mobj, viewPlayer)); +} + INT32 R_GetRollAngle(angle_t rollangle) { INT32 ra = AngleFixed(rollangle)>>FRACBITS; From a3d5804da9c80c8b872735a14239fe9f81ce6681 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 10 Dec 2021 02:45:32 -0800 Subject: [PATCH 11/36] Let flame shield resist slope butter --- 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 6b4a3dfc0..924195239 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2682,7 +2682,7 @@ boolean K_ApplyOffroad(player_t *player) boolean K_SlopeResistance(player_t *player) { - if (player->invincibilitytimer || player->sneakertimer || player->tiregrease) + if (player->invincibilitytimer || player->sneakertimer || player->tiregrease || player->flamedash) return true; return false; } From 1adbfcff03fd256793dad2761f88b9ac49475269 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 10 Dec 2021 14:16:20 +0100 Subject: [PATCH 12/36] Remove Landmine dust ring on explosion --- src/p_enemy.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 7d4ad7400..51319d229 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -14143,7 +14143,6 @@ void A_LandMineExplode(mobj_t *actor) INT32 colour = SKINCOLOR_KETCHUP; // we spell words properly here INT32 i; mobj_t *smoldering; - mobj_t *dust; if (LUA_CallAction(A_LANDMINEEXPLODE, actor)) return; @@ -14161,18 +14160,6 @@ void A_LandMineExplode(mobj_t *actor) P_SetScale(smoldering, actor->scale); smoldering->tics = TICRATE*3; - // Spawn a ring: - for (i = 0; i < 32; i++) - { - dust = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE); - P_SetMobjState(dust, S_OPAQUESMOKE1); - dust->angle = (ANGLE_180/16) * i; - P_SetScale(dust, actor->scale); - dust->destscale = actor->scale*4; - dust->scalespeed = actor->scale/4; - P_InstaThrust(dust, dust->angle, FixedMul(20*FRACUNIT, actor->scale)); - } - actor->fuse = actor->tics; // disappear when this state ends. // spawn a few physics explosions From 07d5691b73673635a9e77f25d6c141f89420c99f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Dec 2021 16:23:41 -0500 Subject: [PATCH 13/36] Make them commit harder to turning - Removed the "minor adjustment" threshold, since that was done before global easing - Removed the "accel + brake to slowdown and reorient yourself if you're facing too far away from the track" bit, because 1.) it was done before accel + brake was turned into EBrake (so it now often makes them come to a STOP instead of simply slowing down) and 2.) turning harshly will slow them down anyway, so maybe isn't necessary --- src/k_bot.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 435eb1c6b..9c5dff35b 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1120,18 +1120,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Don't turn at all turnamt = 0; } - else - { - // Make minor adjustments - turnamt /= 4; - } - } - - if (anglediff > 60) - { - // Actually, don't go too fast... - cmd->forwardmove /= 2; - cmd->buttons |= BT_BRAKE; } } } From 19463d6b207afb1a90f9e1b009ff48b473833fc0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Dec 2021 17:42:00 -0500 Subject: [PATCH 14/36] New bot prediction wall detection Instead of searching for walls around the player, and then deciding to make the radius tighter if it found anyway, it instead checks if the waypoint it's trying to predict towards was blocked by any walls / hazards. Needs adjusted some, I think its being pulled back too hard sometimes, but I am optimistic about some of the improvements I already saw. --- src/k_bot.c | 37 ++++++++-- src/k_bot.h | 15 ++-- src/k_botsearch.c | 181 +-------------------------------------------- src/p_local.h | 3 + src/p_map.c | 56 +++++++++----- src/p_sight.c | 182 +++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 265 insertions(+), 209 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 9c5dff35b..1d9b8ff83 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -575,14 +575,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to - const fixed_t distreduce = K_BotReducePrediction(player); - const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); - const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict - const fixed_t speed = max(P_AproxDistance(player->rmomx, player->rmomy), K_GetKartSpeed(player, false)); + const fixed_t speed = P_AproxDistance(player->rmomx, player->rmomy); - const INT32 startDist = (768 * mapobjectscale) / FRACUNIT; - const INT32 distance = ((FixedMul(speed, distreduce) / FRACUNIT) * futuresight) + startDist; + const INT32 startDist = (1536 * mapobjectscale) / FRACUNIT; + const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); waypoint_t *wp = player->nextwaypoint; @@ -591,6 +588,9 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) fixed_t smallestradius = INT32_MAX; angle_t angletonext = ANGLE_MAX; + // Halves radius when encountering a wall on your way to your destination. + fixed_t radreduce = FRACUNIT; + size_t nwp; size_t i; @@ -603,7 +603,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) { predict->x = wp->mobj->x; predict->y = wp->mobj->y; - predict->radius = FixedMul(wp->mobj->radius, radreduce); + predict->radius = wp->mobj->radius; return predict; } @@ -653,6 +653,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) continue; } + if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[i]->mobj) == false) + { + continue; + } + // Unlike the other parts of this function, we're comparing the player's physical position, NOT the position of the waypoint!! // This should roughly correspond with how players will think about path splits. a = R_PointToAngle2( @@ -672,6 +677,24 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) delta = a; } } + + if (i == wp->numnextwaypoints) + { + // No usable waypoint, we don't want to check any further + radreduce /= 2; + distanceleft = 0; + break; + } + } + else + { + if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[nwp]->mobj) == false) + { + // If we can't get a direct path to this waypoint, we don't want to check any further. + radreduce /= 2; + distanceleft = 0; + break; + } } angletonext = R_PointToAngle2( diff --git a/src/k_bot.h b/src/k_bot.h index 94694c2dd..41c8df5e0 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -185,19 +185,22 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y); /*-------------------------------------------------- - fixed_t K_BotReducePrediction(player_t *player); + boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) - Finds walls nearby the specified player, to create a multiplier - to pull bot predictions back by. + Tells us if a bot will play more careful around + this sector. Checks FOFs in the sector, as well. Input Arguments:- - player - Player to compare. + player - Player to check against. + sec - Sector to check against. + x - Linedef cross X position, for slopes + y - Linedef cross Y position, for slopes Return:- - Multiplier in fixed point scale. + true if avoiding this sector, false otherwise. --------------------------------------------------*/ -fixed_t K_BotReducePrediction(player_t *player); +boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y); /*-------------------------------------------------- diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 80650030d..846521e6f 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -162,21 +162,11 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) } /*-------------------------------------------------- - static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) + boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) - Tells us if a bot will play more careful around - this sector. Checks FOFs in the sector, as well. - - Input Arguments:- - player - Player to check against. - sec - Sector to check against. - x - Linedef cross X position, for slopes - y - Linedef cross Y position, for slopes - - Return:- - true if avoiding this sector, false otherwise. + See header file for description. --------------------------------------------------*/ -static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) +boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) { const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); INT32 specialflag = 0; @@ -257,171 +247,6 @@ static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, return K_BotHatesThisSectorsSpecial(player, bestsector); } -/*-------------------------------------------------- - static boolean K_FindBlockingWalls(line_t *line) - - Blockmap search function. - Reels the bot prediction back in based on solid walls - or other obstacles surrounding the bot. - - Input Arguments:- - line - Linedef passed in from iteration. - - Return:- - true continues searching, false ends the search early. ---------------------------------------------------*/ -static boolean K_FindBlockingWalls(line_t *line) -{ - // Condensed version of PIT_CheckLine - const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); - fixed_t maxstep = maxstepmove; - fixed_t linedist = INT32_MAX; - INT32 lineside = 0; - vertex_t pos; - - if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player) - { - return false; - } - - if (line->polyobj && !(line->polyobj->flags & POF_SOLID)) - { - return true; - } - - if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT] - || tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP]) - { - return true; - } - - if (P_BoxOnLineSide(tmbbox, line) != -1) - { - return true; - } - - lineside = P_PointOnLineSide(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line); - - // one sided line - if (!line->backsector) - { - if (lineside) - { - // don't hit the back side - return true; - } - - goto blocked; - } - - if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS)) - { - goto blocked; - } - - // set openrange, opentop, openbottom - P_LineOpening(line, globalsmuggle.botmo); - - if (globalsmuggle.botmo->player->waterskip) - maxstep += maxstepmove; - - if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 13, false)) - maxstep <<= 1; - else if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 12, false)) - maxstep = 0; - - if ((openrange < globalsmuggle.botmo->height) // doesn't fit - || (opentop - globalsmuggle.botmo->z < globalsmuggle.botmo->height) // mobj is too high - || (openbottom - globalsmuggle.botmo->z > maxstep)) // too big a step up - { - goto blocked; - } - - // Treat damage sectors like walls - P_ClosestPointOnLine(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line, &pos); - - if (lineside) - { - if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->frontsector, pos.x, pos.y)) - goto blocked; - } - else - { - if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->backsector, pos.x, pos.y)) - goto blocked; - } - - // We weren't blocked! - return true; - -blocked: - linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, globalsmuggle.botmo->x, globalsmuggle.botmo->y); - linedist -= (globalsmuggle.botmo->radius * 8); // Maintain a reasonable distance away from it - - if (linedist > globalsmuggle.distancetocheck) - { - return true; - } - - if (linedist <= 0) - { - globalsmuggle.closestlinedist = 0; - return false; - } - - if (linedist < globalsmuggle.closestlinedist) - { - globalsmuggle.closestlinedist = linedist; - } - - return true; -} - -/*-------------------------------------------------- - fixed_t K_BotReducePrediction(player_t *player) - - See header file for description. ---------------------------------------------------*/ -fixed_t K_BotReducePrediction(player_t *player) -{ - INT32 xl, xh, yl, yh, bx, by; - - globalsmuggle.botmo = player->mo; - globalsmuggle.distancetocheck = (player->mo->radius * 32); - globalsmuggle.closestlinedist = INT32_MAX; - - tmx = player->mo->x; - tmy = player->mo->y; - - xl = (unsigned)(tmx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(tmx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(tmy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(tmy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - tmbbox[BOXTOP] = tmy + globalsmuggle.distancetocheck; - tmbbox[BOXBOTTOM] = tmy - globalsmuggle.distancetocheck; - tmbbox[BOXRIGHT] = tmx + globalsmuggle.distancetocheck; - tmbbox[BOXLEFT] = tmx - globalsmuggle.distancetocheck; - - // Check for lines that the bot might collide with - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockLinesIterator(bx, by, K_FindBlockingWalls); - } - } - - if (globalsmuggle.closestlinedist == INT32_MAX) - { - return FRACUNIT; - } - - return (FRACUNIT/2) + (FixedDiv(globalsmuggle.closestlinedist, globalsmuggle.distancetocheck) / 2); -} - /*-------------------------------------------------- static void K_AddAttackObject(mobj_t *thing, UINT8 side, UINT8 weight) diff --git a/src/p_local.h b/src/p_local.h index db9e9e6dd..d90bfde4c 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -404,6 +404,8 @@ boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing); boolean P_IsLineTripWire(const line_t *ld); boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y); boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam); +fixed_t P_BaseStepUp(void); +fixed_t P_GetThingStepUp(mobj_t *thing); boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); boolean P_Move(mobj_t *actor, fixed_t speed); boolean P_SetOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z); @@ -413,6 +415,7 @@ void P_BouncePlayerMove(mobj_t *mo); void P_BounceMove(mobj_t *mo); boolean P_CheckSight(mobj_t *t1, mobj_t *t2); boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2); +boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2); void P_CheckHoopPosition(mobj_t *hoopthing, fixed_t x, fixed_t y, fixed_t z, fixed_t radius); boolean P_CheckSector(sector_t *sector, boolean crunch); diff --git a/src/p_map.c b/src/p_map.c index bcecb1dfa..966c332b8 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2428,6 +2428,42 @@ static boolean P_WaterStepUp(mobj_t *thing) P_WaterRunning(thing); } +fixed_t P_BaseStepUp(void) +{ + return FixedMul(MAXSTEPMOVE, mapobjectscale); +} + +fixed_t P_GetThingStepUp(mobj_t *thing) +{ + const fixed_t maxstepmove = P_BaseStepUp(); + fixed_t maxstep = maxstepmove; + + if (thing->type == MT_SKIM) + { + // Skim special (not needed for kart?) + return 0; + } + + if (P_WaterStepUp(thing) == true) + { + // Add some extra stepmove when waterskipping + maxstep += maxstepmove; + } + + if (P_MobjTouchingSectorSpecial(thing, 1, 13, false)) + { + // If using type Section1:13, double the maxstep. + maxstep <<= 1; + } + else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false)) + { + // If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin + maxstep = 0; + } + + return maxstep; +} + // // P_TryMove // Attempt to move to a new position. @@ -2489,21 +2525,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (!(thing->flags & MF_NOCLIP)) { //All things are affected by their scale. - const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); - fixed_t maxstep = maxstepmove; - - if (thing->player && P_WaterStepUp(thing)) - maxstep += maxstepmove; // Add some extra stepmove when waterskipping - - // If using type Section1:13, double the maxstep. - if (P_MobjTouchingSectorSpecial(thing, 1, 13, false)) - maxstep <<= 1; - // If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin - else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false)) - maxstep = 0; - - if (thing->type == MT_SKIM) - maxstep = 0; + fixed_t maxstep = P_GetThingStepUp(thing); if (tmceilingz - tmfloorz < thing->height) { @@ -2740,7 +2762,7 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y) if (!(thing->flags & MF_NOCLIP)) { - const fixed_t maxstep = FixedMul(MAXSTEPMOVE, mapobjectscale); + const fixed_t maxstep = P_BaseStepUp(); if (tmceilingz - tmfloorz < thing->height) return false; // doesn't fit @@ -3202,7 +3224,7 @@ static boolean PTR_LineIsBlocking(line_t *li) if (opentop - slidemo->z < slidemo->height) return true; // mobj is too high - if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, mapobjectscale)) + if (openbottom - slidemo->z > P_GetThingStepUp(slidemo)) return true; // too big a step up return false; diff --git a/src/p_sight.c b/src/p_sight.c index 38a50df36..a5aebf55c 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -18,6 +18,8 @@ #include "r_main.h" #include "r_state.h" +#include "k_bot.h" // K_BotHatesThisSector + // // P_CheckSight // @@ -572,7 +574,7 @@ static boolean P_CrossBlockingSubsector(size_t num, register traceblocking_t *tb if (P_IsLineBlocking(line, tb->compareThing) == true) { - // This line will block us + // This line will always block us return false; } } @@ -656,3 +658,181 @@ boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2) // the head node is the last node output return P_CrossBSPNodeBlocking((INT32)numnodes - 1, &tb); } + +// +// ANOTHER version, this time for bot traversal. +// (TODO: since we have so many versions of this function, the differences +// should maybe just be a function var that gets called?) +// + +static boolean P_CrossBotTraversalSubsector(size_t num, register traceblocking_t *tb) +{ + seg_t *seg; + INT32 count; + +#ifdef RANGECHECK + if (num >= numsubsectors) + I_Error("P_CrossBotTraversalSubsector: ss %s with numss = %s\n", sizeu1(num), sizeu2(numsubsectors)); +#endif + + // haleyjd 02/23/06: this assignment should be after the above check + seg = segs + subsectors[num].firstline; + + for (count = subsectors[num].numlines; --count >= 0; seg++) // check lines + { + line_t *line = seg->linedef; + divline_t divl; + const vertex_t *v1,*v2; + fixed_t maxstep = INT32_MAX; + + if (seg->glseg) + continue; + + // already checked other side? + if (line->validcount == validcount) + continue; + + line->validcount = validcount; + + // OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test + if (line->bbox[BOXLEFT ] > tb->bbox[BOXRIGHT ] || + line->bbox[BOXRIGHT ] < tb->bbox[BOXLEFT ] || + line->bbox[BOXBOTTOM] > tb->bbox[BOXTOP ] || + line->bbox[BOXTOP] < tb->bbox[BOXBOTTOM]) + continue; + + v1 = line->v1; + v2 = line->v2; + + // line isn't crossed? + if (P_DivlineSide(v1->x, v1->y, &tb->strace) == + P_DivlineSide(v2->x, v2->y, &tb->strace)) + continue; + + // stop because it is not two sided anyway + if (!(line->flags & ML_TWOSIDED)) + return false; + + divl.dx = v2->x - (divl.x = v1->x); + divl.dy = v2->y - (divl.y = v1->y); + + // line isn't crossed? + if (P_DivlineSide(tb->strace.x, tb->strace.y, &divl) == + P_DivlineSide(tb->t2x, tb->t2y, &divl)) + continue; + + if (P_IsLineBlocking(line, tb->compareThing) == true) + { + // This line will always block us + return false; + } + + // set openrange, opentop, openbottom + P_LineOpening(line, tb->compareThing); + maxstep = P_GetThingStepUp(tb->compareThing); + + if ((openrange < tb->compareThing->height) // doesn't fit + || (opentop - tb->compareThing->z < tb->compareThing->height) // mobj is too high + || (openbottom - tb->compareThing->z > maxstep)) // too big a step up + { + // This line situationally blocks us + return false; + } + + // Treat damage sectors like walls + if (tb->compareThing->player != NULL) + { + INT32 lineside = 0; + vertex_t pos; + + P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos); + lineside = P_PointOnLineSide(tb->compareThing->x, tb->compareThing->y, line); + + if (K_BotHatesThisSector(tb->compareThing->player, lineside ? line->frontsector : line->backsector, pos.x, pos.y)) + { + // This line does not block us, but we don't want to be in it. + return false; + } + } + } + + // passed the subsector ok + return true; +} + +static boolean P_CrossBSPNodeBotTraversal(INT32 bspnum, register traceblocking_t *tb) +{ + while (!(bspnum & NF_SUBSECTOR)) + { + register node_t *bsp = nodes + bspnum; + INT32 side = P_DivlineSide(tb->strace.x,tb->strace.y,(divline_t *)bsp)&1; + if (side == P_DivlineSide(tb->t2x, tb->t2y, (divline_t *) bsp)) + bspnum = bsp->children[side]; // doesn't touch the other side + else // the partition plane is crossed here + { + if (!P_CrossBSPNodeBotTraversal(bsp->children[side], tb)) + return false; // cross the starting side + else + bspnum = bsp->children[side^1]; // cross the ending side + } + } + + return P_CrossBotTraversalSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), tb); +} + +boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2) +{ + const sector_t *s1, *s2; + size_t pnum; + traceblocking_t tb; + + // First check for trivial rejection. + if (!t1 || !t2) + return false; + + I_Assert(!P_MobjWasRemoved(t1)); + I_Assert(!P_MobjWasRemoved(t2)); + + if (!t1->subsector || !t2->subsector + || !t1->subsector->sector || !t2->subsector->sector) + return false; + + s1 = t1->subsector->sector; + s2 = t2->subsector->sector; + pnum = (s1-sectors)*numsectors + (s2-sectors); + + if (rejectmatrix != NULL) + { + // Check in REJECT table. + if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected + return false; + } + + // killough 11/98: shortcut for melee situations + // same subsector? obviously visible + // haleyjd 02/23/06: can't do this if there are polyobjects in the subsec + if (!t1->subsector->polyList && + t1->subsector == t2->subsector) + return true; + + validcount++; + + tb.strace.dx = (tb.t2x = t2->x) - (tb.strace.x = t1->x); + tb.strace.dy = (tb.t2y = t2->y) - (tb.strace.y = t1->y); + + if (t1->x > t2->x) + tb.bbox[BOXRIGHT] = t1->x, tb.bbox[BOXLEFT] = t2->x; + else + tb.bbox[BOXRIGHT] = t2->x, tb.bbox[BOXLEFT] = t1->x; + + if (t1->y > t2->y) + tb.bbox[BOXTOP] = t1->y, tb.bbox[BOXBOTTOM] = t2->y; + else + tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y; + + tb.compareThing = t1; + + // the head node is the last node output + return P_CrossBSPNodeBotTraversal((INT32)numnodes - 1, &tb); +} + From 08bfd6e8811f77a666235f79215ce7d6104d72e4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 11 Dec 2021 01:51:40 -0500 Subject: [PATCH 15/36] Improve the new wall detection further They didn't fall off on Hardhat, I'm so proud of my sons --- src/k_bot.c | 34 +++++++++------------------------- src/k_bot.h | 1 + src/p_sight.c | 21 +++++++++++++-------- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 1d9b8ff83..d11142119 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -576,9 +576,9 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict - const fixed_t speed = P_AproxDistance(player->rmomx, player->rmomy); + const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); - const INT32 startDist = (1536 * mapobjectscale) / FRACUNIT; + const INT32 startDist = (768 * mapobjectscale) / FRACUNIT; const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); @@ -653,11 +653,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) continue; } - if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[i]->mobj) == false) - { - continue; - } - // Unlike the other parts of this function, we're comparing the player's physical position, NOT the position of the waypoint!! // This should roughly correspond with how players will think about path splits. a = R_PointToAngle2( @@ -677,24 +672,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) delta = a; } } - - if (i == wp->numnextwaypoints) - { - // No usable waypoint, we don't want to check any further - radreduce /= 2; - distanceleft = 0; - break; - } - } - else - { - if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[nwp]->mobj) == false) - { - // If we can't get a direct path to this waypoint, we don't want to check any further. - radreduce /= 2; - distanceleft = 0; - break; - } } angletonext = R_PointToAngle2( @@ -704,6 +681,13 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) disttonext = (INT32)wp->nextwaypointdistances[nwp]; + if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[nwp]->mobj) == false) + { + // If we can't get a direct path to this waypoint, we don't want to check much further... + disttonext *= 2; + radreduce = FRACUNIT/2; + } + if (disttonext > distanceleft) { break; diff --git a/src/k_bot.h b/src/k_bot.h index 41c8df5e0..d354bbb2c 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -15,6 +15,7 @@ #include "k_waypoint.h" #include "d_player.h" +#include "r_defs.h" // Maximum value of botvars.difficulty #define MAXBOTDIFFICULTY 9 diff --git a/src/p_sight.c b/src/p_sight.c index a5aebf55c..8e63f1be4 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -742,16 +742,21 @@ static boolean P_CrossBotTraversalSubsector(size_t num, register traceblocking_t // Treat damage sectors like walls if (tb->compareThing->player != NULL) { - INT32 lineside = 0; - vertex_t pos; + boolean alreadyHates = K_BotHatesThisSector(tb->compareThing->player, tb->compareThing->subsector->sector, tb->compareThing->x, tb->compareThing->y); - P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos); - lineside = P_PointOnLineSide(tb->compareThing->x, tb->compareThing->y, line); - - if (K_BotHatesThisSector(tb->compareThing->player, lineside ? line->frontsector : line->backsector, pos.x, pos.y)) + if (alreadyHates == false) { - // This line does not block us, but we don't want to be in it. - return false; + INT32 lineside = 0; + vertex_t pos; + + P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos); + lineside = P_PointOnLineSide(tb->compareThing->x, tb->compareThing->y, line); + + if (K_BotHatesThisSector(tb->compareThing->player, ((lineside == 1) ? line->frontsector : line->backsector), pos.x, pos.y)) + { + // This line does not block us, but we don't want to be in it. + return false; + } } } } From ba26a3a22350b442e22e77e9e2f3c2a04b2804e8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 11 Dec 2021 02:22:33 -0500 Subject: [PATCH 16/36] Require bots to be much slower to consider spindash --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index d11142119..1a75ea06c 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -776,7 +776,7 @@ static UINT8 K_TrySpindash(player_t *player) return 0; } - if (speedDiff < (3 * baseAccel / 4)) + if (speedDiff < (baseAccel / 4)) { if (player->botvars.spindashconfirm < BOTSPINDASHCONFIRM) { From 8d5cec5ebf31b2cb74a042f4ebe2d335f82bc049 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 11 Dec 2021 04:12:06 -0500 Subject: [PATCH 17/36] Move where PF_HITFINISHLINE is removed Also removes a few remaining instances of player->bot being checked to affect gameplay where it shouldn't from vanilla, but it's code that is rarely used / completely unused anyway. --- src/k_kart.c | 2 -- src/p_mobj.c | 2 +- src/p_user.c | 8 +++++--- src/r_skins.c | 5 ----- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 7401b9d4c..2b5d1ace9 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8629,8 +8629,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground) boolean HOLDING_ITEM = (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT)); boolean NO_HYUDORO = (player->stealingtimer == 0); - player->pflags &= ~PF_HITFINISHLINE; - if (!player->exiting) { if (player->oldposition < player->position) // But first, if you lost a place, diff --git a/src/p_mobj.c b/src/p_mobj.c index 5a9f0925e..ad86d0434 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4095,7 +4095,7 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest) player = &players[actor->lastlook]; - if (player->bot || player->spectator) + if (player->spectator) continue; // ignore notarget if (!player->mo || P_MobjWasRemoved(player->mo)) diff --git a/src/p_user.c b/src/p_user.c index 5f5242e66..5af3386d6 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4222,9 +4222,6 @@ void P_PlayerThink(player_t *player) ticcmd_t *cmd; const size_t playeri = (size_t)(player - players); - player->old_drawangle = player->drawangle; - player->old_viewrollangle = player->viewrollangle; - #ifdef PARANOIA if (!player->mo) I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri)); @@ -4237,6 +4234,11 @@ void P_PlayerThink(player_t *player) player->playerstate = PST_DEAD; } + player->old_drawangle = player->drawangle; + player->old_viewrollangle = player->viewrollangle; + + player->pflags &= ~PF_HITFINISHLINE; + if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj)) { P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid diff --git a/src/r_skins.c b/src/r_skins.c index 1e80e18ce..ba7ed0811 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -311,11 +311,6 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) } player->skincolor = newcolor = skin->prefcolor; - if (player->bot && botingame) - { - botskin = (UINT8)(skinnum + 1); - botcolor = skin->prefcolor; - } } #endif From bfdae2840995b7031c1c8d64da6e6616bb840f94 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 11 Dec 2021 04:18:09 -0500 Subject: [PATCH 18/36] Not Climable flag on a bot controller disables rubberbanding --- src/k_bot.c | 142 +++++++++++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 64 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 1a75ea06c..f3555deed 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -283,6 +283,70 @@ boolean K_BotCanTakeCut(player_t *player) return false; } +/*-------------------------------------------------- + static INT16 K_FindBotController(mobj_t *mo) + + Finds if any bot controller linedefs are tagged to the bot's sector. + + Input Arguments:- + mo - The bot player's mobj. + + Return:- + Line number of the bot controller. -1 if it doesn't exist. +--------------------------------------------------*/ +static INT16 K_FindBotController(mobj_t *mo) +{ + msecnode_t *node; + ffloor_t *rover; + INT16 lineNum = -1; + mtag_t tag; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) + { + if (!node->m_sector) + { + continue; + } + + tag = Tag_FGet(&node->m_sector->tags); + lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag + + if (lineNum != -1) + { + return lineNum; + } + + for (rover = node->m_sector->ffloors; rover; rover = rover->next) + { + sector_t *rs = NULL; + + if (!(rover->flags & FF_EXISTS)) + { + continue; + } + + if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight) + { + continue; + } + + rs = §ors[rover->secnum]; + tag = Tag_FGet(&rs->tags); + lineNum = P_FindSpecialLineFromTag(2004, tag, -1); + + if (lineNum != -1) + { + return lineNum; + } + } + } + + return -1; +} + /*-------------------------------------------------- static UINT32 K_BotRubberbandDistance(player_t *player) @@ -346,6 +410,7 @@ fixed_t K_BotRubberband(player_t *player) fixed_t rubberband = FRACUNIT; fixed_t max, min; player_t *firstplace = NULL; + INT16 botController = -1; UINT8 i; if (player->exiting) @@ -354,6 +419,19 @@ fixed_t K_BotRubberband(player_t *player) return FRACUNIT; } + botController = K_FindBotController(player->mo); + + if (botController != -1) + { + line_t *controllerLine = &lines[botController]; + + // No Climb Flag: Disable rubberbanding + if (controllerLine->flags & ML_NOCLIMB) + { + return FRACUNIT; + } + } + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) @@ -812,70 +890,6 @@ static UINT8 K_TrySpindash(player_t *player) return 0; } -/*-------------------------------------------------- - static INT16 K_FindBotController(mobj_t *mo) - - Finds if any bot controller linedefs are tagged to the bot's sector. - - Input Arguments:- - mo - The bot player's mobj. - - Return:- - Line number of the bot controller. -1 if it doesn't exist. ---------------------------------------------------*/ -static INT16 K_FindBotController(mobj_t *mo) -{ - msecnode_t *node; - ffloor_t *rover; - INT16 lineNum = -1; - mtag_t tag; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) - { - if (!node->m_sector) - { - continue; - } - - tag = Tag_FGet(&node->m_sector->tags); - lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag - - if (lineNum != -1) - { - return lineNum; - } - - for (rover = node->m_sector->ffloors; rover; rover = rover->next) - { - sector_t *rs = NULL; - - if (!(rover->flags & FF_EXISTS)) - { - continue; - } - - if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight) - { - continue; - } - - rs = §ors[rover->secnum]; - tag = Tag_FGet(&rs->tags); - lineNum = P_FindSpecialLineFromTag(2004, tag, -1); - - if (lineNum != -1) - { - return lineNum; - } - } - } - - return -1; -} - /*-------------------------------------------------- static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player) From 0e43a04dee4196d8f7080b2038a4f6123d84753c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 12 Dec 2021 02:07:21 -0500 Subject: [PATCH 19/36] Try adding tripwire support Kind of lazy. When checking bot traversal, it considers tripwires as walls. K_BotCanTakeCut now is limited to whatever can take tripwires (no more hyudoro invisibility). Probably should have something more foolproof, but it's annoying to test any changes to this. The only maps I can think of with really easy tripwires clearly don't have their waypoints with bots in mind, the rest are very optional or out of the way or otherwise the bots don't want to even touch them at all. --- src/k_bot.c | 16 ++++++++++++---- src/p_sight.c | 6 ++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index f3555deed..6bb5a6080 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -273,14 +273,22 @@ boolean K_PlayerUsesBotMovement(player_t *player) --------------------------------------------------*/ boolean K_BotCanTakeCut(player_t *player) { - if (!K_ApplyOffroad(player) + if ( +#if 1 + K_TripwirePass(player) == true +#else + K_ApplyOffroad(player) == false +#endif || player->itemtype == KITEM_SNEAKER || player->itemtype == KITEM_ROCKETSNEAKER || player->itemtype == KITEM_INVINCIBILITY - || player->itemtype == KITEM_HYUDORO) + ) + { return true; + } return false; +#endif } /*-------------------------------------------------- @@ -721,12 +729,12 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) for (i = 0; i < wp->numnextwaypoints; i++) { - if (!K_GetWaypointIsEnabled(wp->nextwaypoints[i])) + if (K_GetWaypointIsEnabled(wp->nextwaypoints[i]) == false) { continue; } - if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) && !K_BotCanTakeCut(player)) + if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) == true && K_BotCanTakeCut(player) == false) { continue; } diff --git a/src/p_sight.c b/src/p_sight.c index 8e63f1be4..00c2f09d9 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -758,6 +758,12 @@ static boolean P_CrossBotTraversalSubsector(size_t num, register traceblocking_t return false; } } + + if (P_IsLineTripWire(line) == true && K_TripwirePass(tb->compareThing->player) == false) + { + // Can't go through trip wire. + return false; + } } } From cd0a259bbead81294ccbd9f9bfe2c11769ed8e6e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 12 Dec 2021 02:32:03 -0500 Subject: [PATCH 20/36] Code cleanup - K_FindBotController returns the line_t directly, instead of a linedef index. - Trick panel code is in its own function. - Try to fix infinite bot heat death --- src/k_bot.c | 108 ++++++++++++++++++++++++++++++-------------------- src/k_bot.h | 4 +- src/p_sight.c | 1 + src/p_user.c | 16 +++++++- 4 files changed, 84 insertions(+), 45 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 6bb5a6080..2a5bb665d 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -288,11 +288,10 @@ boolean K_BotCanTakeCut(player_t *player) } return false; -#endif } /*-------------------------------------------------- - static INT16 K_FindBotController(mobj_t *mo) + static line_t *K_FindBotController(mobj_t *mo) Finds if any bot controller linedefs are tagged to the bot's sector. @@ -300,9 +299,9 @@ boolean K_BotCanTakeCut(player_t *player) mo - The bot player's mobj. Return:- - Line number of the bot controller. -1 if it doesn't exist. + Linedef of the bot controller. NULL if it doesn't exist. --------------------------------------------------*/ -static INT16 K_FindBotController(mobj_t *mo) +static line_t *K_FindBotController(mobj_t *mo) { msecnode_t *node; ffloor_t *rover; @@ -324,7 +323,7 @@ static INT16 K_FindBotController(mobj_t *mo) if (lineNum != -1) { - return lineNum; + break; } for (rover = node->m_sector->ffloors; rover; rover = rover->next) @@ -347,12 +346,19 @@ static INT16 K_FindBotController(mobj_t *mo) if (lineNum != -1) { - return lineNum; + break; } } } - return -1; + if (lineNum != -1) + { + return &lines[lineNum]; + } + else + { + return NULL; + } } /*-------------------------------------------------- @@ -418,7 +424,7 @@ fixed_t K_BotRubberband(player_t *player) fixed_t rubberband = FRACUNIT; fixed_t max, min; player_t *firstplace = NULL; - INT16 botController = -1; + line_t *botController = NULL; UINT8 i; if (player->exiting) @@ -429,12 +435,10 @@ fixed_t K_BotRubberband(player_t *player) botController = K_FindBotController(player->mo); - if (botController != -1) + if (botController != NULL) { - line_t *controllerLine = &lines[botController]; - // No Climb Flag: Disable rubberbanding - if (controllerLine->flags & ML_NOCLIMB) + if (botController->flags & ML_NOCLIMB) { return FRACUNIT; } @@ -962,6 +966,50 @@ static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player) } } +/*-------------------------------------------------- + static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController) + + Determines inputs for trick panels. + + Input Arguments:- + player - Player to generate the ticcmd for. + cmd - The player's ticcmd to modify. + botController - Linedef for the bot controller. + + Return:- + None +--------------------------------------------------*/ +static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController) +{ + // Trick panel state -- do nothing until a controller line is found, in which case do a trick. + if (botController == NULL) + { + return; + } + + if (player->trickpanel == 1) + { + INT32 type = (sides[botController->sidenum[0]].rowoffset / FRACUNIT); + + // Y Offset: Trick type + switch (type) + { + case 1: + cmd->turning = KART_FULLTURN; + break; + case 2: + cmd->turning = -KART_FULLTURN; + break; + case 3: + cmd->buttons |= BT_FORWARD; + break; + case 4: + cmd->buttons |= BT_BACKWARD; + break; + } + } +} + /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) @@ -973,7 +1021,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) boolean trySpindash = true; UINT8 spindash = 0; INT32 turnamt = 0; - INT16 botController = -1; + line_t *botController = NULL; // Can't build a ticcmd if we aren't spawned... if (!player->mo) @@ -996,7 +1044,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } // Complete override of all ticcmd functionality - if (LUAh_BotTiccmd(player, cmd)) + if (LUAh_BotTiccmd(player, cmd) == true) { return; } @@ -1005,30 +1053,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (player->trickpanel != 0) { - // Trick panel state -- do nothing until a controller line is found, in which case do a trick. - - if (player->trickpanel == 1 && botController != -1) - { - line_t *controllerLine = &lines[botController]; - INT32 type = (sides[controllerLine->sidenum[0]].rowoffset / FRACUNIT); - - // Y Offset: Trick type - switch (type) - { - case 1: - cmd->turning = KART_FULLTURN; - break; - case 2: - cmd->turning = -KART_FULLTURN; - break; - case 3: - cmd->buttons |= BT_FORWARD; - break; - case 4: - cmd->buttons |= BT_BACKWARD; - break; - } - } + K_BotTrick(player, cmd, botController); // Don't do anything else. return; @@ -1037,20 +1062,19 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if ((player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) - || (botController != -1)) + || (botController != NULL)) { // Handle steering towards waypoints! SINT8 turnsign = 0; angle_t destangle, moveangle, angle; INT16 anglediff; - if (botController != -1) + if (botController != NULL) { const fixed_t dist = (player->mo->radius * 4); - line_t *controllerLine = &lines[botController]; // X Offset: Movement direction - destangle = FixedAngle(sides[controllerLine->sidenum[0]].textureoffset); + destangle = FixedAngle(sides[botController->sidenum[0]].textureoffset); // Overwritten prediction predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); diff --git a/src/k_bot.h b/src/k_bot.h index d354bbb2c..a04d5c174 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -223,8 +223,8 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); - Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed, - acceleration, and handling. + Creates a bot's ticcmd, looking at its surroundings to + try and figure out what it should do. Input Arguments:- player - Player to generate the ticcmd for. diff --git a/src/p_sight.c b/src/p_sight.c index 00c2f09d9..cdc8df864 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -19,6 +19,7 @@ #include "r_state.h" #include "k_bot.h" // K_BotHatesThisSector +#include "k_kart.h" // K_TripwirePass // // P_CheckSight diff --git a/src/p_user.c b/src/p_user.c index 5af3386d6..7ab85b6ec 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2502,6 +2502,8 @@ static void P_ConsiderAllGone(void) // static void P_DeathThink(player_t *player) { + boolean playerGone = false; + player->deltaviewheight = 0; if (player->deadtimer < INT32_MAX) @@ -2522,7 +2524,19 @@ static void P_DeathThink(player_t *player) K_KartPlayerHUDUpdate(player); - if (player->lives > 0 && !(player->pflags & PF_NOCONTEST) && player->deadtimer > TICRATE) + if (player->pflags & PF_NOCONTEST) + { + playerGone = true; + } + else if (player->bot == false) + { + if (G_GametypeUsesLives() == true && player->lives == 0) + { + playerGone = true; + } + } + + if (playerGone == false && player->deadtimer > TICRATE) { player->playerstate = PST_REBORN; } From bfa3311eca552fa57fba43f5ef948c55ce681a41 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 12 Dec 2021 03:28:49 -0500 Subject: [PATCH 21/36] Make rivals even more aggressive to players :) --- src/k_botitem.c | 204 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 152 insertions(+), 52 deletions(-) diff --git a/src/k_botitem.c b/src/k_botitem.c index 67d858ea4..4bb83931c 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -86,7 +86,7 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r } /*-------------------------------------------------- - static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) + static player_t *K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) Looks for players around a specified x/y coordinate. @@ -97,9 +97,9 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r radius - The radius to look for players in. Return:- - true if a player was found around the coordinate, otherwise false. + The player we found, NULL if nothing was found. --------------------------------------------------*/ -static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) +static player_t *K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) { UINT8 i; @@ -129,15 +129,15 @@ static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t if (dist <= radius) { - return true; + return target; } } - return false; + return NULL; } /*-------------------------------------------------- - static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) + static player_t *K_PlayerPredictThrow(player_t *player, UINT8 extra) Looks for players around the predicted coordinates of their thrown item. @@ -146,9 +146,9 @@ static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t extra - Extra throwing distance, for aim forward on mines. Return:- - true if a player was found around the coordinate, otherwise false. + The player we're trying to throw at, NULL if none was found. --------------------------------------------------*/ -static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) +static player_t *K_PlayerPredictThrow(player_t *player, UINT8 extra) { const fixed_t dist = (30 + (extra * 10)) * player->mo->scale; const UINT32 airtime = FixedDiv(dist + player->mo->momz, gravity); @@ -159,7 +159,7 @@ static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) } /*-------------------------------------------------- - static boolean K_PlayerInCone(player_t *player, UINT16 cone, boolean flip) + static player_t *K_PlayerInCone(player_t *player, UINT16 cone, boolean flip) Looks for players in the . @@ -172,7 +172,7 @@ static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) Return:- true if a player was found in the cone, otherwise false. --------------------------------------------------*/ -static boolean K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boolean flip) +static player_t *K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boolean flip) { UINT8 i; @@ -222,22 +222,96 @@ static boolean K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boo { if (ad >= 180-cone) { - return true; + return target; } } else { if (ad <= cone) { - return true; + return target; } } } } + return NULL; +} + +/*-------------------------------------------------- + static boolean K_RivalBotAggression(player_t *bot, player_t *target) + + Returns if a bot is a rival & wants to be aggressive to a player. + + Input Arguments:- + bot - Bot to check. + target - Who the bot wants to attack. + + Return:- + false if not the rival. false if the target is another bot. Otherwise, true. +--------------------------------------------------*/ +static boolean K_RivalBotAggression(player_t *bot, player_t *target) +{ + if (bot == NULL || target == NULL) + { + // Invalid. + return false; + } + + if (bot->bot == false) + { + // lol + return false; + } + + if (bot->botvars.rival == false) + { + // Not the rival, we aren't self-aware. + return false; + } + + if (target->bot == false) + { + // This bot knows that the real threat is the player. + return true; + } + + // Calling them your friends is misleading, but you'll at least spare them. return false; } +/*-------------------------------------------------- + static void K_ItemConfirmForTarget(player_t *bot, player_t *target, UINT16 amount) + + Handles updating item confirm values for offense items. + + Input Arguments:- + bot - Bot to check. + target - Who the bot wants to attack. + amount - Amount to increase item confirm time by. + + Return:- + None +--------------------------------------------------*/ +static void K_ItemConfirmForTarget(player_t *bot, player_t *target, UINT16 amount) +{ + if (bot == NULL || target == NULL) + { + return; + } + + if (K_RivalBotAggression(bot, target) == true) + { + // Double the rate when you're aggressive. + bot->botvars.itemconfirm += amount << 1; + } + else + { + // Do as normal. + bot->botvars.itemconfirm += amount; + } +} + /*-------------------------------------------------- static boolean K_BotGenericPressItem(player_t *player, ticcmd_t *cmd, SINT8 dir) @@ -316,21 +390,21 @@ static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean } // Check the predicted throws. - if (K_PlayerPredictThrow(player, 0)) + if (K_PlayerPredictThrow(player, 0) != NULL) { return true; } if (mine) { - if (K_PlayerPredictThrow(player, 1)) + if (K_PlayerPredictThrow(player, 1) != NULL) { return true; } } // Check your behind. - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true) != NULL) { return true; } @@ -447,7 +521,6 @@ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) } } - /*-------------------------------------------------- static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) @@ -464,9 +537,17 @@ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) { SINT8 throwdir = -1; + player_t *target = NULL; player->botvars.itemconfirm++; + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) + { + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); + throwdir = -1; + } + if (abs(turnamt) >= KART_FULLTURN/2) { player->botvars.itemconfirm += player->botvars.difficulty / 2; @@ -474,19 +555,15 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) } else { - if (K_PlayerPredictThrow(player, 0)) + target = K_PlayerPredictThrow(player, 0); + + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2); throwdir = 1; } } - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); @@ -509,12 +586,14 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) { SINT8 throwdir = 0; + player_t *target = NULL; player->botvars.itemconfirm++; - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; } @@ -525,21 +604,21 @@ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) } else { - if (K_PlayerPredictThrow(player, 0)) + target = K_PlayerPredictThrow(player, 0); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2); throwdir = 0; } - if (K_PlayerPredictThrow(player, 1)) + target = K_PlayerPredictThrow(player, 1); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2); throwdir = 1; } } - - if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); @@ -561,6 +640,8 @@ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) --------------------------------------------------*/ static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) { + player_t *target = NULL; + player->botvars.itemconfirm++; if (abs(turnamt) >= KART_FULLTURN/2) @@ -568,9 +649,10 @@ static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) player->botvars.itemconfirm += player->botvars.difficulty / 2; } - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); } if (player->botvars.itemconfirm > 2*TICRATE) @@ -595,18 +677,21 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) { const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); SINT8 throwdir = -1; + player_t *target = NULL; player->botvars.itemconfirm++; - if (K_PlayerPredictThrow(player, 0)) + target = K_PlayerPredictThrow(player, 0); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty / 2; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty / 2); throwdir = 1; } - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; } @@ -636,6 +721,7 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) static boolean K_BotRevealsEggbox(player_t *player) { const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); + player_t *target = NULL; // This is a stealthy spot for an eggbox, lets reveal it! if (stealth > 1) @@ -644,13 +730,15 @@ static boolean K_BotRevealsEggbox(player_t *player) } // Check the predicted throws. - if (K_PlayerPredictThrow(player, 0)) + target = K_PlayerPredictThrow(player, 0); + if (target != NULL) { return true; } // Check your behind. - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) { return true; } @@ -677,7 +765,7 @@ static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd) return; } - if (K_BotRevealsEggbox(player) || (player->botvars.itemconfirm++ > 20*TICRATE)) + if (K_BotRevealsEggbox(player) == true || (player->botvars.itemconfirm++ > 20*TICRATE)) { K_BotGenericPressItem(player, cmd, 0); } @@ -699,8 +787,9 @@ static void K_BotItemEggmanExplosion(player_t *player, ticcmd_t *cmd) { if (player->position == 1) { + // Hey, we aren't gonna find anyone up here... + // why don't we slow down a bit? :) cmd->forwardmove /= 2; - cmd->buttons |= BT_BRAKE; } K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); @@ -724,6 +813,7 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) fixed_t radius = (player->mo->radius * 32); SINT8 throwdir = -1; UINT8 snipeMul = 2; + player_t *target = NULL; if (player->speed > topspeed) { @@ -733,15 +823,21 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) player->botvars.itemconfirm++; - if (K_PlayerInCone(player, radius, 10, false)) + target = K_PlayerInCone(player, radius, 10, false); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty * snipeMul; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } else if (K_PlayerInCone(player, radius, 10, true)) { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; + target = K_PlayerInCone(player, radius, 10, true); + + if (target != NULL) + { + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); + throwdir = -1; + } } if (player->botvars.itemconfirm > 5*TICRATE) @@ -769,6 +865,7 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) SINT8 throwdir = 1; UINT8 snipeMul = 2; INT32 lastTarg = player->lastjawztarget; + player_t *target = NULL; if (player->speed > topspeed) { @@ -778,9 +875,10 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) player->botvars.itemconfirm++; - if (K_PlayerInCone(player, radius, 10, true)) + target = K_PlayerInCone(player, radius, 10, true); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; } @@ -790,16 +888,18 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) && players[lastTarg].mo != NULL && P_MobjWasRemoved(players[lastTarg].mo) == false) { - mobj_t *targ = players[lastTarg].mo; + mobj_t *targMo = players[lastTarg].mo; mobj_t *mobj = NULL, *next = NULL; boolean targettedAlready = false; + target = &players[lastTarg]; + // Make sure no other Jawz are targetting this player. for (mobj = kitemcap; mobj; mobj = next) { next = mobj->itnext; - if (mobj->type == MT_JAWZ && mobj->target == targ) + if (mobj->type == MT_JAWZ && mobj->target == targMo) { targettedAlready = true; break; @@ -808,7 +908,7 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) if (targettedAlready == false) { - player->botvars.itemconfirm += player->botvars.difficulty * snipeMul; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } } @@ -833,7 +933,7 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ static void K_BotItemThunder(player_t *player, ticcmd_t *cmd) { - if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) + if (K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale) == false) { if (player->botvars.itemconfirm > 10*TICRATE) { From cec81378644f74f6675a43907678d89a638de8aa Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 12 Dec 2021 04:07:32 -0500 Subject: [PATCH 22/36] Require Effect 1 for using bot controller angle. --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 2a5bb665d..24f3517a2 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1069,7 +1069,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) angle_t destangle, moveangle, angle; INT16 anglediff; - if (botController != NULL) + if (botController != NULL && (botController->flags & ML_EFFECT1)) { const fixed_t dist = (player->mo->radius * 4); From 9e1146bc3052a2a766a8ebddb9e00df183700104 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Dec 2021 02:47:46 -0800 Subject: [PATCH 23/36] Disable downloading for now --- src/d_netfil.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/d_netfil.c b/src/d_netfil.c index 5714c39cd..f8edfbf03 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -276,11 +276,16 @@ boolean CL_CheckDownloadable(void) } // Downloading locally disabled +#if 0 if (!dlstatus && M_CheckParm("-nodownload")) dlstatus = 3; if (!dlstatus) return true; +#else + if (!dlstatus) + dlstatus = 3; +#endif // not downloadable, put reason in console CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n")); From 32127f37ee5e94844178ab6414ae1ec116306baa Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Dec 2021 02:58:06 -0800 Subject: [PATCH 24/36] Check mainwads netgame md5 in developer build --- src/d_netfil.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index f8edfbf03..7aec8d13e 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -161,10 +161,16 @@ UINT8 *PutFileNeeded(UINT16 firstfile) char wadfilename[MAX_WADPATH] = ""; UINT8 filestatus; - for (i = mainwads+1; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad +#ifdef DEVELOP + i = 0; +#else + i = mainwads + 1; +#endif + + for (; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad { // If it has only music/sound lumps, don't put it in the list - if (!wadfiles[i]->important) + if (i > mainwads && !wadfiles[i]->important) continue; if (firstfile) @@ -494,7 +500,12 @@ INT32 CL_CheckFiles(void) CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename); // Check in already loaded files - for (j = mainwads+1; wadfiles[j]; j++) +#ifdef DEVELOP + j = 0; +#else + j = mainwads + 1; +#endif + for (; wadfiles[j]; j++) { nameonly(strcpy(wadfilename, wadfiles[j]->filename)); if (!stricmp(wadfilename, fileneeded[i].filename) && From 35238510e34d0fb3950170cee5436ea64dccb7b1 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Dec 2021 03:28:59 -0800 Subject: [PATCH 25/36] Use sfx_s3k38 --- 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 9550b0c41..0151b9114 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3065,7 +3065,7 @@ void P_MobjCheckWater(mobj_t *mobj) if (p && !p->waterskip && p->curshield != KSHIELD_BUBBLE && wasinwater) { - S_StartSound(mobj, sfx_gasp); + S_StartSound(mobj, sfx_s3k38); } if (waterwasnotset) From 184a89db4399a61d27dd43d183a48b17e2872b3c Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Dec 2021 03:30:08 -0800 Subject: [PATCH 26/36] Always play splash sfx when entering/exiting water --- src/p_mobj.c | 112 +++++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 0151b9114..61bbe60e5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3068,14 +3068,67 @@ void P_MobjCheckWater(mobj_t *mobj) S_StartSound(mobj, sfx_s3k38); } - if (waterwasnotset) - return; - if ((p) // Players || (mobj->flags & MF_PUSHABLE) // Pushables || ((mobj->info->flags & MF_PUSHABLE) && mobj->fuse) // Previously pushable, might be moving still ) { + // Time to spawn the bubbles! + { + INT32 i; + INT32 bubblecount; + UINT8 prandom[4]; + mobj_t *bubble; + mobjtype_t bubbletype; + + if (mobj->eflags & MFE_GOOWATER || wasingoo) + S_StartSound(mobj, sfx_ghit); + else if (mobj->eflags & MFE_TOUCHLAVA) + S_StartSound(mobj, sfx_splash); + else + S_StartSound(mobj, sfx_splish); // And make a sound! + + bubblecount = FixedDiv(abs(mobj->momz), mobj->scale)>>(FRACBITS-1); + // Max bubble count + if (bubblecount > 128) + bubblecount = 128; + + // Create tons of bubbles + for (i = 0; i < bubblecount; i++) + { + // P_RandomByte()s are called individually to allow consistency + // across various compilers, since the order of function calls + // in C is not part of the ANSI specification. + prandom[0] = P_RandomByte(); + prandom[1] = P_RandomByte(); + prandom[2] = P_RandomByte(); + prandom[3] = P_RandomByte(); + + bubbletype = MT_SMALLBUBBLE; + if (!(prandom[0] & 0x3)) // medium bubble chance up to 64 from 32 + bubbletype = MT_MEDIUMBUBBLE; + + bubble = P_SpawnMobj( + mobj->x + FixedMul((prandom[1]<<(FRACBITS-3)) * (prandom[0]&0x80 ? 1 : -1), mobj->scale), + mobj->y + FixedMul((prandom[2]<<(FRACBITS-3)) * (prandom[0]&0x40 ? 1 : -1), mobj->scale), + mobj->z + FixedMul((prandom[3]<<(FRACBITS-2)), mobj->scale), bubbletype); + + if (bubble) + { + if (P_MobjFlip(mobj)*mobj->momz < 0) + bubble->momz = mobj->momz >> 4; + else + bubble->momz = 0; + + bubble->destscale = mobj->scale; + P_SetScale(bubble, mobj->scale); + } + } + } + + if (waterwasnotset) + return; + // Check to make sure you didn't just cross into a sector to jump out of // that has shallower water than the block you were originally in. if ((!(mobj->eflags & MFE_VERTICALFLIP) && mobj->watertop-mobj->floorz <= height>>1) @@ -3160,59 +3213,6 @@ void P_MobjCheckWater(mobj_t *mobj) P_SetScale(splish, mobj->scale); } } - - // Time to spawn the bubbles! - { - INT32 i; - INT32 bubblecount; - UINT8 prandom[4]; - mobj_t *bubble; - mobjtype_t bubbletype; - - if (mobj->eflags & MFE_GOOWATER || wasingoo) - S_StartSound(mobj, sfx_ghit); - else if (mobj->eflags & MFE_TOUCHLAVA) - S_StartSound(mobj, sfx_splash); - else - S_StartSound(mobj, sfx_splish); // And make a sound! - - bubblecount = FixedDiv(abs(mobj->momz), mobj->scale)>>(FRACBITS-1); - // Max bubble count - if (bubblecount > 128) - bubblecount = 128; - - // Create tons of bubbles - for (i = 0; i < bubblecount; i++) - { - // P_RandomByte()s are called individually to allow consistency - // across various compilers, since the order of function calls - // in C is not part of the ANSI specification. - prandom[0] = P_RandomByte(); - prandom[1] = P_RandomByte(); - prandom[2] = P_RandomByte(); - prandom[3] = P_RandomByte(); - - bubbletype = MT_SMALLBUBBLE; - if (!(prandom[0] & 0x3)) // medium bubble chance up to 64 from 32 - bubbletype = MT_MEDIUMBUBBLE; - - bubble = P_SpawnMobj( - mobj->x + FixedMul((prandom[1]<<(FRACBITS-3)) * (prandom[0]&0x80 ? 1 : -1), mobj->scale), - mobj->y + FixedMul((prandom[2]<<(FRACBITS-3)) * (prandom[0]&0x40 ? 1 : -1), mobj->scale), - mobj->z + FixedMul((prandom[3]<<(FRACBITS-2)), mobj->scale), bubbletype); - - if (bubble) - { - if (P_MobjFlip(mobj)*mobj->momz < 0) - bubble->momz = mobj->momz >> 4; - else - bubble->momz = 0; - - bubble->destscale = mobj->scale; - P_SetScale(bubble, mobj->scale); - } - } - } } } From 033a489b8549517c2bfefe6c79a361a18e3b50ab Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Dec 2021 01:29:20 -0800 Subject: [PATCH 27/36] Fix bad merge @ 4a8340cd4 --- src/hardware/hw_defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index b4dc1accb..2cdbd02c3 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -113,7 +113,7 @@ typedef struct UINT8 splitscreen; boolean flip; // screenflip boolean shearing; // 14042019 - angle_t viewaiming; // 17052019 + float viewaiming; // 17052019 boolean roll; FLOAT rollangle; // done to not override USE_FTRANSFORM_ANGLEZ FLOAT centerx, centery; From 1140410e2cf11dedf6a1aa607e68d82a1d6f68be Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 10 Dec 2021 18:49:40 -0800 Subject: [PATCH 28/36] Spawn SPHERE BOXES randomly in Battle They look like random item boxes (the ones in Race) but have a blue sphere inside of them. One box is worth 10 blue spheres. More boxes spawn during Overtime. New sprite: SBOX --- src/deh_tables.c | 3 +++ src/info.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/info.h | 16 ++++++++++++++++ src/k_battle.c | 38 ++++++++++++++++++++++++++++++++++++++ src/k_battle.h | 1 + src/p_enemy.c | 4 +++- src/p_inter.c | 23 ++++++++--------------- src/p_local.h | 2 +- src/p_mobj.c | 2 ++ src/p_user.c | 20 ++++++++++++++++++++ 10 files changed, 133 insertions(+), 17 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 7e309c33f..894157639 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3476,6 +3476,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_RANDOMITEM12", "S_DEADRANDOMITEM", + // Sphere Box (for Battle) + "S_SPHEREBOX", + // Random Item Pop "S_RANDOMITEMPOP1", "S_RANDOMITEMPOP2", diff --git a/src/info.c b/src/info.c index 5d506d98d..2db296b99 100644 --- a/src/info.c +++ b/src/info.c @@ -526,6 +526,7 @@ char sprnames[NUMSPRITES + 1][5] = //SRB2kart Sprites (sort later) "RNDM", // Random Item Box + "SBOX", // Sphere Box (for Battle) "RPOP", // Random Item Box Pop "SGNS", // Signpost sparkle "FAST", // Speed boost trail @@ -4061,6 +4062,19 @@ state_t states[NUMSTATES] = {SPR_RNDM, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM1}, // S_RANDOMITEM12 {SPR_NULL, 0, 0, {A_ItemPop}, 0, 0, S_NULL}, // S_DEADRANDOMITEM + {SPR_SBOX, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX2}, // S_SPHEREBOX1 + {SPR_SBOX, 2|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX3}, // S_SPHEREBOX2 + {SPR_SBOX, 4|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX4}, // S_SPHEREBOX3 + {SPR_SBOX, 6|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX5}, // S_SPHEREBOX4 + {SPR_SBOX, 8|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX6}, // S_SPHEREBOX5 + {SPR_SBOX, 10|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX7}, // S_SPHEREBOX6 + {SPR_SBOX, 12|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX8}, // S_SPHEREBOX7 + {SPR_SBOX, 14|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX9}, // S_SPHEREBOX8 + {SPR_SBOX, 16|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX10}, // S_SPHEREBOX9 + {SPR_SBOX, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX11}, // S_SPHEREBOX10 + {SPR_SBOX, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX12}, // S_SPHEREBOX11 + {SPR_SBOX, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX1}, // S_SPHEREBOX12 + {SPR_RPOP, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_RANDOMITEMPOP2}, // S_RANDOMITEMPOP1 {SPR_RPOP, FF_FULLBRIGHT|1, 5, {NULL}, 0, 0, S_RANDOMITEMPOP3}, // S_RANDOMITEMPOP2 {SPR_RPOP, FF_FULLBRIGHT|2, 5, {NULL}, 0, 0, S_RANDOMITEMPOP4}, // S_RANDOMITEMPOP3 @@ -23017,6 +23031,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SPHEREBOX + -1, // doomednum + S_SPHEREBOX1, // 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_DEADRANDOMITEM, // deathstate + S_NULL, // xdeathstate + sfx_kc2e, // deathsound + 60*FRACUNIT, // speed + 48*FRACUNIT, // radius + 48*FRACUNIT, // height + 0, // display offset + 100, // mass + MT_RANDOMITEMPOP, // damage + sfx_None, // activesound + MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_RANDOMITEMPOP -1, // doomednum S_INVISIBLE, // spawnstate diff --git a/src/info.h b/src/info.h index 115948905..eab11e86a 100644 --- a/src/info.h +++ b/src/info.h @@ -1070,6 +1070,7 @@ typedef enum sprite // SRB2Kart SPR_RNDM, // Random Item Box + SPR_SBOX, // Sphere Box (for Battle) SPR_RPOP, // Random Item Box Pop SPR_SGNS, // Signpost sparkle SPR_FAST, // Speed boost trail @@ -4453,6 +4454,20 @@ typedef enum state S_RANDOMITEM12, S_DEADRANDOMITEM, + // Sphere Box (for Battle) + S_SPHEREBOX1, + S_SPHEREBOX2, + S_SPHEREBOX3, + S_SPHEREBOX4, + S_SPHEREBOX5, + S_SPHEREBOX6, + S_SPHEREBOX7, + S_SPHEREBOX8, + S_SPHEREBOX9, + S_SPHEREBOX10, + S_SPHEREBOX11, + S_SPHEREBOX12, + // Random Item Pop S_RANDOMITEMPOP1, S_RANDOMITEMPOP2, @@ -6465,6 +6480,7 @@ typedef enum mobj_type // SRB2kart MT_RANDOMITEM, + MT_SPHEREBOX, MT_RANDOMITEMPOP, MT_FLOATINGITEM, MT_ITEMCAPSULE, diff --git a/src/k_battle.c b/src/k_battle.c index c33ea6bcc..83c47d43e 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -229,6 +229,28 @@ mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT return emerald; } +mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 amount) +{ + mobj_t *drop = P_SpawnMobj(x, y, z, MT_SPHEREBOX); + + (void)amount; + + drop->angle = angle; + P_Thrust(drop, + FixedAngle(P_RandomFixed() * 180) + angle, + P_RandomRange(4, 12) * mapobjectscale); + + drop->momz = flip * 12 * mapobjectscale; + if (drop->eflags & MFE_UNDERWATER) + drop->momz = (117 * drop->momz) / 200; + + drop->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); + + drop->extravalue1 = amount; + + return drop; +} + void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType) { UINT8 i; @@ -352,6 +374,14 @@ void K_RunPaperItemSpawners(void) firstUnspawnedEmerald ); } + else if (P_RandomChance(FRACUNIT/2)) + { + K_SpawnSphereBox( + battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + 10 + ); + } else { K_CreatePaperItem( @@ -441,6 +471,14 @@ void K_RunPaperItemSpawners(void) firstUnspawnedEmerald ); } + else if (P_RandomChance(FRACUNIT/3)) + { + drop = K_SpawnSphereBox( + spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + 10 + ); + } else { drop = K_CreatePaperItem( diff --git a/src/k_battle.h b/src/k_battle.h index 221873b04..c56c576d6 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -22,6 +22,7 @@ void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount); void K_CheckBumpers(void); void K_CheckEmeralds(player_t *player); mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT32 emeraldType); +mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 amount); void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType); UINT8 K_NumEmeralds(player_t *player); void K_RunPaperItemSpawners(void); diff --git a/src/p_enemy.c b/src/p_enemy.c index 7d4ad7400..8bc40525e 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13308,7 +13308,9 @@ void A_ItemPop(mobj_t *actor) if (actor->info->deathsound) S_StartSound(remains, actor->info->deathsound); - if (!((gametyperules & GTR_BUMPERS) && actor->target->player->bumpers <= 0)) + if (gametyperules & GTR_BUMPERS) + P_GivePlayerSpheres(actor->target->player, actor->extravalue1); + else actor->target->player->itemroulette = 1; remains->flags2 &= ~MF2_AMBUSH; diff --git a/src/p_inter.c b/src/p_inter.c index 0a2bb4e13..4dafa91c2 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -268,12 +268,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!P_CanPickupItem(player, 1)) return; - if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) - { - return; - } - special->momx = special->momy = special->momz = 0; + P_SetTarget(&special->target, toucher); + P_KillMobj(special, toucher, toucher, DMG_NORMAL); + break; + case MT_SPHEREBOX: + if (player->bumpers <= 0) + return; + P_SetTarget(&special->target, toucher); P_KillMobj(special, toucher, toucher, DMG_NORMAL); break; @@ -510,16 +512,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!(P_CanPickupItem(player, 0))) return; - // Reached the cap, don't waste 'em! - if (player->spheres >= 40) - return; - - // Not alive - if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0)) - return; - - special->momx = special->momy = special->momz = 0; - player->spheres++; + P_GivePlayerSpheres(player, 1); break; // Secret emblem thingy diff --git a/src/p_local.h b/src/p_local.h index d90bfde4c..227d915f8 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -175,7 +175,7 @@ void P_RestoreMusic(player_t *player); boolean P_EndingMusic(player_t *player); mobj_t *P_SpawnGhostMobj(mobj_t *mobj); INT32 P_GivePlayerRings(player_t *player, INT32 num_rings); -void P_GivePlayerSpheres(player_t *player, INT32 num_spheres); +INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres); void P_GivePlayerLives(player_t *player, INT32 numlives); UINT8 P_GetNextEmerald(void); void P_GiveEmerald(boolean spawnObj); diff --git a/src/p_mobj.c b/src/p_mobj.c index ad86d0434..08c6ca088 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8597,6 +8597,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } break; case MT_RANDOMITEM: + case MT_SPHEREBOX: if (gametype == GT_BATTLE && mobj->threshold == 70) { mobj->color = K_RainbowColor(leveltime); @@ -9377,6 +9378,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) thing->shadowscale = 12*FRACUNIT/5; break; case MT_RANDOMITEM: + case MT_SPHEREBOX: thing->shadowscale = FRACUNIT/2; thing->whiteshadow = false; break; diff --git a/src/p_user.c b/src/p_user.c index 7ab85b6ec..4837022e2 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -500,6 +500,26 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings) return num_rings; } +INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres) +{ + num_spheres += player->spheres; + + // Not alive + if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0)) + return 0; + + if (num_spheres > 40) // Reached the cap, don't waste 'em! + num_spheres = 40; + else if (num_spheres < 0) + num_spheres = 0; + + num_spheres -= player->spheres; + + player->spheres += num_spheres; + + return num_spheres; +} + // // P_GivePlayerLives // From 514501125193e32e8d80b1c52248c8d8b8edcd5f Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 14 Dec 2021 18:11:08 -0800 Subject: [PATCH 29/36] Add some missing G_SetGametype calls Fixes timelimit not being reset when switching gametypes on the vote screen. --- src/g_demo.c | 1 + src/y_inter.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/g_demo.c b/src/g_demo.c index 712e628f8..2dd3cd277 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2793,6 +2793,7 @@ void G_DoPlayDemo(char *defdemoname) demoflags = READUINT8(demo_p); gametype = READUINT8(demo_p); + G_SetGametype(gametype); if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running. G_SkipDemoExtraFiles(&demo_p); diff --git a/src/y_inter.c b/src/y_inter.c index e050deddc..415672713 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1459,7 +1459,7 @@ static void Y_VoteStops(SINT8 pick, SINT8 level) if (gametype != votelevels[level][1]) { INT16 lastgametype = gametype; - gametype = votelevels[level][1]; + G_SetGametype(votelevels[level][1]); D_GameTypeChanged(lastgametype); forceresetplayers = true; } From 2ac9761af08301287a3e5a3cff6e1b6b449665ed Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 14 Dec 2021 23:19:45 -0800 Subject: [PATCH 30/36] Free unused texture lumps in R_LoadTextures PU_CACHE never gets freed automatically and Z_Unlock does NOTHING. --- src/r_textures.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/r_textures.c b/src/r_textures.c index 9716724b5..df7709374 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -814,7 +814,7 @@ Rloadflats (INT32 i, INT32 w) patch->lump = texstart + j; patch->flip = 0; - Z_Unlock(flatlump); + Z_Free(flatlump); texturewidth[i] = texture->width; textureheight[i] = texture->height << FRACBITS; @@ -915,7 +915,7 @@ Rloadtextures (INT32 i, INT32 w) patch->lump = texstart + j; patch->flip = 0; - Z_Unlock(patchlump); + Z_Free(patchlump); texturewidth[i] = texture->width; textureheight[i] = texture->height << FRACBITS; From 152f4224d94c133c9cca17c63754e033535d0206 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 15 Dec 2021 14:02:56 -0800 Subject: [PATCH 31/36] Read only header bytes when creating textures list R_LoadTextures was reading in the entirety of every texture lump, now it only reads the 8 byte PNG header. This saved more than 1 second for me (1.68 s -> 0.24 s). PNG still need to be read in entirely to check their dimensions; I didn't bother looking into optimizing it since we don't have many PNG textures right now. --- src/r_picformats.h | 1 + src/r_textures.c | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/r_picformats.h b/src/r_picformats.h index 8d3999013..0a822cc48 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -105,6 +105,7 @@ typedef struct } spriteinfo_t; // Portable Network Graphics +#define PNG_HEADER_SIZE (8) boolean Picture_IsLumpPNG(const UINT8 *d, size_t s); #define Picture_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic diff --git a/src/r_textures.c b/src/r_textures.c index df7709374..4ec110556 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -725,6 +725,7 @@ Rloadflats (INT32 i, INT32 w) UINT16 texstart, texend; texture_t *texture; texpatch_t *patch; + UINT8 header[PNG_HEADER_SIZE]; // Yes if (wadfiles[w]->type == RET_PK3) @@ -743,7 +744,6 @@ Rloadflats (INT32 i, INT32 w) // Work through each lump between the markers in the WAD. for (j = 0; j < (texend - texstart); j++) { - UINT8 *flatlump; UINT16 wadnum = (UINT16)w; lumpnum_t lumpnum = texstart + j; size_t lumplength; @@ -755,7 +755,7 @@ Rloadflats (INT32 i, INT32 w) continue; // If it is then SKIP IT } - flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + W_ReadLumpHeaderPwad(wadnum, lumpnum, header, sizeof header, 0); lumplength = W_LumpLengthPwad(wadnum, lumpnum); switch (lumplength) @@ -790,12 +790,14 @@ Rloadflats (INT32 i, INT32 w) M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); #ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength)) + if (Picture_IsLumpPNG(header, lumplength)) { + UINT8 *flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); INT32 width, height; Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, NULL, NULL, lumplength); texture->width = (INT16)width; texture->height = (INT16)height; + Z_Free(flatlump); } else #endif @@ -814,8 +816,6 @@ Rloadflats (INT32 i, INT32 w) patch->lump = texstart + j; patch->flip = 0; - Z_Free(flatlump); - texturewidth[i] = texture->width; textureheight[i] = texture->height << FRACBITS; i++; @@ -835,8 +835,8 @@ Rloadtextures (INT32 i, INT32 w) UINT16 j; UINT16 texstart, texend, texturesLumpPos; texture_t *texture; - softwarepatch_t *patchlump; texpatch_t *patch; + softwarepatch_t patchlump; // Get the lump numbers for the markers in the WAD, if they exist. if (wadfiles[w]->type == RET_PK3) @@ -876,7 +876,7 @@ Rloadtextures (INT32 i, INT32 w) continue; // If it is then SKIP IT } - patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + W_ReadLumpHeaderPwad(wadnum, lumpnum, &patchlump, PNG_HEADER_SIZE, 0); #ifndef NO_PNG_LUMPS lumplength = W_LumpLengthPwad(wadnum, lumpnum); #endif @@ -888,18 +888,20 @@ Rloadtextures (INT32 i, INT32 w) M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); #ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength)) + if (Picture_IsLumpPNG((UINT8 *)&patchlump, lumplength)) { + UINT8 *png = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); INT32 width, height; - Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, NULL, NULL, lumplength); + Picture_PNGDimensions(png, &width, &height, NULL, NULL, lumplength); texture->width = (INT16)width; texture->height = (INT16)height; + Z_Free(png); } else #endif { - texture->width = SHORT(patchlump->width); - texture->height = SHORT(patchlump->height); + texture->width = SHORT(patchlump.width); + texture->height = SHORT(patchlump.height); } texture->type = TEXTURETYPE_SINGLEPATCH; @@ -915,8 +917,6 @@ Rloadtextures (INT32 i, INT32 w) patch->lump = texstart + j; patch->flip = 0; - Z_Free(patchlump); - texturewidth[i] = texture->width; textureheight[i] = texture->height << FRACBITS; i++; From de2eab5e7a057ff30ae719868c3f33ac065210cc Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 15 Dec 2021 14:01:40 -0800 Subject: [PATCH 32/36] Inflate only size out bytes Saves time uncompressing an entire lump when only part of it is needed. --- src/w_wad.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/w_wad.c b/src/w_wad.c index 4e7e97dbb..60339c426 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1480,10 +1480,10 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si int zErr; // Helper var. z_stream strm; unsigned long rawSize = l->disksize; - unsigned long decSize = l->size; + unsigned long decSize = size; rawData = Z_Malloc(rawSize, PU_STATIC, NULL); - decData = Z_Malloc(decSize, PU_STATIC, NULL); + decData = dest; if (fread(rawData, 1, rawSize, handle) < rawSize) I_Error("wad %d, lump %d: cannot read compressed data", wad, lump); @@ -1501,12 +1501,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si zErr = inflateInit2(&strm, -15); if (zErr == Z_OK) { - zErr = inflate(&strm, Z_FINISH); - if (zErr == Z_STREAM_END) - { - M_Memcpy(dest, decData, size); - } - else + zErr = inflate(&strm, Z_SYNC_FLUSH); + if (zErr != Z_OK && zErr != Z_STREAM_END) { size = 0; zerr(zErr); @@ -1520,7 +1516,6 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si } Z_Free(rawData); - Z_Free(decData); #ifdef NO_PNG_LUMPS if (Picture_IsLumpPNG((UINT8 *)dest, size)) From 29399f603389f587c6228a96a564995f8cbd4994 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 15 Dec 2021 15:34:51 -0800 Subject: [PATCH 33/36] Send R_GenerateBlendTables number crunch to parallel thread --- src/d_main.c | 3 +++ src/r_draw.c | 40 +++++++++++++++++++++++++++++++--------- src/r_main.c | 2 +- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index b8fad8ec6..27ee40926 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1417,6 +1417,9 @@ void D_SRB2Main(void) // setup loading screen SCR_Startup(); + // Do this in background; lots of number crunching + R_InitTranslucencyTables(); + CON_SetLoadingProgress(LOADED_ISTARTUPGRAPHICS); CONS_Printf("HU_Init()...\n"); diff --git a/src/r_draw.c b/src/r_draw.c index 4adfb6663..377cca156 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -26,6 +26,7 @@ #include "z_zone.h" #include "console.h" // Until buffering gets finished #include "k_color.h" // SRB2kart +#include "i_threads.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -192,6 +193,29 @@ CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for " #define TRANSTAB_AMTMUL10 (255.0f / 10.0f) +static void R_AllocateBlendTables(void) +{ + INT32 i; + + for (i = 0; i < NUMBLENDMAPS; i++) + { + if (i == blendtab_modulate) + continue; + blendtables[i] = Z_MallocAlign((NUMTRANSTABLES + 1) * 0x10000, PU_STATIC, NULL, 16); + } + + // Modulation blending only requires a single table + blendtables[blendtab_modulate] = Z_MallocAlign(0x10000, PU_STATIC, NULL, 16); +} + +#ifdef HAVE_THREADS +static void R_GenerateBlendTables_Thread(void *userdata) +{ + (void)userdata; + R_GenerateBlendTables(); +} +#endif + /** \brief Initializes the translucency tables used by the Software renderer. */ void R_InitTranslucencyTables(void) @@ -212,20 +236,20 @@ void R_InitTranslucencyTables(void) W_ReadLump(W_GetNumForName("TRANS80"), transtables+0x70000); W_ReadLump(W_GetNumForName("TRANS90"), transtables+0x80000); + R_AllocateBlendTables(); + +#ifdef HAVE_THREADS + I_spawn_thread("blend-tables", + R_GenerateBlendTables_Thread, NULL); +#else R_GenerateBlendTables(); +#endif } void R_GenerateBlendTables(void) { INT32 i; - for (i = 0; i < NUMBLENDMAPS; i++) - { - if (i == blendtab_modulate) - continue; - blendtables[i] = Z_MallocAlign((NUMTRANSTABLES + 1) * 0x10000, PU_STATIC, NULL, 16); - } - for (i = 0; i <= 9; i++) { const size_t offs = (0x10000 * i); @@ -236,8 +260,6 @@ void R_GenerateBlendTables(void) R_GenerateTranslucencyTable(blendtables[blendtab_reversesubtract] + offs, AST_REVERSESUBTRACT, alpha); } - // Modulation blending only requires a single table - blendtables[blendtab_modulate] = Z_MallocAlign(0x10000, PU_STATIC, NULL, 16); R_GenerateTranslucencyTable(blendtables[blendtab_modulate], AST_MODULATE, 0); } diff --git a/src/r_main.c b/src/r_main.c index 210a97627..c3e044ef2 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1137,7 +1137,7 @@ void R_Init(void) R_InitLightTables(); //I_OutputMsg("\nR_InitTranslucencyTables\n"); - R_InitTranslucencyTables(); + //R_InitTranslucencyTables(); R_InitDrawNodes(); From c39edbbf7fea25a8f915e970af78553d3102149d Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 15 Dec 2021 16:02:23 -0800 Subject: [PATCH 34/36] Read only PNG header bytes to confirm a sprite is a PNG Saves about 300 ms for R_InitSkins. --- src/r_things.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/r_things.c b/src/r_things.c index eccbe7e23..c370a2c67 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -286,16 +286,18 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 #ifndef NO_PNG_LUMPS { - softwarepatch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC); + UINT8 header[PNG_HEADER_SIZE]; size_t len = W_LumpLengthPwad(wadnum, l); - if (Picture_IsLumpPNG((UINT8 *)png, len)) + W_ReadLumpHeaderPwad(wadnum, l, header, sizeof header, 0); + + if (Picture_IsLumpPNG(header, len)) { + UINT8 *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC); Picture_PNGDimensions((UINT8 *)png, &width, &height, &topoffset, &leftoffset, len); isPNG = true; + Z_Free(png); } - - Z_Free(png); } if (!isPNG) From cfe8a1bfa4f11d80bf8d673e60d106ee1eedcd88 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 15 Dec 2021 21:21:54 -0500 Subject: [PATCH 35/36] No random chance for sphere box Asked Oni how it should be done, he said item + sphere box at the same time --- src/k_battle.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 83c47d43e..8a5257206 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -374,14 +374,6 @@ void K_RunPaperItemSpawners(void) firstUnspawnedEmerald ); } - else if (P_RandomChance(FRACUNIT/2)) - { - K_SpawnSphereBox( - battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip), - FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, - 10 - ); - } else { K_CreatePaperItem( @@ -389,6 +381,12 @@ void K_RunPaperItemSpawners(void) FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, 0, 0 ); + + K_SpawnSphereBox( + battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + 10 + ); } } else From ccdbd9ccc9086e8a93f3a39eadecc7e94618ba4b Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 15 Dec 2021 22:32:14 -0800 Subject: [PATCH 36/36] A_ItemPop: var1 determines roulette or sphere award --- src/info.c | 3 ++- src/info.h | 1 + src/p_enemy.c | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/info.c b/src/info.c index 2db296b99..062d1fbb2 100644 --- a/src/info.c +++ b/src/info.c @@ -4074,6 +4074,7 @@ state_t states[NUMSTATES] = {SPR_SBOX, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX11}, // S_SPHEREBOX10 {SPR_SBOX, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX12}, // S_SPHEREBOX11 {SPR_SBOX, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX1}, // S_SPHEREBOX12 + {SPR_NULL, 0, 0, {A_ItemPop}, 1, 0, S_NULL}, // S_DEADSPHEREBOX {SPR_RPOP, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_RANDOMITEMPOP2}, // S_RANDOMITEMPOP1 {SPR_RPOP, FF_FULLBRIGHT|1, 5, {NULL}, 0, 0, S_RANDOMITEMPOP3}, // S_RANDOMITEMPOP2 @@ -23044,7 +23045,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_DEADRANDOMITEM, // deathstate + S_DEADSPHEREBOX, // deathstate S_NULL, // xdeathstate sfx_kc2e, // deathsound 60*FRACUNIT, // speed diff --git a/src/info.h b/src/info.h index eab11e86a..dfb0d3c43 100644 --- a/src/info.h +++ b/src/info.h @@ -4467,6 +4467,7 @@ typedef enum state S_SPHEREBOX10, S_SPHEREBOX11, S_SPHEREBOX12, + S_DEADSPHEREBOX, // Random Item Pop S_RANDOMITEMPOP1, diff --git a/src/p_enemy.c b/src/p_enemy.c index 8bc40525e..3017850cf 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13254,6 +13254,8 @@ void A_ChangeHeight(mobj_t *actor) void A_ItemPop(mobj_t *actor) { + INT32 locvar1 = var1; + mobj_t *remains; mobjtype_t explode; @@ -13308,9 +13310,9 @@ void A_ItemPop(mobj_t *actor) if (actor->info->deathsound) S_StartSound(remains, actor->info->deathsound); - if (gametyperules & GTR_BUMPERS) + if (locvar1 == 1) P_GivePlayerSpheres(actor->target->player, actor->extravalue1); - else + else if (locvar1 == 0) actor->target->player->itemroulette = 1; remains->flags2 &= ~MF2_AMBUSH;