diff --git a/src/d_main.c b/src/d_main.c index 045d558ed..3936559d5 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -233,7 +233,10 @@ void D_ProcessEvents(void) #endif if (eaten) + { + hu_keystrokes = true; continue; // ate the event + } G_Responder(ev); } diff --git a/src/d_player.h b/src/d_player.h index 95fd8adea..5ed5af013 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -697,6 +697,9 @@ typedef struct player_s tic_t jointime; // Timer when player joins game to change skin/color tic_t quittime; // Time elapsed since user disconnected, zero if connected + UINT8 typing_timer : 4; // Counts down while keystrokes are not emitted + UINT8 typing_duration : 6; // How long since resumed timer + #ifdef HWRENDER fixed_t fovadd; // adjust FOV for hw rendering #endif diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index ed72659e5..17c41cfbf 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -54,6 +54,8 @@ typedef enum // ticcmd flags #define TICCMD_RECEIVED 1 +#define TICCMD_TYPING 2/* chat window or console open */ +#define TICCMD_KEYSTROKE 4/* chat character input */ #if defined(_MSC_VER) #pragma pack(1) diff --git a/src/g_game.c b/src/g_game.c index 6cc1f9aa2..9f203e413 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1120,6 +1120,16 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->latency = modeattacking ? 0 : (leveltime & 0xFF); // Send leveltime when this tic was generated to the server for control lag calculations cmd->flags = 0; + if (chat_on || CON_Ready()) + { + cmd->flags |= TICCMD_TYPING; + + if (hu_keystrokes) + { + cmd->flags |= TICCMD_KEYSTROKE; + } + } + /* Lua: Allow this hook to overwrite ticcmd. We check if we're actually in a level because for some reason this Hook would run in menus and on the titlescreen otherwise. Be aware that within this hook, nothing but this player's cmd can be edited (otherwise we'd run in some pretty bad synching problems since this is clientsided, or something) @@ -1357,7 +1367,10 @@ boolean G_Responder(event_t *ev) if (gamestate == GS_LEVEL) { if (HU_Responder(ev)) + { + hu_keystrokes = true; return true; // chat ate the event + } if (AM_Responder(ev)) return true; // automap ate it // map the event (key/mouse/joy) to a gamecontrol @@ -1374,7 +1387,10 @@ boolean G_Responder(event_t *ev) else if (gamestate == GS_CUTSCENE) { if (HU_Responder(ev)) + { + hu_keystrokes = true; return true; // chat ate the event + } if (F_CutsceneResponder(ev)) { @@ -1385,7 +1401,10 @@ boolean G_Responder(event_t *ev) else if (gamestate == GS_CREDITS || gamestate == GS_ENDING) // todo: keep ending here? { if (HU_Responder(ev)) + { + hu_keystrokes = true; return true; // chat ate the event + } if (F_CreditResponder(ev)) { @@ -1408,7 +1427,10 @@ boolean G_Responder(event_t *ev) } else if (gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_EVALUATION) if (HU_Responder(ev)) + { + hu_keystrokes = true; return true; // chat ate the event + } // allow spy mode changes even during the demo if (gamestate == GS_LEVEL && ev->type == ev_keydown diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 35f977bd6..2e6ff3600 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -78,6 +78,7 @@ patch_t *frameslash; // framerate stuff. Used in screen.c static player_t *plr; boolean chat_on; // entering a chat message? +boolean hu_keystrokes; // :) static char w_chat[HU_MAXMSGLEN]; static size_t c_input = 0; // let's try to make the chat input less shitty. static boolean headsupactive = false; @@ -879,6 +880,8 @@ void HU_Ticker(void) hu_showscores = !chat_on; else hu_showscores = false; + + hu_keystrokes = false; } #ifndef NONET diff --git a/src/hu_stuff.h b/src/hu_stuff.h index afbbe7b73..6a425926b 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -94,6 +94,9 @@ void HU_AddChatText(const char *text, boolean playsound); // set true when entering a chat message extern boolean chat_on; +// keystrokes in the console or chat window +extern boolean hu_keystrokes; + extern patch_t *pinggfx[5]; extern patch_t *framecounter; extern patch_t *frameslash; diff --git a/src/k_hud.c b/src/k_hud.c index 87cda81cc..2e635581d 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -133,6 +133,9 @@ static patch_t *kp_check[6]; static patch_t *kp_rival[2]; static patch_t *kp_localtag[4][2]; +static patch_t *kp_talk; +static patch_t *kp_typdot; + static patch_t *kp_eggnum[4]; static patch_t *kp_flameshieldmeter[104][2]; @@ -503,6 +506,10 @@ void K_LoadKartHUDGraphics(void) } } + // Typing indicator + kp_talk = W_CachePatchName("K_TALK", PU_HUDGFX); + kp_typdot = W_CachePatchName("K_TYPDOT", PU_HUDGFX); + // Eggman warning numbers sprintf(buffer, "K_EGGNx"); for (i = 0; i < 4; i++) @@ -2598,6 +2605,43 @@ static void K_DrawRivalTagForPlayer(fixed_t x, fixed_t y) V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_rival[blink], NULL); } +static boolean K_DrawTypingDot(fixed_t x, fixed_t y, UINT8 duration, player_t *p) +{ + if (p->typing_duration > duration) + { + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_typdot, NULL); + return true; + } + else + { + return false; + } +} + +static boolean K_DrawTypingNotifier(fixed_t x, fixed_t y, player_t *p) +{ + if (p->cmd.flags & TICCMD_TYPING) + { + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_talk, NULL); + + y += 4*FRACUNIT; + + /* spacing closer with the last two looks a better most of the time */ + (void) + ( + K_DrawTypingDot(x + 3*FRACUNIT, y, 15, p) && + K_DrawTypingDot(x + 6*FRACUNIT - FRACUNIT/3, y, 31, p) && + K_DrawTypingDot(x + 9*FRACUNIT - FRACUNIT/3, y, 47, p) + ); + + return true; + } + else + { + return false; + } +} + static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT8 cnum) { const INT32 clr = skincolors[p->skincolor].chatcolor; @@ -2840,6 +2884,7 @@ static void K_drawKartNameTags(void) if (K_ShowPlayerNametag(ntplayer) == true) { K_DrawNameTagForPlayer(result.x, result.y, ntplayer, cnum); + K_DrawTypingNotifier(result.x, result.y, ntplayer); } } } diff --git a/src/p_user.c b/src/p_user.c index e4785b249..6577b3eae 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4588,6 +4588,37 @@ void P_PlayerThink(player_t *player) player->mo->drawflags &= ~MFD_DONTDRAW; } + if (cmd->flags & TICCMD_TYPING) + { + if (cmd->flags & TICCMD_KEYSTROKE) + { + player->typing_timer = 15; + } + else if (player->typing_timer > 0) + { + player->typing_timer--; + } + + if (player->typing_timer + player->typing_duration > 0) + { + /* lag a little bit so we always get more than just a singular dot */ + if (player->typing_timer == 0 && + (player->typing_duration < 16 || player->typing_duration > 39)) + { + player->typing_duration = 0; + } + else + { + player->typing_duration++; + } + } + } + else + { + player->typing_timer = 0; + player->typing_duration = 0; + } + player->pflags &= ~PF_SLIDING; K_KartPlayerThink(player, cmd); // SRB2kart