2014-03-15 16:59:03 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
2018-11-25 12:35:38 +00:00
// Copyright (C) 1999-2018 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file hu_stuff.c
/// \brief Heads up display
# include "doomdef.h"
# include "byteptr.h"
# include "hu_stuff.h"
# include "m_menu.h" // gametype_cons_t
# include "m_cond.h" // emblems
# include "d_clisrv.h"
# include "g_game.h"
# include "g_input.h"
# include "i_video.h"
# include "i_system.h"
# include "st_stuff.h" // ST_HEIGHT
# include "r_local.h"
# include "keys.h"
# include "v_video.h"
# include "w_wad.h"
# include "z_zone.h"
# include "console.h"
# include "am_map.h"
# include "d_main.h"
# include "p_local.h" // camera, camera2
# include "p_tick.h"
# ifdef HWRENDER
# include "hardware/hw_main.h"
# endif
# ifdef HAVE_BLUA
# include "lua_hud.h"
2014-06-18 20:28:09 +00:00
# include "lua_hook.h"
2014-03-15 16:59:03 +00:00
# endif
// coords are scaled
# define HU_INPUTX 0
# define HU_INPUTY 0
# define HU_SERVER_SAY 1 // Server message (dedicated).
# define HU_CSAY 2 // Server CECHOes to everyone.
//-------------------------------------------
// heads up font
//-------------------------------------------
patch_t * hu_font [ HU_FONTSIZE ] ;
patch_t * tny_font [ HU_FONTSIZE ] ;
patch_t * tallnum [ 10 ] ; // 0-9
patch_t * nightsnum [ 10 ] ; // 0-9
// Level title and credits fonts
patch_t * lt_font [ LT_FONTSIZE ] ;
patch_t * cred_font [ CRED_FONTSIZE ] ;
2018-02-22 21:49:36 +00:00
patch_t * ttlnum [ 20 ] ; // act numbers (0-19)
2014-03-15 16:59:03 +00:00
static player_t * plr ;
boolean chat_on ; // entering a chat message?
static char w_chat [ HU_MAXMSGLEN ] ;
2018-12-23 02:17:01 +00:00
static size_t c_input = 0 ; // let's try to make the chat input less shitty.
2014-03-15 16:59:03 +00:00
static boolean headsupactive = false ;
boolean hu_showscores ; // draw rankings
static char hu_tick ;
patch_t * rflagico ;
patch_t * bflagico ;
patch_t * rmatcico ;
patch_t * bmatcico ;
patch_t * tagico ;
patch_t * tallminus ;
2017-08-17 23:58:16 +00:00
patch_t * tallinfin ;
2014-03-15 16:59:03 +00:00
//-------------------------------------------
// coop hud
//-------------------------------------------
2018-06-03 21:41:54 +00:00
patch_t * emeraldpics [ 3 ] [ 8 ] ; // 0 = normal, 1 = tiny, 2 = coinbox
2014-03-15 16:59:03 +00:00
static patch_t * emblemicon ;
2017-04-24 20:56:17 +00:00
patch_t * tokenicon ;
2017-07-08 10:41:20 +00:00
static patch_t * exiticon ;
2014-03-15 16:59:03 +00:00
//-------------------------------------------
// misc vars
//-------------------------------------------
// crosshair 0 = off, 1 = cross, 2 = angle, 3 = point, see m_menu.c
static patch_t * crosshair [ HU_CROSSHAIRS ] ; // 3 precached crosshair graphics
// -------
// protos.
// -------
static void HU_DrawRankings ( void ) ;
static void HU_DrawCoopOverlay ( void ) ;
static void HU_DrawNetplayCoopOverlay ( void ) ;
//======================================================================
// KEYBOARD LAYOUTS FOR ENTERING TEXT
//======================================================================
char * shiftxform ;
char english_shiftxform [ ] =
{
0 ,
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ,
11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 ,
21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 ,
31 ,
' ' , ' ! ' , ' " ' , ' # ' , ' $ ' , ' % ' , ' & ' ,
' " ' , // shift-'
' ( ' , ' ) ' , ' * ' , ' + ' ,
' < ' , // shift-,
' _ ' , // shift--
' > ' , // shift-.
' ? ' , // shift-/
' ) ' , // shift-0
' ! ' , // shift-1
' @ ' , // shift-2
' # ' , // shift-3
' $ ' , // shift-4
' % ' , // shift-5
' ^ ' , // shift-6
' & ' , // shift-7
' * ' , // shift-8
' ( ' , // shift-9
' : ' ,
' : ' , // shift-;
' < ' ,
' + ' , // shift-=
' > ' , ' ? ' , ' @ ' ,
' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' , ' G ' , ' H ' , ' I ' , ' J ' , ' K ' , ' L ' , ' M ' , ' N ' ,
' O ' , ' P ' , ' Q ' , ' R ' , ' S ' , ' T ' , ' U ' , ' V ' , ' W ' , ' X ' , ' Y ' , ' Z ' ,
' { ' , // shift-[
' | ' , // shift-backslash - OH MY GOD DOES WATCOM SUCK
' } ' , // shift-]
' " ' , ' _ ' ,
' ~ ' , // shift-`
' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' , ' G ' , ' H ' , ' I ' , ' J ' , ' K ' , ' L ' , ' M ' , ' N ' ,
' O ' , ' P ' , ' Q ' , ' R ' , ' S ' , ' T ' , ' U ' , ' V ' , ' W ' , ' X ' , ' Y ' , ' Z ' ,
' { ' , ' | ' , ' } ' , ' ~ ' , 127
} ;
static char cechotext [ 1024 ] ;
static tic_t cechotimer = 0 ;
static tic_t cechoduration = 5 * TICRATE ;
static INT32 cechoflags = 0 ;
//======================================================================
// HEADS UP INIT
//======================================================================
# ifndef NONET
// just after
static void Command_Say_f ( void ) ;
static void Command_Sayto_f ( void ) ;
static void Command_Sayteam_f ( void ) ;
static void Command_CSay_f ( void ) ;
static void Got_Saycmd ( UINT8 * * p , INT32 playernum ) ;
# endif
void HU_LoadGraphics ( void )
{
char buffer [ 9 ] ;
INT32 i , j ;
if ( dedicated )
return ;
j = HU_FONTSTART ;
for ( i = 0 ; i < HU_FONTSIZE ; i + + , j + + )
{
// cache the heads-up font for entire game execution
sprintf ( buffer , " STCFN%.3d " , j ) ;
if ( W_CheckNumForName ( buffer ) = = LUMPERROR )
hu_font [ i ] = NULL ;
else
hu_font [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
// tiny version of the heads-up font
sprintf ( buffer , " TNYFN%.3d " , j ) ;
if ( W_CheckNumForName ( buffer ) = = LUMPERROR )
tny_font [ i ] = NULL ;
else
tny_font [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
}
j = LT_FONTSTART ;
for ( i = 0 ; i < LT_FONTSIZE ; i + + )
{
sprintf ( buffer , " LTFNT%.3d " , j ) ;
j + + ;
if ( W_CheckNumForName ( buffer ) = = LUMPERROR )
lt_font [ i ] = NULL ;
else
lt_font [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
}
// cache the credits font for entire game execution (why not?)
j = CRED_FONTSTART ;
for ( i = 0 ; i < CRED_FONTSIZE ; i + + )
{
sprintf ( buffer , " CRFNT%.3d " , j ) ;
j + + ;
if ( W_CheckNumForName ( buffer ) = = LUMPERROR )
cred_font [ i ] = NULL ;
else
cred_font [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
}
//cache numbers too!
for ( i = 0 ; i < 10 ; i + + )
{
sprintf ( buffer , " STTNUM%d " , i ) ;
tallnum [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
sprintf ( buffer , " NGTNUM%d " , i ) ;
nightsnum [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
}
// minus for negative tallnums
tallminus = ( patch_t * ) W_CachePatchName ( " STTMINUS " , PU_HUDGFX ) ;
2017-08-17 23:58:16 +00:00
tallinfin = ( patch_t * ) W_CachePatchName ( " STTINFIN " , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
2018-02-22 21:49:36 +00:00
// cache act numbers for level titles
for ( i = 0 ; i < 20 ; i + + )
{
sprintf ( buffer , " TTL%.2d " , i ) ;
ttlnum [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
}
2014-03-15 16:59:03 +00:00
// cache the crosshairs, don't bother to know which one is being used,
// just cache all 3, they're so small anyway.
for ( i = 0 ; i < HU_CROSSHAIRS ; i + + )
{
sprintf ( buffer , " CROSHAI%c " , ' 1 ' + i ) ;
crosshair [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
}
emblemicon = W_CachePatchName ( " EMBLICON " , PU_HUDGFX ) ;
tokenicon = W_CachePatchName ( " TOKNICON " , PU_HUDGFX ) ;
2017-07-08 10:41:20 +00:00
exiticon = W_CachePatchName ( " EXITICON " , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
2017-11-17 23:02:34 +00:00
emeraldpics [ 0 ] [ 0 ] = W_CachePatchName ( " CHAOS1 " , PU_HUDGFX ) ;
emeraldpics [ 0 ] [ 1 ] = W_CachePatchName ( " CHAOS2 " , PU_HUDGFX ) ;
emeraldpics [ 0 ] [ 2 ] = W_CachePatchName ( " CHAOS3 " , PU_HUDGFX ) ;
emeraldpics [ 0 ] [ 3 ] = W_CachePatchName ( " CHAOS4 " , PU_HUDGFX ) ;
emeraldpics [ 0 ] [ 4 ] = W_CachePatchName ( " CHAOS5 " , PU_HUDGFX ) ;
emeraldpics [ 0 ] [ 5 ] = W_CachePatchName ( " CHAOS6 " , PU_HUDGFX ) ;
emeraldpics [ 0 ] [ 6 ] = W_CachePatchName ( " CHAOS7 " , PU_HUDGFX ) ;
2018-06-03 21:41:54 +00:00
emeraldpics [ 0 ] [ 7 ] = W_CachePatchName ( " CHAOS8 " , PU_HUDGFX ) ;
2017-11-17 23:02:34 +00:00
emeraldpics [ 1 ] [ 0 ] = W_CachePatchName ( " TEMER1 " , PU_HUDGFX ) ;
emeraldpics [ 1 ] [ 1 ] = W_CachePatchName ( " TEMER2 " , PU_HUDGFX ) ;
emeraldpics [ 1 ] [ 2 ] = W_CachePatchName ( " TEMER3 " , PU_HUDGFX ) ;
emeraldpics [ 1 ] [ 3 ] = W_CachePatchName ( " TEMER4 " , PU_HUDGFX ) ;
emeraldpics [ 1 ] [ 4 ] = W_CachePatchName ( " TEMER5 " , PU_HUDGFX ) ;
emeraldpics [ 1 ] [ 5 ] = W_CachePatchName ( " TEMER6 " , PU_HUDGFX ) ;
2017-12-09 18:48:25 +00:00
emeraldpics [ 1 ] [ 6 ] = W_CachePatchName ( " TEMER7 " , PU_HUDGFX ) ;
2018-06-03 21:41:54 +00:00
//emeraldpics[1][7] = W_CachePatchName("TEMER8", PU_HUDGFX); -- unused
2017-11-17 23:02:34 +00:00
emeraldpics [ 2 ] [ 0 ] = W_CachePatchName ( " EMBOX1 " , PU_HUDGFX ) ;
emeraldpics [ 2 ] [ 1 ] = W_CachePatchName ( " EMBOX2 " , PU_HUDGFX ) ;
emeraldpics [ 2 ] [ 2 ] = W_CachePatchName ( " EMBOX3 " , PU_HUDGFX ) ;
emeraldpics [ 2 ] [ 3 ] = W_CachePatchName ( " EMBOX4 " , PU_HUDGFX ) ;
emeraldpics [ 2 ] [ 4 ] = W_CachePatchName ( " EMBOX5 " , PU_HUDGFX ) ;
emeraldpics [ 2 ] [ 5 ] = W_CachePatchName ( " EMBOX6 " , PU_HUDGFX ) ;
emeraldpics [ 2 ] [ 6 ] = W_CachePatchName ( " EMBOX7 " , PU_HUDGFX ) ;
2018-06-03 21:41:54 +00:00
//emeraldpics[2][7] = W_CachePatchName("EMBOX8", PU_HUDGFX); -- unused
2014-03-15 16:59:03 +00:00
}
// Initialise Heads up
// once at game startup.
//
void HU_Init ( void )
{
# ifndef NONET
COM_AddCommand ( " say " , Command_Say_f ) ;
COM_AddCommand ( " sayto " , Command_Sayto_f ) ;
COM_AddCommand ( " sayteam " , Command_Sayteam_f ) ;
COM_AddCommand ( " csay " , Command_CSay_f ) ;
RegisterNetXCmd ( XD_SAY , Got_Saycmd ) ;
# endif
// set shift translation table
shiftxform = english_shiftxform ;
HU_LoadGraphics ( ) ;
}
static inline void HU_Stop ( void )
{
headsupactive = false ;
}
//
// Reset Heads up when consoleplayer spawns
//
void HU_Start ( void )
{
if ( headsupactive )
HU_Stop ( ) ;
plr = & players [ consoleplayer ] ;
headsupactive = true ;
}
//======================================================================
// EXECUTION
//======================================================================
# ifndef NONET
2018-07-31 09:10:02 +00:00
// EVERY CHANGE IN THIS SCRIPT IS LOL XD! BY VINCYTM
static UINT32 chat_nummsg_log = 0 ;
static UINT32 chat_nummsg_min = 0 ;
2018-12-17 19:43:59 +00:00
static UINT32 chat_scroll = 0 ;
2018-07-31 09:10:02 +00:00
static tic_t chat_scrolltime = 0 ;
2018-12-23 02:17:01 +00:00
static UINT32 chat_maxscroll = 0 ; // how far can we scroll?
2018-07-31 09:10:02 +00:00
2018-12-23 02:17:01 +00:00
//static chatmsg_t chat_mini[CHAT_BUFSIZE]; // Display the last few messages sent.
//static chatmsg_t chat_log[CHAT_BUFSIZE]; // Keep every message sent to us in memory so we can scroll n shit, it's cool.
2018-07-31 09:10:02 +00:00
2018-12-23 02:17:01 +00:00
static char chat_log [ CHAT_BUFSIZE ] [ 255 ] ; // hold the last 48 or so messages in that log.
static char chat_mini [ 8 ] [ 255 ] ; // display up to 8 messages that will fade away / get overwritten
2018-07-31 09:10:02 +00:00
static tic_t chat_timers [ 8 ] ;
2018-12-23 02:17:01 +00:00
static boolean chat_scrollmedown = false ; // force instant scroll down on the chat log. Happens when you open it / send a message.
2018-07-31 09:10:02 +00:00
// remove text from minichat table
2018-12-23 02:17:01 +00:00
static INT16 addy = 0 ; // use this to make the messages scroll smoothly when one fades away
2018-07-31 09:10:02 +00:00
static void HU_removeChatText_Mini ( void )
{
// MPC: Don't create new arrays, just iterate through an existing one
2018-12-17 23:55:21 +00:00
size_t i ;
2018-07-31 09:10:02 +00:00
for ( i = 0 ; i < chat_nummsg_min - 1 ; i + + ) {
strcpy ( chat_mini [ i ] , chat_mini [ i + 1 ] ) ;
chat_timers [ i ] = chat_timers [ i + 1 ] ;
}
2018-12-23 02:17:01 +00:00
chat_nummsg_min - - ; // lost 1 msg.
2018-07-31 09:10:02 +00:00
// use addy and make shit slide smoothly af.
addy + = ( vid . width < 640 ) ? 8 : 6 ;
}
// same but w the log. TODO: optimize this and maybe merge in a single func? im bad at C.
static void HU_removeChatText_Log ( void )
{
// MPC: Don't create new arrays, just iterate through an existing one
2018-12-17 23:55:21 +00:00
size_t i ;
2018-07-31 09:10:02 +00:00
for ( i = 0 ; i < chat_nummsg_log - 1 ; i + + ) {
strcpy ( chat_log [ i ] , chat_log [ i + 1 ] ) ;
}
2018-12-23 02:17:01 +00:00
chat_nummsg_log - - ; // lost 1 msg.
2018-07-31 09:10:02 +00:00
}
2018-12-26 21:20:17 +00:00
# endif
2018-12-17 19:43:59 +00:00
void HU_AddChatText ( const char * text , boolean playsound )
2018-07-31 09:10:02 +00:00
{
2018-12-26 21:20:17 +00:00
# ifndef NONET
2018-12-23 02:17:01 +00:00
if ( playsound & & cv_consolechat . value ! = 2 ) // Don't play the sound if we're using hidden chat.
2018-12-17 19:43:59 +00:00
S_StartSound ( NULL , sfx_radio ) ;
// reguardless of our preferences, put all of this in the chat buffer in case we decide to change from oldchat mid-game.
2018-12-23 02:17:01 +00:00
if ( chat_nummsg_log > = CHAT_BUFSIZE ) // too many messages!
2018-07-31 09:10:02 +00:00
HU_removeChatText_Log ( ) ;
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
strcpy ( chat_log [ chat_nummsg_log ] , text ) ;
chat_nummsg_log + + ;
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
if ( chat_nummsg_min > = 8 )
HU_removeChatText_Mini ( ) ;
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
strcpy ( chat_mini [ chat_nummsg_min ] , text ) ;
chat_timers [ chat_nummsg_min ] = TICRATE * cv_chattime . value ;
chat_nummsg_min + + ;
2018-12-17 19:43:59 +00:00
2018-12-23 02:17:01 +00:00
if ( OLDCHAT ) // if we're using oldchat, print directly in console
2018-12-17 19:43:59 +00:00
CONS_Printf ( " %s \n " , text ) ;
else // if we aren't, still save the message to log.txt
CON_LogMessage ( va ( " %s \n " , text ) ) ;
2018-12-26 21:20:17 +00:00
# else
( void ) playsound ;
CONS_Printf ( " %s \n " , text ) ;
# endif
2018-12-17 19:43:59 +00:00
}
2018-12-26 21:20:17 +00:00
# ifndef NONET
2018-07-31 09:10:02 +00:00
2014-03-15 16:59:03 +00:00
/** Runs a say command, sending an ::XD_SAY message.
* A say command consists of a signed 8 - bit integer for the target , an
* unsigned 8 - bit flag variable , and then the message itself .
*
* The target is 0 to say to everyone , 1 to 32 to say to that player , or - 1
* to - 32 to say to everyone on that player ' s team . Note : This means you
* have to add 1 to the player number , since they are 0 to 31 internally .
*
* The flag HU_SERVER_SAY will be set if it is the dedicated server speaking .
*
* This function obtains the message using COM_Argc ( ) and COM_Argv ( ) .
*
* \ param target Target to send message to .
* \ param usedargs Number of arguments to ignore .
* \ param flags Set HU_CSAY for server / admin to CECHO everyone .
* \ sa Command_Say_f , Command_Sayteam_f , Command_Sayto_f , Got_Saycmd
* \ author Graue < graue @ oceanbase . org >
*/
2018-12-17 19:43:59 +00:00
2014-03-15 16:59:03 +00:00
static void DoSayCommand ( SINT8 target , size_t usedargs , UINT8 flags )
{
2017-09-29 18:46:23 +00:00
char buf [ 254 ] ;
2014-03-15 16:59:03 +00:00
size_t numwords , ix ;
char * msg = & buf [ 2 ] ;
const size_t msgspace = sizeof buf - 2 ;
numwords = COM_Argc ( ) - usedargs ;
I_Assert ( numwords > 0 ) ;
2018-12-23 02:17:01 +00:00
if ( CHAT_MUTE ) // TODO: Per Player mute.
2014-03-15 16:59:03 +00:00
{
2018-12-17 19:43:59 +00:00
HU_AddChatText ( va ( " %s>ERROR: The chat is muted. You can't say anything. " , " \x85 " ) , false ) ;
2014-03-15 16:59:03 +00:00
return ;
}
// Only servers/admins can CSAY.
2018-12-17 19:43:59 +00:00
if ( ! server & & ! ( IsPlayerAdmin ( consoleplayer ) ) )
2014-03-15 16:59:03 +00:00
flags & = ~ HU_CSAY ;
// We handle HU_SERVER_SAY, not the caller.
flags & = ~ HU_SERVER_SAY ;
if ( dedicated & & ! ( flags & HU_CSAY ) )
flags | = HU_SERVER_SAY ;
buf [ 0 ] = target ;
buf [ 1 ] = flags ;
msg [ 0 ] = ' \0 ' ;
for ( ix = 0 ; ix < numwords ; ix + + )
{
if ( ix > 0 )
strlcat ( msg , " " , msgspace ) ;
strlcat ( msg , COM_Argv ( ix + usedargs ) , msgspace ) ;
}
2018-12-23 02:17:01 +00:00
if ( strlen ( msg ) > 4 & & strnicmp ( msg , " /pm " , 3 ) = = 0 ) // used /pm
2018-07-31 09:10:02 +00:00
{
// what we're gonna do now is check if the node exists
// with that logic, characters 4 and 5 are our numbers:
2018-12-17 19:43:59 +00:00
const char * newmsg ;
2018-07-31 09:10:02 +00:00
char * nodenum = ( char * ) malloc ( 3 ) ;
2018-12-23 02:17:01 +00:00
INT32 spc = 1 ; // used if nodenum[1] is a space.
2018-12-18 00:09:05 +00:00
2018-12-18 02:31:00 +00:00
strncpy ( nodenum , msg + 3 , 3 ) ;
2018-07-31 09:10:02 +00:00
// check for undesirable characters in our "number"
if ( ( ( nodenum [ 0 ] < ' 0 ' ) | | ( nodenum [ 0 ] > ' 9 ' ) ) | | ( ( nodenum [ 1 ] < ' 0 ' ) | | ( nodenum [ 1 ] > ' 9 ' ) ) )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
// check if nodenum[1] is a space
if ( nodenum [ 1 ] = = ' ' )
spc = 0 ;
// let it slide
else
2018-12-17 19:43:59 +00:00
{
HU_AddChatText ( " \x82 NOTICE: \x80 Invalid command format. Correct format is \' /pm<node> \' . " , false ) ;
2018-12-22 11:34:17 +00:00
free ( nodenum ) ;
2018-07-31 09:10:02 +00:00
return ;
2018-12-17 19:43:59 +00:00
}
2018-07-31 09:10:02 +00:00
}
// I'm very bad at C, I swear I am, additional checks eww!
if ( spc ! = 0 )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
if ( msg [ 5 ] ! = ' ' )
{
2018-12-17 19:43:59 +00:00
HU_AddChatText ( " \x82 NOTICE: \x80 Invalid command format. Correct format is \' /pm<node> \' . " , false ) ;
2018-12-22 11:34:17 +00:00
free ( nodenum ) ;
2018-07-31 09:10:02 +00:00
return ;
}
}
2018-12-17 19:43:59 +00:00
2018-12-23 02:17:01 +00:00
target = atoi ( ( const char * ) nodenum ) ; // turn that into a number
2018-12-22 11:34:17 +00:00
free ( nodenum ) ;
2018-07-31 09:10:02 +00:00
//CONS_Printf("%d\n", target);
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
// check for target player, if it doesn't exist then we can't send the message!
2018-12-23 07:25:58 +00:00
if ( target < MAXPLAYERS & & playeringame [ target ] ) // player exists
2018-12-23 02:17:01 +00:00
target + + ; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
2018-07-31 09:10:02 +00:00
else
{
2018-12-23 02:17:01 +00:00
HU_AddChatText ( va ( " \x82 NOTICE: \x80 Player %d does not exist. " , target ) , false ) ; // same
2018-07-31 09:10:02 +00:00
return ;
}
buf [ 0 ] = target ;
2018-12-17 19:43:59 +00:00
newmsg = msg + 5 + spc ;
2018-12-24 23:37:54 +00:00
strlcpy ( msg , newmsg , 252 ) ;
2018-07-31 09:10:02 +00:00
}
2014-03-15 16:59:03 +00:00
SendNetXCmd ( XD_SAY , buf , strlen ( msg ) + 1 + msg - buf ) ;
}
/** Send a message to everyone.
* \ sa DoSayCommand , Command_Sayteam_f , Command_Sayto_f
* \ author Graue < graue @ oceanbase . org >
*/
static void Command_Say_f ( void )
{
if ( COM_Argc ( ) < 2 )
{
CONS_Printf ( M_GetText ( " say <message>: send a message \n " ) ) ;
return ;
}
DoSayCommand ( 0 , 1 , 0 ) ;
}
/** Send a message to a particular person.
* \ sa DoSayCommand , Command_Sayteam_f , Command_Say_f
* \ author Graue < graue @ oceanbase . org >
*/
static void Command_Sayto_f ( void )
{
INT32 target ;
if ( COM_Argc ( ) < 3 )
{
CONS_Printf ( M_GetText ( " sayto <playername|playernum> <message>: send a message to a player \n " ) ) ;
return ;
}
target = nametonum ( COM_Argv ( 1 ) ) ;
if ( target = = - 1 )
{
CONS_Alert ( CONS_NOTICE , M_GetText ( " No player with that name! \n " ) ) ;
return ;
}
target + + ; // Internally we use 0 to 31, but say command uses 1 to 32.
DoSayCommand ( ( SINT8 ) target , 2 , 0 ) ;
}
/** Send a message to members of the player's team.
* \ sa DoSayCommand , Command_Say_f , Command_Sayto_f
* \ author Graue < graue @ oceanbase . org >
*/
static void Command_Sayteam_f ( void )
{
if ( COM_Argc ( ) < 2 )
{
CONS_Printf ( M_GetText ( " sayteam <message>: send a message to your team \n " ) ) ;
return ;
}
if ( dedicated )
{
CONS_Alert ( CONS_NOTICE , M_GetText ( " Dedicated servers can't send team messages. Use \" say \" . \n " ) ) ;
return ;
}
DoSayCommand ( - 1 , 1 , 0 ) ;
}
/** Send a message to everyone, to be displayed by CECHO. Only
* permitted to servers and admins .
*/
static void Command_CSay_f ( void )
{
if ( COM_Argc ( ) < 2 )
{
CONS_Printf ( M_GetText ( " csay <message>: send a message to be shown in the middle of the screen \n " ) ) ;
return ;
}
2017-11-11 05:34:37 +00:00
if ( ! server & & ! IsPlayerAdmin ( consoleplayer ) )
2014-03-15 16:59:03 +00:00
{
CONS_Alert ( CONS_NOTICE , M_GetText ( " Only servers and admins can use csay. \n " ) ) ;
return ;
}
DoSayCommand ( 0 , 1 , HU_CSAY ) ;
}
2018-12-23 02:17:01 +00:00
static tic_t stop_spamming [ MAXPLAYERS ] ;
2014-03-15 16:59:03 +00:00
/** Receives a message, processing an ::XD_SAY command.
* \ sa DoSayCommand
* \ author Graue < graue @ oceanbase . org >
*/
static void Got_Saycmd ( UINT8 * * p , INT32 playernum )
{
SINT8 target ;
UINT8 flags ;
const char * dispname ;
char * msg ;
boolean action = false ;
char * ptr ;
2018-12-18 00:09:05 +00:00
INT32 spam_eatmsg = 0 ;
2014-03-15 16:59:03 +00:00
2017-01-13 19:53:52 +00:00
CONS_Debug ( DBG_NETPLAY , " Received SAY cmd from Player %d (%s) \n " , playernum + 1 , player_names [ playernum ] ) ;
2014-03-15 16:59:03 +00:00
target = READSINT8 ( * p ) ;
flags = READUINT8 ( * p ) ;
msg = ( char * ) * p ;
SKIPSTRING ( * p ) ;
2018-12-17 19:43:59 +00:00
if ( ( cv_mute . value | | flags & ( HU_CSAY | HU_SERVER_SAY ) ) & & playernum ! = serverplayer & & ! ( IsPlayerAdmin ( playernum ) ) )
2014-03-15 16:59:03 +00:00
{
CONS_Alert ( CONS_WARNING , cv_mute . value ?
M_GetText ( " Illegal say command received from %s while muted \n " ) : M_GetText ( " Illegal csay command received from non-admin %s \n " ) ,
player_names [ playernum ] ) ;
if ( server )
{
2017-09-29 18:46:23 +00:00
UINT8 buf [ 2 ] ;
2014-03-15 16:59:03 +00:00
buf [ 0 ] = ( UINT8 ) playernum ;
buf [ 1 ] = KICK_MSG_CON_FAIL ;
SendNetXCmd ( XD_KICK , & buf , 2 ) ;
}
return ;
}
//check for invalid characters (0x80 or above)
{
size_t i ;
const size_t j = strlen ( msg ) ;
for ( i = 0 ; i < j ; i + + )
{
if ( msg [ i ] & 0x80 )
{
CONS_Alert ( CONS_WARNING , M_GetText ( " Illegal say command received from %s containing invalid characters \n " ) , player_names [ playernum ] ) ;
if ( server )
{
2017-09-29 18:46:23 +00:00
char buf [ 2 ] ;
2014-03-15 16:59:03 +00:00
buf [ 0 ] = ( char ) playernum ;
buf [ 1 ] = KICK_MSG_CON_FAIL ;
SendNetXCmd ( XD_KICK , & buf , 2 ) ;
}
return ;
}
}
}
2018-07-31 09:10:02 +00:00
// before we do anything, let's verify the guy isn't spamming, get this easier on us.
2018-12-17 19:43:59 +00:00
2018-12-23 02:17:01 +00:00
//if (stop_spamming[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY))
if ( stop_spamming [ playernum ] ! = 0 & & consoleplayer ! = playernum & & cv_chatspamprotection . value & & ! ( flags & HU_CSAY ) )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
CONS_Debug ( DBG_NETPLAY , " Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message. \n " , playernum + 1 , player_names [ playernum ] ) ;
2018-12-23 02:17:01 +00:00
stop_spamming [ playernum ] = 4 ;
2018-07-31 09:10:02 +00:00
spam_eatmsg = 1 ;
}
else
2018-12-23 02:17:01 +00:00
stop_spamming [ playernum ] = 4 ; // you can hold off for 4 tics, can you?
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
// run the lua hook even if we were supposed to eat the msg, netgame consistency goes first.
2018-12-17 19:43:59 +00:00
2014-06-18 20:28:09 +00:00
# ifdef HAVE_BLUA
if ( LUAh_PlayerMsg ( playernum , target , flags , msg ) )
return ;
# endif
2014-03-15 16:59:03 +00:00
2018-07-31 09:10:02 +00:00
if ( spam_eatmsg )
2018-12-23 02:17:01 +00:00
return ; // don't proceed if we were supposed to eat the message.
2018-12-17 19:43:59 +00:00
2014-03-15 16:59:03 +00:00
// If it's a CSAY, just CECHO and be done with it.
if ( flags & HU_CSAY )
{
HU_SetCEchoDuration ( 5 ) ;
I_OutputMsg ( " Server message: " ) ;
HU_DoCEcho ( msg ) ;
return ;
}
// Handle "/me" actions, but only in messages to everyone.
if ( target = = 0 & & strlen ( msg ) > 4 & & strnicmp ( msg , " /me " , 4 ) = = 0 )
{
msg + = 4 ;
action = true ;
}
if ( flags & HU_SERVER_SAY )
dispname = " SERVER " ;
else
dispname = player_names [ playernum ] ;
// Clean up message a bit
// If you use a \r character, you can remove your name
// from before the text and then pretend to be someone else!
ptr = msg ;
while ( * ptr ! = ' \0 ' )
{
if ( * ptr = = ' \r ' )
* ptr = ' ' ;
ptr + + ;
}
// Show messages sent by you, to you, to your team, or to everyone:
if ( playernum = = consoleplayer // By you
| | ( target = = - 1 & & ST_SameTeam ( & players [ consoleplayer ] , & players [ playernum ] ) ) // To your team
| | target = = 0 // To everyone
| | consoleplayer = = target - 1 ) // To you
{
2018-12-17 19:43:59 +00:00
const char * prefix = " " , * cstart = " " , * cend = " " , * adminchar = " \x82 ~ \x83 " , * remotechar = " \x82 @ \x83 " , * fmt2 , * textcolor = " \x80 " ;
2014-03-15 16:59:03 +00:00
char * tempchar = NULL ;
2018-12-17 19:43:59 +00:00
// player is a spectator?
if ( players [ playernum ] . spectator )
2014-03-15 16:59:03 +00:00
{
2018-12-17 19:43:59 +00:00
cstart = " \x86 " ; // grey name
textcolor = " \x86 " ;
}
2018-12-23 02:17:01 +00:00
else if ( target = = - 1 ) // say team
2014-03-15 16:59:03 +00:00
{
if ( players [ playernum ] . ctfteam = = 1 ) // red
2018-12-17 19:43:59 +00:00
{
2014-03-15 16:59:03 +00:00
cstart = " \x85 " ;
2018-12-17 19:43:59 +00:00
textcolor = " \x85 " ;
}
else // blue
{
2014-03-15 16:59:03 +00:00
cstart = " \x84 " ;
2018-12-17 19:43:59 +00:00
textcolor = " \x84 " ;
}
2014-03-15 16:59:03 +00:00
}
2018-12-17 19:43:59 +00:00
else
{
2018-12-18 00:02:22 +00:00
const UINT8 color = players [ playernum ] . skincolor ;
2018-12-17 19:43:59 +00:00
cstart = " \x83 " ;
2018-12-18 00:02:22 +00:00
2019-01-08 03:23:59 +00:00
// Follow palette order at r_draw.c Color_Names
2019-06-20 14:34:27 +00:00
switch ( color )
{
default :
case SKINCOLOR_WHITE :
case SKINCOLOR_BONE :
case SKINCOLOR_CLOUDY :
case SKINCOLOR_GREY :
case SKINCOLOR_SILVER :
case SKINCOLOR_AETHER :
case SKINCOLOR_SLATE :
cstart = " \x80 " ; // white
break ;
case SKINCOLOR_CARBON :
case SKINCOLOR_JET :
case SKINCOLOR_BLACK :
cstart = " \x86 " ; // V_GRAYMAP
break ;
case SKINCOLOR_PINK :
case SKINCOLOR_RUBY :
case SKINCOLOR_SALMON :
case SKINCOLOR_RED :
case SKINCOLOR_CRIMSON :
case SKINCOLOR_FLAME :
cstart = " \x85 " ; // V_REDMAP
break ;
case SKINCOLOR_YOGURT :
case SKINCOLOR_BROWN :
case SKINCOLOR_TAN :
case SKINCOLOR_BEIGE :
case SKINCOLOR_QUAIL :
cstart = " \x8d " ; // V_BROWNMAP
break ;
case SKINCOLOR_MOSS :
case SKINCOLOR_GREEN :
case SKINCOLOR_FOREST :
case SKINCOLOR_EMERALD :
case SKINCOLOR_MINT :
cstart = " \x83 " ; // V_GREENMAP
break ;
case SKINCOLOR_AZURE :
cstart = " \x8c " ; // V_AZUREMAP
break ;
case SKINCOLOR_LAVENDER :
case SKINCOLOR_PASTEL :
case SKINCOLOR_PURPLE :
cstart = " \x89 " ; // V_PURPLEMAP
break ;
case SKINCOLOR_PEACHY :
case SKINCOLOR_LILAC :
case SKINCOLOR_PLUM :
case SKINCOLOR_ROSY :
cstart = " \x8e " ; // V_ROSYMAP
break ;
case SKINCOLOR_SUNSET :
case SKINCOLOR_APRICOT :
case SKINCOLOR_ORANGE :
case SKINCOLOR_RUST :
cstart = " \x87 " ; // V_ORANGEMAP
break ;
case SKINCOLOR_GOLD :
case SKINCOLOR_SANDY :
case SKINCOLOR_YELLOW :
case SKINCOLOR_OLIVE :
cstart = " \x82 " ; // V_YELLOWMAP
break ;
case SKINCOLOR_LIME :
case SKINCOLOR_PERIDOT :
cstart = " \x8b " ; // V_PERIDOTMAP
break ;
case SKINCOLOR_SEAFOAM :
case SKINCOLOR_AQUA :
cstart = " \x8a " ; // V_AQUAMAP
break ;
case SKINCOLOR_TEAL :
case SKINCOLOR_WAVE :
case SKINCOLOR_CYAN :
case SKINCOLOR_SKY :
case SKINCOLOR_CERULEAN :
case SKINCOLOR_ICY :
case SKINCOLOR_SAPPHIRE :
case SKINCOLOR_VAPOR :
cstart = " \x88 " ; // V_SKYMAP
break ;
case SKINCOLOR_CORNFLOWER :
case SKINCOLOR_BLUE :
case SKINCOLOR_COBALT :
case SKINCOLOR_DUSK :
cstart = " \x84 " ; // V_BLUEMAP
break ;
case SKINCOLOR_BUBBLEGUM :
case SKINCOLOR_MAGENTA :
case SKINCOLOR_NEON :
case SKINCOLOR_VIOLET :
cstart = " \x81 " ; // V_MAGENTAMAP
break ;
}
2018-12-17 19:43:59 +00:00
}
prefix = cstart ;
2014-03-15 16:59:03 +00:00
// Give admins and remote admins their symbols.
if ( playernum = = serverplayer )
tempchar = ( char * ) Z_Calloc ( strlen ( cstart ) + strlen ( adminchar ) + 1 , PU_STATIC , NULL ) ;
2017-11-11 05:34:37 +00:00
else if ( IsPlayerAdmin ( playernum ) )
2014-03-15 16:59:03 +00:00
tempchar = ( char * ) Z_Calloc ( strlen ( cstart ) + strlen ( remotechar ) + 1 , PU_STATIC , NULL ) ;
if ( tempchar )
{
if ( playernum = = serverplayer )
strcat ( tempchar , adminchar ) ;
else
strcat ( tempchar , remotechar ) ;
2018-07-31 09:10:02 +00:00
strcat ( tempchar , cstart ) ;
2014-03-15 16:59:03 +00:00
cstart = tempchar ;
}
// Choose the proper format string for display.
// Each format includes four strings: color start, display
// name, color end, and the message itself.
// '\4' makes the message yellow and beeps; '\3' just beeps.
if ( action )
2018-12-17 19:43:59 +00:00
fmt2 = " * %s%s%s%s \x82 %s%s " ;
2014-03-15 16:59:03 +00:00
else if ( target - 1 = = consoleplayer ) // To you
2018-07-31 09:10:02 +00:00
{
prefix = " \x82 [PM] " ;
cstart = " \x82 " ;
2018-12-17 19:43:59 +00:00
textcolor = " \x82 " ;
fmt2 = " %s<%s%s>%s \x80 %s%s " ;
2018-07-31 09:10:02 +00:00
}
2014-03-15 16:59:03 +00:00
else if ( target > 0 ) // By you, to another player
{
// Use target's name.
dispname = player_names [ target - 1 ] ;
2018-07-31 09:10:02 +00:00
prefix = " \x82 [TO] " ;
cstart = " \x82 " ;
2018-12-17 19:43:59 +00:00
fmt2 = " %s<%s%s>%s \x80 %s%s " ;
2014-03-15 16:59:03 +00:00
}
2018-12-17 19:43:59 +00:00
else if ( target = = 0 ) // To everyone
fmt2 = " %s<%s%s%s> \x80 %s%s " ;
2014-03-15 16:59:03 +00:00
else // To your team
2018-07-31 09:10:02 +00:00
{
2018-12-17 19:43:59 +00:00
if ( players [ playernum ] . ctfteam = = 1 ) // red
prefix = " \x85 [TEAM] " ;
2018-07-31 09:10:02 +00:00
else if ( players [ playernum ] . ctfteam = = 2 ) // blue
prefix = " \x84 [TEAM] " ;
else
2018-12-23 02:17:01 +00:00
prefix = " \x83 " ; // makes sure this doesn't implode if you sayteam on non-team gamemodes
2018-12-17 19:43:59 +00:00
fmt2 = " %s<%s%s> \x80 %s %s%s " ;
}
HU_AddChatText ( va ( fmt2 , prefix , cstart , dispname , cend , textcolor , msg ) , cv_chatnotifications . value ) ; // add to chat
2014-03-15 16:59:03 +00:00
if ( tempchar )
Z_Free ( tempchar ) ;
}
2014-03-17 12:13:16 +00:00
# ifdef _DEBUG
// I just want to point out while I'm here that because the data is still
// sent to all players, techincally anyone can see your chat if they really
// wanted to, even if you used sayto or sayteam.
// You should never send any sensitive info through sayto for that reason.
2014-03-15 16:59:03 +00:00
else
CONS_Printf ( " Dropped chat: %d %d %s \n " , playernum , target , msg ) ;
2014-03-17 12:13:16 +00:00
# endif
2014-03-15 16:59:03 +00:00
}
// Handles key input and string input
//
static inline boolean HU_keyInChatString ( char * s , char ch )
{
size_t l ;
if ( ( ch > = HU_FONTSTART & & ch < = HU_FONTEND & & hu_font [ ch - HU_FONTSTART ] )
| | ch = = ' ' ) // Allow spaces, of course
{
l = strlen ( s ) ;
if ( l < HU_MAXMSGLEN - 1 )
{
2018-12-23 02:17:01 +00:00
if ( c_input > = strlen ( s ) ) // don't do anything complicated
2018-07-31 09:10:02 +00:00
{
s [ l + + ] = ch ;
s [ l ] = 0 ;
2018-12-17 19:43:59 +00:00
}
2018-07-31 09:10:02 +00:00
else
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
// move everything past c_input for new characters:
2018-12-17 23:55:21 +00:00
size_t m = HU_MAXMSGLEN - 1 ;
2018-12-23 01:17:50 +00:00
while ( m > = c_input )
2018-07-31 09:10:02 +00:00
{
if ( s [ m ] )
s [ m + 1 ] = ( s [ m ] ) ;
2018-12-23 01:17:50 +00:00
if ( m = = 0 ) // prevent overflow
break ;
m - - ;
2018-07-31 09:10:02 +00:00
}
2018-12-23 02:17:01 +00:00
s [ c_input ] = ch ; // and replace this.
2018-07-31 09:10:02 +00:00
}
c_input + + ;
2014-03-15 16:59:03 +00:00
return true ;
}
return false ;
}
else if ( ch = = KEY_BACKSPACE )
{
2018-12-18 00:02:22 +00:00
size_t i = c_input ;
2018-07-31 09:10:02 +00:00
if ( c_input < = 0 )
return false ;
2018-12-18 00:02:22 +00:00
2018-07-31 09:10:02 +00:00
if ( ! s [ i - 1 ] )
return false ;
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
if ( i > = strlen ( s ) - 1 )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
s [ strlen ( s ) - 1 ] = 0 ;
c_input - - ;
2014-03-15 16:59:03 +00:00
return false ;
2018-12-17 19:43:59 +00:00
}
2018-07-31 09:10:02 +00:00
for ( ; ( i < HU_MAXMSGLEN ) ; i + + )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
s [ i - 1 ] = s [ i ] ;
}
c_input - - ;
2014-03-15 16:59:03 +00:00
}
else if ( ch ! = KEY_ENTER )
return false ; // did not eat key
return true ; // ate the key
}
2018-12-26 21:20:17 +00:00
# endif
2014-03-15 16:59:03 +00:00
//
//
void HU_Ticker ( void )
{
if ( dedicated )
return ;
hu_tick + + ;
hu_tick & = 7 ; // currently only to blink chat input cursor
if ( PLAYER1INPUTDOWN ( gc_scores ) )
hu_showscores = ! chat_on ;
else
hu_showscores = false ;
}
2018-12-26 21:20:17 +00:00
# ifndef NONET
2014-03-15 16:59:03 +00:00
static boolean teamtalk = false ;
2018-07-31 09:10:02 +00:00
/*static char chatchars[QUEUESIZE];
static INT32 head = 0 , tail = 0 ; */
// WHY DO YOU OVERCOMPLICATE EVERYTHING?????????
2014-03-15 16:59:03 +00:00
2018-12-22 11:34:17 +00:00
// Clear spaces so we don't end up with messages only made out of emptiness
2018-12-23 08:39:27 +00:00
static boolean HU_clearChatSpaces ( void )
2014-03-15 16:59:03 +00:00
{
2018-12-23 02:17:01 +00:00
size_t i = 0 ; // Used to just check our message
char c ; // current character we're iterating.
2018-12-22 11:34:17 +00:00
boolean nothingbutspaces = true ;
2014-03-15 16:59:03 +00:00
2018-12-23 02:17:01 +00:00
for ( ; i < strlen ( w_chat ) ; i + + ) // iterate through message and eradicate all spaces that don't belong.
2014-03-15 16:59:03 +00:00
{
2018-12-22 11:34:17 +00:00
c = w_chat [ i ] ;
if ( ! c )
2018-12-23 02:17:01 +00:00
break ; // if there's nothing, it's safe to assume our message has ended, so let's not waste any more time here.
2014-03-15 16:59:03 +00:00
2018-12-23 02:17:01 +00:00
if ( c ! = ' ' ) // Isn't a space
2018-12-22 11:34:17 +00:00
{
nothingbutspaces = false ;
}
}
return nothingbutspaces ;
2014-03-15 16:59:03 +00:00
}
//
//
static void HU_queueChatChar ( char c )
{
// send automaticly the message (no more chat char)
if ( c = = KEY_ENTER )
{
char buf [ 2 + 256 ] ;
2018-07-31 09:10:02 +00:00
char * msg = & buf [ 2 ] ;
2018-12-18 00:02:22 +00:00
size_t i = 0 ;
2014-03-15 16:59:03 +00:00
size_t ci = 2 ;
2018-12-18 00:02:22 +00:00
INT32 target = 0 ;
2018-12-23 02:17:01 +00:00
if ( HU_clearChatSpaces ( ) ) // Avoids being able to send empty messages, or something.
return ; // If this returns true, that means our message was NOTHING but spaces, so don't send it period.
2014-03-15 16:59:03 +00:00
do {
2018-07-31 09:10:02 +00:00
c = w_chat [ - 2 + ci + + ] ;
2014-03-15 16:59:03 +00:00
if ( ! c | | ( c > = ' ' & & ! ( c & 0x80 ) ) ) // copy printable characters and terminating '\0' only.
2018-07-31 09:10:02 +00:00
buf [ ci - 1 ] = c ;
2014-03-15 16:59:03 +00:00
} while ( c ) ;
2018-07-31 09:10:02 +00:00
for ( ; ( i < HU_MAXMSGLEN ) ; i + + )
2018-12-23 02:17:01 +00:00
w_chat [ i ] = 0 ; // reset this.
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
c_input = 0 ;
2018-12-17 19:43:59 +00:00
2014-03-15 16:59:03 +00:00
// last minute mute check
2018-12-17 19:43:59 +00:00
if ( CHAT_MUTE )
2014-03-15 16:59:03 +00:00
{
2018-12-17 19:43:59 +00:00
HU_AddChatText ( va ( " %s>ERROR: The chat is muted. You can't say anything. " , " \x85 " ) , false ) ;
2014-03-15 16:59:03 +00:00
return ;
}
2018-12-23 02:17:01 +00:00
if ( strlen ( msg ) > 4 & & strnicmp ( msg , " /pm " , 3 ) = = 0 ) // used /pm
2018-07-31 09:10:02 +00:00
{
2018-12-23 02:17:01 +00:00
INT32 spc = 1 ; // used if nodenum[1] is a space.
2018-12-18 00:02:22 +00:00
char * nodenum = ( char * ) malloc ( 3 ) ;
2018-12-22 03:28:28 +00:00
const char * newmsg ;
2018-12-18 00:02:22 +00:00
2018-07-31 09:10:02 +00:00
// what we're gonna do now is check if the node exists
// with that logic, characters 4 and 5 are our numbers:
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
// teamtalk can't send PMs, just don't send it, else everyone would be able to see it, and no one wants to see your sex RP sicko.
if ( teamtalk )
{
2018-12-17 19:43:59 +00:00
HU_AddChatText ( va ( " %sCannot send sayto in Say-Team. " , " \x85 " ) , false ) ;
2018-07-31 09:10:02 +00:00
return ;
2018-12-17 19:43:59 +00:00
}
2018-12-18 02:31:00 +00:00
strncpy ( nodenum , msg + 3 , 3 ) ;
2018-07-31 09:10:02 +00:00
// check for undesirable characters in our "number"
if ( ( ( nodenum [ 0 ] < ' 0 ' ) | | ( nodenum [ 0 ] > ' 9 ' ) ) | | ( ( nodenum [ 1 ] < ' 0 ' ) | | ( nodenum [ 1 ] > ' 9 ' ) ) )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
// check if nodenum[1] is a space
if ( nodenum [ 1 ] = = ' ' )
spc = 0 ;
// let it slide
else
2018-12-17 19:43:59 +00:00
{
HU_AddChatText ( " \x82 NOTICE: \x80 Invalid command format. Correct format is \' /pm<node> \' . " , false ) ;
2018-12-22 11:34:17 +00:00
free ( nodenum ) ;
2018-07-31 09:10:02 +00:00
return ;
2018-12-17 19:43:59 +00:00
}
2018-07-31 09:10:02 +00:00
}
// I'm very bad at C, I swear I am, additional checks eww!
if ( spc ! = 0 )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
if ( msg [ 5 ] ! = ' ' )
{
2018-12-17 19:43:59 +00:00
HU_AddChatText ( " \x82 NOTICE: \x80 Invalid command format. Correct format is \' /pm<node> \' . " , false ) ;
2018-12-22 11:34:17 +00:00
free ( nodenum ) ;
2018-07-31 09:10:02 +00:00
return ;
}
}
2018-12-17 19:43:59 +00:00
2018-12-23 02:17:01 +00:00
target = atoi ( ( const char * ) nodenum ) ; // turn that into a number
2018-12-22 11:34:17 +00:00
free ( nodenum ) ;
2018-07-31 09:10:02 +00:00
//CONS_Printf("%d\n", target);
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
// check for target player, if it doesn't exist then we can't send the message!
2018-12-23 07:25:58 +00:00
if ( target < MAXPLAYERS & & playeringame [ target ] ) // player exists
2018-12-23 02:17:01 +00:00
target + + ; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
2018-07-31 09:10:02 +00:00
else
{
2018-12-23 02:17:01 +00:00
HU_AddChatText ( va ( " \x82 NOTICE: \x80 Player %d does not exist. " , target ) , false ) ; // same
2018-07-31 09:10:02 +00:00
return ;
}
2018-12-18 00:02:22 +00:00
2018-07-31 09:10:02 +00:00
// we need to get rid of the /pm<node>
2018-12-22 03:28:28 +00:00
newmsg = msg + 5 + spc ;
2018-12-24 23:37:54 +00:00
strlcpy ( msg , newmsg , 255 ) ;
2018-12-17 19:43:59 +00:00
}
2014-03-15 16:59:03 +00:00
if ( ci > 3 ) // don't send target+flags+empty message.
{
if ( teamtalk )
buf [ 0 ] = - 1 ; // target
else
2018-07-31 09:10:02 +00:00
buf [ 0 ] = target ;
2018-12-17 19:43:59 +00:00
2014-03-15 16:59:03 +00:00
buf [ 1 ] = 0 ; // flags
SendNetXCmd ( XD_SAY , buf , 2 + strlen ( & buf [ 2 ] ) + 1 ) ;
}
return ;
}
}
2018-12-26 21:20:17 +00:00
# endif
2014-03-15 16:59:03 +00:00
void HU_clearChatChars ( void )
{
2018-07-31 09:10:02 +00:00
size_t i = 0 ;
for ( ; i < HU_MAXMSGLEN ; i + + )
2018-12-23 02:17:01 +00:00
w_chat [ i ] = 0 ; // reset this.
2014-03-15 16:59:03 +00:00
chat_on = false ;
2018-07-31 09:10:02 +00:00
c_input = 0 ;
2014-03-15 16:59:03 +00:00
}
2018-12-26 21:20:17 +00:00
# ifndef NONET
2018-07-31 09:10:02 +00:00
static boolean justscrolleddown ;
static boolean justscrolledup ;
2018-12-23 02:17:01 +00:00
static INT16 typelines = 1 ; // number of drawfill lines we need when drawing the chat. it's some weird hack and might be one frame off but I'm lazy to make another loop.
2018-12-17 19:43:59 +00:00
// It's up here since it has to be reset when we open the chat.
2018-12-26 21:20:17 +00:00
# endif
2018-07-31 09:10:02 +00:00
2014-03-15 16:59:03 +00:00
//
// Returns true if key eaten
//
boolean HU_Responder ( event_t * ev )
{
2019-05-13 05:51:36 +00:00
# ifndef NONET
2018-12-17 19:43:59 +00:00
INT32 c = 0 ;
2019-05-13 05:51:36 +00:00
# endif
2014-03-15 16:59:03 +00:00
if ( ev - > type ! = ev_keydown )
return false ;
// only KeyDown events now...
2018-12-17 19:43:59 +00:00
/*// Shoot, to prevent P1 chatting from ruining the game for everyone else, it's either:
// A. completely disallow opening chat entirely in online splitscreen
// or B. iterate through all controls to make sure it's bound to player 1 before eating
// You can see which one I chose.
// (Unless if you're sharing a keyboard, since you probably establish when you start chatting that you have dibs on it...)
// (Ahhh, the good ol days when I was a kid who couldn't afford an extra USB controller...)
if ( ev - > data1 > = KEY_MOUSE1 )
{
INT32 i ;
for ( i = 0 ; i < num_gamecontrols ; i + + )
{
if ( gamecontrol [ i ] [ 0 ] = = ev - > data1 | | gamecontrol [ i ] [ 1 ] = = ev - > data1 )
break ;
}
if ( i = = num_gamecontrols )
return false ;
} */ //We don't actually care about that unless we get splitscreen netgames. :V
2019-05-13 05:51:36 +00:00
# ifndef NONET
2018-12-17 19:43:59 +00:00
c = ( INT32 ) ev - > data1 ;
2014-03-15 16:59:03 +00:00
if ( ! chat_on )
{
// enter chat mode
if ( ( ev - > data1 = = gamecontrol [ gc_talkkey ] [ 0 ] | | ev - > data1 = = gamecontrol [ gc_talkkey ] [ 1 ] )
2018-12-23 02:17:01 +00:00
& & netgame & & ! OLD_MUTE ) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise.
2014-03-15 16:59:03 +00:00
{
chat_on = true ;
w_chat [ 0 ] = 0 ;
teamtalk = false ;
2018-07-31 09:10:02 +00:00
chat_scrollmedown = true ;
2018-12-17 19:43:59 +00:00
typelines = 1 ;
2014-03-15 16:59:03 +00:00
return true ;
}
if ( ( ev - > data1 = = gamecontrol [ gc_teamkey ] [ 0 ] | | ev - > data1 = = gamecontrol [ gc_teamkey ] [ 1 ] )
2018-12-17 19:43:59 +00:00
& & netgame & & ! OLD_MUTE )
{
2014-03-15 16:59:03 +00:00
chat_on = true ;
w_chat [ 0 ] = 0 ;
2018-12-23 02:17:01 +00:00
teamtalk = G_GametypeHasTeams ( ) ; // Don't teamtalk if we don't have teams.
2018-07-31 09:10:02 +00:00
chat_scrollmedown = true ;
2018-12-17 19:43:59 +00:00
typelines = 1 ;
2014-03-15 16:59:03 +00:00
return true ;
}
}
else // if chat_on
{
2018-12-17 19:43:59 +00:00
2016-12-27 05:32:35 +00:00
// Ignore modifier keys
// Note that we do this here so users can still set
// their chat keys to one of these, if they so desire.
if ( ev - > data1 = = KEY_LSHIFT | | ev - > data1 = = KEY_RSHIFT
| | ev - > data1 = = KEY_LCTRL | | ev - > data1 = = KEY_RCTRL
| | ev - > data1 = = KEY_LALT | | ev - > data1 = = KEY_RALT )
2014-03-15 16:59:03 +00:00
return true ;
2016-12-27 05:32:35 +00:00
2018-12-17 19:43:59 +00:00
c = ( INT32 ) ev - > data1 ;
2018-08-19 09:25:20 +00:00
2018-12-17 19:43:59 +00:00
// I know this looks very messy but this works. If it ain't broke, don't fix it!
// shift LETTERS to uppercase if we have capslock or are holding shift
if ( ( c > = ' a ' & & c < = ' z ' ) | | ( c > = ' A ' & & c < = ' Z ' ) )
2018-08-19 09:25:20 +00:00
{
2018-12-17 19:43:59 +00:00
if ( shiftdown ^ capslock )
2018-08-19 09:25:20 +00:00
c = shiftxform [ c ] ;
}
2018-12-17 19:43:59 +00:00
else // if we're holding shift we should still shift non letter symbols
{
if ( shiftdown )
c = shiftxform [ c ] ;
}
2018-07-31 09:10:02 +00:00
// pasting. pasting is cool. chat is a bit limited, though :(
2018-12-17 19:43:59 +00:00
if ( ( ( c = = ' v ' | | c = = ' V ' ) & & ctrldown ) & & ! CHAT_MUTE )
2018-07-31 09:10:02 +00:00
{
const char * paste = I_ClipboardPaste ( ) ;
2018-12-17 19:43:59 +00:00
size_t chatlen ;
size_t pastelen ;
2018-07-31 09:10:02 +00:00
// create a dummy string real quickly
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
if ( paste = = NULL )
return true ;
2018-12-17 19:43:59 +00:00
chatlen = strlen ( w_chat ) ;
pastelen = strlen ( paste ) ;
2018-07-31 09:10:02 +00:00
if ( chatlen + pastelen > HU_MAXMSGLEN )
return true ; // we can't paste this!!
2018-12-17 19:43:59 +00:00
2018-12-23 02:17:01 +00:00
if ( c_input > = strlen ( w_chat ) ) // add it at the end of the string.
2018-07-31 09:10:02 +00:00
{
2018-12-23 02:17:01 +00:00
memcpy ( & w_chat [ chatlen ] , paste , pastelen ) ; // copy all of that.
2018-07-31 09:10:02 +00:00
c_input + = pastelen ;
/*size_t i = 0;
for ( ; i < pastelen ; i + + )
{
2018-12-23 02:17:01 +00:00
HU_queueChatChar ( paste [ i ] ) ; // queue it so that it's actually sent. (this chat write thing is REALLY messy.)
2018-07-31 09:10:02 +00:00
} */
return true ;
}
else // otherwise, we need to shift everything and make space, etc etc
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
size_t i = HU_MAXMSGLEN - 1 ;
2018-12-23 01:17:50 +00:00
while ( i > = c_input )
2018-07-31 09:10:02 +00:00
{
if ( w_chat [ i ] )
w_chat [ i + pastelen ] = w_chat [ i ] ;
2018-12-23 01:17:50 +00:00
if ( i = = 0 ) // prevent overflow
break ;
i - - ;
2018-07-31 09:10:02 +00:00
}
2018-12-23 02:17:01 +00:00
memcpy ( & w_chat [ c_input ] , paste , pastelen ) ; // copy all of that.
2018-07-31 09:10:02 +00:00
c_input + = pastelen ;
return true ;
}
}
2018-12-17 19:43:59 +00:00
if ( ! CHAT_MUTE & & HU_keyInChatString ( w_chat , c ) )
{
2014-03-15 16:59:03 +00:00
HU_queueChatChar ( c ) ;
2018-12-17 19:43:59 +00:00
}
2014-03-15 16:59:03 +00:00
if ( c = = KEY_ENTER )
2018-12-17 19:43:59 +00:00
{
2014-03-15 16:59:03 +00:00
chat_on = false ;
2018-12-23 02:17:01 +00:00
c_input = 0 ; // reset input cursor
2018-07-31 09:10:02 +00:00
chat_scrollmedown = true ; // you hit enter, so you might wanna autoscroll to see what you just sent. :)
2018-12-17 19:43:59 +00:00
}
else if ( c = = KEY_ESCAPE
| | ( ( c = = gamecontrol [ gc_talkkey ] [ 0 ] | | c = = gamecontrol [ gc_talkkey ] [ 1 ]
| | c = = gamecontrol [ gc_teamkey ] [ 0 ] | | c = = gamecontrol [ gc_teamkey ] [ 1 ] )
& & c > = KEY_MOUSE1 ) ) // If it's not a keyboard key, then the chat button is used as a toggle.
{
2014-03-15 16:59:03 +00:00
chat_on = false ;
2018-12-23 02:17:01 +00:00
c_input = 0 ; // reset input cursor
2018-12-17 19:43:59 +00:00
}
2018-12-23 02:17:01 +00:00
else if ( ( c = = KEY_UPARROW | | c = = KEY_MOUSEWHEELUP ) & & chat_scroll > 0 & & ! OLDCHAT ) // CHAT SCROLLING YAYS!
2018-07-31 09:10:02 +00:00
{
chat_scroll - - ;
justscrolledup = true ;
chat_scrolltime = 4 ;
2018-12-17 19:43:59 +00:00
}
else if ( ( c = = KEY_DOWNARROW | | c = = KEY_MOUSEWHEELDOWN ) & & chat_scroll < chat_maxscroll & & chat_maxscroll > 0 & & ! OLDCHAT )
{
2018-07-31 09:10:02 +00:00
chat_scroll + + ;
justscrolleddown = true ;
chat_scrolltime = 4 ;
}
2018-12-23 02:17:01 +00:00
else if ( c = = KEY_LEFTARROW & & c_input ! = 0 & & ! OLDCHAT ) // i said go back
2018-07-31 09:10:02 +00:00
c_input - - ;
2018-12-23 02:17:01 +00:00
else if ( c = = KEY_RIGHTARROW & & c_input < strlen ( w_chat ) & & ! OLDCHAT ) // don't need to check for admin or w/e here since the chat won't ever contain anything if it's muted.
2018-12-17 19:43:59 +00:00
c_input + + ;
2014-03-15 16:59:03 +00:00
return true ;
}
2018-12-26 21:20:17 +00:00
# endif
2014-03-15 16:59:03 +00:00
return false ;
}
2018-12-17 19:43:59 +00:00
2014-03-15 16:59:03 +00:00
//======================================================================
// HEADS UP DRAWING
//======================================================================
2018-12-26 21:20:17 +00:00
# ifndef NONET
2018-07-31 09:10:02 +00:00
// Precompile a wordwrapped string to any given width.
// This is a muuuch better method than V_WORDWRAP.
// again stolen and modified a bit from video.c, don't mind me, will need to rearrange this one day.
// this one is simplified for the chat drawer.
2018-12-17 23:55:21 +00:00
static char * CHAT_WordWrap ( INT32 x , INT32 w , INT32 option , const char * string )
2018-07-31 09:10:02 +00:00
{
2018-12-18 00:09:05 +00:00
INT32 c ;
2018-07-31 09:10:02 +00:00
size_t chw , i , lastusablespace = 0 ;
2018-12-17 19:43:59 +00:00
size_t slen ;
2018-07-31 09:10:02 +00:00
char * newstring = Z_StrDup ( string ) ;
INT32 spacewidth = ( vid . width < 640 ) ? 8 : 4 , charwidth = ( vid . width < 640 ) ? 8 : 4 ;
slen = strlen ( string ) ;
x = 0 ;
for ( i = 0 ; i < slen ; + + i )
{
c = newstring [ i ] ;
if ( ( UINT8 ) c > = 0x80 & & ( UINT8 ) c < = 0x89 ) //color parsing! -Inuyasha 2.16.09
continue ;
if ( c = = ' \n ' )
{
x = 0 ;
lastusablespace = 0 ;
continue ;
}
if ( ! ( option & V_ALLOWLOWERCASE ) )
c = toupper ( c ) ;
c - = HU_FONTSTART ;
if ( c < 0 | | c > = HU_FONTSIZE | | ! hu_font [ c ] )
{
chw = spacewidth ;
lastusablespace = i ;
}
else
chw = charwidth ;
x + = chw ;
if ( lastusablespace ! = 0 & & x > w )
{
//CONS_Printf("Wrap at index %d\n", i);
newstring [ lastusablespace ] = ' \n ' ;
2018-12-17 19:43:59 +00:00
i = lastusablespace + 1 ;
2018-07-31 09:10:02 +00:00
lastusablespace = 0 ;
x = 0 ;
}
}
return newstring ;
}
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
// 30/7/18: chaty is now the distance at which the lowest point of the chat will be drawn if that makes any sense.
2019-06-20 14:34:27 +00:00
INT16 chatx = 13 , chaty = 169 ; // let's use this as our coordinates
2018-07-31 09:10:02 +00:00
// chat stuff by VincyTM LOL XD!
// HU_DrawMiniChat
static void HU_drawMiniChat ( void )
{
2018-08-19 08:58:29 +00:00
INT32 x = chatx + 2 ;
INT32 charwidth = 4 , charheight = 6 ;
2018-12-17 19:43:59 +00:00
INT32 boxw = cv_chatwidth . value ;
2018-07-31 09:10:02 +00:00
INT32 dx = 0 , dy = 0 ;
size_t i = chat_nummsg_min ;
2018-12-23 02:17:01 +00:00
boolean prev_linereturn = false ; // a hack to prevent double \n while I have no idea why they happen in the first place.
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
INT32 msglines = 0 ;
// process all messages once without rendering anything or doing anything fancy so that we know how many lines each message has...
2018-12-17 19:43:59 +00:00
INT32 y ;
if ( ! chat_nummsg_min )
2018-12-23 02:17:01 +00:00
return ; // needless to say it's useless to do anything if we don't have anything to draw.
2018-12-17 19:43:59 +00:00
/*if (splitscreen > 1)
boxw = max ( 64 , boxw / 2 ) ; */
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
for ( ; i > 0 ; i - - )
2018-08-19 08:58:29 +00:00
{
2018-12-17 19:43:59 +00:00
const char * msg = CHAT_WordWrap ( x + 2 , boxw - ( charwidth * 2 ) , V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_ALLOWLOWERCASE , chat_mini [ i - 1 ] ) ;
2018-07-31 09:10:02 +00:00
size_t j = 0 ;
INT32 linescount = 0 ;
2018-08-19 08:58:29 +00:00
2018-12-23 02:17:01 +00:00
while ( msg [ j ] ) // iterate through msg
2018-08-19 08:58:29 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] < HU_FONTSTART ) // don't draw
2018-08-19 08:58:29 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] = = ' \n ' ) // get back down.
2018-07-31 09:10:02 +00:00
{
+ + j ;
2018-08-19 08:58:29 +00:00
if ( ! prev_linereturn )
2018-12-17 19:43:59 +00:00
{
2018-08-19 08:58:29 +00:00
linescount + = 1 ;
dx = 0 ;
2018-12-17 19:43:59 +00:00
}
2018-08-19 08:58:29 +00:00
prev_linereturn = true ;
2018-07-31 09:10:02 +00:00
continue ;
}
else if ( msg [ j ] & 0x80 ) // stolen from video.c, nice.
{
+ + j ;
continue ;
}
2018-08-19 08:58:29 +00:00
+ + j ;
2018-07-31 09:10:02 +00:00
}
else
{
j + + ;
}
2018-08-19 08:58:29 +00:00
prev_linereturn = false ;
2018-07-31 09:10:02 +00:00
dx + = charwidth ;
2018-12-17 19:43:59 +00:00
if ( dx > = boxw )
2018-07-31 09:10:02 +00:00
{
dx = 0 ;
linescount + = 1 ;
}
}
dy = 0 ;
dx = 0 ;
msglines + = linescount + 1 ;
}
2018-08-19 08:58:29 +00:00
2018-12-17 19:43:59 +00:00
y = chaty - charheight * ( msglines + 1 ) ;
2018-12-17 20:31:31 +00:00
/*if (splitscreen)
2018-12-17 19:43:59 +00:00
{
y - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
y + = 16 ;
2018-12-17 20:31:31 +00:00
} */
2018-12-17 19:43:59 +00:00
2018-08-19 08:58:29 +00:00
dx = 0 ;
2018-07-31 09:10:02 +00:00
dy = 0 ;
i = 0 ;
2018-08-19 08:58:29 +00:00
prev_linereturn = false ;
2018-12-23 02:17:01 +00:00
for ( ; i < = ( chat_nummsg_min - 1 ) ; i + + ) // iterate through our hot messages
2018-07-31 09:10:02 +00:00
{
INT32 clrflag = 0 ;
2018-12-23 02:17:01 +00:00
INT32 timer = ( ( cv_chattime . value * TICRATE ) - chat_timers [ i ] ) - cv_chattime . value * TICRATE + 9 ; // see below...
INT32 transflag = ( timer > = 0 & & timer < = 9 ) ? ( timer * V_10TRANS ) : 0 ; // you can make bad jokes out of this one.
2018-07-31 09:10:02 +00:00
size_t j = 0 ;
2018-12-23 02:17:01 +00:00
const char * msg = CHAT_WordWrap ( x + 2 , boxw - ( charwidth * 2 ) , V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_ALLOWLOWERCASE , chat_mini [ i ] ) ; // get the current message, and word wrap it.
2018-12-17 19:43:59 +00:00
UINT8 * colormap = NULL ;
2018-08-19 08:58:29 +00:00
2018-12-23 02:17:01 +00:00
while ( msg [ j ] ) // iterate through msg
2018-08-19 08:58:29 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] < HU_FONTSTART ) // don't draw
2018-08-19 08:58:29 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] = = ' \n ' ) // get back down.
2018-07-31 09:10:02 +00:00
{
+ + j ;
2018-08-19 08:58:29 +00:00
if ( ! prev_linereturn )
2018-12-17 19:43:59 +00:00
{
2018-08-19 08:58:29 +00:00
dy + = charheight ;
dx = 0 ;
2018-12-17 19:43:59 +00:00
}
2018-08-19 08:58:29 +00:00
prev_linereturn = true ;
2018-07-31 09:10:02 +00:00
continue ;
}
else if ( msg [ j ] & 0x80 ) // stolen from video.c, nice.
{
clrflag = ( ( msg [ j ] & 0x7f ) < < V_CHARCOLORSHIFT ) & V_CHARCOLORMASK ;
2018-12-17 19:43:59 +00:00
colormap = V_GetStringColormap ( clrflag ) ;
2018-07-31 09:10:02 +00:00
+ + j ;
continue ;
}
2018-08-19 08:58:29 +00:00
+ + j ;
2018-07-31 09:10:02 +00:00
}
else
{
2018-12-23 02:17:01 +00:00
if ( cv_chatbacktint . value ) // on request of wolfy
2018-08-19 08:58:29 +00:00
V_DrawFillConsoleMap ( x + dx + 2 , y + dy , charwidth , charheight , 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT ) ;
2018-07-31 13:57:19 +00:00
V_DrawChatCharacter ( x + dx + 2 , y + dy , msg [ j + + ] | V_SNAPTOBOTTOM | V_SNAPTOLEFT | transflag , ! cv_allcaps . value , colormap ) ;
2018-07-31 09:10:02 +00:00
}
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
dx + = charwidth ;
2018-08-19 08:58:29 +00:00
prev_linereturn = false ;
2018-12-17 19:43:59 +00:00
if ( dx > = boxw )
2018-07-31 09:10:02 +00:00
{
dx = 0 ;
dy + = charheight ;
}
}
dy + = charheight ;
dx = 0 ;
}
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
// decrement addy and make that shit smooth:
addy / = 2 ;
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
}
// HU_DrawChatLog
static void HU_drawChatLog ( INT32 offset )
{
2018-12-17 19:43:59 +00:00
INT32 charwidth = 4 , charheight = 6 ;
INT32 boxw = cv_chatwidth . value , boxh = cv_chatheight . value ;
INT32 x = chatx + 2 , y , dx = 0 , dy = 0 ;
UINT32 i = 0 ;
INT32 chat_topy , chat_bottomy ;
boolean atbottom = false ;
2018-08-19 08:58:29 +00:00
2018-12-17 19:43:59 +00:00
// make sure that our scroll position isn't "illegal";
2018-07-31 09:10:02 +00:00
if ( chat_scroll > chat_maxscroll )
chat_scroll = chat_maxscroll ;
2018-08-19 08:58:29 +00:00
2018-12-23 01:52:32 +00:00
# ifdef NETSPLITSCREEN
if ( splitscreen )
2018-12-17 19:43:59 +00:00
{
boxh = max ( 6 , boxh / 2 ) ;
if ( splitscreen > 1 )
boxw = max ( 64 , boxw / 2 ) ;
2018-12-23 01:52:32 +00:00
}
# endif
2018-12-17 19:43:59 +00:00
y = chaty - offset * charheight - ( chat_scroll * charheight ) - boxh * charheight - 12 ;
2018-12-23 01:52:32 +00:00
# ifdef NETSPLITSCREEN
if ( splitscreen )
2018-12-17 19:43:59 +00:00
{
y - = BASEVIDHEIGHT / 2 ;
2019-06-18 17:51:24 +00:00
//if (splitscreen > 1)
//y += 16;
2018-12-23 01:52:32 +00:00
}
# endif
2018-12-17 19:43:59 +00:00
chat_topy = y + chat_scroll * charheight ;
chat_bottomy = chat_topy + boxh * charheight ;
2018-12-23 02:17:01 +00:00
V_DrawFillConsoleMap ( chatx , chat_topy , boxw , boxh * charheight + 2 , 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT ) ; // log box
2018-08-19 08:58:29 +00:00
2018-12-23 02:17:01 +00:00
for ( i = 0 ; i < chat_nummsg_log ; i + + ) // iterate through our chatlog
2018-07-31 09:10:02 +00:00
{
INT32 clrflag = 0 ;
2018-08-19 08:58:29 +00:00
INT32 j = 0 ;
2018-12-23 02:17:01 +00:00
const char * msg = CHAT_WordWrap ( x + 2 , boxw - ( charwidth * 2 ) , V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_ALLOWLOWERCASE , chat_log [ i ] ) ; // get the current message, and word wrap it.
2018-12-17 19:43:59 +00:00
UINT8 * colormap = NULL ;
2018-12-23 02:17:01 +00:00
while ( msg [ j ] ) // iterate through msg
2018-08-19 08:58:29 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] < HU_FONTSTART ) // don't draw
2018-08-19 08:58:29 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] = = ' \n ' ) // get back down.
2018-07-31 09:10:02 +00:00
{
+ + j ;
dy + = charheight ;
dx = 0 ;
continue ;
}
else if ( msg [ j ] & 0x80 ) // stolen from video.c, nice.
{
clrflag = ( ( msg [ j ] & 0x7f ) < < V_CHARCOLORSHIFT ) & V_CHARCOLORMASK ;
2018-12-17 19:43:59 +00:00
colormap = V_GetStringColormap ( clrflag ) ;
2018-07-31 09:10:02 +00:00
+ + j ;
continue ;
}
2018-08-19 08:58:29 +00:00
+ + j ;
2018-07-31 09:10:02 +00:00
}
else
{
if ( ( y + dy + 2 > = chat_topy ) & & ( y + dy < ( chat_bottomy ) ) )
2018-07-31 13:57:19 +00:00
V_DrawChatCharacter ( x + dx + 2 , y + dy + 2 , msg [ j + + ] | V_SNAPTOBOTTOM | V_SNAPTOLEFT , ! cv_allcaps . value , colormap ) ;
2018-07-31 09:10:02 +00:00
else
2018-12-23 02:17:01 +00:00
j + + ; // don't forget to increment this or we'll get stuck in the limbo.
2018-07-31 09:10:02 +00:00
}
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
dx + = charwidth ;
2018-12-17 19:43:59 +00:00
if ( dx > = boxw - charwidth - 2 & & i < chat_nummsg_log & & msg [ j ] > = HU_FONTSTART ) // end of message shouldn't count, nor should invisible characters!!!!
2018-07-31 09:10:02 +00:00
{
dx = 0 ;
dy + = charheight ;
}
}
dy + = charheight ;
dx = 0 ;
}
2018-08-19 08:58:29 +00:00
2018-12-17 19:43:59 +00:00
2018-12-23 02:17:01 +00:00
if ( ( ( chat_scroll > = chat_maxscroll ) | | ( chat_scrollmedown ) ) & & ! ( justscrolleddown | | justscrolledup | | chat_scrolltime ) ) // was already at the bottom of the page before new maxscroll calculation and was NOT scrolling.
2018-07-31 09:10:02 +00:00
{
2018-12-23 02:17:01 +00:00
atbottom = true ; // we should scroll
2018-07-31 09:10:02 +00:00
}
chat_scrollmedown = false ;
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
// getmaxscroll through a lazy hack. We do all these loops, so let's not do more loops that are gonna lag the game more. :P
2018-12-23 02:17:01 +00:00
chat_maxscroll = ( dy / charheight ) ; // welcome to C, we don't know what min() and max() are.
2018-08-19 08:58:29 +00:00
if ( chat_maxscroll < = ( UINT32 ) cv_chatheight . value )
2018-07-31 09:10:02 +00:00
chat_maxscroll = 0 ;
2018-08-19 08:58:29 +00:00
else
chat_maxscroll - = cv_chatheight . value ;
2018-07-31 09:10:02 +00:00
// if we're not bound by the time, autoscroll for next frame:
if ( atbottom )
chat_scroll = chat_maxscroll ;
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
// draw arrows to indicate that we can (or not) scroll.
2019-01-08 03:34:48 +00:00
// account for Y = -1 offset in tinyfont
2018-07-31 09:10:02 +00:00
if ( chat_scroll > 0 )
2019-01-08 03:34:48 +00:00
V_DrawThinString ( chatx - 8 , ( ( justscrolledup ) ? ( chat_topy - 1 ) : ( chat_topy ) ) - 1 , V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_YELLOWMAP , " \x1A " ) ; // up arrow
2018-07-31 09:10:02 +00:00
if ( chat_scroll < chat_maxscroll )
2019-01-08 03:34:48 +00:00
V_DrawThinString ( chatx - 8 , chat_bottomy - ( ( justscrolleddown ) ? 5 : 6 ) - 1 , V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_YELLOWMAP , " \x1B " ) ; // down arrow
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
justscrolleddown = false ;
justscrolledup = false ;
2018-08-19 08:58:29 +00:00
}
2018-07-31 09:10:02 +00:00
2014-03-15 16:59:03 +00:00
//
// HU_DrawChat
//
// Draw chat input
//
2018-07-31 09:10:02 +00:00
2014-03-15 16:59:03 +00:00
static void HU_DrawChat ( void )
2018-08-19 08:58:29 +00:00
{
2018-07-31 13:57:19 +00:00
INT32 charwidth = 4 , charheight = 6 ;
2018-12-17 19:43:59 +00:00
INT32 boxw = cv_chatwidth . value ;
2018-07-31 13:57:19 +00:00
INT32 t = 0 , c = 0 , y = chaty - ( typelines * charheight ) ;
2018-12-23 02:17:01 +00:00
UINT32 i = 0 , saylen = strlen ( w_chat ) ; // You learn new things everyday!
2018-12-17 19:43:59 +00:00
INT32 cflag = 0 ;
2018-07-31 09:10:02 +00:00
const char * ntalk = " Say: " , * ttalk = " Team: " ;
const char * talk = ntalk ;
2018-12-17 19:43:59 +00:00
const char * mute = " Chat has been muted. " ;
2018-12-23 01:52:32 +00:00
# ifdef NETSPLITSCREEN
if ( splitscreen )
2018-12-17 19:43:59 +00:00
{
y - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
{
y + = 16 ;
boxw = max ( 64 , boxw / 2 ) ;
}
2018-12-23 01:52:32 +00:00
}
# endif
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
if ( teamtalk )
{
talk = ttalk ;
#if 0
if ( players [ consoleplayer ] . ctfteam = = 1 )
t = 0x500 ; // Red
else if ( players [ consoleplayer ] . ctfteam = = 2 )
t = 0x400 ; // Blue
# endif
}
2018-08-19 08:58:29 +00:00
2018-12-17 19:43:59 +00:00
if ( CHAT_MUTE )
{
talk = mute ;
typelines = 1 ;
2018-12-23 02:17:01 +00:00
cflag = V_GRAYMAP ; // set text in gray if chat is muted.
2018-12-17 19:43:59 +00:00
}
V_DrawFillConsoleMap ( chatx , y - 1 , boxw , ( typelines * charheight ) , 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT ) ;
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
while ( talk [ i ] )
{
if ( talk [ i ] < HU_FONTSTART )
+ + i ;
else
2018-12-17 19:43:59 +00:00
{
V_DrawChatCharacter ( chatx + c + 2 , y , talk [ i ] | V_SNAPTOBOTTOM | V_SNAPTOLEFT | cflag , ! cv_allcaps . value , V_GetStringColormap ( talk [ i ] | cflag ) ) ;
i + + ;
}
2018-07-31 09:10:02 +00:00
c + = charwidth ;
}
2018-08-19 08:58:29 +00:00
2018-12-17 19:43:59 +00:00
// if chat is muted, just draw the log and get it over with, no need to draw anything else.
if ( CHAT_MUTE )
{
HU_drawChatLog ( 0 ) ;
return ;
}
2018-07-31 09:10:02 +00:00
i = 0 ;
typelines = 1 ;
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
if ( ( strlen ( w_chat ) = = 0 | | c_input = = 0 ) & & hu_tick < 4 )
2018-07-31 13:57:19 +00:00
V_DrawChatCharacter ( chatx + 2 + c , y + 1 , ' _ ' | V_SNAPTOBOTTOM | V_SNAPTOLEFT | t , ! cv_allcaps . value , NULL ) ;
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
while ( w_chat [ i ] )
{
2018-07-31 13:57:19 +00:00
boolean skippedline = false ;
if ( c_input = = ( i + 1 ) )
2018-07-31 09:10:02 +00:00
{
2018-12-23 02:17:01 +00:00
INT32 cursorx = ( c + charwidth < boxw - charwidth ) ? ( chatx + 2 + c + charwidth ) : ( chatx + 1 ) ; // we may have to go down.
2018-12-18 00:09:05 +00:00
INT32 cursory = ( cursorx ! = chatx + 1 ) ? ( y ) : ( y + charheight ) ;
2018-07-31 13:57:19 +00:00
if ( hu_tick < 4 )
2018-08-19 08:58:29 +00:00
V_DrawChatCharacter ( cursorx , cursory + 1 , ' _ ' | V_SNAPTOBOTTOM | V_SNAPTOLEFT | t , ! cv_allcaps . value , NULL ) ;
2018-12-23 02:17:01 +00:00
if ( cursorx = = chatx + 1 & & saylen = = i ) // a weirdo hack
2018-07-31 13:57:19 +00:00
{
typelines + = 1 ;
skippedline = true ;
2018-08-19 08:58:29 +00:00
}
}
2018-07-31 09:10:02 +00:00
//Hurdler: isn't it better like that?
if ( w_chat [ i ] < HU_FONTSTART )
+ + i ;
else
2018-07-31 13:57:19 +00:00
V_DrawChatCharacter ( chatx + c + 2 , y , w_chat [ i + + ] | V_SNAPTOBOTTOM | V_SNAPTOLEFT | t , ! cv_allcaps . value , NULL ) ;
2018-07-31 09:10:02 +00:00
c + = charwidth ;
2018-12-17 19:43:59 +00:00
if ( c > boxw - ( charwidth * 2 ) & & ! skippedline )
2018-07-31 09:10:02 +00:00
{
c = 0 ;
y + = charheight ;
typelines + = 1 ;
}
}
2018-12-17 19:43:59 +00:00
// handle /pm list. It's messy, horrible and I don't care.
2018-12-23 02:17:01 +00:00
if ( strnicmp ( w_chat , " /pm " , 3 ) = = 0 & & vid . width > = 400 & & ! teamtalk ) // 320x200 unsupported kthxbai
2018-08-19 08:58:29 +00:00
{
INT32 count = 0 ;
2018-07-31 09:10:02 +00:00
INT32 p_dispy = chaty - charheight - 1 ;
2018-12-23 01:52:32 +00:00
# ifdef NETSPLITSCREEN
if ( splitscreen )
2018-12-17 19:43:59 +00:00
{
p_dispy - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
p_dispy + = 16 ;
2018-12-23 01:52:32 +00:00
}
# endif
2018-12-17 19:43:59 +00:00
i = 0 ;
2018-07-31 09:10:02 +00:00
for ( i = 0 ; ( i < MAXPLAYERS ) ; i + + )
{
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
// filter: (code needs optimization pls help I'm bad with C)
if ( w_chat [ 3 ] )
{
2018-12-17 19:43:59 +00:00
char * nodenum ;
UINT32 n ;
2018-07-31 09:10:02 +00:00
// right, that's half important: (w_chat[4] may be a space since /pm0 msg is perfectly acceptable!)
if ( ( ( ( w_chat [ 3 ] ! = 0 ) & & ( ( w_chat [ 3 ] < ' 0 ' ) | | ( w_chat [ 3 ] > ' 9 ' ) ) ) | | ( ( w_chat [ 4 ] ! = 0 ) & & ( ( ( w_chat [ 4 ] < ' 0 ' ) | | ( w_chat [ 4 ] > ' 9 ' ) ) ) ) ) & & ( w_chat [ 4 ] ! = ' ' ) )
break ;
2018-08-19 08:58:29 +00:00
2018-12-17 19:43:59 +00:00
nodenum = ( char * ) malloc ( 3 ) ;
2018-12-18 02:31:00 +00:00
strncpy ( nodenum , w_chat + 3 , 3 ) ;
2018-12-23 02:17:01 +00:00
n = atoi ( ( const char * ) nodenum ) ; // turn that into a number
2018-12-23 01:35:12 +00:00
free ( nodenum ) ;
2018-07-31 09:10:02 +00:00
// special cases:
2018-08-19 08:58:29 +00:00
2018-07-31 09:10:02 +00:00
if ( ( n = = 0 ) & & ! ( w_chat [ 4 ] = = ' 0 ' ) )
2018-08-19 08:58:29 +00:00
{
2018-07-31 09:10:02 +00:00
if ( ! ( i < 10 ) )
2018-08-19 08:58:29 +00:00
continue ;
2018-07-31 09:10:02 +00:00
}
else if ( ( n = = 1 ) & & ! ( w_chat [ 3 ] = = ' 0 ' ) )
2018-08-19 08:58:29 +00:00
{
2018-07-31 09:10:02 +00:00
if ( ! ( ( i = = 1 ) | | ( ( i > = 10 ) & & ( i < = 19 ) ) ) )
continue ;
}
else if ( ( n = = 2 ) & & ! ( w_chat [ 3 ] = = ' 0 ' ) )
2018-08-19 08:58:29 +00:00
{
2018-07-31 09:10:02 +00:00
if ( ! ( ( i = = 2 ) | | ( ( i > = 20 ) & & ( i < = 29 ) ) ) )
continue ;
}
else if ( ( n = = 3 ) & & ! ( w_chat [ 3 ] = = ' 0 ' ) )
2018-08-19 08:58:29 +00:00
{
2018-07-31 09:10:02 +00:00
if ( ! ( ( i = = 3 ) | | ( ( i > = 30 ) & & ( i < = 31 ) ) ) )
continue ;
}
else // general case.
2018-08-19 08:58:29 +00:00
{
2018-07-31 09:10:02 +00:00
if ( i ! = n )
continue ;
}
}
2018-08-19 08:58:29 +00:00
2018-07-31 13:57:19 +00:00
if ( playeringame [ i ] )
2018-08-19 08:58:29 +00:00
{
2018-07-31 09:10:02 +00:00
char name [ MAXPLAYERNAME + 1 ] ;
2018-12-23 02:17:01 +00:00
strlcpy ( name , player_names [ i ] , 7 ) ; // shorten name to 7 characters.
V_DrawFillConsoleMap ( chatx + boxw + 2 , p_dispy - ( 6 * count ) , 48 , 6 , 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT ) ; // fill it like the chat so the text doesn't become hard to read because of the hud.
2018-12-17 19:43:59 +00:00
V_DrawSmallString ( chatx + boxw + 4 , p_dispy - ( 6 * count ) , V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_ALLOWLOWERCASE , va ( " \x82 %d \x80 - %s " , i , name ) ) ;
2018-07-31 09:10:02 +00:00
count + + ;
}
2014-03-15 16:59:03 +00:00
}
2018-12-23 02:17:01 +00:00
if ( count = = 0 ) // no results.
2014-03-15 16:59:03 +00:00
{
2018-12-23 02:17:01 +00:00
V_DrawFillConsoleMap ( chatx + boxw + 2 , p_dispy - ( 6 * count ) , 48 , 6 , 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT ) ; // fill it like the chat so the text doesn't become hard to read because of the hud.
2018-12-22 11:34:17 +00:00
V_DrawSmallString ( chatx + boxw + 4 , p_dispy - ( 6 * count ) , V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_ALLOWLOWERCASE , " NO RESULT. " ) ;
2014-03-15 16:59:03 +00:00
}
}
2018-12-23 02:17:01 +00:00
HU_drawChatLog ( typelines - 1 ) ; // typelines is the # of lines we're typing. If there's more than 1 then the log should scroll up to give us more space.
2014-03-15 16:59:03 +00:00
}
2018-12-17 19:43:59 +00:00
// For anyone who, for some godforsaken reason, likes oldchat.
2018-07-31 09:10:02 +00:00
static void HU_DrawChat_Old ( void )
2014-03-15 16:59:03 +00:00
{
INT32 t = 0 , c = 0 , y = HU_INPUTY ;
size_t i = 0 ;
const char * ntalk = " Say: " , * ttalk = " Say-Team: " ;
const char * talk = ntalk ;
INT32 charwidth = 8 * con_scalefactor ; //SHORT(hu_font['A'-HU_FONTSTART]->width) * con_scalefactor;
INT32 charheight = 8 * con_scalefactor ; //SHORT(hu_font['A'-HU_FONTSTART]->height) * con_scalefactor;
if ( teamtalk )
{
talk = ttalk ;
#if 0
if ( players [ consoleplayer ] . ctfteam = = 1 )
t = 0x500 ; // Red
else if ( players [ consoleplayer ] . ctfteam = = 2 )
t = 0x400 ; // Blue
# endif
}
while ( talk [ i ] )
{
if ( talk [ i ] < HU_FONTSTART )
{
+ + i ;
//charwidth = 4 * con_scalefactor;
}
else
{
//charwidth = SHORT(hu_font[talk[i]-HU_FONTSTART]->width) * con_scalefactor;
2017-04-14 19:43:30 +00:00
V_DrawCharacter ( HU_INPUTX + c , y , talk [ i + + ] | cv_constextsize . value | V_NOSCALESTART , true ) ;
2014-03-15 16:59:03 +00:00
}
c + = charwidth ;
}
2018-07-31 09:10:02 +00:00
if ( ( strlen ( w_chat ) = = 0 | | c_input = = 0 ) & & hu_tick < 4 )
V_DrawCharacter ( HU_INPUTX + c , y + 2 * con_scalefactor , ' _ ' | cv_constextsize . value | V_NOSCALESTART | t , ! cv_allcaps . value ) ;
2018-12-17 19:43:59 +00:00
2014-03-15 16:59:03 +00:00
i = 0 ;
while ( w_chat [ i ] )
{
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
if ( c_input = = ( i + 1 ) & & hu_tick < 4 )
{
2018-12-23 02:17:01 +00:00
INT32 cursorx = ( HU_INPUTX + c + charwidth < vid . width ) ? ( HU_INPUTX + c + charwidth ) : ( HU_INPUTX ) ; // we may have to go down.
2018-12-18 00:09:05 +00:00
INT32 cursory = ( cursorx ! = HU_INPUTX ) ? ( y ) : ( y + charheight ) ;
2018-12-17 19:43:59 +00:00
V_DrawCharacter ( cursorx , cursory + 2 * con_scalefactor , ' _ ' | cv_constextsize . value | V_NOSCALESTART | t , ! cv_allcaps . value ) ;
}
2014-03-15 16:59:03 +00:00
//Hurdler: isn't it better like that?
if ( w_chat [ i ] < HU_FONTSTART )
{
+ + i ;
//charwidth = 4 * con_scalefactor;
}
else
{
//charwidth = SHORT(hu_font[w_chat[i]-HU_FONTSTART]->width) * con_scalefactor;
2017-04-14 19:43:30 +00:00
V_DrawCharacter ( HU_INPUTX + c , y , w_chat [ i + + ] | cv_constextsize . value | V_NOSCALESTART | t , true ) ;
2014-03-15 16:59:03 +00:00
}
c + = charwidth ;
if ( c > = vid . width )
{
c = 0 ;
y + = charheight ;
}
}
if ( hu_tick < 4 )
2017-04-14 19:43:30 +00:00
V_DrawCharacter ( HU_INPUTX + c , y , ' _ ' | cv_constextsize . value | V_NOSCALESTART | t , true ) ;
2014-03-15 16:59:03 +00:00
}
2018-12-26 21:20:17 +00:00
# endif
2014-03-15 16:59:03 +00:00
// draw the Crosshair, at the exact center of the view.
//
// Crosshairs are pre-cached at HU_Init
static inline void HU_DrawCrosshair ( void )
{
INT32 i , y ;
i = cv_crosshair . value & 3 ;
if ( ! i )
return ;
if ( ( netgame | | multiplayer ) & & players [ displayplayer ] . spectator )
return ;
# ifdef HWRENDER
if ( rendermode ! = render_soft )
y = ( INT32 ) gr_basewindowcentery ;
else
# endif
y = viewwindowy + ( viewheight > > 1 ) ;
2014-03-21 18:42:55 +00:00
V_DrawScaledPatch ( vid . width > > 1 , y , V_NOSCALESTART | V_OFFSET | V_TRANSLUCENT , crosshair [ i - 1 ] ) ;
2014-03-15 16:59:03 +00:00
}
static inline void HU_DrawCrosshair2 ( void )
{
INT32 i , y ;
i = cv_crosshair2 . value & 3 ;
if ( ! i )
return ;
if ( ( netgame | | multiplayer ) & & players [ secondarydisplayplayer ] . spectator )
return ;
# ifdef HWRENDER
if ( rendermode ! = render_soft )
y = ( INT32 ) gr_basewindowcentery ;
else
# endif
y = viewwindowy + ( viewheight > > 1 ) ;
if ( splitscreen )
{
# ifdef HWRENDER
if ( rendermode ! = render_soft )
y + = ( INT32 ) gr_viewheight ;
else
# endif
y + = viewheight ;
2014-03-21 18:42:55 +00:00
V_DrawScaledPatch ( vid . width > > 1 , y , V_NOSCALESTART | V_OFFSET | V_TRANSLUCENT , crosshair [ i - 1 ] ) ;
2014-03-15 16:59:03 +00:00
}
}
static void HU_DrawCEcho ( void )
{
INT32 i = 0 ;
INT32 y = ( BASEVIDHEIGHT / 2 ) - 4 ;
INT32 pnumlines = 0 ;
2018-03-30 15:39:59 +00:00
UINT32 realflags = cechoflags | V_PERPLAYER ; // requested as part of splitscreen's stuff
2014-03-15 16:59:03 +00:00
INT32 realalpha = ( INT32 ) ( ( cechoflags & V_ALPHAMASK ) > > V_ALPHASHIFT ) ;
char * line ;
char * echoptr ;
char temp [ 1024 ] ;
for ( i = 0 ; cechotext [ i ] ! = ' \0 ' ; + + i )
if ( cechotext [ i ] = = ' \\ ' )
pnumlines + + ;
y - = ( pnumlines - 1 ) * ( ( realflags & V_RETURN8 ) ? 4 : 6 ) ;
// Prevent crashing because I'm sick of this
if ( y < 0 )
{
CONS_Alert ( CONS_WARNING , " CEcho contained too many lines, not displaying \n " ) ;
cechotimer = 0 ;
return ;
}
// Automatic fadeout
if ( realflags & V_AUTOFADEOUT )
{
UINT32 tempalpha = ( UINT32 ) max ( ( INT32 ) ( 10 - cechotimer ) , realalpha ) ;
realflags & = ~ V_ALPHASHIFT ;
realflags | = ( tempalpha < < V_ALPHASHIFT ) ;
}
strcpy ( temp , cechotext ) ;
echoptr = & temp [ 0 ] ;
while ( * echoptr ! = ' \0 ' )
{
line = strchr ( echoptr , ' \\ ' ) ;
if ( line = = NULL )
break ;
* line = ' \0 ' ;
V_DrawCenteredString ( BASEVIDWIDTH / 2 , y , realflags , echoptr ) ;
2018-03-30 15:39:59 +00:00
if ( splitscreen )
{
stplyr = ( ( stplyr = = & players [ displayplayer ] ) ? & players [ secondarydisplayplayer ] : & players [ displayplayer ] ) ;
V_DrawCenteredString ( BASEVIDWIDTH / 2 , y , realflags , echoptr ) ;
stplyr = ( ( stplyr = = & players [ displayplayer ] ) ? & players [ secondarydisplayplayer ] : & players [ displayplayer ] ) ;
}
2014-03-15 16:59:03 +00:00
y + = ( ( realflags & V_RETURN8 ) ? 8 : 12 ) ;
echoptr = line ;
echoptr + + ;
}
- - cechotimer ;
}
static void HU_drawGametype ( void )
{
2017-06-25 16:28:07 +00:00
const char * strvalue = NULL ;
2017-11-03 03:13:13 +00:00
2017-06-25 16:28:07 +00:00
if ( gametype < 0 | | gametype > = NUMGAMETYPES )
return ; // not a valid gametype???
2014-03-15 16:59:03 +00:00
2017-06-25 16:28:07 +00:00
strvalue = Gametype_Names [ gametype ] ;
2017-11-03 03:13:13 +00:00
2017-06-25 16:28:07 +00:00
if ( splitscreen )
V_DrawString ( 4 , 184 , 0 , strvalue ) ;
else
V_DrawString ( 4 , 192 , 0 , strvalue ) ;
2014-03-15 16:59:03 +00:00
}
//
// demo info stuff
//
UINT32 hu_demoscore ;
UINT32 hu_demotime ;
UINT16 hu_demorings ;
static void HU_DrawDemoInfo ( void )
{
2018-06-13 16:52:23 +00:00
INT32 h = 188 ;
if ( modeattacking = = ATTACKING_NIGHTS )
h - = 12 ;
V_DrawString ( 4 , h - 24 , V_YELLOWMAP | V_ALLOWLOWERCASE , va ( M_GetText ( " %s's replay " ) , player_names [ 0 ] ) ) ;
2014-11-12 00:55:07 +00:00
if ( modeattacking )
{
2018-06-13 16:52:23 +00:00
V_DrawString ( 4 , h - 16 , V_YELLOWMAP | V_MONOSPACE , " SCORE: " ) ;
V_DrawRightAlignedString ( 120 , h - 16 , V_MONOSPACE , va ( " %d " , hu_demoscore ) ) ;
2014-11-12 00:55:07 +00:00
2018-06-13 16:52:23 +00:00
V_DrawString ( 4 , h - 8 , V_YELLOWMAP | V_MONOSPACE , " TIME: " ) ;
2014-11-12 00:55:07 +00:00
if ( hu_demotime ! = UINT32_MAX )
2018-06-13 16:52:23 +00:00
V_DrawRightAlignedString ( 120 , h - 8 , V_MONOSPACE , va ( " %i:%02i.%02i " ,
2014-11-12 00:55:07 +00:00
G_TicsToMinutes ( hu_demotime , true ) ,
G_TicsToSeconds ( hu_demotime ) ,
G_TicsToCentiseconds ( hu_demotime ) ) ) ;
else
2018-06-13 16:52:23 +00:00
V_DrawRightAlignedString ( 120 , h - 8 , V_MONOSPACE , " --:--.-- " ) ;
2014-03-15 16:59:03 +00:00
2014-11-12 00:55:07 +00:00
if ( modeattacking = = ATTACKING_RECORD )
{
2018-06-13 16:52:23 +00:00
V_DrawString ( 4 , h , V_YELLOWMAP | V_MONOSPACE , " RINGS: " ) ;
V_DrawRightAlignedString ( 120 , h , V_MONOSPACE , va ( " %d " , hu_demorings ) ) ;
2014-11-12 00:55:07 +00:00
}
}
2014-03-15 16:59:03 +00:00
}
// Heads up displays drawer, call each frame
//
void HU_Drawer ( void )
{
2018-12-26 21:20:17 +00:00
# ifndef NONET
2014-03-15 16:59:03 +00:00
// draw chat string plus cursor
if ( chat_on )
2018-12-17 19:43:59 +00:00
{
// count down the scroll timer.
2018-07-31 09:10:02 +00:00
if ( chat_scrolltime > 0 )
chat_scrolltime - - ;
2018-08-19 09:12:21 +00:00
if ( ! OLDCHAT )
2018-07-31 09:10:02 +00:00
HU_DrawChat ( ) ;
else
2018-12-23 02:17:01 +00:00
HU_DrawChat_Old ( ) ;
2018-07-31 09:10:02 +00:00
}
else
{
2018-12-17 19:43:59 +00:00
typelines = 1 ;
chat_scrolltime = 0 ;
2018-12-23 09:50:36 +00:00
if ( ! OLDCHAT & & cv_consolechat . value < 2 & & netgame ) // Don't display minimized chat if you set the mode to Window (Hidden)
2018-12-23 02:17:01 +00:00
HU_drawMiniChat ( ) ; // draw messages in a cool fashion.
2018-07-31 09:10:02 +00:00
}
2018-12-23 02:17:01 +00:00
if ( netgame ) // would handle that in hu_drawminichat, but it's actually kinda awkward when you're typing a lot of messages. (only handle that in netgames duh)
2018-07-31 09:10:02 +00:00
{
size_t i = 0 ;
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
// handle spam while we're at it:
for ( ; ( i < MAXPLAYERS ) ; i + + )
2018-12-17 19:43:59 +00:00
{
2018-12-23 02:17:01 +00:00
if ( stop_spamming [ i ] > 0 )
stop_spamming [ i ] - - ;
2018-12-17 19:43:59 +00:00
}
2018-07-31 09:10:02 +00:00
// handle chat timers
for ( i = 0 ; ( i < chat_nummsg_min ) ; i + + )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
if ( chat_timers [ i ] > 0 )
chat_timers [ i ] - - ;
else
HU_removeChatText_Mini ( ) ;
}
}
2018-12-26 21:20:17 +00:00
# endif
2014-03-15 16:59:03 +00:00
if ( cechotimer )
HU_DrawCEcho ( ) ;
if ( demoplayback & & hu_showscores )
HU_DrawDemoInfo ( ) ;
if ( ! Playing ( )
| | gamestate = = GS_INTERMISSION | | gamestate = = GS_CUTSCENE
| | gamestate = = GS_CREDITS | | gamestate = = GS_EVALUATION
| | gamestate = = GS_GAMEEND )
return ;
// draw multiplayer rankings
if ( hu_showscores )
{
if ( netgame | | multiplayer )
{
# ifdef HAVE_BLUA
if ( LUA_HudEnabled ( hud_rankings ) )
# endif
HU_DrawRankings ( ) ;
if ( gametype = = GT_COOP )
HU_DrawNetplayCoopOverlay ( ) ;
}
else
HU_DrawCoopOverlay ( ) ;
# ifdef HAVE_BLUA
LUAh_ScoresHUD ( ) ;
# endif
}
if ( gamestate ! = GS_LEVEL )
return ;
// draw the crosshair, not when viewing demos nor with chasecam
if ( ! automapactive & & cv_crosshair . value & & ! demoplayback & & ! camera . chase & & ! players [ displayplayer ] . spectator )
HU_DrawCrosshair ( ) ;
if ( ! automapactive & & cv_crosshair2 . value & & ! demoplayback & & ! camera2 . chase & & ! players [ secondarydisplayplayer ] . spectator )
HU_DrawCrosshair2 ( ) ;
2014-03-21 18:42:55 +00:00
// draw desynch text
if ( hu_resynching )
2017-01-13 19:53:52 +00:00
{
static UINT32 resynch_ticker = 0 ;
char resynch_text [ 14 ] ;
2017-01-13 20:30:30 +00:00
UINT32 i ;
2017-01-13 19:53:52 +00:00
// Animate the dots
resynch_ticker + + ;
strcpy ( resynch_text , " Resynching " ) ;
for ( i = 0 ; i < ( resynch_ticker / 16 ) % 4 ; i + + )
strcat ( resynch_text , " . " ) ;
V_DrawCenteredString ( BASEVIDWIDTH / 2 , 180 , V_YELLOWMAP | V_ALLOWLOWERCASE , resynch_text ) ;
}
2018-06-13 23:58:28 +00:00
2018-11-21 16:10:18 +00:00
if ( modeattacking & & pausedelay > 0 & & ! pausebreakkey )
2018-06-13 23:58:28 +00:00
{
2018-06-14 13:41:14 +00:00
INT32 strength = ( ( pausedelay - 1 - NEWTICRATE / 2 ) * 10 ) / ( NEWTICRATE / 3 ) ;
2018-06-13 23:58:28 +00:00
INT32 y = hudinfo [ HUD_LIVES ] . y - 13 ;
if ( players [ consoleplayer ] . powers [ pw_carry ] = = CR_NIGHTSMODE )
y - = 16 ;
else
{
if ( players [ consoleplayer ] . pflags & PF_AUTOBRAKE )
y - = 8 ;
if ( players [ consoleplayer ] . pflags & PF_ANALOGMODE )
y - = 8 ;
}
2018-06-14 13:41:14 +00:00
V_DrawThinString ( hudinfo [ HUD_LIVES ] . x - 2 , y ,
hudinfo [ HUD_LIVES ] . f | ( ( leveltime & 4 ) ? V_SKYMAP : V_BLUEMAP ) ,
" HOLD TO RETRY... " ) ;
if ( strength > 9 )
V_DrawFill ( 0 , 0 , BASEVIDWIDTH , BASEVIDHEIGHT , 0 ) ;
else if ( strength > 0 )
V_DrawFadeScreen ( 0 , strength ) ;
2018-06-13 23:58:28 +00:00
}
2014-03-15 16:59:03 +00:00
}
//======================================================================
// HUD MESSAGES CLEARING FROM SCREEN
//======================================================================
// Clear old messages from the borders around the view window
// (only for reduced view, refresh the borders when needed)
//
// startline: y coord to start clear,
// clearlines: how many lines to clear.
//
static INT32 oldclearlines ;
void HU_Erase ( void )
{
INT32 topline , bottomline ;
INT32 y , yoffset ;
# ifdef HWRENDER
// clear hud msgs on double buffer (OpenGL mode)
boolean secondframe ;
static INT32 secondframelines ;
# endif
if ( con_clearlines = = oldclearlines & & ! con_hudupdate & & ! chat_on )
return ;
# ifdef HWRENDER
// clear the other frame in double-buffer modes
secondframe = ( con_clearlines ! = oldclearlines ) ;
if ( secondframe )
secondframelines = oldclearlines ;
# endif
// clear the message lines that go away, so use _oldclearlines_
bottomline = oldclearlines ;
oldclearlines = con_clearlines ;
2018-08-19 09:12:21 +00:00
if ( chat_on & & OLDCHAT )
2014-03-15 16:59:03 +00:00
if ( bottomline < 8 )
2018-12-23 02:17:01 +00:00
bottomline = 8 ; // only do it for consolechat. consolechat is gay.
2014-03-15 16:59:03 +00:00
if ( automapactive | | viewwindowx = = 0 ) // hud msgs don't need to be cleared
return ;
// software mode copies view border pattern & beveled edges from the backbuffer
if ( rendermode = = render_soft )
{
topline = 0 ;
for ( y = topline , yoffset = y * vid . width ; y < bottomline ; y + + , yoffset + = vid . width )
{
if ( y < viewwindowy | | y > = viewwindowy + viewheight )
R_VideoErase ( yoffset , vid . width ) ; // erase entire line
else
{
R_VideoErase ( yoffset , viewwindowx ) ; // erase left border
// erase right border
R_VideoErase ( yoffset + viewwindowx + viewwidth , viewwindowx ) ;
}
}
con_hudupdate = false ; // if it was set..
}
# ifdef HWRENDER
else if ( rendermode ! = render_none )
{
// refresh just what is needed from the view borders
HWR_DrawViewBorder ( secondframelines ) ;
con_hudupdate = secondframe ;
}
# endif
}
//======================================================================
// IN-LEVEL MULTIPLAYER RANKINGS
//======================================================================
2017-10-04 16:45:03 +00:00
# define supercheckdef ((players[tab[i].num].powers[pw_super] && players[tab[i].num].mo && (players[tab[i].num].mo->state < &states[S_PLAY_SUPER_TRANS1] || players[tab[i].num].mo->state >= &states[S_PLAY_SUPER_TRANS6])) || (players[tab[i].num].powers[pw_carry] == CR_NIGHTSMODE && skins[players[tab[i].num].skin].flags & SF_SUPER))
2019-06-20 14:34:27 +00:00
# define greycheckdef (players[tab[i].num].spectator || players[tab[i].num].playerstate == PST_DEAD || (G_IsSpecialStage(gamemap) && players[tab[i].num].exiting))
2017-07-07 12:35:48 +00:00
2018-07-31 09:10:02 +00:00
//
// HU_drawPing
//
void HU_drawPing ( INT32 x , INT32 y , INT32 ping , boolean notext )
{
2018-12-23 02:17:01 +00:00
UINT8 numbars = 1 ; // how many ping bars do we draw?
2019-06-20 14:34:27 +00:00
UINT8 barcolor = 35 ; // color we use for the bars (green, yellow or red)
2018-07-31 09:10:02 +00:00
SINT8 i = 0 ;
SINT8 yoffset = 6 ;
2018-12-18 00:02:22 +00:00
INT32 dx = x + 1 - ( V_SmallStringWidth ( va ( " %dms " , ping ) , V_ALLOWLOWERCASE ) / 2 ) ;
2018-07-31 09:10:02 +00:00
if ( ping < 128 )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
numbars = 3 ;
2019-06-20 14:34:27 +00:00
barcolor = 112 ;
2018-12-17 19:43:59 +00:00
}
2018-07-31 09:10:02 +00:00
else if ( ping < 256 )
2018-12-17 19:43:59 +00:00
{
2018-12-23 02:17:01 +00:00
numbars = 2 ; // Apparently ternaries w/ multiple statements don't look good in C so I decided against it.
2019-06-20 14:34:27 +00:00
barcolor = 73 ;
2018-12-17 19:43:59 +00:00
}
2018-12-23 02:17:01 +00:00
if ( ! notext | | vid . width > = 640 ) // how sad, we're using a shit resolution.
2018-07-31 09:10:02 +00:00
V_DrawSmallString ( dx , y + 4 , V_ALLOWLOWERCASE , va ( " %dms " , ping ) ) ;
2018-12-17 19:43:59 +00:00
2018-12-23 02:17:01 +00:00
for ( i = 0 ; ( i < 3 ) ; i + + ) // Draw the ping bar
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
V_DrawFill ( x + 2 * ( i - 1 ) , y + yoffset - 4 , 2 , 8 - yoffset , 31 ) ;
if ( i < numbars )
V_DrawFill ( x + 2 * ( i - 1 ) , y + yoffset - 3 , 1 , 8 - yoffset - 1 , barcolor ) ;
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
yoffset - = 2 ;
}
2018-12-17 19:43:59 +00:00
}
2018-07-31 09:10:02 +00:00
2014-03-15 16:59:03 +00:00
//
// HU_DrawTabRankings
//
void HU_DrawTabRankings ( INT32 x , INT32 y , playersort_t * tab , INT32 scorelines , INT32 whiteplayer )
{
INT32 i ;
const UINT8 * colormap ;
2017-07-07 12:35:48 +00:00
boolean greycheck , supercheck ;
2014-03-15 16:59:03 +00:00
//this function is designed for 9 or less score lines only
I_Assert ( scorelines < = 9 ) ;
V_DrawFill ( 1 , 26 , 318 , 1 , 0 ) ; //Draw a horizontal line because it looks nice!
for ( i = 0 ; i < scorelines ; i + + )
{
2017-07-07 12:35:48 +00:00
if ( players [ tab [ i ] . num ] . spectator & & gametype ! = GT_COOP )
2014-03-15 16:59:03 +00:00
continue ; //ignore them.
2017-07-07 12:35:48 +00:00
greycheck = greycheckdef ;
supercheck = supercheckdef ;
2018-12-23 02:17:01 +00:00
if ( ! splitscreen ) // don't draw it on splitscreen,
2018-07-31 09:10:02 +00:00
{
if ( ! ( tab [ i ] . num = = serverplayer ) )
HU_drawPing ( x + 253 , y + 2 , playerpingtable [ tab [ i ] . num ] , false ) ;
//else
2018-12-17 19:43:59 +00:00
// V_DrawSmallString(x+ 246, y+4, V_YELLOWMAP, "SERVER");
}
2014-03-18 17:56:54 +00:00
V_DrawString ( x + 20 , y ,
2014-03-15 16:59:03 +00:00
( ( tab [ i ] . num = = whiteplayer ) ? V_YELLOWMAP : 0 )
2017-07-07 12:35:48 +00:00
| ( greycheck ? V_60TRANS : 0 )
2014-03-15 16:59:03 +00:00
| V_ALLOWLOWERCASE , tab [ i ] . name ) ;
// Draw emeralds
2019-06-18 17:51:24 +00:00
if ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] & & ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] = = players [ tab [ i ] . num ] . powers [ pw_sneakers ] ) & & ( ( leveltime / 7 ) & 1 ) )
HU_DrawEmeralds ( x - 12 , y + 2 , 255 ) ;
else if ( ! players [ tab [ i ] . num ] . powers [ pw_super ]
2014-03-15 16:59:03 +00:00
| | ( ( leveltime / 7 ) & 1 ) )
{
HU_DrawEmeralds ( x - 12 , y + 2 , tab [ i ] . emeralds ) ;
}
2017-07-07 12:35:48 +00:00
if ( greycheck )
2014-03-15 16:59:03 +00:00
V_DrawSmallTranslucentPatch ( x , y - 4 , V_80TRANS , livesback ) ;
else
V_DrawSmallScaledPatch ( x , y - 4 , 0 , livesback ) ;
if ( tab [ i ] . color = = 0 )
{
colormap = colormaps ;
2017-07-07 12:35:48 +00:00
if ( supercheck )
2014-03-15 16:59:03 +00:00
V_DrawSmallScaledPatch ( x , y - 4 , 0 , superprefix [ players [ tab [ i ] . num ] . skin ] ) ;
else
{
2017-07-07 12:35:48 +00:00
if ( greycheck )
2014-03-15 16:59:03 +00:00
V_DrawSmallTranslucentPatch ( x , y - 4 , V_80TRANS , faceprefix [ players [ tab [ i ] . num ] . skin ] ) ;
else
V_DrawSmallScaledPatch ( x , y - 4 , 0 , faceprefix [ players [ tab [ i ] . num ] . skin ] ) ;
}
}
else
{
2017-07-07 12:35:48 +00:00
if ( supercheck )
2014-03-15 16:59:03 +00:00
{
2017-03-11 17:14:39 +00:00
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo - > color , GTC_CACHE ) ;
2014-03-15 16:59:03 +00:00
V_DrawSmallMappedPatch ( x , y - 4 , 0 , superprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
else
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
2017-07-07 12:35:48 +00:00
if ( greycheck )
2014-03-15 16:59:03 +00:00
V_DrawSmallTranslucentMappedPatch ( x , y - 4 , V_80TRANS , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
else
V_DrawSmallMappedPatch ( x , y - 4 , 0 , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
}
2018-08-23 19:09:39 +00:00
if ( G_GametypeUsesLives ( ) & & ! ( gametype = = GT_COOP & & ( cv_cooplives . value = = 0 | | cv_cooplives . value = = 3 ) ) & & ( players [ tab [ i ] . num ] . lives ! = INFLIVES ) ) //show lives
2017-07-07 12:35:48 +00:00
V_DrawRightAlignedString ( x , y + 4 , V_ALLOWLOWERCASE | ( greycheck ? V_60TRANS : 0 ) , va ( " %dx " , players [ tab [ i ] . num ] . lives ) ) ;
2014-03-15 16:59:03 +00:00
else if ( G_TagGametype ( ) & & players [ tab [ i ] . num ] . pflags & PF_TAGIT )
{
2017-07-07 12:35:48 +00:00
if ( greycheck )
2014-03-15 16:59:03 +00:00
V_DrawSmallTranslucentPatch ( x - 32 , y - 4 , V_60TRANS , tagico ) ;
else
V_DrawSmallScaledPatch ( x - 32 , y - 4 , 0 , tagico ) ;
}
2017-07-08 10:41:20 +00:00
if ( players [ tab [ i ] . num ] . exiting )
V_DrawSmallScaledPatch ( x - SHORT ( exiticon - > width ) / 2 - 1 , y - 3 , 0 , exiticon ) ;
2014-03-15 16:59:03 +00:00
if ( gametype = = GT_RACE )
{
if ( circuitmap )
{
if ( players [ tab [ i ] . num ] . exiting )
V_DrawRightAlignedString ( x + 240 , y , 0 , va ( " %i:%02i.%02i " , G_TicsToMinutes ( players [ tab [ i ] . num ] . realtime , true ) , G_TicsToSeconds ( players [ tab [ i ] . num ] . realtime ) , G_TicsToCentiseconds ( players [ tab [ i ] . num ] . realtime ) ) ) ;
else
2017-07-07 12:35:48 +00:00
V_DrawRightAlignedString ( x + 240 , y , ( greycheck ? V_60TRANS : 0 ) , va ( " %u " , tab [ i ] . count ) ) ;
2014-03-15 16:59:03 +00:00
}
else
2017-07-07 12:35:48 +00:00
V_DrawRightAlignedString ( x + 240 , y , ( greycheck ? V_60TRANS : 0 ) , va ( " %i:%02i.%02i " , G_TicsToMinutes ( tab [ i ] . count , true ) , G_TicsToSeconds ( tab [ i ] . count ) , G_TicsToCentiseconds ( tab [ i ] . count ) ) ) ;
2014-03-15 16:59:03 +00:00
}
else
2017-07-07 12:35:48 +00:00
V_DrawRightAlignedString ( x + 240 , y , ( greycheck ? V_60TRANS : 0 ) , va ( " %u " , tab [ i ] . count ) ) ;
2014-03-15 16:59:03 +00:00
y + = 16 ;
}
}
//
2018-07-31 09:10:02 +00:00
// HU_Draw32Emeralds
2014-03-15 16:59:03 +00:00
//
2018-07-31 09:10:02 +00:00
static void HU_Draw32Emeralds ( INT32 x , INT32 y , INT32 pemeralds )
{
//Draw the emeralds, in the CORRECT order, using tiny emerald sprites.
if ( pemeralds & EMERALD1 )
2019-01-07 22:56:55 +00:00
V_DrawSmallScaledPatch ( x , y , 0 , emeraldpics [ 1 ] [ 0 ] ) ;
2018-07-31 09:10:02 +00:00
if ( pemeralds & EMERALD2 )
2019-01-07 22:56:55 +00:00
V_DrawSmallScaledPatch ( x + 4 , y , 0 , emeraldpics [ 1 ] [ 1 ] ) ;
2018-07-31 09:10:02 +00:00
if ( pemeralds & EMERALD3 )
2019-01-07 22:56:55 +00:00
V_DrawSmallScaledPatch ( x + 8 , y , 0 , emeraldpics [ 1 ] [ 2 ] ) ;
2018-07-31 09:10:02 +00:00
if ( pemeralds & EMERALD4 )
2019-01-07 22:56:55 +00:00
V_DrawSmallScaledPatch ( x + 12 , y , 0 , emeraldpics [ 1 ] [ 3 ] ) ;
2018-07-31 09:10:02 +00:00
if ( pemeralds & EMERALD5 )
2019-01-07 22:56:55 +00:00
V_DrawSmallScaledPatch ( x + 16 , y , 0 , emeraldpics [ 1 ] [ 4 ] ) ;
2018-07-31 09:10:02 +00:00
if ( pemeralds & EMERALD6 )
2019-01-07 22:56:55 +00:00
V_DrawSmallScaledPatch ( x + 20 , y , 0 , emeraldpics [ 1 ] [ 5 ] ) ;
2018-07-31 09:10:02 +00:00
if ( pemeralds & EMERALD7 )
2019-01-07 22:56:55 +00:00
V_DrawSmallScaledPatch ( x + 24 , y , 0 , emeraldpics [ 1 ] [ 6 ] ) ;
2018-07-31 09:10:02 +00:00
}
//
// HU_Draw32TeamTabRankings
//
static void HU_Draw32TeamTabRankings ( playersort_t * tab , INT32 whiteplayer )
2014-03-15 16:59:03 +00:00
{
INT32 i , x , y ;
INT32 redplayers = 0 , blueplayers = 0 ;
const UINT8 * colormap ;
char name [ MAXPLAYERNAME + 1 ] ;
2019-06-20 14:34:27 +00:00
boolean greycheck , supercheck ;
2014-03-15 16:59:03 +00:00
V_DrawFill ( 160 , 26 , 1 , 154 , 0 ) ; //Draw a vertical line to separate the two teams.
V_DrawFill ( 1 , 26 , 318 , 1 , 0 ) ; //And a horizontal line to make a T.
V_DrawFill ( 1 , 180 , 318 , 1 , 0 ) ; //And a horizontal line near the bottom.
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( players [ tab [ i ] . num ] . spectator )
continue ; //ignore them.
2019-06-20 14:34:27 +00:00
greycheck = greycheckdef ;
supercheck = supercheckdef ;
2018-07-31 09:10:02 +00:00
if ( tab [ i ] . color = = skincolor_redteam ) //red
{
redplayers + + ;
x = 14 + ( BASEVIDWIDTH / 2 ) ;
y = ( redplayers * 9 ) + 20 ;
}
else if ( tab [ i ] . color = = skincolor_blueteam ) //blue
{
blueplayers + + ;
x = 14 ;
y = ( blueplayers * 9 ) + 20 ;
}
else //er? not on red or blue, so ignore them
continue ;
2019-06-20 14:34:27 +00:00
greycheck = greycheckdef ;
supercheck = supercheckdef ;
2018-07-31 09:10:02 +00:00
strlcpy ( name , tab [ i ] . name , 8 ) ;
V_DrawString ( x + 10 , y ,
( ( tab [ i ] . num = = whiteplayer ) ? V_YELLOWMAP : 0 )
2019-06-20 14:34:27 +00:00
| ( greycheck ? 0 : V_TRANSLUCENT )
2018-07-31 09:10:02 +00:00
| V_ALLOWLOWERCASE , name ) ;
if ( gametype = = GT_CTF )
{
if ( players [ tab [ i ] . num ] . gotflag & GF_REDFLAG ) // Red
V_DrawFixedPatch ( ( x - 10 ) * FRACUNIT , ( y ) * FRACUNIT , FRACUNIT / 4 , 0 , rflagico , 0 ) ;
else if ( players [ tab [ i ] . num ] . gotflag & GF_BLUEFLAG ) // Blue
V_DrawFixedPatch ( ( x - 10 ) * FRACUNIT , ( y ) * FRACUNIT , FRACUNIT / 4 , 0 , bflagico , 0 ) ;
}
// Draw emeralds
2019-06-20 14:34:27 +00:00
if ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] & & ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] = = players [ tab [ i ] . num ] . powers [ pw_sneakers ] ) & & ( ( leveltime / 7 ) & 1 ) )
{
HU_Draw32Emeralds ( x + 60 , y + 2 , 255 ) ;
//HU_DrawEmeralds(x-12,y+2,255);
}
else if ( ! players [ tab [ i ] . num ] . powers [ pw_super ]
2018-07-31 09:10:02 +00:00
| | ( ( leveltime / 7 ) & 1 ) )
{
HU_Draw32Emeralds ( x + 60 , y + 2 , tab [ i ] . emeralds ) ;
2019-06-20 14:34:27 +00:00
//HU_DrawEmeralds(x-12,y+2,tab[i].emeralds);
2018-07-31 09:10:02 +00:00
}
2019-06-20 14:34:27 +00:00
if ( supercheck )
2018-07-31 09:10:02 +00:00
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
V_DrawFixedPatch ( x * FRACUNIT , y * FRACUNIT , FRACUNIT / 4 , 0 , superprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
else
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
2019-06-20 14:34:27 +00:00
if ( players [ tab [ i ] . num ] . spectator | | players [ tab [ i ] . num ] . playerstate = = PST_DEAD )
2018-07-31 09:10:02 +00:00
V_DrawFixedPatch ( x * FRACUNIT , y * FRACUNIT , FRACUNIT / 4 , V_HUDTRANSHALF , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
else
V_DrawFixedPatch ( x * FRACUNIT , y * FRACUNIT , FRACUNIT / 4 , 0 , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
2019-06-20 14:34:27 +00:00
V_DrawRightAlignedThinString ( x + 128 , y , ( ( players [ tab [ i ] . num ] . spectator | | players [ tab [ i ] . num ] . playerstate = = PST_DEAD ) ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
2018-07-31 09:10:02 +00:00
if ( ! splitscreen )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
if ( ! ( tab [ i ] . num = = serverplayer ) )
HU_drawPing ( x + 135 , y + 3 , playerpingtable [ tab [ i ] . num ] , true ) ;
//else
2018-12-17 19:43:59 +00:00
//V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST");
2018-07-31 09:10:02 +00:00
}
}
}
2014-03-15 16:59:03 +00:00
//
// HU_DrawTeamTabRankings
//
void HU_DrawTeamTabRankings ( playersort_t * tab , INT32 whiteplayer )
{
INT32 i , x , y ;
INT32 redplayers = 0 , blueplayers = 0 ;
2018-07-31 09:10:02 +00:00
boolean smol = false ;
2014-03-15 16:59:03 +00:00
const UINT8 * colormap ;
char name [ MAXPLAYERNAME + 1 ] ;
2017-07-07 12:35:48 +00:00
boolean greycheck , supercheck ;
2014-03-15 16:59:03 +00:00
2018-07-31 09:10:02 +00:00
// before we draw, we must count how many players are in each team. It makes an additional loop, but we need to know if we have to draw a big or a small ranking.
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( players [ tab [ i ] . num ] . spectator )
continue ; //ignore them.
2014-03-15 16:59:03 +00:00
2018-07-31 09:10:02 +00:00
if ( tab [ i ] . color = = skincolor_redteam ) //red
{
if ( redplayers + + > 8 )
{
smol = true ;
2018-12-23 02:17:01 +00:00
break ; // don't make more loops than we need to.
2018-12-17 19:43:59 +00:00
}
2018-07-31 09:10:02 +00:00
}
else if ( tab [ i ] . color = = skincolor_blueteam ) //blue
{
if ( blueplayers + + > 8 )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
smol = true ;
break ;
2018-12-17 19:43:59 +00:00
}
2018-07-31 09:10:02 +00:00
}
else //er? not on red or blue, so ignore them
continue ;
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
}
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
// I'll be blunt with you, this may add more lines, but I'm not adding weird cases for this, so we're executing a separate function.
if ( smol = = true | | cv_compactscoreboard . value )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
HU_Draw32TeamTabRankings ( tab , whiteplayer ) ;
return ;
2018-12-17 19:43:59 +00:00
}
2014-03-15 16:59:03 +00:00
V_DrawFill ( 160 , 26 , 1 , 154 , 0 ) ; //Draw a vertical line to separate the two teams.
V_DrawFill ( 1 , 26 , 318 , 1 , 0 ) ; //And a horizontal line to make a T.
V_DrawFill ( 1 , 180 , 318 , 1 , 0 ) ; //And a horizontal line near the bottom.
2018-07-31 09:10:02 +00:00
i = 0 , redplayers = 0 , blueplayers = 0 ;
2018-12-17 19:43:59 +00:00
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( players [ tab [ i ] . num ] . spectator )
continue ; //ignore them.
if ( tab [ i ] . color = = skincolor_redteam ) //red
{
if ( redplayers + + > 8 )
continue ;
x = 32 + ( BASEVIDWIDTH / 2 ) ;
y = ( redplayers * 16 ) + 16 ;
}
else if ( tab [ i ] . color = = skincolor_blueteam ) //blue
{
if ( blueplayers + + > 8 )
continue ;
x = 32 ;
y = ( blueplayers * 16 ) + 16 ;
}
else //er? not on red or blue, so ignore them
continue ;
2017-07-07 12:35:48 +00:00
greycheck = greycheckdef ;
supercheck = supercheckdef ;
2018-07-31 09:10:02 +00:00
strlcpy ( name , tab [ i ] . name , 7 ) ;
2014-03-18 17:56:54 +00:00
V_DrawString ( x + 20 , y ,
2014-03-15 16:59:03 +00:00
( ( tab [ i ] . num = = whiteplayer ) ? V_YELLOWMAP : 0 )
2017-07-07 12:35:48 +00:00
| ( greycheck ? V_TRANSLUCENT : 0 )
2014-03-15 16:59:03 +00:00
| V_ALLOWLOWERCASE , name ) ;
if ( gametype = = GT_CTF )
{
if ( players [ tab [ i ] . num ] . gotflag & GF_REDFLAG ) // Red
V_DrawSmallScaledPatch ( x - 28 , y - 4 , 0 , rflagico ) ;
else if ( players [ tab [ i ] . num ] . gotflag & GF_BLUEFLAG ) // Blue
V_DrawSmallScaledPatch ( x - 28 , y - 4 , 0 , bflagico ) ;
}
// Draw emeralds
2019-06-18 17:51:24 +00:00
if ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] & & ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] = = players [ tab [ i ] . num ] . powers [ pw_sneakers ] ) & & ( ( leveltime / 7 ) & 1 ) )
HU_DrawEmeralds ( x - 12 , y + 2 , 255 ) ;
else if ( ! players [ tab [ i ] . num ] . powers [ pw_super ]
2014-03-15 16:59:03 +00:00
| | ( ( leveltime / 7 ) & 1 ) )
{
HU_DrawEmeralds ( x - 12 , y + 2 , tab [ i ] . emeralds ) ;
}
2017-07-07 12:35:48 +00:00
if ( supercheck )
2014-03-15 16:59:03 +00:00
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
V_DrawSmallMappedPatch ( x , y - 4 , 0 , superprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
else
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
2017-07-07 12:35:48 +00:00
if ( greycheck )
V_DrawSmallTranslucentMappedPatch ( x , y - 4 , V_80TRANS , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
2014-03-15 16:59:03 +00:00
else
V_DrawSmallMappedPatch ( x , y - 4 , 0 , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
2019-01-02 06:09:15 +00:00
V_DrawRightAlignedThinString ( x + 100 , y , ( greycheck ? V_TRANSLUCENT : 0 ) , va ( " %u " , tab [ i ] . count ) ) ;
2018-07-31 09:10:02 +00:00
if ( ! splitscreen )
2018-12-17 19:43:59 +00:00
{
2018-07-31 09:10:02 +00:00
if ( ! ( tab [ i ] . num = = serverplayer ) )
HU_drawPing ( x + 113 , y + 2 , playerpingtable [ tab [ i ] . num ] , false ) ;
//else
2018-12-17 19:43:59 +00:00
// V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER");
2018-07-31 09:10:02 +00:00
}
2014-03-15 16:59:03 +00:00
}
}
//
// HU_DrawDualTabRankings
//
void HU_DrawDualTabRankings ( INT32 x , INT32 y , playersort_t * tab , INT32 scorelines , INT32 whiteplayer )
{
INT32 i ;
const UINT8 * colormap ;
char name [ MAXPLAYERNAME + 1 ] ;
2017-07-07 12:35:48 +00:00
boolean greycheck , supercheck ;
2014-03-15 16:59:03 +00:00
V_DrawFill ( 160 , 26 , 1 , 154 , 0 ) ; //Draw a vertical line to separate the two sides.
V_DrawFill ( 1 , 26 , 318 , 1 , 0 ) ; //And a horizontal line to make a T.
V_DrawFill ( 1 , 180 , 318 , 1 , 0 ) ; //And a horizontal line near the bottom.
for ( i = 0 ; i < scorelines ; i + + )
{
2017-07-07 12:35:48 +00:00
if ( players [ tab [ i ] . num ] . spectator & & gametype ! = GT_COOP )
2014-03-15 16:59:03 +00:00
continue ; //ignore them.
2017-07-07 12:35:48 +00:00
greycheck = greycheckdef ;
supercheck = supercheckdef ;
2018-07-31 09:10:02 +00:00
strlcpy ( name , tab [ i ] . name , 7 ) ;
if ( ! ( tab [ i ] . num = = serverplayer ) )
HU_drawPing ( x + 113 , y + 2 , playerpingtable [ tab [ i ] . num ] , false ) ;
//else
2018-12-17 19:43:59 +00:00
// V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER");
2014-03-18 17:56:54 +00:00
V_DrawString ( x + 20 , y ,
2014-03-15 16:59:03 +00:00
( ( tab [ i ] . num = = whiteplayer ) ? V_YELLOWMAP : 0 )
2017-07-07 12:35:48 +00:00
| ( greycheck ? V_TRANSLUCENT : 0 )
2014-03-15 16:59:03 +00:00
| V_ALLOWLOWERCASE , name ) ;
2018-08-23 19:09:39 +00:00
if ( G_GametypeUsesLives ( ) & & ! ( gametype = = GT_COOP & & ( cv_cooplives . value = = 0 | | cv_cooplives . value = = 3 ) ) & & ( players [ tab [ i ] . num ] . lives ! = INFLIVES ) ) //show lives
2014-03-15 16:59:03 +00:00
V_DrawRightAlignedString ( x , y + 4 , V_ALLOWLOWERCASE , va ( " %dx " , players [ tab [ i ] . num ] . lives ) ) ;
else if ( G_TagGametype ( ) & & players [ tab [ i ] . num ] . pflags & PF_TAGIT )
V_DrawSmallScaledPatch ( x - 28 , y - 4 , 0 , tagico ) ;
2017-07-08 10:41:20 +00:00
if ( players [ tab [ i ] . num ] . exiting )
V_DrawSmallScaledPatch ( x - SHORT ( exiticon - > width ) / 2 - 1 , y - 3 , 0 , exiticon ) ;
2014-03-15 16:59:03 +00:00
// Draw emeralds
2019-06-18 17:51:24 +00:00
if ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] & & ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] = = players [ tab [ i ] . num ] . powers [ pw_sneakers ] ) & & ( ( leveltime / 7 ) & 1 ) )
HU_DrawEmeralds ( x - 12 , y + 2 , 255 ) ;
else if ( ! players [ tab [ i ] . num ] . powers [ pw_super ]
2014-03-15 16:59:03 +00:00
| | ( ( leveltime / 7 ) & 1 ) )
{
HU_DrawEmeralds ( x - 12 , y + 2 , tab [ i ] . emeralds ) ;
}
//V_DrawSmallScaledPatch (x, y-4, 0, livesback);
if ( tab [ i ] . color = = 0 )
{
colormap = colormaps ;
2017-07-07 12:35:48 +00:00
if ( supercheck )
2014-03-15 16:59:03 +00:00
V_DrawSmallScaledPatch ( x , y - 4 , 0 , superprefix [ players [ tab [ i ] . num ] . skin ] ) ;
else
{
2017-07-07 12:35:48 +00:00
if ( greycheck )
V_DrawSmallTranslucentPatch ( x , y - 4 , V_80TRANS , faceprefix [ players [ tab [ i ] . num ] . skin ] ) ;
2014-03-15 16:59:03 +00:00
else
V_DrawSmallScaledPatch ( x , y - 4 , 0 , faceprefix [ players [ tab [ i ] . num ] . skin ] ) ;
}
}
else
{
2017-07-07 12:35:48 +00:00
if ( supercheck )
2014-03-15 16:59:03 +00:00
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
V_DrawSmallMappedPatch ( x , y - 4 , 0 , superprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
else
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
2017-07-07 12:35:48 +00:00
if ( greycheck )
V_DrawSmallTranslucentMappedPatch ( x , y - 4 , V_80TRANS , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
2014-03-15 16:59:03 +00:00
else
V_DrawSmallMappedPatch ( x , y - 4 , 0 , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
}
2014-03-18 17:56:54 +00:00
// All data drawn with thin string for space.
2014-03-15 16:59:03 +00:00
if ( gametype = = GT_RACE )
{
if ( circuitmap )
{
if ( players [ tab [ i ] . num ] . exiting )
2018-07-31 09:10:02 +00:00
V_DrawRightAlignedThinString ( x + 146 , y , 0 , va ( " %i:%02i.%02i " , G_TicsToMinutes ( players [ tab [ i ] . num ] . realtime , true ) , G_TicsToSeconds ( players [ tab [ i ] . num ] . realtime ) , G_TicsToCentiseconds ( players [ tab [ i ] . num ] . realtime ) ) ) ;
2014-03-15 16:59:03 +00:00
else
2019-01-02 06:09:15 +00:00
V_DrawRightAlignedThinString ( x + 146 , y , ( greycheck ? V_TRANSLUCENT : 0 ) , va ( " %u " , tab [ i ] . count ) ) ;
2014-03-15 16:59:03 +00:00
}
else
2019-01-02 06:09:15 +00:00
V_DrawRightAlignedThinString ( x + 146 , y , ( greycheck ? V_TRANSLUCENT : 0 ) , va ( " %i:%02i.%02i " , G_TicsToMinutes ( tab [ i ] . count , true ) , G_TicsToSeconds ( tab [ i ] . count ) , G_TicsToCentiseconds ( tab [ i ] . count ) ) ) ;
2014-03-15 16:59:03 +00:00
}
else
2019-01-02 06:09:15 +00:00
V_DrawRightAlignedThinString ( x + 100 , y , ( greycheck ? V_TRANSLUCENT : 0 ) , va ( " %u " , tab [ i ] . count ) ) ;
2014-03-15 16:59:03 +00:00
y + = 16 ;
if ( y > 160 )
{
y = 32 ;
x + = BASEVIDWIDTH / 2 ;
}
}
}
2018-07-31 09:10:02 +00:00
//
// HU_Draw32TabRankings
//
2018-12-17 23:55:21 +00:00
static void HU_Draw32TabRankings ( INT32 x , INT32 y , playersort_t * tab , INT32 scorelines , INT32 whiteplayer )
2018-07-31 09:10:02 +00:00
{
INT32 i ;
const UINT8 * colormap ;
char name [ MAXPLAYERNAME + 1 ] ;
2019-06-20 14:34:27 +00:00
boolean greycheck , supercheck ;
2018-07-31 09:10:02 +00:00
V_DrawFill ( 160 , 26 , 1 , 154 , 0 ) ; //Draw a vertical line to separate the two sides.
V_DrawFill ( 1 , 26 , 318 , 1 , 0 ) ; //And a horizontal line to make a T.
V_DrawFill ( 1 , 180 , 318 , 1 , 0 ) ; //And a horizontal line near the bottom.
for ( i = 0 ; i < scorelines ; i + + )
{
2019-06-20 14:34:27 +00:00
if ( players [ tab [ i ] . num ] . spectator & & gametype ! = GT_COOP )
2018-07-31 09:10:02 +00:00
continue ; //ignore them.
2019-06-20 14:34:27 +00:00
greycheck = greycheckdef ;
supercheck = supercheckdef ;
2018-07-31 09:10:02 +00:00
strlcpy ( name , tab [ i ] . name , 7 ) ;
2018-12-23 02:17:01 +00:00
if ( ! splitscreen ) // don't draw it on splitscreen,
2018-07-31 09:10:02 +00:00
{
if ( ! ( tab [ i ] . num = = serverplayer ) )
HU_drawPing ( x + 135 , y + 3 , playerpingtable [ tab [ i ] . num ] , true ) ;
//else
2018-12-17 19:43:59 +00:00
// V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST");
2018-07-31 09:10:02 +00:00
}
2018-12-17 19:43:59 +00:00
2018-07-31 09:10:02 +00:00
V_DrawString ( x + 10 , y ,
( ( tab [ i ] . num = = whiteplayer ) ? V_YELLOWMAP : 0 )
2019-06-20 14:34:27 +00:00
| ( greycheck ? 0 : V_TRANSLUCENT )
2018-07-31 09:10:02 +00:00
| V_ALLOWLOWERCASE , name ) ;
if ( G_GametypeUsesLives ( ) ) //show lives
V_DrawRightAlignedThinString ( x - 1 , y , V_ALLOWLOWERCASE , va ( " %d " , players [ tab [ i ] . num ] . lives ) ) ;
else if ( G_TagGametype ( ) & & players [ tab [ i ] . num ] . pflags & PF_TAGIT )
V_DrawFixedPatch ( ( x - 10 ) * FRACUNIT , ( y ) * FRACUNIT , FRACUNIT / 4 , 0 , tagico , 0 ) ;
// Draw emeralds
2019-06-20 14:34:27 +00:00
if ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] & & ( players [ tab [ i ] . num ] . powers [ pw_invulnerability ] = = players [ tab [ i ] . num ] . powers [ pw_sneakers ] ) & & ( ( leveltime / 7 ) & 1 ) )
2019-06-18 17:51:24 +00:00
{
HU_Draw32Emeralds ( x + 60 , y + 2 , 255 ) ;
//HU_DrawEmeralds(x-12,y+2,255);
}
else if ( ! players [ tab [ i ] . num ] . powers [ pw_super ]
2018-07-31 09:10:02 +00:00
| | ( ( leveltime / 7 ) & 1 ) )
{
HU_Draw32Emeralds ( x + 60 , y + 2 , tab [ i ] . emeralds ) ;
//HU_DrawEmeralds(x-12,y+2,tab[i].emeralds);
}
//V_DrawSmallScaledPatch (x, y-4, 0, livesback);
if ( tab [ i ] . color = = 0 )
{
colormap = colormaps ;
if ( players [ tab [ i ] . num ] . powers [ pw_super ] )
V_DrawFixedPatch ( x * FRACUNIT , y * FRACUNIT , FRACUNIT / 4 , 0 , superprefix [ players [ tab [ i ] . num ] . skin ] , 0 ) ;
else
{
2019-06-20 14:34:27 +00:00
if ( greycheck )
2018-07-31 09:10:02 +00:00
V_DrawFixedPatch ( x * FRACUNIT , ( y ) * FRACUNIT , FRACUNIT / 4 , V_HUDTRANSHALF , faceprefix [ players [ tab [ i ] . num ] . skin ] , 0 ) ;
else
V_DrawFixedPatch ( x * FRACUNIT , ( y ) * FRACUNIT , FRACUNIT / 4 , 0 , faceprefix [ players [ tab [ i ] . num ] . skin ] , 0 ) ;
}
}
else
{
2019-06-20 14:34:27 +00:00
if ( supercheck )
2018-07-31 09:10:02 +00:00
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
V_DrawFixedPatch ( x * FRACUNIT , y * FRACUNIT , FRACUNIT / 4 , 0 , superprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
else
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
2019-06-20 14:34:27 +00:00
if ( greycheck )
2018-07-31 09:10:02 +00:00
V_DrawFixedPatch ( x * FRACUNIT , ( y ) * FRACUNIT , FRACUNIT / 4 , V_HUDTRANSHALF , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
else
V_DrawFixedPatch ( x * FRACUNIT , ( y ) * FRACUNIT , FRACUNIT / 4 , 0 , faceprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
}
}
// All data drawn with thin string for space.
if ( gametype = = GT_RACE )
{
if ( circuitmap )
{
if ( players [ tab [ i ] . num ] . exiting )
V_DrawRightAlignedThinString ( x + 128 , y , 0 , va ( " %i:%02i.%02i " , G_TicsToMinutes ( players [ tab [ i ] . num ] . realtime , true ) , G_TicsToSeconds ( players [ tab [ i ] . num ] . realtime ) , G_TicsToCentiseconds ( players [ tab [ i ] . num ] . realtime ) ) ) ;
else
2019-06-20 14:34:27 +00:00
V_DrawRightAlignedThinString ( x + 128 , y , ( greycheck ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
2018-07-31 09:10:02 +00:00
}
else
2019-06-20 14:34:27 +00:00
V_DrawRightAlignedThinString ( x + 128 , y , ( greycheck ? 0 : V_TRANSLUCENT ) , va ( " %i:%02i.%02i " , G_TicsToMinutes ( tab [ i ] . count , true ) , G_TicsToSeconds ( tab [ i ] . count ) , G_TicsToCentiseconds ( tab [ i ] . count ) ) ) ;
2018-07-31 09:10:02 +00:00
}
else
2019-06-20 14:34:27 +00:00
V_DrawRightAlignedThinString ( x + 128 , y , ( greycheck ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
2018-07-31 09:10:02 +00:00
y + = 9 ;
if ( i = = 16 )
{
y = 32 ;
x + = BASEVIDWIDTH / 2 ;
}
}
}
2014-03-15 16:59:03 +00:00
//
// HU_DrawEmeralds
//
void HU_DrawEmeralds ( INT32 x , INT32 y , INT32 pemeralds )
{
//Draw the emeralds, in the CORRECT order, using tiny emerald sprites.
if ( pemeralds & EMERALD1 )
2017-11-17 23:02:34 +00:00
V_DrawSmallScaledPatch ( x , y - 6 , 0 , emeraldpics [ 1 ] [ 0 ] ) ;
2014-03-15 16:59:03 +00:00
if ( pemeralds & EMERALD2 )
2017-11-17 23:02:34 +00:00
V_DrawSmallScaledPatch ( x + 4 , y - 3 , 0 , emeraldpics [ 1 ] [ 1 ] ) ;
2014-03-15 16:59:03 +00:00
if ( pemeralds & EMERALD3 )
2017-11-17 23:02:34 +00:00
V_DrawSmallScaledPatch ( x + 4 , y + 3 , 0 , emeraldpics [ 1 ] [ 2 ] ) ;
2014-03-15 16:59:03 +00:00
if ( pemeralds & EMERALD4 )
2017-11-17 23:02:34 +00:00
V_DrawSmallScaledPatch ( x , y + 6 , 0 , emeraldpics [ 1 ] [ 3 ] ) ;
2014-03-15 16:59:03 +00:00
if ( pemeralds & EMERALD5 )
2017-11-17 23:02:34 +00:00
V_DrawSmallScaledPatch ( x - 4 , y + 3 , 0 , emeraldpics [ 1 ] [ 4 ] ) ;
2014-03-15 16:59:03 +00:00
if ( pemeralds & EMERALD6 )
2017-11-17 23:02:34 +00:00
V_DrawSmallScaledPatch ( x - 4 , y - 3 , 0 , emeraldpics [ 1 ] [ 5 ] ) ;
2014-03-15 16:59:03 +00:00
if ( pemeralds & EMERALD7 )
2017-11-17 23:02:34 +00:00
V_DrawSmallScaledPatch ( x , y , 0 , emeraldpics [ 1 ] [ 6 ] ) ;
2014-03-15 16:59:03 +00:00
}
//
// HU_DrawSpectatorTicker
//
static inline void HU_DrawSpectatorTicker ( void )
{
int i ;
int length = 0 , height = 174 ;
int totallength = 0 , templength = 0 ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
if ( playeringame [ i ] & & players [ i ] . spectator )
totallength + = ( signed ) strlen ( player_names [ i ] ) * 8 + 16 ;
length - = ( leveltime % ( totallength + BASEVIDWIDTH ) ) ;
length + = BASEVIDWIDTH ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
if ( playeringame [ i ] & & players [ i ] . spectator )
{
char * pos ;
char initial [ MAXPLAYERNAME + 1 ] ;
char current [ MAXPLAYERNAME + 1 ] ;
strcpy ( initial , player_names [ i ] ) ;
pos = initial ;
if ( length > = - ( ( signed ) strlen ( player_names [ i ] ) * 8 + 16 ) & & length < = BASEVIDWIDTH )
{
if ( length < 0 )
{
UINT8 eatenchars = ( UINT8 ) ( abs ( length ) / 8 + 1 ) ;
if ( eatenchars < = strlen ( initial ) )
{
// Eat one letter off the left side,
// then compensate the drawing position.
pos + = eatenchars ;
strcpy ( current , pos ) ;
templength = length % 8 + 8 ;
}
else
{
strcpy ( current , " " ) ;
templength = length ;
}
}
else
{
strcpy ( current , initial ) ;
templength = length ;
}
2018-03-26 22:53:09 +00:00
V_DrawString ( templength , height + 8 , V_TRANSLUCENT | V_ALLOWLOWERCASE , current ) ;
2014-03-15 16:59:03 +00:00
}
length + = ( signed ) strlen ( player_names [ i ] ) * 8 + 16 ;
}
}
//
// HU_DrawRankings
//
static void HU_DrawRankings ( void )
{
playersort_t tab [ MAXPLAYERS ] ;
INT32 i , j , scorelines ;
boolean completed [ MAXPLAYERS ] ;
UINT32 whiteplayer ;
// draw the current gametype in the lower right
HU_drawGametype ( ) ;
if ( gametype ! = GT_RACE & & gametype ! = GT_COMPETITION & & gametype ! = GT_COOP )
{
if ( cv_timelimit . value & & timelimitintics > 0 )
{
2018-03-21 18:18:45 +00:00
V_DrawCenteredString ( 64 , 8 , 0 , " TIME " ) ;
V_DrawCenteredString ( 64 , 16 , 0 , va ( " %i:%02i " , G_TicsToMinutes ( stplyr - > realtime , true ) , G_TicsToSeconds ( stplyr - > realtime ) ) ) ;
2014-03-15 16:59:03 +00:00
}
if ( cv_pointlimit . value > 0 )
{
V_DrawCenteredString ( 256 , 8 , 0 , " POINT LIMIT " ) ;
V_DrawCenteredString ( 256 , 16 , 0 , va ( " %d " , cv_pointlimit . value ) ) ;
}
}
else if ( gametype = = GT_COOP )
{
INT32 totalscore = 0 ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( playeringame [ i ] )
totalscore + = players [ i ] . score ;
}
V_DrawCenteredString ( 256 , 8 , 0 , " TOTAL SCORE " ) ;
V_DrawCenteredString ( 256 , 16 , 0 , va ( " %u " , totalscore ) ) ;
}
else
{
if ( circuitmap )
{
V_DrawCenteredString ( 64 , 8 , 0 , " NUMBER OF LAPS " ) ;
V_DrawCenteredString ( 64 , 16 , 0 , va ( " %d " , cv_numlaps . value ) ) ;
}
}
// When you play, you quickly see your score because your name is displayed in white.
// When playing back a demo, you quickly see who's the view.
whiteplayer = demoplayback ? displayplayer : consoleplayer ;
scorelines = 0 ;
memset ( completed , 0 , sizeof ( completed ) ) ;
memset ( tab , 0 , sizeof ( playersort_t ) * MAXPLAYERS ) ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
tab [ i ] . num = - 1 ;
tab [ i ] . name = 0 ;
if ( gametype = = GT_RACE & & ! circuitmap )
tab [ i ] . count = INT32_MAX ;
}
for ( j = 0 ; j < MAXPLAYERS ; j + + )
{
2017-07-07 12:35:48 +00:00
if ( ! playeringame [ j ] )
continue ;
if ( gametype ! = GT_COOP & & players [ j ] . spectator )
2014-03-15 16:59:03 +00:00
continue ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2017-07-07 12:35:48 +00:00
if ( ! playeringame [ i ] )
continue ;
if ( gametype ! = GT_COOP & & players [ i ] . spectator )
continue ;
if ( gametype = = GT_RACE )
2014-03-15 16:59:03 +00:00
{
2017-07-07 12:35:48 +00:00
if ( circuitmap )
2014-03-15 16:59:03 +00:00
{
2017-07-07 12:35:48 +00:00
if ( ( unsigned ) players [ i ] . laps + 1 > = tab [ scorelines ] . count & & completed [ i ] = = false )
2014-03-15 16:59:03 +00:00
{
2017-07-07 12:35:48 +00:00
tab [ scorelines ] . count = players [ i ] . laps + 1 ;
2014-03-15 16:59:03 +00:00
tab [ scorelines ] . num = i ;
tab [ scorelines ] . color = players [ i ] . skincolor ;
tab [ scorelines ] . name = player_names [ i ] ;
}
}
else
{
2017-07-07 12:35:48 +00:00
if ( players [ i ] . realtime < = tab [ scorelines ] . count & & completed [ i ] = = false )
2014-03-15 16:59:03 +00:00
{
2017-07-07 12:35:48 +00:00
tab [ scorelines ] . count = players [ i ] . realtime ;
2014-03-15 16:59:03 +00:00
tab [ scorelines ] . num = i ;
tab [ scorelines ] . color = players [ i ] . skincolor ;
tab [ scorelines ] . name = player_names [ i ] ;
}
}
}
2017-07-07 12:35:48 +00:00
else if ( gametype = = GT_COMPETITION )
{
// todo put something more fitting for the gametype here, such as current
// number of categories led
if ( players [ i ] . score > = tab [ scorelines ] . count & & completed [ i ] = = false )
{
tab [ scorelines ] . count = players [ i ] . score ;
tab [ scorelines ] . num = i ;
tab [ scorelines ] . color = players [ i ] . skincolor ;
tab [ scorelines ] . name = player_names [ i ] ;
tab [ scorelines ] . emeralds = players [ i ] . powers [ pw_emeralds ] ;
}
}
else
{
if ( players [ i ] . score > = tab [ scorelines ] . count & & completed [ i ] = = false )
{
tab [ scorelines ] . count = players [ i ] . score ;
tab [ scorelines ] . num = i ;
tab [ scorelines ] . color = players [ i ] . skincolor ;
tab [ scorelines ] . name = player_names [ i ] ;
tab [ scorelines ] . emeralds = players [ i ] . powers [ pw_emeralds ] ;
}
}
2014-03-15 16:59:03 +00:00
}
completed [ tab [ scorelines ] . num ] = true ;
scorelines + + ;
}
2018-07-31 09:10:02 +00:00
//if (scorelines > 20)
// scorelines = 20; //dont draw past bottom of screen, show the best only
// shush, we'll do it anyway.
2014-03-15 16:59:03 +00:00
if ( G_GametypeHasTeams ( ) )
2019-06-20 14:34:27 +00:00
HU_DrawTeamTabRankings ( tab , whiteplayer ) ;
2018-07-31 09:10:02 +00:00
else if ( scorelines < = 9 & & ! cv_compactscoreboard . value )
2014-03-15 16:59:03 +00:00
HU_DrawTabRankings ( 40 , 32 , tab , scorelines , whiteplayer ) ;
2018-07-31 09:10:02 +00:00
else if ( scorelines < = 20 & & ! cv_compactscoreboard . value )
2014-03-15 16:59:03 +00:00
HU_DrawDualTabRankings ( 32 , 32 , tab , scorelines , whiteplayer ) ;
2018-07-31 09:10:02 +00:00
else
HU_Draw32TabRankings ( 14 , 28 , tab , scorelines , whiteplayer ) ;
2014-03-15 16:59:03 +00:00
// draw spectators in a ticker across the bottom
if ( ! splitscreen & & G_GametypeHasSpectators ( ) )
HU_DrawSpectatorTicker ( ) ;
}
static void HU_DrawCoopOverlay ( void )
{
if ( token
# ifdef HAVE_BLUA
& & LUA_HudEnabled ( hud_tokens )
# endif
)
{
V_DrawString ( 168 , 176 , 0 , va ( " - %d " , token ) ) ;
V_DrawSmallScaledPatch ( 148 , 172 , 0 , tokenicon ) ;
}
# ifdef HAVE_BLUA
if ( LUA_HudEnabled ( hud_tabemblems ) )
# endif
if ( ! modifiedgame | | savemoddata )
{
V_DrawString ( 160 , 144 , 0 , va ( " - %d/%d " , M_CountEmblems ( ) , numemblems + numextraemblems ) ) ;
V_DrawScaledPatch ( 128 , 144 - SHORT ( emblemicon - > height ) / 4 , 0 , emblemicon ) ;
}
# ifdef HAVE_BLUA
if ( ! LUA_HudEnabled ( hud_coopemeralds ) )
return ;
# endif
if ( emeralds & EMERALD1 )
2017-11-17 23:02:34 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 , ( BASEVIDHEIGHT / 3 ) - 32 , 0 , emeraldpics [ 0 ] [ 0 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD2 )
2017-11-17 23:02:34 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 + 24 , ( BASEVIDHEIGHT / 3 ) - 16 , 0 , emeraldpics [ 0 ] [ 1 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD3 )
2017-11-17 23:02:34 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 + 24 , ( BASEVIDHEIGHT / 3 ) + 16 , 0 , emeraldpics [ 0 ] [ 2 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD4 )
2017-11-17 23:02:34 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 , ( BASEVIDHEIGHT / 3 ) + 32 , 0 , emeraldpics [ 0 ] [ 3 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD5 )
2017-11-17 23:02:34 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 - 24 , ( BASEVIDHEIGHT / 3 ) + 16 , 0 , emeraldpics [ 0 ] [ 4 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD6 )
2017-11-17 23:02:34 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 - 24 , ( BASEVIDHEIGHT / 3 ) - 16 , 0 , emeraldpics [ 0 ] [ 5 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD7 )
2017-11-17 23:02:34 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 , ( BASEVIDHEIGHT / 3 ) , 0 , emeraldpics [ 0 ] [ 6 ] ) ;
2014-03-15 16:59:03 +00:00
}
static void HU_DrawNetplayCoopOverlay ( void )
{
int i ;
2019-06-20 14:34:27 +00:00
if ( token
# ifdef HAVE_BLUA
& & LUA_HudEnabled ( hud_tokens )
# endif
)
{
V_DrawString ( 168 , 10 , 0 , va ( " - %d " , token ) ) ;
V_DrawSmallScaledPatch ( 148 , 6 , 0 , tokenicon ) ;
}
2014-03-15 16:59:03 +00:00
# ifdef HAVE_BLUA
if ( ! LUA_HudEnabled ( hud_coopemeralds ) )
return ;
# endif
for ( i = 0 ; i < 7 ; + + i )
{
if ( emeralds & ( 1 < < i ) )
2019-06-20 14:34:27 +00:00
V_DrawScaledPatch ( 20 + ( i * 10 ) , 9 , 0 , emeraldpics [ 1 ] [ i ] ) ;
2014-03-15 16:59:03 +00:00
}
}
// Interface to CECHO settings for the outside world, avoiding the
// expense (and security problems) of going via the console buffer.
void HU_ClearCEcho ( void )
{
cechotimer = 0 ;
}
void HU_SetCEchoDuration ( INT32 seconds )
{
cechoduration = seconds * TICRATE ;
}
void HU_SetCEchoFlags ( INT32 flags )
{
// Don't allow cechoflags to contain any bits in V_PARAMMASK
cechoflags = ( flags & ~ V_PARAMMASK ) ;
}
void HU_DoCEcho ( const char * msg )
{
I_OutputMsg ( " %s \n " , msg ) ; // print to log
strncpy ( cechotext , msg , sizeof ( cechotext ) ) ;
strncat ( cechotext , " \\ " , sizeof ( cechotext ) - strlen ( cechotext ) - 1 ) ;
cechotext [ sizeof ( cechotext ) - 1 ] = ' \0 ' ;
cechotimer = cechoduration ;
}