From e9ee268965efd55a16ba139ab59ea6b7a76dfaa0 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sun, 14 Jan 2024 17:13:03 -0700 Subject: [PATCH] Per-viewport HUD messages --- src/k_hud.cpp | 191 +++++++++++++++++++++++++++++++--------------- src/k_kart.c | 2 +- src/k_powerup.cpp | 6 ++ 3 files changed, 135 insertions(+), 64 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 4738c8623..5c8bbd1b2 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -5621,97 +5621,162 @@ typedef struct sfxenum_t sound; } message_t; -static std::deque messages; -static tic_t messagetimer = 0; -static messagemode_t messagemode = MM_IN; +typedef struct +{ + std::deque messages; + tic_t timer = 0; + messagemode_t mode = MM_IN; + const tic_t speedyswitch = 2*TICRATE; + const tic_t lazyswitch = 4*TICRATE; -static tic_t speedyswitch = 2*TICRATE; -static tic_t lazyswitch = 4*TICRATE; + void add(std::string msg) + { + messages.push_back(msg); + } + + void clear() + { + messages.clear(); + } + + void switch_mode(messagemode_t nextmode) + { + mode = nextmode; + timer = 0; + } + + void tick() + { + if (messages.size() == 0) + return; + + if (timer == 0 && mode == MM_IN) + S_StartSound(NULL, sfx_cdfm15); + + timer++; + + switch (mode) + { + case MM_IN: + if (timer > messages[0].length()) + switch_mode(MM_HOLD); + break; + case MM_HOLD: + if (messages.size() > 1 && timer > speedyswitch) // Waiting message, switch to it right away! + next(); + else if (timer > lazyswitch) // If there's no pending message, we can chill for a bit. + switch_mode(MM_OUT); + break; + case MM_OUT: + if (timer > messages[0].length()) + next(); + break; + } + } + + void next() + { + switch_mode(MM_IN); + if (messages.size() > 0) + messages.pop_front(); + } + +} messagestate_t; + +static std::vector messagestates{MAXSPLITSCREENPLAYERS}; void K_AddMessage(char *msg, boolean interrupt) { - if (interrupt) - messages.clear(); - messages.push_back(msg); + for (auto &state : messagestates) + { + if (interrupt) + state.clear(); + state.add(msg); + } } +// Return value can be used for "paired" splitscreen messages, true = was displayed void K_AddMessageForPlayer(player_t *player, char *msg, boolean interrupt) { - if (!P_IsDisplayPlayer(player)) + if (!player) return; - K_AddMessage(msg, interrupt); -} -static void K_SwitchMessageMode(messagemode_t mode) -{ - messagemode = mode; - messagetimer = 0; -} + if (player && !P_IsDisplayPlayer(player)) + return; -static void K_NextMessage() -{ - K_SwitchMessageMode(MM_IN); - if (messages.size() > 0) - messages.pop_front(); + messagestate_t *state = &messagestates[G_PartyPosition(player - players)]; + + if (interrupt) + state->clear(); + + state->add(msg); } void K_TickMessages() { - if (messages.size() == 0) - return; - - if (messagetimer == 0 && messagemode == MM_IN) - S_StartSound(NULL, sfx_cdfm15); - - messagetimer++; - - switch (messagemode) + for (auto &state : messagestates) { - case MM_IN: - if (messagetimer > messages[0].length()) - K_SwitchMessageMode(MM_HOLD); - break; - case MM_HOLD: - if (messages.size() > 1 && messagetimer > speedyswitch) // Waiting message, switch to it right away! - K_NextMessage(); - else if (messagetimer > lazyswitch) // If there's no pending message, we can chill for a bit. - K_SwitchMessageMode(MM_OUT); - break; - case MM_OUT: - if (messagetimer > messages[0].length()) - K_NextMessage(); - break; + state.tick(); } } static void K_DrawMessageFeed(void) { - if (messages.size() == 0) - return; + int i; + for (i = 0; i <= splitscreen; i++) + { + messagestate_t state = messagestates[i]; - std::string msg = messages[0]; + if (state.messages.size() == 0) + continue; - UINT8 sublen = messagetimer; - if (messagemode == MM_IN) - sublen = messagetimer; - else if (messagemode == MM_HOLD) - sublen = msg.length(); - else if (messagemode == MM_OUT) - sublen = msg.length() - messagetimer; + std::string msg = state.messages[0]; - std::string submsg = msg.substr(0, sublen); + UINT8 sublen = state.timer; + if (state.mode == MM_IN) + sublen = state.timer; + else if (state.mode == MM_HOLD) + sublen = msg.length(); + else if (state.mode == MM_OUT) + sublen = msg.length() - state.timer; - using srb2::Draw; + std::string submsg = msg.substr(0, sublen); - Draw::TextElement text(submsg); - text.font(Draw::Font::kMenu); + using srb2::Draw; - UINT8 x = 160; - UINT8 y = 10; - UINT8 sw = text.width(); + Draw::TextElement text(submsg); - K_DrawSticker(x - sw/2, y, sw, 0, true); - Draw(x, y).align(Draw::Align::kCenter).text(text); + text.font(Draw::Font::kMenu); + + UINT8 x = 160; + UINT8 y = 10; + SINT8 shift = 0; + if (splitscreen > 2) + { + text.font(Draw::Font::kThin); + shift = -2; + + x = BASEVIDWIDTH/4; + y = 5; + + if (i % 2) + x += BASEVIDWIDTH/2; + + if (i >= 2) + y += BASEVIDHEIGHT / 2; + } + else if (splitscreen > 1) + { + y = 5; + + if (i >= 1) + y += BASEVIDHEIGHT / 2; + } + UINT8 sw = text.width(); + + K_DrawSticker(x - sw/2, y, sw, 0, true); + Draw(x, y+shift).align(Draw::Align::kCenter).text(text); + } } void K_drawKartHUD(void) diff --git a/src/k_kart.c b/src/k_kart.c index 22d90e2e2..f37ae19b6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3836,8 +3836,8 @@ void K_DoGuardBreak(mobj_t *t1, mobj_t *t2) { S_StartSound(t1, sfx_gbrk); K_AddHitLag(t1, 24, true); - K_AddMessageForPlayer(t1->player, va("Barrier Break..."), false); K_AddMessageForPlayer(t2->player, va("Smashed 'em!"), false); + K_AddMessageForPlayer(t1->player, va("Barrier Break..."), false); angle_t thrangle = R_PointToAngle2(t1->x, t1->y, t2->x, t2->y); P_Thrust(t1, thrangle, 7*mapobjectscale); diff --git a/src/k_powerup.cpp b/src/k_powerup.cpp index 51b9f96c7..69bbddc36 100644 --- a/src/k_powerup.cpp +++ b/src/k_powerup.cpp @@ -5,6 +5,7 @@ #include "k_kart.h" #include "k_objects.h" #include "k_powerup.h" +#include "k_hud.h" // K_AddMessage tic_t K_PowerUpRemaining(const player_t* player, kartitems_t powerup) { @@ -52,24 +53,29 @@ void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time) switch (powerup) { case POWERUP_SMONITOR: + K_AddMessageForPlayer(player, va("Got S MONITOR!"), true); K_DoInvincibility(player, time); player->powerup.superTimer += time; break; case POWERUP_BARRIER: + K_AddMessageForPlayer(player, va("Got MEGA BARRIER!"), true); player->powerup.barrierTimer += time; Obj_SpawnMegaBarrier(player); break; case POWERUP_BUMPER: + K_AddMessageForPlayer(player, va("Got BUMPER RESTOCK!"), true); K_GiveBumpersToPlayer(player, nullptr, 5); break; case POWERUP_BADGE: + K_AddMessageForPlayer(player, va("Got RHYTHM BADGE!"), true); player->powerup.rhythmBadgeTimer += time; break; case POWERUP_SUPERFLICKY: + K_AddMessageForPlayer(player, va("Got SUPER FLICKY!"), true); if (K_PowerUpRemaining(player, POWERUP_SUPERFLICKY)) { Obj_ExtendSuperFlickySwarm(player->powerup.flickyController, time);