RingRacers/src/hud/spectator.cpp

247 lines
4.8 KiB
C++

// 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 <initializer_list>
#include <optional>
#include <utility>
#include <fmt/format.h>
#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<bool> 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<bool> pressed_;
};
List(int x, int y) : row_(split_draw(x, y, left_)) {}
void insert(std::initializer_list<Field> 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}});
}
}