diff --git a/src/k_collide.cpp b/src/k_collide.cpp index b9fc3c391..04e0025e7 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -19,6 +19,7 @@ #include "k_powerup.h" #include "k_hitlag.h" #include "m_random.h" +#include "k_hud.h" // K_AddMessage angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) { @@ -872,6 +873,9 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) attackerPlayer->instaWhipCharge = 0; attackerPlayer->flashing = 0; + K_AddMessageForPlayer(victimPlayer, va("Whip Reflected!"), false); + K_AddMessageForPlayer(attackerPlayer, va("COUNTERED!!"), false); + // Localized broly for a local event. if (mobj_t *broly = Obj_SpawnBrolyKi(victim, victimHitlag/2)) { diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 7afd5a493..4283d8b96 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "v_draw.hpp" @@ -5618,6 +5619,178 @@ static void K_DrawGPRankDebugger(void) va(" ** FINAL GRADE: %c", gradeChar)); } +typedef enum +{ + MM_IN, + MM_HOLD, + MM_OUT, +} messagemode_t; + +typedef struct +{ + std::string text; + sfxenum_t sound; +} message_t; + +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; + + void add(std::string msg) + { + messages.push_back(msg); + } + + void clear() + { + messages.clear(); + switch_mode(MM_IN); + } + + 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_s3k47); + + 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) +{ + 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 (!player) + return; + + if (player && !P_IsDisplayPlayer(player)) + return; + + messagestate_t *state = &messagestates[G_PartyPosition(player - players)]; + + if (interrupt) + state->clear(); + + state->add(msg); +} + +void K_TickMessages() +{ + for (auto &state : messagestates) + { + state.tick(); + } +} + +static void K_DrawMessageFeed(void) +{ + int i; + for (i = 0; i <= splitscreen; i++) + { + messagestate_t state = messagestates[i]; + + if (state.messages.size() == 0) + continue; + + std::string msg = state.messages[0]; + + 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; + + std::string submsg = msg.substr(0, sublen); + + using srb2::Draw; + + Draw::TextElement text(submsg); + + 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) { boolean islonesome = false; @@ -5911,6 +6084,7 @@ void K_drawKartHUD(void) K_DrawBotDebugger(); K_DrawDirectorDebugger(); K_DrawGPRankDebugger(); + K_DrawMessageFeed(); } void K_DrawSticker(INT32 x, INT32 y, INT32 width, INT32 flags, boolean isSmall) diff --git a/src/k_hud.h b/src/k_hud.h index 2f1096965..185315d06 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -93,6 +93,10 @@ extern patch_t *kp_button_left[2]; extern patch_t *kp_eggnum[6]; extern patch_t *kp_facenum[MAXPLAYERS+1]; +void K_AddMessage(char *msg, boolean interrupt); +void K_AddMessageForPlayer(player_t *player, char *msg, boolean interrupt); +void K_TickMessages(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_kart.c b/src/k_kart.c index 540a7cd4e..fb519e521 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3841,6 +3841,9 @@ void K_DoGuardBreak(mobj_t *t1, mobj_t *t2) { S_StartSound(t1, sfx_gbrk); K_AddHitLag(t1, 24, true); + 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); diff --git a/src/objects/battle-ufo.cpp b/src/objects/battle-ufo.cpp index 5c3f34148..2057e5f8d 100644 --- a/src/objects/battle-ufo.cpp +++ b/src/objects/battle-ufo.cpp @@ -13,6 +13,7 @@ #include "../k_battle.h" #include "../k_objects.h" #include "../k_kart.h" +#include "../k_hud.h" // K_AddMessage using srb2::math::Fixed; using srb2::Mobj; @@ -114,6 +115,8 @@ public: Spawner* spawner = next(g_battleufo.previousId); UFO* ufo = static_cast(P_SpawnMobjFromMobj(spawner, 0, 0, 250*FRACUNIT - ofs, MT_BATTLEUFO)); + K_AddMessage(va("Crack the Combat UFO!"), true); + S_StartSound(NULL, sfx_mbs54); ufo->sprzoff(ofs * spawner->scale()); diff --git a/src/p_tick.c b/src/p_tick.c index d8e26624f..c8bb7c4ec 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -47,6 +47,7 @@ #include "music.h" #include "k_dialogue.h" #include "m_easing.h" +#include "k_hud.h" // messagetimer #include "lua_profile.h" @@ -1203,6 +1204,7 @@ void P_Ticker(boolean run) if (run && !levelloading && leveltime) { K_TickDialogue(); + K_TickMessages(); } LUA_HOOK(PostThinkFrame);