From be33a1b6590da2d73250cbb69d8d1aff268e5bae Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Tue, 18 Jan 2022 22:41:07 -0600 Subject: [PATCH 01/13] Director cam barebones functionality --- src/Sourcefile | 1 + src/k_director.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++ src/k_director.h | 7 ++ src/k_kart.c | 5 ++ src/p_tick.c | 2 + 5 files changed, 184 insertions(+) create mode 100644 src/k_director.c create mode 100644 src/k_director.h diff --git a/src/Sourcefile b/src/Sourcefile index 91851dc62..6c8cabbcd 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -113,3 +113,4 @@ k_grandprix.c k_hud.c k_terrain.c k_brightmap.c +k_director.c \ No newline at end of file diff --git a/src/k_director.c b/src/k_director.c new file mode 100644 index 000000000..685e581c9 --- /dev/null +++ b/src/k_director.c @@ -0,0 +1,169 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +/// \file k_director.c +/// \brief SRB2kart automatic spectator camera. + +#include "k_kart.h" +#include "k_respawn.h" +#include "doomdef.h" +#include "g_game.h" +#include "v_video.h" +#include "k_director.h" +#include "d_netcmd.h" +#include "p_local.h" + +#define SWITCHTIME TICRATE*5 +#define DEBOUNCETIME TICRATE*2 +#define BOREDOMTIME 3*TICRATE/2 +#define TRANSFERTIME TICRATE +#define BREAKAWAYDIST 4000 +#define CONFUSINGDIST 250 + +INT32 FindPlayerByPlace(INT32 place); +void K_UpdateDirectorPositions(void); +boolean K_CanSwitchDirector(void); +fixed_t K_GetFinishGap(INT32 leader, INT32 follower); +void K_DirectorSwitch(INT32 player); +void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); +void K_UpdateDirector(void); + +INT32 cooldown = 0; +INT32 bored = 0; +INT32 positions[MAXPLAYERS] = {0}; +INT32 confidence[MAXPLAYERS] = {0}; + +INT32 freeze = 0; +INT32 attacker = 0; + +INT32 FindPlayerByPlace(INT32 place) +{ + INT32 playernum; + for (playernum = 0; playernum < MAXPLAYERS; ++playernum) + if (playeringame[playernum]) + { + if (players[playernum].position == place) + { + return playernum; + } + } + return -1; +} + +void K_UpdateDirectorPositions(void) { + INT32 playernum; + for (playernum = 0; playernum < MAXPLAYERS; ++playernum) + if (playeringame[playernum]) + { + if (players[playernum].position == positions[playernum]) { + confidence[playernum]++; + } + else { + confidence[playernum] = 0; + positions[playernum] = players[playernum].position; + } + } else { + positions[playernum] = 0; + confidence[playernum] = 0; + } +} + +boolean K_CanSwitchDirector(void) { + INT32 *displayplayerp = &displayplayers[0]; + if (players[*displayplayerp].trickpanel > 0) { + // CONS_Printf("NO SWITCH, panel"); + return false; + } + return cooldown >= SWITCHTIME; +} + +fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { + fixed_t dista = players[follower].distancetofinish; + fixed_t distb = players[leader].distancetofinish; + if (players[follower].position < players[leader].position) { + return distb-dista; + } else { + return dista-distb; + } +} + +void K_DirectorSwitch(INT32 player) { + if (!K_CanSwitchDirector()) + return; + // CONS_Printf("SWITCHING: %s\n", player_names[player]); + G_ResetView(1, player, true); + cooldown = 0; +} + +void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) { + if (!P_IsDisplayPlayer(player)) + return; + if (inflictor && inflictor->player) { + // CONS_Printf("INFLICTOR SET\n"); + attacker = inflictor->player-players; + freeze = TRANSFERTIME; + cooldown = SWITCHTIME; + } + if (source && source->player) { + // CONS_Printf("SOURCE SET\n"); + attacker = source->player-players; + freeze = TRANSFERTIME; + cooldown = SWITCHTIME; + } + // CONS_Printf("FOLLOW ATTACK\n"); + // CONS_Printf(M_GetText("%s attacked\n"), player_names[attacker]); +} + +void K_UpdateDirector(void) { + INT32 *displayplayerp = &displayplayers[0]; + INT32 targetposition; + + K_UpdateDirectorPositions(); + cooldown++; + + if (freeze >= 1) { + // CONS_Printf("FROZEN\n"); + if (freeze == 1) { + K_DirectorSwitch(attacker); + // CONS_Printf("ATTACKER SWITCH\n"); + } + freeze--; + return; + } + + for(targetposition = 2; targetposition < MAXPLAYERS; targetposition++) { + INT32 leader = FindPlayerByPlace(targetposition - 1); + INT32 follower = FindPlayerByPlace(targetposition); + fixed_t gap = K_GetFinishGap(leader, follower); + + /* + CONS_Printf("Eval %d GP %d CD %d FC %d DC %d\n", + targetposition, gap, cooldown, confidence[follower], confidence[*displayplayerp]); + */ + + if (gap > BREAKAWAYDIST) { + bored++; + if (bored > BOREDOMTIME) { + // CONS_Printf("BREAKAWAY, falling back to %d\n", targetposition); + continue; + } + } else if (bored > 0) { + bored--; + } + + /* + if (gap < CONFUSINGDIST && *displayplayerp == leader) { + CONS_Printf("No switch: too close\n"); + break; + } + */ + + if (*displayplayerp != follower && confidence[follower] > DEBOUNCETIME) { + K_DirectorSwitch(FindPlayerByPlace(targetposition)); + } + if (positions[*displayplayerp] != targetposition && confidence[*displayplayerp] > DEBOUNCETIME) { + K_DirectorSwitch(FindPlayerByPlace(targetposition)); + } + + break; + } +} \ No newline at end of file diff --git a/src/k_director.h b/src/k_director.h new file mode 100644 index 000000000..96701ffa7 --- /dev/null +++ b/src/k_director.h @@ -0,0 +1,7 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +/// \file k_director.h +/// \brief SRB2kart automatic spectator camera. + +void K_UpdateDirector(void); +void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); \ No newline at end of file diff --git a/src/k_kart.c b/src/k_kart.c index b9fff95ba..6c61ab6b1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -34,6 +34,7 @@ #include "k_bot.h" #include "k_hud.h" #include "k_terrain.h" +#include "k_director.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -3231,6 +3232,7 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type) { + K_DirectorFollowAttack(player, inflictor, source); (void)inflictor; (void)source; @@ -3273,6 +3275,7 @@ static void K_RemoveGrowShrink(player_t *player) void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) { + K_DirectorFollowAttack(player, inflictor, source); fixed_t gravityadjust; (void)source; @@ -3410,6 +3413,8 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A { INT32 ringburst = 10; + K_DirectorFollowAttack(player, inflictor, source); + (void)source; player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! diff --git a/src/p_tick.c b/src/p_tick.c index 9a4baa092..a320ce5b3 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -706,6 +706,8 @@ void P_Ticker(boolean run) } } + K_UpdateDirector(); + // Always move the camera. for (i = 0; i <= r_splitscreen; i++) { From e949e3afb48e3adee0783c20ea5251f7065afec7 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Wed, 19 Jan 2022 23:56:23 -0600 Subject: [PATCH 02/13] Write the rest of the owl --- src/k_director.c | 219 +++++++++++++++++++++++++---------------------- src/k_director.h | 1 + src/k_hud.c | 2 + 3 files changed, 121 insertions(+), 101 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 685e581c9..26358680f 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -13,68 +13,30 @@ #include "p_local.h" #define SWITCHTIME TICRATE*5 -#define DEBOUNCETIME TICRATE*2 +#define DEBOUNCETIME TICRATE/6 #define BOREDOMTIME 3*TICRATE/2 #define TRANSFERTIME TICRATE #define BREAKAWAYDIST 4000 -#define CONFUSINGDIST 250 +#define WALKBACKDIST 600 +#define PINCHDIST 30000 -INT32 FindPlayerByPlace(INT32 place); void K_UpdateDirectorPositions(void); boolean K_CanSwitchDirector(void); fixed_t K_GetFinishGap(INT32 leader, INT32 follower); -void K_DirectorSwitch(INT32 player); +void K_DirectorSwitch(INT32 player, boolean force); void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); void K_UpdateDirector(void); +void K_DirectorForceSwitch(INT32 player, INT32 time); -INT32 cooldown = 0; -INT32 bored = 0; -INT32 positions[MAXPLAYERS] = {0}; -INT32 confidence[MAXPLAYERS] = {0}; +INT32 cooldown = 0; // how long has it been since we last switched? +INT32 freeze = 0; // when nonzero, fixed switch pending, freeze logic! +INT32 attacker = 0; // who to switch to when freeze delay elapses +INT32 maxdist = 0; -INT32 freeze = 0; -INT32 attacker = 0; +INT32 sortedplayers[MAXPLAYERS] = {0}; // position-1 goes in, player index comes out. +INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest pursuer +INT32 boredom[MAXPLAYERS] = {0}; // how long has a given position had no credible attackers? -INT32 FindPlayerByPlace(INT32 place) -{ - INT32 playernum; - for (playernum = 0; playernum < MAXPLAYERS; ++playernum) - if (playeringame[playernum]) - { - if (players[playernum].position == place) - { - return playernum; - } - } - return -1; -} - -void K_UpdateDirectorPositions(void) { - INT32 playernum; - for (playernum = 0; playernum < MAXPLAYERS; ++playernum) - if (playeringame[playernum]) - { - if (players[playernum].position == positions[playernum]) { - confidence[playernum]++; - } - else { - confidence[playernum] = 0; - positions[playernum] = players[playernum].position; - } - } else { - positions[playernum] = 0; - confidence[playernum] = 0; - } -} - -boolean K_CanSwitchDirector(void) { - INT32 *displayplayerp = &displayplayers[0]; - if (players[*displayplayerp].trickpanel > 0) { - // CONS_Printf("NO SWITCH, panel"); - return false; - } - return cooldown >= SWITCHTIME; -} fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { fixed_t dista = players[follower].distancetofinish; @@ -86,31 +48,88 @@ fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { } } -void K_DirectorSwitch(INT32 player) { - if (!K_CanSwitchDirector()) +void K_UpdateDirectorPositions(void) { + INT32 playernum; + + memset(sortedplayers, -1, sizeof(sortedplayers)); + for (playernum = 0; playernum < MAXPLAYERS; ++playernum) { + if (playeringame[playernum]) + sortedplayers[players[playernum].position-1] = playernum; + } + + memset(gap, INT32_MAX, sizeof(gap)); + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { + if (sortedplayers[playernum] == -1 || sortedplayers[playernum+1] == -1) + break; + gap[playernum] = P_ScaleFromMap(K_GetFinishGap(sortedplayers[playernum], sortedplayers[playernum+1]), FRACUNIT); + if (gap[playernum] >= BREAKAWAYDIST) + boredom[playernum] = min(BOREDOMTIME*2, boredom[playernum]+1); + else if (boredom[playernum] > 0) + boredom[playernum]--; + } + + maxdist = P_ScaleFromMap(players[sortedplayers[0]].distancetofinish, FRACUNIT); +} + +boolean K_CanSwitchDirector(void) { + INT32 *displayplayerp = &displayplayers[0]; + if (players[*displayplayerp].trickpanel > 0) + return false; + return cooldown >= SWITCHTIME; +} + +void K_DirectorSwitch(INT32 player, boolean force) { + if (P_IsDisplayPlayer(&players[player])) + return; + if (!force && !K_CanSwitchDirector()) return; - // CONS_Printf("SWITCHING: %s\n", player_names[player]); G_ResetView(1, player, true); cooldown = 0; } +void K_DirectorForceSwitch(INT32 player, INT32 time) { + attacker = player; + freeze = time; +} + void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) { if (!P_IsDisplayPlayer(player)) return; - if (inflictor && inflictor->player) { - // CONS_Printf("INFLICTOR SET\n"); - attacker = inflictor->player-players; - freeze = TRANSFERTIME; - cooldown = SWITCHTIME; + if (inflictor && inflictor->player) + K_DirectorForceSwitch(inflictor->player-players, TRANSFERTIME); + if (source && source->player) + K_DirectorForceSwitch(source->player-players, TRANSFERTIME); +} + +void K_DrawDirectorDebugger(void) { + INT32 playernum; + INT32 leader; + INT32 follower; + INT32 ytxt; + V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); + V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); + V_DrawThinString(80, 0, V_70TRANS, va("GAP")); + V_DrawThinString(120, 0, V_70TRANS, va("BORED")); + V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); + V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { + ytxt = 10*playernum+10; + leader = sortedplayers[playernum]; + follower = sortedplayers[playernum+1]; + if (leader == -1 || follower == -1) + break; + V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); + V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); + if (players[leader].positiondelay) + V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); + V_DrawThinString(80, ytxt, V_70TRANS, va("%d", gap[playernum])); + if (boredom[playernum] >= BOREDOMTIME) + V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); + else + V_DrawThinString(120, ytxt, V_70TRANS, va("%d", boredom[playernum])); + V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); + V_DrawThinString(230, ytxt, V_70TRANS, va("%s", player_names[follower])); } - if (source && source->player) { - // CONS_Printf("SOURCE SET\n"); - attacker = source->player-players; - freeze = TRANSFERTIME; - cooldown = SWITCHTIME; - } - // CONS_Printf("FOLLOW ATTACK\n"); - // CONS_Printf(M_GetText("%s attacked\n"), player_names[attacker]); } void K_UpdateDirector(void) { @@ -120,50 +139,48 @@ void K_UpdateDirector(void) { K_UpdateDirectorPositions(); cooldown++; - if (freeze >= 1) { - // CONS_Printf("FROZEN\n"); - if (freeze == 1) { - K_DirectorSwitch(attacker); - // CONS_Printf("ATTACKER SWITCH\n"); - } + // handle pending forced switches + if (freeze > 0) { + if (freeze == 1) + K_DirectorSwitch(attacker, true); freeze--; return; } - for(targetposition = 2; targetposition < MAXPLAYERS; targetposition++) { - INT32 leader = FindPlayerByPlace(targetposition - 1); - INT32 follower = FindPlayerByPlace(targetposition); - fixed_t gap = K_GetFinishGap(leader, follower); + // aaight, time to walk through the standings to find the first interesting pair + for(targetposition = 0; targetposition < MAXPLAYERS; targetposition++) { + INT32 target; - /* - CONS_Printf("Eval %d GP %d CD %d FC %d DC %d\n", - targetposition, gap, cooldown, confidence[follower], confidence[*displayplayerp]); - */ - - if (gap > BREAKAWAYDIST) { - bored++; - if (bored > BOREDOMTIME) { - // CONS_Printf("BREAKAWAY, falling back to %d\n", targetposition); - continue; - } - } else if (bored > 0) { - bored--; - } - - /* - if (gap < CONFUSINGDIST && *displayplayerp == leader) { - CONS_Printf("No switch: too close\n"); + // you are out of players, try again + if (sortedplayers[targetposition+1] == -1) break; - } - */ - if (*displayplayerp != follower && confidence[follower] > DEBOUNCETIME) { - K_DirectorSwitch(FindPlayerByPlace(targetposition)); - } - if (positions[*displayplayerp] != targetposition && confidence[*displayplayerp] > DEBOUNCETIME) { - K_DirectorSwitch(FindPlayerByPlace(targetposition)); + // pair too far apart? try the next one + if (boredom[targetposition] >= BOREDOMTIME) + continue; + + // pair finished? try the next one + if (players[sortedplayers[targetposition+1]].exiting) + continue; + + if (maxdist > PINCHDIST) { + // if the "next" player is close enough, they should be able to see everyone fine! + // walk back through the standings to find a vantage that gets everyone in frame. + while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { + targetposition++; + } } + + target = sortedplayers[targetposition+1]; + + // if we're certain the back half of the pair is actually in this position, try to switch + if (*displayplayerp != target && !players[target].positiondelay) + K_DirectorSwitch(target, false); + // even if we're not certain, if we're certain we're watching the WRONG player, try to switch + if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) + K_DirectorSwitch(target, false); + break; } } \ No newline at end of file diff --git a/src/k_director.h b/src/k_director.h index 96701ffa7..a7d9fd2a6 100644 --- a/src/k_director.h +++ b/src/k_director.h @@ -4,4 +4,5 @@ /// \brief SRB2kart automatic spectator camera. void K_UpdateDirector(void); +void K_DrawDirectorDebugger(void); void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); \ No newline at end of file diff --git a/src/k_hud.c b/src/k_hud.c index fd83afd56..62253ba68 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -13,6 +13,7 @@ #include "k_kart.h" #include "k_battle.h" #include "k_color.h" +#include "k_director.h" #include "screen.h" #include "doomtype.h" #include "doomdef.h" @@ -4504,6 +4505,7 @@ void K_drawKartHUD(void) } K_DrawWaypointDebugger(); + K_DrawDirectorDebugger(); if (gametype == GT_BATTLE) { From 1e4bc482d044b4ea16947389553f19b60fb3104b Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 02:07:48 -0600 Subject: [PATCH 03/13] very small tiny fixes --- src/d_netcmd.c | 1 + src/d_netcmd.h | 2 +- src/k_director.c | 15 ++++++++++++--- src/k_kart.c | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7fa504e90..ed2d47ede 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -433,6 +433,7 @@ consvar_t cv_kartdebugbotpredict = CVAR_INIT ("kartdebugbotpredict", "Off", CV_N consvar_t cv_kartdebugcheckpoint = CVAR_INIT ("kartdebugcheckpoint", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_kartdebugnodes = CVAR_INIT ("kartdebugnodes", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_kartdebugcolorize = CVAR_INIT ("kartdebugcolorize", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); +consvar_t cv_kartdebugdirector = CVAR_INIT ("kartdebugdirector", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}}; consvar_t cv_votetime = CVAR_INIT ("votetime", "20", CV_NETVAR, votetime_cons_t, NULL); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 0e8ef25e2..4813dd07e 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -87,7 +87,7 @@ extern consvar_t cv_kartusepwrlv; extern consvar_t cv_votetime; extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartallowgiveitem, cv_kartdebugshrink, cv_kartdebugdistribution, cv_kartdebughuddrop; -extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize; +extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector; extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbotpredict; extern consvar_t cv_itemfinder; diff --git a/src/k_director.c b/src/k_director.c index 26358680f..3461a6327 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -37,7 +37,6 @@ INT32 sortedplayers[MAXPLAYERS] = {0}; // position-1 goes in, player index comes INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest pursuer INT32 boredom[MAXPLAYERS] = {0}; // how long has a given position had no credible attackers? - fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { fixed_t dista = players[follower].distancetofinish; fixed_t distb = players[leader].distancetofinish; @@ -75,12 +74,16 @@ boolean K_CanSwitchDirector(void) { INT32 *displayplayerp = &displayplayers[0]; if (players[*displayplayerp].trickpanel > 0) return false; - return cooldown >= SWITCHTIME; + if (cooldown < SWITCHTIME); + return false; + return true; } void K_DirectorSwitch(INT32 player, boolean force) { if (P_IsDisplayPlayer(&players[player])) return; + if (players[player].exiting) + return; if (!force && !K_CanSwitchDirector()) return; G_ResetView(1, player, true); @@ -88,6 +91,8 @@ void K_DirectorSwitch(INT32 player, boolean force) { } void K_DirectorForceSwitch(INT32 player, INT32 time) { + if (players[player].exiting) + return; attacker = player; freeze = time; } @@ -106,6 +111,8 @@ void K_DrawDirectorDebugger(void) { INT32 leader; INT32 follower; INT32 ytxt; + if (!cv_kartdebugdirector.value) + return; V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); V_DrawThinString(80, 0, V_70TRANS, va("GAP")); @@ -148,7 +155,7 @@ void K_UpdateDirector(void) { } // aaight, time to walk through the standings to find the first interesting pair - for(targetposition = 0; targetposition < MAXPLAYERS; targetposition++) { + for(targetposition = 0; targetposition < MAXPLAYERS-1; targetposition++) { INT32 target; // you are out of players, try again @@ -163,9 +170,11 @@ void K_UpdateDirector(void) { if (players[sortedplayers[targetposition+1]].exiting) continue; + // don't risk switching away from forward pairs at race end, might miss something! if (maxdist > PINCHDIST) { // if the "next" player is close enough, they should be able to see everyone fine! // walk back through the standings to find a vantage that gets everyone in frame. + // (also creates a pretty cool effect w/ overtakes at speed) while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { targetposition++; } diff --git a/src/k_kart.c b/src/k_kart.c index 6c61ab6b1..d1b654411 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -261,6 +261,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartdebugcheckpoint); CV_RegisterVar(&cv_kartdebugnodes); CV_RegisterVar(&cv_kartdebugcolorize); + CV_RegisterVar(&cv_kartdebugdirector); } //} From 24b05451f6792ae2868d4998e4566c540d4529ca Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 03:39:00 -0600 Subject: [PATCH 04/13] my workflow is beyond your understanding --- src/d_netcmd.c | 4 ++++ src/d_netcmd.h | 2 ++ src/k_director.c | 25 +++++++++++++++---------- src/k_kart.c | 3 ++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ed2d47ede..87580d21a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -510,6 +510,8 @@ static CV_PossibleValue_t perfstats_cons_t[] = { {0, "Off"}, {1, "Rendering"}, {2, "Logic"}, {3, "ThinkFrame"}, {0, NULL}}; consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", 0, perfstats_cons_t, NULL); +consvar_t cv_director = CVAR_INIT ("director", "Off", 0, CV_OnOff, NULL); + char timedemo_name[256]; boolean timedemo_csv; char timedemo_csv_id[256]; @@ -742,6 +744,8 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_showping); CV_RegisterVar(&cv_showviewpointtext); + CV_RegisterVar(&cv_director); + CV_RegisterVar(&cv_dummyconsvar); #ifdef USE_STUN diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 4813dd07e..ba3c68452 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -113,6 +113,8 @@ extern consvar_t cv_sleep; extern consvar_t cv_perfstats; +extern consvar_t cv_director; + extern char timedemo_name[256]; extern boolean timedemo_csv; extern char timedemo_csv_id[256]; diff --git a/src/k_director.c b/src/k_director.c index 3461a6327..0de6b8957 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -12,13 +12,12 @@ #include "d_netcmd.h" #include "p_local.h" -#define SWITCHTIME TICRATE*5 -#define DEBOUNCETIME TICRATE/6 -#define BOREDOMTIME 3*TICRATE/2 -#define TRANSFERTIME TICRATE -#define BREAKAWAYDIST 4000 -#define WALKBACKDIST 600 -#define PINCHDIST 30000 +#define SWITCHTIME TICRATE*5 // cooldown between unforced switches +#define BOREDOMTIME 3*TICRATE/2 // how long until players considered far apart? +#define TRANSFERTIME TICRATE // how long to delay reaction shots? +#define BREAKAWAYDIST 4000 // how *far* until players considered far apart? +#define WALKBACKDIST 600 // how close should a trailing player be before we switch? +#define PINCHDIST 30000 // how close should the leader be to be considered "end of race"? void K_UpdateDirectorPositions(void); boolean K_CanSwitchDirector(void); @@ -31,7 +30,7 @@ void K_DirectorForceSwitch(INT32 player, INT32 time); INT32 cooldown = 0; // how long has it been since we last switched? INT32 freeze = 0; // when nonzero, fixed switch pending, freeze logic! INT32 attacker = 0; // who to switch to when freeze delay elapses -INT32 maxdist = 0; +INT32 maxdist = 0; // how far is the closest player from finishing? INT32 sortedplayers[MAXPLAYERS] = {0}; // position-1 goes in, player index comes out. INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest pursuer @@ -74,7 +73,7 @@ boolean K_CanSwitchDirector(void) { INT32 *displayplayerp = &displayplayers[0]; if (players[*displayplayerp].trickpanel > 0) return false; - if (cooldown < SWITCHTIME); + if (cooldown < SWITCHTIME) return false; return true; } @@ -111,20 +110,24 @@ void K_DrawDirectorDebugger(void) { INT32 leader; INT32 follower; INT32 ytxt; + if (!cv_kartdebugdirector.value) return; + V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); V_DrawThinString(80, 0, V_70TRANS, va("GAP")); V_DrawThinString(120, 0, V_70TRANS, va("BORED")); V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { ytxt = 10*playernum+10; leader = sortedplayers[playernum]; follower = sortedplayers[playernum+1]; if (leader == -1 || follower == -1) break; + V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); if (players[leader].positiondelay) @@ -143,6 +146,9 @@ void K_UpdateDirector(void) { INT32 *displayplayerp = &displayplayers[0]; INT32 targetposition; + if (!cv_director.value) + return; + K_UpdateDirectorPositions(); cooldown++; @@ -180,7 +186,6 @@ void K_UpdateDirector(void) { } } - target = sortedplayers[targetposition+1]; // if we're certain the back half of the pair is actually in this position, try to switch diff --git a/src/k_kart.c b/src/k_kart.c index d1b654411..4332d4416 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3276,10 +3276,11 @@ static void K_RemoveGrowShrink(player_t *player) void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) { - K_DirectorFollowAttack(player, inflictor, source); fixed_t gravityadjust; (void)source; + K_DirectorFollowAttack(player, inflictor, source); + player->tumbleBounces = 1; if (player->tripWireState == TRIP_PASSED) From 8a2be8556354e834d7e1fe63cb788b88f1097ab0 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Jan 2022 04:50:11 -0800 Subject: [PATCH 05/13] Include k_director.h --- src/p_tick.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_tick.c b/src/p_tick.c index a320ce5b3..69641d6be 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -34,6 +34,7 @@ #include "k_race.h" #include "k_battle.h" #include "k_waypoint.h" +#include "k_director.h" tic_t leveltime; From 5d3fb30a07c0080af8df3dce7ff3ee02ec729fc7 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 06:56:07 -0600 Subject: [PATCH 06/13] tabs, thank you vscode for not reading the room --- src/k_director.c | 238 +++++++++++++++++++++++------------------------ 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 0de6b8957..0dc33c140 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -37,164 +37,164 @@ INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest p INT32 boredom[MAXPLAYERS] = {0}; // how long has a given position had no credible attackers? fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { - fixed_t dista = players[follower].distancetofinish; - fixed_t distb = players[leader].distancetofinish; - if (players[follower].position < players[leader].position) { - return distb-dista; - } else { - return dista-distb; - } + fixed_t dista = players[follower].distancetofinish; + fixed_t distb = players[leader].distancetofinish; + if (players[follower].position < players[leader].position) { + return distb-dista; + } else { + return dista-distb; + } } void K_UpdateDirectorPositions(void) { - INT32 playernum; + INT32 playernum; - memset(sortedplayers, -1, sizeof(sortedplayers)); + memset(sortedplayers, -1, sizeof(sortedplayers)); for (playernum = 0; playernum < MAXPLAYERS; ++playernum) { if (playeringame[playernum]) - sortedplayers[players[playernum].position-1] = playernum; - } + sortedplayers[players[playernum].position-1] = playernum; + } - memset(gap, INT32_MAX, sizeof(gap)); - for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { - if (sortedplayers[playernum] == -1 || sortedplayers[playernum+1] == -1) - break; - gap[playernum] = P_ScaleFromMap(K_GetFinishGap(sortedplayers[playernum], sortedplayers[playernum+1]), FRACUNIT); - if (gap[playernum] >= BREAKAWAYDIST) - boredom[playernum] = min(BOREDOMTIME*2, boredom[playernum]+1); - else if (boredom[playernum] > 0) - boredom[playernum]--; - } + memset(gap, INT32_MAX, sizeof(gap)); + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { + if (sortedplayers[playernum] == -1 || sortedplayers[playernum+1] == -1) + break; + gap[playernum] = P_ScaleFromMap(K_GetFinishGap(sortedplayers[playernum], sortedplayers[playernum+1]), FRACUNIT); + if (gap[playernum] >= BREAKAWAYDIST) + boredom[playernum] = min(BOREDOMTIME*2, boredom[playernum]+1); + else if (boredom[playernum] > 0) + boredom[playernum]--; + } - maxdist = P_ScaleFromMap(players[sortedplayers[0]].distancetofinish, FRACUNIT); + maxdist = P_ScaleFromMap(players[sortedplayers[0]].distancetofinish, FRACUNIT); } boolean K_CanSwitchDirector(void) { - INT32 *displayplayerp = &displayplayers[0]; - if (players[*displayplayerp].trickpanel > 0) - return false; - if (cooldown < SWITCHTIME) - return false; - return true; + INT32 *displayplayerp = &displayplayers[0]; + if (players[*displayplayerp].trickpanel > 0) + return false; + if (cooldown < SWITCHTIME) + return false; + return true; } void K_DirectorSwitch(INT32 player, boolean force) { - if (P_IsDisplayPlayer(&players[player])) - return; - if (players[player].exiting) - return; - if (!force && !K_CanSwitchDirector()) - return; - G_ResetView(1, player, true); - cooldown = 0; + if (P_IsDisplayPlayer(&players[player])) + return; + if (players[player].exiting) + return; + if (!force && !K_CanSwitchDirector()) + return; + G_ResetView(1, player, true); + cooldown = 0; } void K_DirectorForceSwitch(INT32 player, INT32 time) { - if (players[player].exiting) - return; - attacker = player; - freeze = time; + if (players[player].exiting) + return; + attacker = player; + freeze = time; } void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) { - if (!P_IsDisplayPlayer(player)) - return; - if (inflictor && inflictor->player) - K_DirectorForceSwitch(inflictor->player-players, TRANSFERTIME); - if (source && source->player) - K_DirectorForceSwitch(source->player-players, TRANSFERTIME); + if (!P_IsDisplayPlayer(player)) + return; + if (inflictor && inflictor->player) + K_DirectorForceSwitch(inflictor->player-players, TRANSFERTIME); + if (source && source->player) + K_DirectorForceSwitch(source->player-players, TRANSFERTIME); } void K_DrawDirectorDebugger(void) { - INT32 playernum; - INT32 leader; - INT32 follower; - INT32 ytxt; + INT32 playernum; + INT32 leader; + INT32 follower; + INT32 ytxt; - if (!cv_kartdebugdirector.value) - return; + if (!cv_kartdebugdirector.value) + return; - V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); - V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); - V_DrawThinString(80, 0, V_70TRANS, va("GAP")); - V_DrawThinString(120, 0, V_70TRANS, va("BORED")); - V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); - V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); + V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); + V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); + V_DrawThinString(80, 0, V_70TRANS, va("GAP")); + V_DrawThinString(120, 0, V_70TRANS, va("BORED")); + V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); + V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); - for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { - ytxt = 10*playernum+10; - leader = sortedplayers[playernum]; - follower = sortedplayers[playernum+1]; - if (leader == -1 || follower == -1) - break; + for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { + ytxt = 10*playernum+10; + leader = sortedplayers[playernum]; + follower = sortedplayers[playernum+1]; + if (leader == -1 || follower == -1) + break; - V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); - V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); - if (players[leader].positiondelay) - V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); - V_DrawThinString(80, ytxt, V_70TRANS, va("%d", gap[playernum])); - if (boredom[playernum] >= BOREDOMTIME) - V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); - else - V_DrawThinString(120, ytxt, V_70TRANS, va("%d", boredom[playernum])); - V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); - V_DrawThinString(230, ytxt, V_70TRANS, va("%s", player_names[follower])); - } + V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); + V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); + if (players[leader].positiondelay) + V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); + V_DrawThinString(80, ytxt, V_70TRANS, va("%d", gap[playernum])); + if (boredom[playernum] >= BOREDOMTIME) + V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); + else + V_DrawThinString(120, ytxt, V_70TRANS, va("%d", boredom[playernum])); + V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); + V_DrawThinString(230, ytxt, V_70TRANS, va("%s", player_names[follower])); + } } void K_UpdateDirector(void) { - INT32 *displayplayerp = &displayplayers[0]; - INT32 targetposition; + INT32 *displayplayerp = &displayplayers[0]; + INT32 targetposition; - if (!cv_director.value) - return; + if (!cv_director.value) + return; - K_UpdateDirectorPositions(); - cooldown++; + K_UpdateDirectorPositions(); + cooldown++; - // handle pending forced switches - if (freeze > 0) { - if (freeze == 1) - K_DirectorSwitch(attacker, true); - freeze--; - return; - } + // handle pending forced switches + if (freeze > 0) { + if (freeze == 1) + K_DirectorSwitch(attacker, true); + freeze--; + return; + } - // aaight, time to walk through the standings to find the first interesting pair - for(targetposition = 0; targetposition < MAXPLAYERS-1; targetposition++) { - INT32 target; + // aaight, time to walk through the standings to find the first interesting pair + for(targetposition = 0; targetposition < MAXPLAYERS-1; targetposition++) { + INT32 target; - // you are out of players, try again - if (sortedplayers[targetposition+1] == -1) - break; + // you are out of players, try again + if (sortedplayers[targetposition+1] == -1) + break; - // pair too far apart? try the next one - if (boredom[targetposition] >= BOREDOMTIME) - continue; + // pair too far apart? try the next one + if (boredom[targetposition] >= BOREDOMTIME) + continue; - // pair finished? try the next one - if (players[sortedplayers[targetposition+1]].exiting) - continue; + // pair finished? try the next one + if (players[sortedplayers[targetposition+1]].exiting) + continue; - // don't risk switching away from forward pairs at race end, might miss something! - if (maxdist > PINCHDIST) { - // if the "next" player is close enough, they should be able to see everyone fine! - // walk back through the standings to find a vantage that gets everyone in frame. - // (also creates a pretty cool effect w/ overtakes at speed) - while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { - targetposition++; - } - } + // don't risk switching away from forward pairs at race end, might miss something! + if (maxdist > PINCHDIST) { + // if the "next" player is close enough, they should be able to see everyone fine! + // walk back through the standings to find a vantage that gets everyone in frame. + // (also creates a pretty cool effect w/ overtakes at speed) + while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { + targetposition++; + } + } - target = sortedplayers[targetposition+1]; + target = sortedplayers[targetposition+1]; - // if we're certain the back half of the pair is actually in this position, try to switch - if (*displayplayerp != target && !players[target].positiondelay) - K_DirectorSwitch(target, false); - // even if we're not certain, if we're certain we're watching the WRONG player, try to switch - if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) - K_DirectorSwitch(target, false); + // if we're certain the back half of the pair is actually in this position, try to switch + if (*displayplayerp != target && !players[target].positiondelay) + K_DirectorSwitch(target, false); + // even if we're not certain, if we're certain we're watching the WRONG player, try to switch + if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) + K_DirectorSwitch(target, false); - break; - } + break; + } } \ No newline at end of file From 5efd706d3b68f3fca88799d64bdf73bab3cab8a6 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:23:32 -0600 Subject: [PATCH 07/13] general post-review unfuck --- src/k_director.c | 254 +++++++++++++++++++++++++++++++++-------------- src/k_director.h | 13 +++ src/k_kart.c | 7 +- src/p_setup.c | 3 + 4 files changed, 198 insertions(+), 79 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 0dc33c140..d3b2ce7bd 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -12,13 +12,14 @@ #include "d_netcmd.h" #include "p_local.h" -#define SWITCHTIME TICRATE*5 // cooldown between unforced switches -#define BOREDOMTIME 3*TICRATE/2 // how long until players considered far apart? -#define TRANSFERTIME TICRATE // how long to delay reaction shots? -#define BREAKAWAYDIST 4000 // how *far* until players considered far apart? -#define WALKBACKDIST 600 // how close should a trailing player be before we switch? -#define PINCHDIST 30000 // how close should the leader be to be considered "end of race"? +#define SWITCHTIME TICRATE * 5 // cooldown between unforced switches +#define BOREDOMTIME 3 * TICRATE / 2 // how long until players considered far apart? +#define TRANSFERTIME TICRATE // how long to delay reaction shots? +#define BREAKAWAYDIST 4000 // how *far* until players considered far apart? +#define WALKBACKDIST 600 // how close should a trailing player be before we switch? +#define PINCHDIST 30000 // how close should the leader be to be considered "end of race"? +void K_InitDirector(void); void K_UpdateDirectorPositions(void); boolean K_CanSwitchDirector(void); fixed_t K_GetFinishGap(INT32 leader, INT32 follower); @@ -27,173 +28,274 @@ void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) void K_UpdateDirector(void); void K_DirectorForceSwitch(INT32 player, INT32 time); -INT32 cooldown = 0; // how long has it been since we last switched? -INT32 freeze = 0; // when nonzero, fixed switch pending, freeze logic! -INT32 attacker = 0; // who to switch to when freeze delay elapses -INT32 maxdist = 0; // how far is the closest player from finishing? +struct directorinfo directorinfo; -INT32 sortedplayers[MAXPLAYERS] = {0}; // position-1 goes in, player index comes out. -INT32 gap[MAXPLAYERS] = {0}; // gap between a given position and their closest pursuer -INT32 boredom[MAXPLAYERS] = {0}; // how long has a given position had no credible attackers? - -fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { - fixed_t dista = players[follower].distancetofinish; - fixed_t distb = players[leader].distancetofinish; - if (players[follower].position < players[leader].position) { - return distb-dista; - } else { - return dista-distb; - } -} - -void K_UpdateDirectorPositions(void) { +void K_InitDirector(void) +{ INT32 playernum; - memset(sortedplayers, -1, sizeof(sortedplayers)); - for (playernum = 0; playernum < MAXPLAYERS; ++playernum) { - if (playeringame[playernum]) - sortedplayers[players[playernum].position-1] = playernum; - } + directorinfo.cooldown = SWITCHTIME; + directorinfo.freeze = 0; + directorinfo.attacker = 0; + directorinfo.maxdist = 0; - memset(gap, INT32_MAX, sizeof(gap)); - for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { - if (sortedplayers[playernum] == -1 || sortedplayers[playernum+1] == -1) - break; - gap[playernum] = P_ScaleFromMap(K_GetFinishGap(sortedplayers[playernum], sortedplayers[playernum+1]), FRACUNIT); - if (gap[playernum] >= BREAKAWAYDIST) - boredom[playernum] = min(BOREDOMTIME*2, boredom[playernum]+1); - else if (boredom[playernum] > 0) - boredom[playernum]--; + for (playernum = 0; playernum < MAXPLAYERS; playernum++) + { + directorinfo.sortedplayers[playernum] = -1; + directorinfo.gap[playernum] = INT32_MAX; + directorinfo.boredom[playernum] = 0; } - - maxdist = P_ScaleFromMap(players[sortedplayers[0]].distancetofinish, FRACUNIT); } -boolean K_CanSwitchDirector(void) { +fixed_t K_GetFinishGap(INT32 leader, INT32 follower) +{ + fixed_t dista = players[follower].distancetofinish; + fixed_t distb = players[leader].distancetofinish; + + if (players[follower].position < players[leader].position) + { + return distb - dista; + } + else + { + return dista - distb; + } +} + +void K_UpdateDirectorPositions(void) +{ + INT32 playernum; + INT32 position; + player_t target; + + memset(directorinfo.sortedplayers, -1, sizeof(directorinfo.sortedplayers)); + + for (playernum = 0; playernum < MAXPLAYERS; playernum++) + { + target = players[playernum]; + + if (playeringame[playernum] && !target.spectator && target.position > 0) + { + directorinfo.sortedplayers[target.position - 1] = playernum; + } + } + + for (position = 0; position < MAXPLAYERS - 1; position++) + { + directorinfo.gap[position] = INT32_MAX; + + if (directorinfo.sortedplayers[position] == -1 || directorinfo.sortedplayers[position + 1] == -1) + { + continue; + } + + directorinfo.gap[position] = P_ScaleFromMap(K_GetFinishGap(directorinfo.sortedplayers[position], directorinfo.sortedplayers[position + 1]), FRACUNIT); + + if (directorinfo.gap[position] >= BREAKAWAYDIST) + { + directorinfo.boredom[position] = min(BOREDOMTIME * 2, directorinfo.boredom[position] + 1); + } + else if (directorinfo.boredom[position] > 0) + { + directorinfo.boredom[position]--; + } + } + + directorinfo.maxdist = P_ScaleFromMap(players[directorinfo.sortedplayers[0]].distancetofinish, FRACUNIT); +} + +boolean K_CanSwitchDirector(void) +{ INT32 *displayplayerp = &displayplayers[0]; + if (players[*displayplayerp].trickpanel > 0) + { return false; - if (cooldown < SWITCHTIME) + } + + if (directorinfo.cooldown > 0) + { return false; + } + return true; } -void K_DirectorSwitch(INT32 player, boolean force) { +void K_DirectorSwitch(INT32 player, boolean force) +{ if (P_IsDisplayPlayer(&players[player])) + { return; + } + if (players[player].exiting) + { return; + } + if (!force && !K_CanSwitchDirector()) + { return; + } + G_ResetView(1, player, true); - cooldown = 0; + directorinfo.cooldown = SWITCHTIME; } -void K_DirectorForceSwitch(INT32 player, INT32 time) { +void K_DirectorForceSwitch(INT32 player, INT32 time) +{ if (players[player].exiting) + { return; - attacker = player; - freeze = time; + } + + directorinfo.attacker = player; + directorinfo.freeze = time; } -void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) { +void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) +{ if (!P_IsDisplayPlayer(player)) + { return; + } + if (inflictor && inflictor->player) - K_DirectorForceSwitch(inflictor->player-players, TRANSFERTIME); - if (source && source->player) - K_DirectorForceSwitch(source->player-players, TRANSFERTIME); + { + K_DirectorForceSwitch(inflictor->player - players, TRANSFERTIME); + } + else if (source && source->player) + { + K_DirectorForceSwitch(source->player - players, TRANSFERTIME); + } } -void K_DrawDirectorDebugger(void) { +void K_DrawDirectorDebugger(void) +{ INT32 playernum; INT32 leader; INT32 follower; INT32 ytxt; if (!cv_kartdebugdirector.value) + { return; + } V_DrawThinString(10, 0, V_70TRANS, va("PLACE")); V_DrawThinString(40, 0, V_70TRANS, va("CONF?")); V_DrawThinString(80, 0, V_70TRANS, va("GAP")); V_DrawThinString(120, 0, V_70TRANS, va("BORED")); - V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", cooldown)); - V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", maxdist)); + V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", directorinfo.cooldown)); + V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", directorinfo.maxdist)); + + for (playernum = 0; playernum < MAXPLAYERS - 1; playernum++) + { + ytxt = 10 * (playernum + 1); + leader = directorinfo.sortedplayers[playernum]; + follower = directorinfo.sortedplayers[playernum + 1]; - for (playernum = 0; playernum < MAXPLAYERS-1; ++playernum) { - ytxt = 10*playernum+10; - leader = sortedplayers[playernum]; - follower = sortedplayers[playernum+1]; if (leader == -1 || follower == -1) break; V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); - V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum+1)); + V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum + 1)); + if (players[leader].positiondelay) + { V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); - V_DrawThinString(80, ytxt, V_70TRANS, va("%d", gap[playernum])); - if (boredom[playernum] >= BOREDOMTIME) + } + + V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfo.gap[playernum])); + + if (directorinfo.boredom[playernum] >= BOREDOMTIME) + { V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); + } else - V_DrawThinString(120, ytxt, V_70TRANS, va("%d", boredom[playernum])); + { + V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfo.boredom[playernum])); + } + V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); V_DrawThinString(230, ytxt, V_70TRANS, va("%s", player_names[follower])); } } -void K_UpdateDirector(void) { +void K_UpdateDirector(void) +{ INT32 *displayplayerp = &displayplayers[0]; INT32 targetposition; if (!cv_director.value) + { return; + } K_UpdateDirectorPositions(); - cooldown++; + + if (directorinfo.cooldown > 0) { + directorinfo.cooldown--; + } // handle pending forced switches - if (freeze > 0) { - if (freeze == 1) - K_DirectorSwitch(attacker, true); - freeze--; + if (directorinfo.freeze > 0) + { + if (!(--directorinfo.freeze)) + K_DirectorSwitch(directorinfo.attacker, true); + + directorinfo.freeze--; return; } // aaight, time to walk through the standings to find the first interesting pair - for(targetposition = 0; targetposition < MAXPLAYERS-1; targetposition++) { + for (targetposition = 1; targetposition < MAXPLAYERS; targetposition++) + { INT32 target; // you are out of players, try again - if (sortedplayers[targetposition+1] == -1) + if (directorinfo.sortedplayers[targetposition] == -1) + { break; + } // pair too far apart? try the next one - if (boredom[targetposition] >= BOREDOMTIME) + if (directorinfo.boredom[targetposition - 1] >= BOREDOMTIME) + { continue; + } // pair finished? try the next one - if (players[sortedplayers[targetposition+1]].exiting) + if (players[directorinfo.sortedplayers[targetposition]].exiting) + { continue; + } // don't risk switching away from forward pairs at race end, might miss something! - if (maxdist > PINCHDIST) { + if (directorinfo.maxdist > PINCHDIST) + { // if the "next" player is close enough, they should be able to see everyone fine! // walk back through the standings to find a vantage that gets everyone in frame. // (also creates a pretty cool effect w/ overtakes at speed) - while (targetposition < MAXPLAYERS && gap[targetposition+1] < WALKBACKDIST) { + while (targetposition < MAXPLAYERS && directorinfo.gap[targetposition] < WALKBACKDIST) + { targetposition++; } } - target = sortedplayers[targetposition+1]; + target = directorinfo.sortedplayers[targetposition]; // if we're certain the back half of the pair is actually in this position, try to switch if (*displayplayerp != target && !players[target].positiondelay) + { K_DirectorSwitch(target, false); + } + // even if we're not certain, if we're certain we're watching the WRONG player, try to switch if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) + { K_DirectorSwitch(target, false); + } break; } diff --git a/src/k_director.h b/src/k_director.h index a7d9fd2a6..db19fbe8b 100644 --- a/src/k_director.h +++ b/src/k_director.h @@ -3,6 +3,19 @@ /// \file k_director.h /// \brief SRB2kart automatic spectator camera. +extern struct directorinfo +{ + tic_t cooldown; // how long has it been since we last switched? + tic_t freeze; // when nonzero, fixed switch pending, freeze logic! + INT32 attacker; // who to switch to when freeze delay elapses + INT32 maxdist; // how far is the closest player from finishing? + + INT32 sortedplayers[MAXPLAYERS]; // position-1 goes in, player index comes out. + INT32 gap[MAXPLAYERS]; // gap between a given position and their closest pursuer + INT32 boredom[MAXPLAYERS]; // how long has a given position had no credible attackers? +} directorinfo; + +void K_InitDirector(void); void K_UpdateDirector(void); void K_DrawDirectorDebugger(void); void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); \ No newline at end of file diff --git a/src/k_kart.c b/src/k_kart.c index 4332d4416..f7fc380a3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3233,10 +3233,11 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type) { - K_DirectorFollowAttack(player, inflictor, source); (void)inflictor; (void)source; + K_DirectorFollowAttack(player, inflictor, source); + player->spinouttype = type; if (( player->spinouttype & KSPIN_THRUST )) @@ -3415,10 +3416,10 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A { INT32 ringburst = 10; - K_DirectorFollowAttack(player, inflictor, source); - (void)source; + K_DirectorFollowAttack(player, inflictor, source); + player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! player->mo->momx = player->mo->momy = 0; diff --git a/src/p_setup.c b/src/p_setup.c index 8c5f5d59b..faf79af2e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -93,6 +93,7 @@ #include "k_grandprix.h" #include "k_terrain.h" // TRF_TRIPWIRE #include "k_brightmap.h" +#include "k_director.h" // K_InitDirector // Replay names have time #if !defined (UNDER_CE) @@ -4142,6 +4143,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) memset(localaiming, 0, sizeof(localaiming)); } + K_InitDirector(); + wantedcalcdelay = wantedfrequency*2; indirectitemcooldown = 0; hyubgone = 0; From ef3ae2044aa439a5fc986c5d588aa86811295e4b Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:26:32 -0600 Subject: [PATCH 08/13] appease linker wrt internal functions --- src/k_director.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index d3b2ce7bd..291cb3953 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -47,7 +47,7 @@ void K_InitDirector(void) } } -fixed_t K_GetFinishGap(INT32 leader, INT32 follower) +static fixed_t K_GetFinishGap(INT32 leader, INT32 follower) { fixed_t dista = players[follower].distancetofinish; fixed_t distb = players[leader].distancetofinish; @@ -104,7 +104,7 @@ void K_UpdateDirectorPositions(void) directorinfo.maxdist = P_ScaleFromMap(players[directorinfo.sortedplayers[0]].distancetofinish, FRACUNIT); } -boolean K_CanSwitchDirector(void) +static boolean K_CanSwitchDirector(void) { INT32 *displayplayerp = &displayplayers[0]; @@ -121,7 +121,7 @@ boolean K_CanSwitchDirector(void) return true; } -void K_DirectorSwitch(INT32 player, boolean force) +static void K_DirectorSwitch(INT32 player, boolean force) { if (P_IsDisplayPlayer(&players[player])) { @@ -142,7 +142,7 @@ void K_DirectorSwitch(INT32 player, boolean force) directorinfo.cooldown = SWITCHTIME; } -void K_DirectorForceSwitch(INT32 player, INT32 time) +static void K_DirectorForceSwitch(INT32 player, INT32 time) { if (players[player].exiting) { From 3ce1f712304bac483cc2aa9b5c16495d04f6bf3a Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:29:13 -0600 Subject: [PATCH 09/13] how is static formed --- src/k_director.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 291cb3953..169cd8af4 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -19,15 +19,6 @@ #define WALKBACKDIST 600 // how close should a trailing player be before we switch? #define PINCHDIST 30000 // how close should the leader be to be considered "end of race"? -void K_InitDirector(void); -void K_UpdateDirectorPositions(void); -boolean K_CanSwitchDirector(void); -fixed_t K_GetFinishGap(INT32 leader, INT32 follower); -void K_DirectorSwitch(INT32 player, boolean force); -void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); -void K_UpdateDirector(void); -void K_DirectorForceSwitch(INT32 player, INT32 time); - struct directorinfo directorinfo; void K_InitDirector(void) @@ -62,7 +53,7 @@ static fixed_t K_GetFinishGap(INT32 leader, INT32 follower) } } -void K_UpdateDirectorPositions(void) +static void K_UpdateDirectorPositions(void) { INT32 playernum; INT32 position; From daabde098106abc5ca1d4eb47c1986eee33a9f9c Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:35:26 -0600 Subject: [PATCH 10/13] fix double speed attack transfers --- src/k_director.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/k_director.c b/src/k_director.c index 169cd8af4..34ee9d6b3 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -235,7 +235,6 @@ void K_UpdateDirector(void) if (!(--directorinfo.freeze)) K_DirectorSwitch(directorinfo.attacker, true); - directorinfo.freeze--; return; } From 9366049c89a85d359be9ca646a47d81337fbf144 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 15:56:36 -0600 Subject: [PATCH 11/13] fix low-confidence switch behavior --- src/k_director.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 34ee9d6b3..4603beb31 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -163,7 +163,7 @@ void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) void K_DrawDirectorDebugger(void) { - INT32 playernum; + INT32 position; INT32 leader; INT32 follower; INT32 ytxt; @@ -180,32 +180,32 @@ void K_DrawDirectorDebugger(void) V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", directorinfo.cooldown)); V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", directorinfo.maxdist)); - for (playernum = 0; playernum < MAXPLAYERS - 1; playernum++) + for (position = 0; position < MAXPLAYERS - 1; position++) { - ytxt = 10 * (playernum + 1); - leader = directorinfo.sortedplayers[playernum]; - follower = directorinfo.sortedplayers[playernum + 1]; + ytxt = 10 * (position + 1); + leader = directorinfo.sortedplayers[position]; + follower = directorinfo.sortedplayers[position + 1]; if (leader == -1 || follower == -1) break; - V_DrawThinString(10, ytxt, V_70TRANS, va("%d", playernum)); - V_DrawThinString(20, ytxt, V_70TRANS, va("%d", playernum + 1)); + V_DrawThinString(10, ytxt, V_70TRANS, va("%d", position)); + V_DrawThinString(20, ytxt, V_70TRANS, va("%d", position + 1)); if (players[leader].positiondelay) { V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); } - V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfo.gap[playernum])); + V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfo.gap[position])); - if (directorinfo.boredom[playernum] >= BOREDOMTIME) + if (directorinfo.boredom[position] >= BOREDOMTIME) { V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); } else { - V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfo.boredom[playernum])); + V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfo.boredom[position])); } V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); @@ -282,7 +282,7 @@ void K_UpdateDirector(void) } // even if we're not certain, if we're certain we're watching the WRONG player, try to switch - if (players[*displayplayerp].position != targetposition && !players[target].positiondelay) + if (players[*displayplayerp].position != targetposition+1 && !players[target].positiondelay) { K_DirectorSwitch(target, false); } From 07498a122abae0823867963b3856ea6dd2893f42 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 20 Jan 2022 16:00:51 -0600 Subject: [PATCH 12/13] clarify potential footgunnery in K_UpdateDirector --- src/k_director.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/k_director.c b/src/k_director.c index 4603beb31..947e73cc3 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -239,6 +239,8 @@ void K_UpdateDirector(void) } // aaight, time to walk through the standings to find the first interesting pair + // NB: targetposition/sortedplayers is 0-indexed, aiming at the "back half" of a given pair by default. + // we adjust for this when comparing to player->position or when looking at the leading player, Don't Freak Out for (targetposition = 1; targetposition < MAXPLAYERS; targetposition++) { INT32 target; From 3ccbc5c705485324c18edc9c0cea12f6365af486 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Fri, 21 Jan 2022 02:42:16 -0600 Subject: [PATCH 13/13] avoid wasteful player_t copy --- src/k_director.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_director.c b/src/k_director.c index 947e73cc3..a35c3da84 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -57,17 +57,17 @@ static void K_UpdateDirectorPositions(void) { INT32 playernum; INT32 position; - player_t target; + player_t* target; memset(directorinfo.sortedplayers, -1, sizeof(directorinfo.sortedplayers)); for (playernum = 0; playernum < MAXPLAYERS; playernum++) { - target = players[playernum]; + target = &players[playernum]; - if (playeringame[playernum] && !target.spectator && target.position > 0) + if (playeringame[playernum] && !target->spectator && target->position > 0) { - directorinfo.sortedplayers[target.position - 1] = playernum; + directorinfo.sortedplayers[target->position - 1] = playernum; } }