diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b05af4224..52eab7007 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -572,7 +572,15 @@ static CV_PossibleValue_t ps_descriptor_cons_t[] = { {1, "Average"}, {2, "SD"}, {3, "Minimum"}, {4, "Maximum"}, {0, NULL}}; consvar_t cv_ps_descriptor = {"ps_descriptor", "Average", 0, ps_descriptor_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_director = {"director", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +//consvar_t cv_director = {"director", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; + +consvar_t cv_director[MAXSPLITSCREENPLAYERS] = { + {"director", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"director2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"director3", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"director4", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL} +}; + consvar_t cv_kartdebugdirector = {"debugdirector", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_showdirectorhud = {"showdirectorhud", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -1005,7 +1013,11 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_showviewpointtext); CV_RegisterVar(&cv_showdownloadprompt); - CV_RegisterVar(&cv_director); + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + CV_RegisterVar(&cv_director[i]); + } + CV_RegisterVar(&cv_kartdebugdirector); CV_RegisterVar(&cv_showdirectorhud); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 87d96b40d..77852f60c 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -197,7 +197,8 @@ extern consvar_t cv_ps_thinkframe_page; extern consvar_t cv_ps_samplesize; extern consvar_t cv_ps_descriptor; -extern consvar_t cv_director, cv_kartdebugdirector, cv_showdirectorhud; +extern consvar_t cv_director[4]; // screw you includes +extern consvar_t cv_kartdebugdirector, cv_showdirectorhud; extern consvar_t cv_showtrackaddon; diff --git a/src/g_game.c b/src/g_game.c index 6732e8c70..c8ca9c4eb 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -336,7 +336,7 @@ INT16 prevmap, nextmap; // save if director is enabled // so demos can disable it by default and restore it after -static int directorstate = 0; +static int directorstate[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; tic_t directortoggletimer = 0; static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; @@ -1209,9 +1209,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { displayplayers[0] = consoleplayer; G_FixCamera(1); - // i dont like this lmao - if (cv_director.value) - CV_SetValue(&cv_director, 0); } } @@ -1547,10 +1544,22 @@ boolean G_Responder(event_t *ev) COM_ImmedExecute("changeteam4 spectator"); } } - if (ev->data1 == gamecontrol[gc_director][0] - || ev->data1 == gamecontrol[gc_director][1]) + + if (ev->data1 == gamecontrol[gc_director][0] || ev->data1 == gamecontrol[gc_director][1]) { - K_ToggleDirector(); + K_ToggleDirector(0); + } + else if (ev->data1 == gamecontrolbis[gc_director][0] || ev->data1 == gamecontrolbis[gc_director][1]) + { + K_ToggleDirector(1); + } + else if (ev->data1 == gamecontrol3[gc_director][0] || ev->data1 == gamecontrol3[gc_director][1]) + { + K_ToggleDirector(2); + } + else if (ev->data1 == gamecontrol4[gc_director][0] || ev->data1 == gamecontrol4[gc_director][1]) + { + K_ToggleDirector(3); } if (ev->data1 == gamecontrol[gc_freecam][0] || ev->data1 == gamecontrol[gc_freecam][1]) @@ -7303,8 +7312,11 @@ post_compat: players[i].kartweight = kartweight[i]; } - directorstate = cv_director.value; - CV_SetValue(&cv_director, 0); + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + directorstate[i] = cv_director[i].value; + CV_SetValue(&cv_director[i], 0); + } demo.deferstart = true; } @@ -7871,8 +7883,11 @@ void G_StopDemo(void) demobuf.buffer = NULL; if (demo.playback) { - CV_SetValue(&cv_director, directorstate); - directorstate = 0; + for (UINT8 i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + CV_SetValue(&cv_director[i], directorstate[i]); + directorstate[i] = 0; + } } demo.playback = false; if (demo.title) diff --git a/src/k_director.c b/src/k_director.c index 1385754f0..ef4e2975d 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -12,6 +12,7 @@ //#include "k_kart.h" #include "doomdef.h" +#include "doomstat.h" #include "g_game.h" #include "v_video.h" #include "k_director.h" @@ -19,6 +20,8 @@ #include "p_local.h" #include "st_stuff.h" +#include "r_fps.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? @@ -28,6 +31,8 @@ struct directorinfo { + UINT8 viewnum; + player_t* viewplayer; 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 @@ -38,6 +43,8 @@ struct directorinfo INT32 boredom[MAXPLAYERS]; // how long has a given position had no credible attackers? } directorinfo; +struct directorinfo directorinfosplit[MAXSPLITSCREENPLAYERS]; + boolean K_DirectorIsPlayerAlone(void) { UINT8 pingame = 0; @@ -67,25 +74,38 @@ static fixed_t ScaleFromMap(fixed_t n, fixed_t scale) return FixedMul(n, FixedDiv(scale, mapobjectscale)); } -static boolean K_DirectorIsEnabled(void) +boolean K_DirectorIsAvailable(UINT8 viewnum) { - return cv_director.value && !splitscreen && (gamestate == GS_LEVEL && (((!playeringame[consoleplayer] || players[consoleplayer].spectator)) || (demo.playback && !camera[0].freecam && (!demo.title || !modeattacking))) && !K_DirectorIsPlayerAlone()); + if ((demo.playback && demo.title) || modeattacking) + return false; + return ((gamestate == GS_LEVEL) && (viewnum <= splitscreen) && (demo.playback || (players[displayplayers[viewnum]].spectator && !K_DirectorIsPlayerAlone()))); +} + +static boolean K_DirectorIsEnabled(const UINT8 viewnum) +{ + return (cv_director[viewnum].value && K_DirectorIsAvailable(viewnum)); } void K_InitDirector(void) { INT32 playernum; - directorinfo.cooldown = SWITCHTIME; - directorinfo.freeze = 0; - directorinfo.attacker = 0; - directorinfo.maxdist = 0; + directorinfo.viewnum = 0; - for (playernum = 0; playernum < MAXPLAYERS; playernum++) + for (UINT8 i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - directorinfo.sortedplayers[playernum] = -1; - directorinfo.gap[playernum] = INT32_MAX; - directorinfo.boredom[playernum] = 0; + directorinfosplit[i].cooldown = SWITCHTIME; + directorinfosplit[i].freeze = 0; + directorinfosplit[i].attacker = 0; + directorinfosplit[i].maxdist = 0; + directorinfosplit[i].viewplayer = NULL; + + for (playernum = 0; playernum < MAXPLAYERS; playernum++) + { + directorinfosplit[i].sortedplayers[playernum] = -1; + directorinfosplit[i].gap[playernum] = INT32_MAX; + directorinfosplit[i].boredom[playernum] = 0; + } } } @@ -104,8 +124,8 @@ static fixed_t K_GetDistanceToFinish(player_t player) continue; dist = P_AproxDistance(P_AproxDistance(mo->x - player.mo->x, - mo->y - player.mo->y), - mo->z - player.mo->z) / FRACUNIT; + mo->y - player.mo->y), + mo->z - player.mo->z) / FRACUNIT; break; } @@ -124,7 +144,7 @@ static fixed_t K_GetDistanceToFinish(player_t player) dist = P_AproxDistance(P_AproxDistance(mo->x - player.mo->x, mo->y - player.mo->y), - mo->z - player.mo->z) / FRACUNIT; + mo->z - player.mo->z) / FRACUNIT; break; } @@ -148,13 +168,13 @@ static fixed_t K_GetFinishGap(INT32 leader, INT32 follower) } } -static void K_UpdateDirectorPositions(void) +static void K_UpdateDirectorPositions(const UINT8 viewnum) { INT32 playernum; INT32 position; player_t* target; - memset(directorinfo.sortedplayers, -1, sizeof(directorinfo.sortedplayers)); + memset(directorinfosplit[viewnum].sortedplayers, -1, sizeof(directorinfo.sortedplayers)); for (playernum = 0; playernum < MAXPLAYERS; playernum++) { @@ -162,43 +182,43 @@ static void K_UpdateDirectorPositions(void) if (playeringame[playernum] && !target->spectator && target->kartstuff[k_position] > 0) { - directorinfo.sortedplayers[target->kartstuff[k_position] - 1] = playernum; + directorinfosplit[viewnum].sortedplayers[target->kartstuff[k_position] - 1] = playernum; } } for (position = 0; position < MAXPLAYERS - 1; position++) { - directorinfo.gap[position] = INT32_MAX; + directorinfosplit[viewnum].gap[position] = INT32_MAX; - if (directorinfo.sortedplayers[position] == -1 || directorinfo.sortedplayers[position + 1] == -1) + if (directorinfosplit[viewnum].sortedplayers[position] == -1 || directorinfosplit[viewnum].sortedplayers[position + 1] == -1) { continue; } - directorinfo.gap[position] = ScaleFromMap(K_GetFinishGap(directorinfo.sortedplayers[position], directorinfo.sortedplayers[position + 1]), FRACUNIT); + directorinfosplit[viewnum].gap[position] = ScaleFromMap(K_GetFinishGap(directorinfosplit[viewnum].sortedplayers[position], directorinfosplit[viewnum].sortedplayers[position + 1]), FRACUNIT); - if (directorinfo.gap[position] >= BREAKAWAYDIST) + if (directorinfosplit[viewnum].gap[position] >= BREAKAWAYDIST) { - directorinfo.boredom[position] = (INT32)(min(BOREDOMTIME * 2, directorinfo.boredom[position] + 1)); + directorinfosplit[viewnum].boredom[position] = (INT32)(min(BOREDOMTIME * 2, directorinfosplit[viewnum].boredom[position] + 1)); } - else if (directorinfo.boredom[position] > 0) + else if (directorinfosplit[viewnum].boredom[position] > 0) { - directorinfo.boredom[position]--; + directorinfosplit[viewnum].boredom[position]--; } } - if (directorinfo.sortedplayers[0] == -1) + if (directorinfosplit[viewnum].sortedplayers[0] == -1) { - directorinfo.maxdist = -1; + directorinfosplit[viewnum].maxdist = -1; return; } - directorinfo.maxdist = ScaleFromMap(K_GetDistanceToFinish(players[directorinfo.sortedplayers[0]]), FRACUNIT); + directorinfosplit[viewnum].maxdist = ScaleFromMap(K_GetDistanceToFinish(players[directorinfosplit[viewnum].sortedplayers[0]]), FRACUNIT); } -static boolean K_CanSwitchDirector(void) +static boolean K_CanSwitchDirector(const UINT8 viewnum) { - if (directorinfo.cooldown > 0) + if (directorinfosplit[viewnum].cooldown > 0) { return false; } @@ -206,9 +226,9 @@ static boolean K_CanSwitchDirector(void) return true; } -static void K_DirectorSwitch(INT32 player, boolean force) +static void K_DirectorSwitch(INT32 player, boolean force, const UINT8 viewnum) { - if (!K_DirectorIsEnabled()) + if (!K_DirectorIsEnabled(viewnum)) { return; } @@ -223,45 +243,45 @@ static void K_DirectorSwitch(INT32 player, boolean force) return; } - if (!force && !K_CanSwitchDirector()) + if (!force && !K_CanSwitchDirector(viewnum)) { return; } - G_ResetView(1, player, true); - directorinfo.cooldown = SWITCHTIME; + G_ResetView(viewnum+1, player, true); + directorinfosplit[viewnum].cooldown = SWITCHTIME; } -static void K_DirectorForceSwitch(INT32 player, INT32 time) +static void K_DirectorForceSwitch(INT32 player, INT32 time, const UINT8 viewnum) { if (players[player].exiting) { return; } - directorinfo.attacker = player; - directorinfo.freeze = time; + directorinfosplit[viewnum].attacker = player; + directorinfosplit[viewnum].freeze = time; } void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source) { - if (!K_DirectorIsEnabled()) + if (!K_DirectorIsEnabled(directorinfo.viewnum)) { return; } - if (!P_IsDisplayPlayer(player)) + if (directorinfosplit[directorinfo.viewnum].viewplayer != player) { return; } if (inflictor && inflictor->player) { - K_DirectorForceSwitch(inflictor->player - players, TRANSFERTIME); + K_DirectorForceSwitch(inflictor->player - players, TRANSFERTIME, directorinfo.viewnum); } else if (source && source->player) { - K_DirectorForceSwitch(source->player - players, TRANSFERTIME); + K_DirectorForceSwitch(source->player - players, TRANSFERTIME, directorinfo.viewnum); } } @@ -281,14 +301,14 @@ void K_DrawDirectorDebugger(void) 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", directorinfo.cooldown)); - V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", directorinfo.maxdist)); + V_DrawThinString(150, 0, V_70TRANS, va("COOLDOWN: %d", directorinfosplit[0].cooldown)); + V_DrawThinString(230, 0, V_70TRANS, va("MAXDIST: %d", directorinfosplit[0].maxdist)); for (position = 0; position < MAXPLAYERS - 1; position++) { ytxt = 10 * (position + 1); - leader = directorinfo.sortedplayers[position]; - follower = directorinfo.sortedplayers[position + 1]; + leader = directorinfosplit[0].sortedplayers[position]; + follower = directorinfosplit[0].sortedplayers[position + 1]; if (leader == -1 || follower == -1) break; @@ -301,15 +321,15 @@ void K_DrawDirectorDebugger(void) V_DrawThinString(40, ytxt, V_70TRANS, va("NG")); } - V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfo.gap[position])); + V_DrawThinString(80, ytxt, V_70TRANS, va("%d", directorinfosplit[0].gap[position])); - if (directorinfo.boredom[position] >= BOREDOMTIME) + if (directorinfosplit[0].boredom[position] >= BOREDOMTIME) { V_DrawThinString(120, ytxt, V_70TRANS, va("BORED")); } else { - V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfo.boredom[position])); + V_DrawThinString(120, ytxt, V_70TRANS, va("%d", directorinfosplit[0].boredom[position])); } V_DrawThinString(150, ytxt, V_70TRANS, va("%s", player_names[leader])); @@ -317,37 +337,38 @@ void K_DrawDirectorDebugger(void) } } -void K_UpdateDirector(void) +void K_UpdateDirector(const UINT8 viewnum) { - INT32 *displayplayerp = &displayplayers[0]; INT32 targetposition; + directorinfo.viewnum = viewnum; + directorinfosplit[directorinfo.viewnum].viewplayer = &players[displayplayers[directorinfo.viewnum]]; - if (!K_DirectorIsEnabled()) + if (!K_DirectorIsEnabled(directorinfo.viewnum)) { return; } - K_UpdateDirectorPositions(); + K_UpdateDirectorPositions(directorinfo.viewnum); - if (directorinfo.cooldown > 0) { - directorinfo.cooldown--; + if (directorinfosplit[directorinfo.viewnum].cooldown > 0) { + directorinfosplit[directorinfo.viewnum].cooldown--; } // handle pending forced switches - if (directorinfo.freeze > 0) + if (directorinfosplit[directorinfo.viewnum].freeze > 0) { - if (!(--directorinfo.freeze)) - K_DirectorSwitch(directorinfo.attacker, true); + if (!(--directorinfosplit[directorinfo.viewnum].freeze)) + K_DirectorSwitch(directorinfosplit[directorinfo.viewnum].attacker, true, directorinfo.viewnum); return; } // if there's only one player left in the list, just switch to that player - if (directorinfo.sortedplayers[0] != -1 && (directorinfo.sortedplayers[1] == -1 || + if (directorinfosplit[directorinfo.viewnum].sortedplayers[0] != -1 && (directorinfosplit[directorinfo.viewnum].sortedplayers[1] == -1 || // TODO: Battle; I just threw this together quick. Focus on leader. !race_rules())) { - K_DirectorSwitch(directorinfo.sortedplayers[0], false); + K_DirectorSwitch(directorinfosplit[directorinfo.viewnum].sortedplayers[0], false, directorinfo.viewnum); return; } @@ -359,39 +380,39 @@ void K_UpdateDirector(void) INT32 target; // you are out of players, try again - if (directorinfo.sortedplayers[targetposition] == -1) + if (directorinfosplit[directorinfo.viewnum].sortedplayers[targetposition] == -1) { break; } // pair too far apart? try the next one - if (directorinfo.boredom[targetposition - 1] >= BOREDOMTIME) + if (directorinfosplit[directorinfo.viewnum].boredom[targetposition - 1] >= BOREDOMTIME) { continue; } // pair finished? try the next one - if (players[directorinfo.sortedplayers[targetposition]].exiting) + if (players[directorinfosplit[directorinfo.viewnum].sortedplayers[targetposition]].exiting) { continue; } // don't risk switching away from forward pairs at race end, might miss something! - if (directorinfo.maxdist > PINCHDIST) + if (directorinfosplit[directorinfo.viewnum].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 && directorinfo.gap[targetposition] < WALKBACKDIST) + while (targetposition < MAXPLAYERS && directorinfosplit[directorinfo.viewnum].gap[targetposition] < WALKBACKDIST) { targetposition++; } } - target = directorinfo.sortedplayers[targetposition]; + target = directorinfosplit[directorinfo.viewnum].sortedplayers[targetposition]; // stop here since we're already viewing this player - if (*displayplayerp == target) + if (displayplayers[directorinfo.viewnum] == target) { break; } @@ -405,30 +426,30 @@ void K_UpdateDirector(void) // if we're certain the back half of the pair is actually in this position, try to switch if (!players[target].kartstuff[k_positiondelay]) { - K_DirectorSwitch(target, false); + K_DirectorSwitch(target, false, directorinfo.viewnum); } - // even if we're not certain, if we're certain we're watching the WRONG player, try to switch - if (players[*displayplayerp].kartstuff[k_position] != targetposition+1 && !players[*displayplayerp].kartstuff[k_positiondelay]) + // even if we're not certain, if we're cetain we're watching the WRONG player, try to switch + if (directorinfosplit[directorinfo.viewnum].viewplayer->kartstuff[k_position] != targetposition+1 && !directorinfosplit[directorinfo.viewnum].viewplayer->kartstuff[k_positiondelay]) { - K_DirectorSwitch(target, false); + K_DirectorSwitch(target, false, directorinfo.viewnum); } break; } } -void K_ToggleDirector(void) +void K_ToggleDirector(const UINT8 viewnum) { - if (!directortextactive) + if (!K_DirectorIsAvailable(viewnum)) return; - if (!K_DirectorIsEnabled()) + if (!K_DirectorIsEnabled(viewnum)) { - directorinfo.cooldown = 0; // switch immediately + directorinfosplit[viewnum].cooldown = 0; // switch immediately } directortoggletimer = 0; - COM_ImmedExecute("add director 1"); + CV_SetValue(&cv_director[viewnum], (cv_director[viewnum].value ^ 1)); } diff --git a/src/k_director.h b/src/k_director.h index 3be71b675..acc00b253 100644 --- a/src/k_director.h +++ b/src/k_director.h @@ -10,8 +10,9 @@ /// \file k_director.h /// \brief SRB2kart automatic spectator camera. +boolean K_DirectorIsAvailable(UINT8 viewnum); void K_InitDirector(void); -void K_UpdateDirector(void); +void K_UpdateDirector(UINT8 viewnum); void K_DrawDirectorDebugger(void); void K_DirectorFollowAttack(player_t *player, mobj_t *inflictor, mobj_t *source); -void K_ToggleDirector(void); +void K_ToggleDirector(UINT8 viewnum); diff --git a/src/p_tick.c b/src/p_tick.c index 0784957f3..1bd6fa72b 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -698,7 +698,10 @@ void P_Ticker(boolean run) PS_STOP_TIMING(ps_lua_postthinkframe_time); } - K_UpdateDirector(); + for (i = 0; i <= splitscreen; i++) + { + K_UpdateDirector(i); + } // Always move the camera. P_RunChaseCameras(); diff --git a/src/st_stuff.c b/src/st_stuff.c index d5d438414..2769ae413 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -16,6 +16,7 @@ #include "doomdef.h" #include "g_game.h" #include "g_input.h" +#include "k_director.h" #include "r_local.h" #include "p_local.h" #include "f_finale.h" @@ -156,8 +157,6 @@ static patch_t *envelope; player_t *stplyr; UINT8 stplyrnum; -boolean directortextactive = false; - // SRB2kart hudinfo_t hudinfo[NUMHUDITEMS] = @@ -679,13 +678,12 @@ static void ST_overlayDrawer(void) if (!hu_showscores) // hide the following if TAB is held { - if (cv_showdirectorhud.value && !splitscreen && ((demo.playback && !camera[stplyrnum].freecam && (!demo.title || !modeattacking)) || !P_IsLocalPlayer(stplyr)) && !K_DirectorIsPlayerAlone()) + // TODO: splitscreen support! + if (cv_showdirectorhud.value && !splitscreen && !P_IsLocalPlayer(stplyr) && K_DirectorIsAvailable(0) && !K_DirectorIsPlayerAlone()) { char directortext[20] = {0}; - snprintf(directortext, 20, "Director: %s", cv_director.value ? "On" : "Off"); - - directortextactive = true; + snprintf(directortext, 20, "Director: %s", cv_director[0].value ? "On" : "Off"); if ((!demo.playback && directortoggletimer < 13*TICRATE) || (demo.playback && directortoggletimer < 4*TICRATE)) { @@ -701,7 +699,6 @@ static void ST_overlayDrawer(void) else { directortoggletimer = 0; - directortextactive = false; } if (cv_showviewpointtext.value) @@ -795,8 +792,6 @@ static void ST_overlayDrawer(void) V_DrawString(2, BASEVIDHEIGHT-30, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_HUDTRANSHALF, ST_GetButtonName(gc_camfloat, "Float")); V_DrawString(2, BASEVIDHEIGHT-20, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_HUDTRANSHALF, ST_GetButtonName(gc_camsink, "Sink")); V_DrawString(2, BASEVIDHEIGHT-10, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_HUDTRANSHALF, ST_GetButtonName(gc_director, "Toggle Director")); - - directortextactive = true; } else {