From 1f3a9a4758f4e6fdb5753ed5324884ead5c95d40 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 3 Oct 2023 20:25:02 -0700 Subject: [PATCH] Splitscreen Director HUD; replace freecam HUD --- src/hud/CMakeLists.txt | 1 + src/hud/spectator.cpp | 247 +++++++++++++++++++++++++++++++++++++++++ src/k_hud.c | 79 +------------ src/k_hud.h | 1 + src/st_stuff.c | 49 -------- 5 files changed, 253 insertions(+), 124 deletions(-) create mode 100644 src/hud/spectator.cpp diff --git a/src/hud/CMakeLists.txt b/src/hud/CMakeLists.txt index b30d1156e..8331e2871 100644 --- a/src/hud/CMakeLists.txt +++ b/src/hud/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(SRB2SDL2 PRIVATE powerup.cpp + spectator.cpp timer.cpp ) diff --git a/src/hud/spectator.cpp b/src/hud/spectator.cpp new file mode 100644 index 000000000..2f4298e1a --- /dev/null +++ b/src/hud/spectator.cpp @@ -0,0 +1,247 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include +#include +#include + +#include + +#include "../v_draw.hpp" + +#include "../command.h" +#include "../d_clisrv.h" +#include "../d_player.h" +#include "../doomdef.h" +#include "../doomstat.h" +#include "../g_game.h" +#include "../g_party.h" +#include "../i_time.h" +#include "../k_director.h" +#include "../k_hud.h" +#include "../p_local.h" +#include "../r_fps.h" + +extern "C" consvar_t cv_maxplayers; + +using srb2::Draw; + +namespace +{ + +struct List +{ + struct Field + { + Field(const char* label, Draw::Button button, std::optional pressed = {}) : + label_(Draw::TextElement(label).font(Draw::Font::kThin)), + button_(button), + pressed_(pressed) + { + } + + int width() const { return label_.width() + kButtonWidth + kButtonMargin + kFieldSpacing; } + + void draw(const Draw& row, bool left) const + { + Draw col = row.x(left ? kButtonWidth + kButtonMargin : kFieldSpacing); + + col.text(label_); + col = col.x(left ? -(kButtonWidth + kButtonMargin) : width() - (kButtonWidth + kFieldSpacing)); + + if (r_splitscreen) + { + auto small_button_offset = [&] + { + switch (button_) + { + case Draw::Button::l: + case Draw::Button::r: + return -4; + + default: + return -2; + } + }; + + col.y(small_button_offset()).small_button(button_, pressed_); + } + else + { + col.y(-4).button(button_, pressed_); + } + } + + private: + static constexpr int kButtonWidth = 14; + static constexpr int kButtonMargin = 2; + static constexpr int kFieldSpacing = 8; + + Draw::TextElement label_; + Draw::Button button_; + std::optional pressed_; + }; + + List(int x, int y) : row_(split_draw(x, y, left_)) {} + + void insert(std::initializer_list fields) + { + auto total_width = [&fields] + { + int width = 0; + + for (const Field& field : fields) + { + width += field.width(); + } + + return width; + }; + + Draw col = left_ ? row_ : row_.x(-total_width()); + + for (const Field& field : fields) + { + field.draw(col, left_); + col = col.x(field.width()); + } + + row_ = row_.y(r_splitscreen ? -13 : -17); + } + +private: + bool left_ = r_splitscreen > 1 && R_GetViewNumber() & 1; + Draw row_; + + static Draw split_draw(int x, int y, bool left) + { + return Draw( + left ? x : (BASEVIDWIDTH / (r_splitscreen > 1 ? 2 : 1)) - x, + (BASEVIDHEIGHT / (r_splitscreen ? 2 : 1)) - y + ) + .align(Draw::Align::kLeft) + .flags( + V_SNAPTOBOTTOM | + (left ? V_SNAPTOLEFT : V_SNAPTORIGHT) | + (r_splitscreen > 1 ? V_HUDTRANS : V_SLIDEIN) | + V_SPLITSCREEN + ); + } +}; + +}; // namespace + +void K_drawSpectatorHUD(boolean director) +{ + const UINT8 viewnum = R_GetViewNumber(); + + UINT8 numingame = 0; + + for (UINT8 i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + { + numingame++; + } + } + + player_t* player = [viewnum]() -> player_t* + { + if (viewnum >= G_PartySize(consoleplayer)) + { + return nullptr; + } + + UINT8 p = G_PartyMember(consoleplayer, viewnum); + + if (!playeringame[p] || players[p].spectator == false) + { + return nullptr; + } + + return &players[p]; + }(); + + List list = [director] + { + switch (r_splitscreen) + { + case 0: + return List(director ? 80 : 20, 20); + + case 1: + return List(40, 20); + + default: + return List(10, 20); + } + }(); + + if (player) + { + std::string label = [player] + { + if (player->flashing) + { + return ". . ."; + } + else if (player->pflags & PF_WANTSTOJOIN) + { + return "Cancel Join"; + } + else + { + return "Join"; + } + }(); + + if (cv_maxplayers.value) + { + label += fmt::format(" [{}/{}]", numingame, cv_maxplayers.value); + } + + list.insert({{label.c_str(), Draw::Button::l}}); + } + + if (director || camera[viewnum].freecam) + { + // Not locked into freecam -- can toggle it. + if (director) + { + list.insert({{"Freecam", Draw::Button::c}}); + } + else + { + bool press = D_LocalTiccmd(viewnum)->buttons & BT_RESPAWN; + const char* label = (press && I_GetTime() % 16 < 8) ? "> <" : ">< "; + + list.insert({{label, Draw::Button::y, press}, {"Exit", Draw::Button::c}}); + } + } + + if (director) + { + if (numingame > 1) + { + list.insert({{"+", Draw::Button::a}, {"-", Draw::Button::x}}); + } + + if (player) + { + list.insert({{K_DirectorIsEnabled(viewnum) ? "\x82" "Director" : "Director", Draw::Button::r}}); + } + } + else + { + auto bt = D_LocalTiccmd(viewnum)->buttons; + + list.insert({{"", Draw::Button::r, bt & BT_DRIFT}, {"Pivot", Draw::Button::b, bt & BT_LOOKBACK}}); + list.insert({{"+", Draw::Button::a, bt & BT_ACCELERATE}, {"-", Draw::Button::x, bt & BT_BRAKE}}); + } +} diff --git a/src/k_hud.c b/src/k_hud.c index ed033d3d9..d3fc3cd14 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5169,77 +5169,6 @@ void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t a K_drawButton(x << FRACBITS, y << FRACBITS, flags, button, anim); } -static void K_DrawDirectorButton(INT32 idx, const char *label, patch_t *kp[2], INT32 textflags) -{ - INT32 flags = V_SNAPTORIGHT | V_SLIDEIN | V_SPLITSCREEN; - INT32 x = (BASEVIDWIDTH/2) - 10; - INT32 y = (idx * 16); - - if (r_splitscreen <= 1) - { - x = BASEVIDWIDTH - 60; - if (r_splitscreen == 0) - { - y += BASEVIDHEIGHT - 78; - } - } - - textflags |= flags; - - K_drawButtonAnim(x, y - 4, flags, kp, leveltime); - V_DrawRightAlignedThinString(x - 2, y, textflags, label); -} - -static void K_drawDirectorHUD(void) -{ - const UINT8 viewnum = R_GetViewNumber(); - const INT32 p = viewnum < G_PartySize(consoleplayer) ? G_PartyMember(consoleplayer, viewnum) : -1; - const char *itemtxt = "Join"; - UINT8 offs = 0; - - UINT8 numingame = 0; - UINT8 i; - - if (!LUA_HudEnabled(hud_textspectator)) - { - return; - } - - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && !players[i].spectator) - numingame++; - - if (numingame > 1 && r_splitscreen == 0) // simplifies things a lot - { - K_DrawDirectorButton(1, "Next Player", kp_button_a[0], 0); - K_DrawDirectorButton(2, "Prev Player", kp_button_x[0], 0); - offs = 2; - } - - K_DrawDirectorButton(offs + 1, "Freecam", kp_button_c[0], 0); - - if (p == -1 || !playeringame[p] || players[p].spectator == false) - { - return; - } - - // TODO: this is too close to the screen bottom - K_DrawDirectorButton(offs + 2, "Director", kp_button_r, - (K_DirectorIsEnabled(viewnum) ? V_YELLOWMAP : 0)); - - if (players[p].flashing) - itemtxt = ". . ."; - else if (players[p].pflags & PF_WANTSTOJOIN) - itemtxt = "Cancel Join"; - - if (cv_maxplayers.value) - { - itemtxt = va("%s [%d/%d]", itemtxt, numingame, cv_maxplayers.value); - } - - K_DrawDirectorButton(0, itemtxt, kp_button_l, 0); -} - static void K_drawDistributionDebugger(void) { itemroulette_t rouletteData = {0}; @@ -5666,9 +5595,9 @@ void K_drawKartHUD(void) if (stplyr->karthud[khud_trickcool]) K_drawTrickCool(); - if (freecam) + if ((freecam || stplyr->spectator) && LUA_HudEnabled(hud_textspectator)) { - K_DrawDirectorButton(3, "Freecam", kp_button_c[0], 0); + K_drawSpectatorHUD(false); } if (modeattacking || freecam) // everything after here is MP and debug only @@ -5687,9 +5616,9 @@ void K_drawKartHUD(void) K_drawKartPowerUps(); - if (K_DirectorIsAvailable(viewnum) == true) + if (K_DirectorIsAvailable(viewnum) == true && LUA_HudEnabled(hud_textspectator)) { - K_drawDirectorHUD(); + K_drawSpectatorHUD(true); } if (cv_kartdebugdistribution.value) diff --git a/src/k_hud.h b/src/k_hud.h index b16c15e2c..c5efb621b 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -43,6 +43,7 @@ void K_LoadKartHUDGraphics(void); void K_drawKartHUD(void); void K_drawKartFreePlay(void); void K_drawKartPowerUps(void); +void K_drawSpectatorHUD(boolean director); void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, UINT8 mode); void K_drawKart2PTimestamp(void); void K_drawKart4PTimestamp(void); diff --git a/src/st_stuff.c b/src/st_stuff.c index 0ccddfc31..451de0f16 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1215,55 +1215,6 @@ static void ST_overlayDrawer(void) } } - if (!hu_showscores && netgame && !mapreset) - { - if (stplyr->spectator && displayplayers[viewnum] == g_localplayers[viewnum] && LUA_HudEnabled(hud_textspectator)) - { - const char *itemtxt = M_GetText("Item - Join Game"); - - if (stplyr->flashing) - itemtxt = M_GetText("Item - . . ."); - else if (stplyr->pflags & PF_WANTSTOJOIN) - itemtxt = M_GetText("Item - Cancel Join"); - else if (G_GametypeHasTeams()) - itemtxt = M_GetText("Item - Join Team"); - - if (cv_maxplayers.value) - { - UINT8 numingame = 0; - UINT8 i; - - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && !players[i].spectator) - numingame++; - - itemtxt = va("%s (%s: %d)", itemtxt, M_GetText("Slots left"), max(0, cv_maxplayers.value - numingame)); - } - - // SRB2kart: changed positions & text - if (r_splitscreen) - { - V_DrawThinString(2, (BASEVIDHEIGHT/2)-20, V_YELLOWMAP|V_HUDTRANSHALF|V_SPLITSCREEN|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("- SPECTATING -")); - V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|V_SPLITSCREEN|V_SNAPTOLEFT|V_SNAPTOBOTTOM, itemtxt); - } - else - { - V_DrawString(2, BASEVIDHEIGHT-40, V_HUDTRANSHALF|V_SPLITSCREEN|V_YELLOWMAP|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("- SPECTATING -")); - V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF|V_SPLITSCREEN|V_SNAPTOLEFT|V_SNAPTOBOTTOM, itemtxt); - if (stplyr->cmd.buttons & BT_LOOKBACK) - { - V_DrawString(2, BASEVIDHEIGHT-20, V_HUDTRANSHALF|V_SPLITSCREEN|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Lookback - Camera pivot")); - V_DrawString(2, BASEVIDHEIGHT-10, V_HUDTRANSHALF|V_SPLITSCREEN|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Aim forward/backward")); - } - else - { - V_DrawString(2, BASEVIDHEIGHT-20, V_HUDTRANSHALF|V_SPLITSCREEN|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Accelerate - Float")); - V_DrawString(2, BASEVIDHEIGHT-10, V_HUDTRANSHALF|V_SPLITSCREEN|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Brake - Sink")); - } - } - } - } - K_DrawMidVote(); K_DrawDialogue();