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"
2017-12-17 03:32:43 +00:00
# include "p_local.h" // camera, camera2, camera3, camera4
2014-03-15 16:59:03 +00:00
# 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
2019-01-04 09:44:55 +00:00
# include "s_sound.h" // song credits
2017-12-18 05:12:51 +00:00
# include "k_kart.h"
2014-03-15 16:59:03 +00:00
// 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 ] ;
2016-08-15 03:51:08 +00:00
patch_t * kart_font [ KART_FONTSIZE ] ; // SRB2kart
2014-03-15 16:59:03 +00:00
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 ] ;
2019-02-16 03:08:08 +00:00
// ping font
// Note: I'd like to adress that at this point we might *REALLY* want to work towards a common drawString function that can take any font we want because this is really turning into a MESS. :V -Lat'
patch_t * pingnum [ 10 ] ;
patch_t * pinggfx [ 5 ] ; // small ping graphic
patch_t * framecounter ;
patch_t * frameslash ; // framerate stuff. Used in screen.c
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 ;
//-------------------------------------------
// coop hud
//-------------------------------------------
patch_t * emeraldpics [ 7 ] ;
patch_t * tinyemeraldpics [ 7 ] ;
static patch_t * emblemicon ;
static patch_t * tokenicon ;
//-------------------------------------------
// 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
2019-01-04 09:44:55 +00:00
// song credits
static patch_t * songcreditbg ;
2014-03-15 16:59:03 +00:00
// -------
// protos.
// -------
static void HU_DrawRankings ( 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 ) ;
}
// cache the level title font for entire game execution
lt_font [ 0 ] = ( patch_t * ) W_CachePatchName ( " LTFNT039 " , PU_HUDGFX ) ; /// \note fake start hack
// Number support
lt_font [ 9 ] = ( patch_t * ) W_CachePatchName ( " LTFNT048 " , PU_HUDGFX ) ;
lt_font [ 10 ] = ( patch_t * ) W_CachePatchName ( " LTFNT049 " , PU_HUDGFX ) ;
lt_font [ 11 ] = ( patch_t * ) W_CachePatchName ( " LTFNT050 " , PU_HUDGFX ) ;
lt_font [ 12 ] = ( patch_t * ) W_CachePatchName ( " LTFNT051 " , PU_HUDGFX ) ;
lt_font [ 13 ] = ( patch_t * ) W_CachePatchName ( " LTFNT052 " , PU_HUDGFX ) ;
lt_font [ 14 ] = ( patch_t * ) W_CachePatchName ( " LTFNT053 " , PU_HUDGFX ) ;
lt_font [ 15 ] = ( patch_t * ) W_CachePatchName ( " LTFNT054 " , PU_HUDGFX ) ;
lt_font [ 16 ] = ( patch_t * ) W_CachePatchName ( " LTFNT055 " , PU_HUDGFX ) ;
lt_font [ 17 ] = ( patch_t * ) W_CachePatchName ( " LTFNT056 " , PU_HUDGFX ) ;
lt_font [ 18 ] = ( patch_t * ) W_CachePatchName ( " LTFNT057 " , PU_HUDGFX ) ;
2016-08-15 03:51:08 +00:00
// SRB2kart
j = KART_FONTSTART ;
for ( i = 0 ; i < KART_FONTSIZE ; i + + , j + + )
{
// cache the heads-up font for entire game execution
sprintf ( buffer , " MKFNT%.3d " , j ) ;
if ( W_CheckNumForName ( buffer ) = = LUMPERROR )
kart_font [ i ] = NULL ;
else
kart_font [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
}
//
2014-03-15 16:59:03 +00:00
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 ) ;
2019-02-16 03:08:08 +00:00
sprintf ( buffer , " PINGN%d " , i ) ;
pingnum [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
}
// minus for negative tallnums
tallminus = ( patch_t * ) W_CachePatchName ( " STTMINUS " , PU_HUDGFX ) ;
// 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 ) ;
emeraldpics [ 0 ] = W_CachePatchName ( " CHAOS1 " , PU_HUDGFX ) ;
emeraldpics [ 1 ] = W_CachePatchName ( " CHAOS2 " , PU_HUDGFX ) ;
emeraldpics [ 2 ] = W_CachePatchName ( " CHAOS3 " , PU_HUDGFX ) ;
emeraldpics [ 3 ] = W_CachePatchName ( " CHAOS4 " , PU_HUDGFX ) ;
emeraldpics [ 4 ] = W_CachePatchName ( " CHAOS5 " , PU_HUDGFX ) ;
emeraldpics [ 5 ] = W_CachePatchName ( " CHAOS6 " , PU_HUDGFX ) ;
emeraldpics [ 6 ] = W_CachePatchName ( " CHAOS7 " , PU_HUDGFX ) ;
tinyemeraldpics [ 0 ] = W_CachePatchName ( " TEMER1 " , PU_HUDGFX ) ;
tinyemeraldpics [ 1 ] = W_CachePatchName ( " TEMER2 " , PU_HUDGFX ) ;
tinyemeraldpics [ 2 ] = W_CachePatchName ( " TEMER3 " , PU_HUDGFX ) ;
tinyemeraldpics [ 3 ] = W_CachePatchName ( " TEMER4 " , PU_HUDGFX ) ;
tinyemeraldpics [ 4 ] = W_CachePatchName ( " TEMER5 " , PU_HUDGFX ) ;
tinyemeraldpics [ 5 ] = W_CachePatchName ( " TEMER6 " , PU_HUDGFX ) ;
tinyemeraldpics [ 6 ] = W_CachePatchName ( " TEMER7 " , PU_HUDGFX ) ;
2019-01-04 09:44:55 +00:00
2019-01-04 21:47:03 +00:00
songcreditbg = W_CachePatchName ( " K_SONGCR " , PU_HUDGFX ) ;
2019-02-16 03:08:08 +00:00
// cache ping gfx:
for ( i = 0 ; i < 5 ; i + + )
{
sprintf ( buffer , " PINGGFX%d " , i + 1 ) ;
pinggfx [ i ] = ( patch_t * ) W_CachePatchName ( buffer , PU_HUDGFX ) ;
}
// fps stuff
framecounter = W_CachePatchName ( " FRAMER " , PU_HUDGFX ) ;
frameslash = W_CachePatchName ( " FRAMESL " , PU_HUDGFX ) ; ;
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-06-15 10:20:01 +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-08-02 22:52:07 +00:00
static UINT32 chat_scroll = 0 ;
2018-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +00:00
}
2018-12-26 21:20:17 +00:00
# endif
2018-08-02 22:52:07 +00:00
2018-10-10 19:24:55 +00:00
void HU_AddChatText ( const char * text , boolean playsound )
2018-06-15 10:20:01 +00:00
{
2018-12-26 21:20:17 +00:00
# ifndef NONET
2018-10-10 19:24:55 +00:00
if ( playsound & & cv_consolechat . value ! = 2 ) // Don't play the sound if we're using hidden chat.
2018-08-03 22:01:09 +00:00
S_StartSound ( NULL , sfx_radio ) ;
2018-10-10 19:24:55 +00:00
// reguardless of our preferences, put all of this in the chat buffer in case we decide to change from oldchat mid-game.
2018-10-25 11:21:44 +00:00
2018-12-23 02:17:01 +00:00
if ( chat_nummsg_log > = CHAT_BUFSIZE ) // too many messages!
2018-06-15 10:20:01 +00:00
HU_removeChatText_Log ( ) ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
strcpy ( chat_log [ chat_nummsg_log ] , text ) ;
chat_nummsg_log + + ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
if ( chat_nummsg_min > = 8 )
HU_removeChatText_Mini ( ) ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
strcpy ( chat_mini [ chat_nummsg_min ] , text ) ;
chat_timers [ chat_nummsg_min ] = TICRATE * cv_chattime . value ;
chat_nummsg_min + + ;
2018-10-25 11:21:44 +00:00
2018-12-23 02:17:01 +00:00
if ( OLDCHAT ) // if we're using oldchat, print directly in console
2018-10-10 19:24:55 +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-06-15 10:20:01 +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 )
{
XBOXSTATIC char buf [ 254 ] ;
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-10-10 19:24:55 +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-06-15 10:20:01 +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-08-02 22:52:07 +00:00
2018-12-23 02:17:01 +00:00
if ( strlen ( msg ) > 4 & & strnicmp ( msg , " /pm " , 3 ) = = 0 ) // used /pm
2018-06-15 10:20:01 +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-10-07 14:35:54 +00:00
const char * newmsg ;
2018-12-23 02:17:01 +00:00
INT32 spc = 1 ; // used if nodenum[1] is a space.
2018-06-15 10:20:01 +00:00
char * nodenum = ( char * ) malloc ( 3 ) ;
2018-12-18 02:31:00 +00:00
strncpy ( nodenum , msg + 3 , 3 ) ;
2018-06-15 10:20:01 +00:00
// check for undesirable characters in our "number"
if ( ( ( nodenum [ 0 ] < ' 0 ' ) | | ( nodenum [ 0 ] > ' 9 ' ) ) | | ( ( nodenum [ 1 ] < ' 0 ' ) | | ( nodenum [ 1 ] > ' 9 ' ) ) )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
// check if nodenum[1] is a space
if ( nodenum [ 1 ] = = ' ' )
spc = 0 ;
// let it slide
else
2018-08-02 22:52:07 +00:00
{
2018-10-10 19:24:55 +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-06-15 10:20:01 +00:00
return ;
2018-08-02 22:52:07 +00:00
}
2018-06-15 10:20:01 +00:00
}
// I'm very bad at C, I swear I am, additional checks eww!
if ( spc ! = 0 )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( msg [ 5 ] ! = ' ' )
{
2018-10-10 19:24:55 +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-06-15 10:20:01 +00:00
return ;
}
}
2018-08-02 22:52:07 +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-06-15 10:20:01 +00:00
//CONS_Printf("%d\n", target);
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +00:00
return ;
}
buf [ 0 ] = target ;
2018-10-07 14:35:54 +00:00
newmsg = msg + 5 + spc ;
2018-12-24 23:37:54 +00:00
strlcpy ( msg , newmsg , 252 ) ;
2018-06-15 10:20:01 +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 ;
}
2018-10-07 14:35:54 +00:00
2018-10-05 10:13:11 +00:00
if ( G_GametypeHasTeams ( ) ) // revert to normal say if we don't have teams in this gametype.
DoSayCommand ( - 1 , 1 , 0 ) ;
2018-10-07 14:35:54 +00:00
else
2018-10-05 10:13:11 +00:00
DoSayCommand ( 0 , 1 , 0 ) ;
2014-03-15 16:59:03 +00:00
}
/** 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 ;
2018-08-02 22:52:07 +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-06-15 10:20:01 +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 )
{
XBOXSTATIC UINT8 buf [ 2 ] ;
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 )
{
XBOXSTATIC char buf [ 2 ] ;
buf [ 0 ] = ( char ) playernum ;
buf [ 1 ] = KICK_MSG_CON_FAIL ;
SendNetXCmd ( XD_KICK , & buf , 2 ) ;
}
return ;
}
}
}
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// before we do anything, let's verify the guy isn't spamming, get this easier on us.
2018-08-02 22:52:07 +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-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +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-06-15 10:20:01 +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-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// run the lua hook even if we were supposed to eat the msg, netgame consistency goes first.
2018-08-02 22:52:07 +00:00
2014-06-18 20:28:09 +00:00
# ifdef HAVE_BLUA
2018-06-15 10:20:01 +00:00
if ( LUAh_PlayerMsg ( playernum , target , flags , msg , spam_eatmsg ) )
2014-06-18 20:28:09 +00:00
return ;
# endif
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +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-08-02 22:52:07 +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-10-10 19:24:55 +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-08-02 22:52:07 +00:00
2018-08-31 13:02:48 +00:00
// player is a spectator?
if ( players [ playernum ] . spectator )
2018-10-07 14:35:54 +00:00
{
2018-08-31 13:02:48 +00:00
cstart = " \x86 " ; // grey name
textcolor = " \x86 " ;
2018-10-01 18:23:56 +00:00
}
2018-12-23 02:17:01 +00:00
else if ( target = = - 1 ) // say team
2018-10-01 18:23:56 +00:00
{
if ( players [ playernum ] . ctfteam = = 1 ) // red
{
cstart = " \x85 " ;
textcolor = " \x85 " ;
2018-10-07 14:35:54 +00:00
}
2018-10-01 18:23:56 +00:00
else // blue
{
cstart = " \x84 " ;
textcolor = " \x84 " ;
2018-10-07 14:35:54 +00:00
}
2018-10-01 18:23:56 +00:00
}
2018-08-31 13:02:48 +00:00
else
2019-03-06 08:15:07 +00:00
{
2018-08-31 13:02:48 +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-03-06 08:15:07 +00:00
switch ( color )
{
case SKINCOLOR_WHITE :
case SKINCOLOR_SILVER :
case SKINCOLOR_SLATE :
cstart = " \x80 " ; // White
break ;
case SKINCOLOR_GREY :
case SKINCOLOR_NICKEL :
case SKINCOLOR_BLACK :
case SKINCOLOR_JET :
cstart = " \x86 " ; // V_GRAYMAP
break ;
case SKINCOLOR_SEPIA :
case SKINCOLOR_BEIGE :
case SKINCOLOR_BROWN :
case SKINCOLOR_LEATHER :
case SKINCOLOR_RUST :
cstart = " \x8e " ; // V_BROWNMAP
break ;
case SKINCOLOR_FAIRY :
case SKINCOLOR_SALMON :
case SKINCOLOR_PINK :
case SKINCOLOR_ROSE :
case SKINCOLOR_BRICK :
case SKINCOLOR_BUBBLEGUM :
case SKINCOLOR_LILAC :
cstart = " \x8d " ; // V_PINKMAP
break ;
case SKINCOLOR_RUBY :
case SKINCOLOR_RASPBERRY :
case SKINCOLOR_CHERRY :
case SKINCOLOR_RED :
case SKINCOLOR_CRIMSON :
case SKINCOLOR_MAROON :
case SKINCOLOR_FLAME :
case SKINCOLOR_SCARLET :
case SKINCOLOR_KETCHUP :
cstart = " \x85 " ; // V_REDMAP
break ;
case SKINCOLOR_DAWN :
case SKINCOLOR_SUNSET :
case SKINCOLOR_CREAMSICLE :
case SKINCOLOR_ORANGE :
case SKINCOLOR_PUMPKIN :
case SKINCOLOR_ROSEWOOD :
case SKINCOLOR_BURGUNDY :
case SKINCOLOR_TANGERINE :
cstart = " \x87 " ; // V_ORANGEMAP
break ;
case SKINCOLOR_PEACH :
case SKINCOLOR_CARAMEL :
case SKINCOLOR_CREAM :
cstart = " \x8f " ; // V_PEACHMAP
break ;
case SKINCOLOR_GOLD :
case SKINCOLOR_ROYAL :
case SKINCOLOR_BRONZE :
case SKINCOLOR_COPPER :
cstart = " \x8A " ; // V_GOLDMAP
break ;
case SKINCOLOR_POPCORN :
case SKINCOLOR_YELLOW :
case SKINCOLOR_MUSTARD :
case SKINCOLOR_OLIVE :
cstart = " \x82 " ; // V_YELLOWMAP
break ;
case SKINCOLOR_VOMIT :
case SKINCOLOR_GARDEN :
case SKINCOLOR_TEA :
case SKINCOLOR_PISTACHIO :
cstart = " \x8b " ; // V_TEAMAP
break ;
case SKINCOLOR_LIME :
case SKINCOLOR_HANDHELD :
case SKINCOLOR_MOSS :
case SKINCOLOR_CAMOUFLAGE :
case SKINCOLOR_ROBOHOOD :
case SKINCOLOR_MINT :
case SKINCOLOR_GREEN :
case SKINCOLOR_PINETREE :
case SKINCOLOR_EMERALD :
case SKINCOLOR_SWAMP :
case SKINCOLOR_DREAM :
case SKINCOLOR_PLAGUE :
case SKINCOLOR_ALGAE :
cstart = " \x83 " ; // V_GREENMAP
break ;
case SKINCOLOR_CARIBBEAN :
case SKINCOLOR_AQUA :
case SKINCOLOR_TEAL :
case SKINCOLOR_CYAN :
case SKINCOLOR_JAWZ :
case SKINCOLOR_CERULEAN :
case SKINCOLOR_NAVY :
case SKINCOLOR_SAPPHIRE :
cstart = " \x88 " ; // V_SKYMAP
break ;
case SKINCOLOR_PLATINUM :
case SKINCOLOR_STEEL :
cstart = " \x8c " ; // V_STEELMAP
break ;
case SKINCOLOR_PERIWINKLE :
case SKINCOLOR_BLUE :
case SKINCOLOR_BLUEBERRY :
case SKINCOLOR_NOVA :
cstart = " \x84 " ; // V_BLUEMAP
break ;
case SKINCOLOR_ULTRAVIOLET :
case SKINCOLOR_PURPLE :
case SKINCOLOR_FUCHSIA :
cstart = " \x81 " ; // V_PURPLEMAP
break ;
case SKINCOLOR_PASTEL :
case SKINCOLOR_MOONSLAM :
case SKINCOLOR_DUSK :
case SKINCOLOR_TOXIC :
case SKINCOLOR_MAUVE :
case SKINCOLOR_LAVENDER :
case SKINCOLOR_BYZANTIUM :
case SKINCOLOR_POMEGRANATE :
cstart = " \x89 " ; // V_LAVENDERMAP
break ;
default :
break ;
}
}
2018-08-31 13:02:48 +00:00
prefix = cstart ;
2018-10-07 14:35:54 +00:00
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-06-15 10:20:01 +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-10-01 18:23:56 +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-06-15 10:20:01 +00:00
{
prefix = " \x82 [PM] " ;
cstart = " \x82 " ;
2018-08-31 13:02:48 +00:00
textcolor = " \x82 " ;
fmt2 = " %s<%s%s>%s \x80 %s%s " ;
2018-06-15 10:20:01 +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-06-15 10:20:01 +00:00
prefix = " \x82 [TO] " ;
cstart = " \x82 " ;
2018-08-31 13:02:48 +00:00
fmt2 = " %s<%s%s>%s \x80 %s%s " ;
2018-08-02 22:52:07 +00:00
2014-03-15 16:59:03 +00:00
}
2018-10-01 18:23:56 +00:00
else // To everyone or sayteam, it doesn't change anything.
fmt2 = " %s<%s%s%s> \x80 %s%s " ;
/*else // To your team
2018-06-15 10:20:01 +00:00
{
2018-08-02 22:52:07 +00:00
if ( players [ playernum ] . ctfteam = = 1 ) // red
prefix = " \x85 [TEAM] " ;
2018-06-15 10:20:01 +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-08-02 22:52:07 +00:00
2018-08-31 13:02:48 +00:00
fmt2 = " %s<%s%s> \x80 %s %s%s " ;
2018-10-01 18:23:56 +00:00
} */
2018-08-02 22:52:07 +00:00
2018-10-10 19:24:55 +00:00
HU_AddChatText ( va ( fmt2 , prefix , cstart , dispname , cend , textcolor , msg ) , cv_chatnotifications . value ) ; // add to chat
2018-08-02 22:52:07 +00:00
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-06-15 10:20:01 +00:00
{
s [ l + + ] = ch ;
s [ l ] = 0 ;
2018-08-02 22:52:07 +00:00
}
2018-06-15 10:20:01 +00:00
else
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +00:00
}
2018-12-23 02:17:01 +00:00
s [ c_input ] = ch ; // and replace this.
2018-06-15 10:20:01 +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-06-15 10:20:01 +00:00
if ( c_input < = 0 )
return false ;
2018-12-18 00:02:22 +00:00
2018-06-15 10:20:01 +00:00
if ( ! s [ i - 1 ] )
return false ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
if ( i > = strlen ( s ) - 1 )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
s [ strlen ( s ) - 1 ] = 0 ;
c_input - - ;
2014-03-15 16:59:03 +00:00
return false ;
2018-08-02 22:52:07 +00:00
}
2018-06-15 10:20:01 +00:00
for ( ; ( i < HU_MAXMSGLEN ) ; i + + )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +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 ; */
2018-06-15 10:20:01 +00:00
// 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 )
2018-12-22 11:34:17 +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 ;
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.
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.
2018-12-22 11:34:17 +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
//
//
2018-08-02 22:52:07 +00:00
static void HU_queueChatChar ( INT32 c )
2014-03-15 16:59:03 +00:00
{
// send automaticly the message (no more chat char)
if ( c = = KEY_ENTER )
{
char buf [ 2 + 256 ] ;
2018-06-15 10:20:01 +00:00
char * msg = & buf [ 2 ] ;
2018-10-07 14:35:54 +00:00
size_t i ;
2018-12-18 00:02:22 +00:00
size_t ci = 2 ;
2018-10-07 14:35:54 +00:00
INT32 target = 0 ;
2018-12-18 00:02:22 +00:00
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.
2018-12-22 11:34:17 +00:00
2014-03-15 16:59:03 +00:00
do {
2018-06-15 10:20:01 +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-06-15 10:20:01 +00:00
buf [ ci - 1 ] = c ;
2014-03-15 16:59:03 +00:00
} while ( c ) ;
2018-10-07 14:35:54 +00:00
i = 0 ;
2018-06-15 10:20:01 +00:00
for ( ; ( i < HU_MAXMSGLEN ) ; i + + )
w_chat [ i ] = 0 ; // reset this.
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
c_input = 0 ;
2018-08-02 22:52:07 +00:00
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-10-03 18:09:32 +00:00
if ( CHAT_MUTE )
2014-03-15 16:59:03 +00:00
{
2018-10-10 19:24:55 +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-08-02 22:52:07 +00:00
2018-12-23 02:17:01 +00:00
if ( strlen ( msg ) > 4 & & strnicmp ( msg , " /pm " , 3 ) = = 0 ) // used /pm
2018-06-15 10:20:01 +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-10-07 14:35:54 +00:00
const char * newmsg ;
2018-12-18 00:02:22 +00:00
2018-06-15 10:20:01 +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-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +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-10-10 19:24:55 +00:00
HU_AddChatText ( va ( " %sCannot send sayto in Say-Team. " , " \x85 " ) , false ) ;
2018-06-15 10:20:01 +00:00
return ;
2018-08-02 22:52:07 +00:00
}
2018-12-18 02:31:00 +00:00
strncpy ( nodenum , msg + 3 , 3 ) ;
2019-01-05 20:59:23 +00:00
2018-06-15 10:20:01 +00:00
// check for undesirable characters in our "number"
if ( ( ( nodenum [ 0 ] < ' 0 ' ) | | ( nodenum [ 0 ] > ' 9 ' ) ) | | ( ( nodenum [ 1 ] < ' 0 ' ) | | ( nodenum [ 1 ] > ' 9 ' ) ) )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
// check if nodenum[1] is a space
if ( nodenum [ 1 ] = = ' ' )
spc = 0 ;
// let it slide
else
2018-08-02 22:52:07 +00:00
{
2018-10-10 19:24:55 +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-06-15 10:20:01 +00:00
return ;
2018-08-02 22:52:07 +00:00
}
2018-06-15 10:20:01 +00:00
}
// I'm very bad at C, I swear I am, additional checks eww!
if ( spc ! = 0 )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( msg [ 5 ] ! = ' ' )
{
2018-10-10 19:24:55 +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-06-15 10:20:01 +00:00
return ;
}
}
2018-08-02 22:52:07 +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-06-15 10:20:01 +00:00
//CONS_Printf("%d\n", target);
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +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-06-15 10:20:01 +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-06-15 10:20:01 +00:00
return ;
}
2018-12-18 00:02:22 +00:00
2018-06-15 10:20:01 +00:00
// we need to get rid of the /pm<node>
2018-10-07 14:35:54 +00:00
newmsg = msg + 5 + spc ;
2018-12-24 23:37:54 +00:00
strlcpy ( msg , newmsg , 255 ) ;
2018-08-02 22:52:07 +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-06-15 10:20:01 +00:00
buf [ 0 ] = target ;
2018-08-02 22:52:07 +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-06-15 10:20:01 +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-06-15 10:20:01 +00:00
c_input = 0 ;
2014-03-15 16:59:03 +00:00
}
2018-12-26 21:20:17 +00:00
# ifndef NONET
2018-06-15 10:20:01 +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-06-15 10:20:01 +00:00
2014-03-15 16:59:03 +00:00
//
// Returns true if key eaten
//
boolean HU_Responder ( event_t * ev )
{
if ( ev - > type ! = ev_keydown )
return false ;
// only KeyDown events now...
2018-10-07 14:35:54 +00:00
2018-10-25 18:02:43 +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 ;
}
2018-12-26 21:20:17 +00:00
# ifndef NONET
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-06-15 10:20:01 +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-10-03 18:09:32 +00:00
& & netgame & & ! OLD_MUTE )
2014-03-15 16:59:03 +00:00
{
chat_on = true ;
w_chat [ 0 ] = 0 ;
2018-10-05 10:13:11 +00:00
teamtalk = G_GametypeHasTeams ( ) ; // Don't teamtalk if we don't have teams.
2018-06-15 10:20:01 +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
{
2019-03-09 23:17:38 +00:00
INT32 c = ( INT32 ) ev - > data1 ;
2018-10-07 14:35:54 +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 )
return true ;
2019-01-14 01:22:54 +00:00
// Ignore non-keyboard keys, except when the talk key is bound
2019-02-05 20:42:37 +00:00
if ( ev - > data1 > = KEY_MOUSE1
& & ( ev - > data1 ! = gamecontrol [ gc_talkkey ] [ 0 ]
2019-01-11 08:38:49 +00:00
& & ev - > data1 ! = gamecontrol [ gc_talkkey ] [ 1 ] ) )
2019-01-10 10:04:26 +00:00
return false ;
2019-03-10 23:09:15 +00:00
c = CON_ShiftChar ( c ) ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// pasting. pasting is cool. chat is a bit limited, though :(
2018-10-03 18:09:32 +00:00
if ( ( ( c = = ' v ' | | c = = ' V ' ) & & ctrldown ) & & ! CHAT_MUTE )
2018-06-15 10:20:01 +00:00
{
const char * paste = I_ClipboardPaste ( ) ;
2018-10-07 14:35:54 +00:00
size_t chatlen ;
size_t pastelen ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// create a dummy string real quickly
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
if ( paste = = NULL )
return true ;
2018-08-02 22:52:07 +00:00
2018-10-07 14:35:54 +00:00
chatlen = strlen ( w_chat ) ;
pastelen = strlen ( paste ) ;
2018-06-15 10:20:01 +00:00
if ( chatlen + pastelen > HU_MAXMSGLEN )
return true ; // we can't paste this!!
2018-08-02 22:52:07 +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-06-15 10:20:01 +00:00
{
2018-12-23 02:17:01 +00:00
memcpy ( & w_chat [ chatlen ] , paste , pastelen ) ; // copy all of that.
2018-06-15 10:20:01 +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-06-15 10:20:01 +00:00
} */
return true ;
}
else // otherwise, we need to shift everything and make space, etc etc
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
size_t i = HU_MAXMSGLEN - 1 ;
2018-12-23 01:17:50 +00:00
while ( i > = c_input )
2018-06-15 10:20:01 +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-06-15 10:20:01 +00:00
}
2018-12-23 02:17:01 +00:00
memcpy ( & w_chat [ c_input ] , paste , pastelen ) ; // copy all of that.
2018-06-15 10:20:01 +00:00
c_input + = pastelen ;
return true ;
}
}
2018-08-02 22:52:07 +00:00
2018-10-03 18:09:32 +00:00
if ( ! CHAT_MUTE & & HU_keyInChatString ( w_chat , c ) )
2018-08-02 22:52:07 +00:00
{
2014-03-15 16:59:03 +00:00
HU_queueChatChar ( c ) ;
2018-08-02 22:52:07 +00:00
}
2014-03-15 16:59:03 +00:00
if ( c = = KEY_ENTER )
2018-08-02 22:52:07 +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-06-15 10:20:01 +00:00
chat_scrollmedown = true ; // you hit enter, so you might wanna autoscroll to see what you just sent. :)
2018-08-02 22:52:07 +00:00
}
2018-09-27 22:32:48 +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.
2018-08-02 22:52:07 +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-08-02 22:52:07 +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-06-15 10:20:01 +00:00
{
chat_scroll - - ;
justscrolledup = true ;
chat_scrolltime = 4 ;
2018-08-02 22:52:07 +00:00
}
2018-10-03 18:09:32 +00:00
else if ( ( c = = KEY_DOWNARROW | | c = = KEY_MOUSEWHEELDOWN ) & & chat_scroll < chat_maxscroll & & chat_maxscroll > 0 & & ! OLDCHAT )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +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-06-15 10:20:01 +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-08-02 22:52:07 +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 ;
}
//======================================================================
// HEADS UP DRAWING
//======================================================================
2018-12-26 21:20:17 +00:00
# ifndef NONET
2018-06-15 10:20:01 +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-08-02 22:52:07 +00:00
static char * CHAT_WordWrap ( INT32 x , INT32 w , INT32 option , const char * string )
2018-06-15 10:20:01 +00:00
{
2018-12-18 00:09:05 +00:00
INT32 c ;
2018-06-15 10:20:01 +00:00
size_t chw , i , lastusablespace = 0 ;
size_t slen ;
char * newstring = Z_StrDup ( string ) ;
2018-07-31 09:10:02 +00:00
INT32 spacewidth = ( vid . width < 640 ) ? 8 : 4 , charwidth = ( vid . width < 640 ) ? 8 : 4 ;
2018-06-15 10:20:01 +00:00
slen = strlen ( string ) ;
x = 0 ;
for ( i = 0 ; i < slen ; + + i )
{
c = newstring [ i ] ;
2018-09-22 17:22:04 +00:00
if ( ( UINT8 ) c > = 0x80 & & ( UINT8 ) c < = 0x8F ) //color parsing! -Inuyasha 2.16.09
2018-06-15 10:20:01 +00:00
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 ] )
{
2018-07-31 09:10:02 +00:00
chw = spacewidth ;
2018-06-15 10:20:01 +00:00
lastusablespace = i ;
}
else
chw = charwidth ;
x + = chw ;
if ( lastusablespace ! = 0 & & x > w )
{
//CONS_Printf("Wrap at index %d\n", i);
newstring [ lastusablespace ] = ' \n ' ;
2018-08-02 22:52:07 +00:00
i = lastusablespace + 1 ;
2018-06-15 10:20:01 +00:00
lastusablespace = 0 ;
x = 0 ;
}
}
return newstring ;
}
2018-08-19 08:58:29 +00:00
2018-07-31 21:35:16 +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.
2018-12-23 02:17:01 +00:00
INT16 chatx = 13 , chaty = 169 ; // let's use this as our coordinates, shh
2018-06-15 10:20:01 +00:00
// chat stuff by VincyTM LOL XD!
// HU_DrawMiniChat
static void HU_drawMiniChat ( void )
{
2018-08-02 22:52:07 +00:00
INT32 x = chatx + 2 ;
INT32 charwidth = 4 , charheight = 6 ;
2018-10-21 12:51:34 +00:00
INT32 boxw = cv_chatwidth . value ;
2018-07-31 21:35:16 +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-02 22:52:07 +00:00
2018-07-31 21:35:16 +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-10-07 14:35:54 +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-08-02 22:52:07 +00:00
2018-10-21 12:51:34 +00:00
if ( splitscreen > 1 )
boxw = max ( 64 , boxw / 2 ) ;
2018-07-31 21:35:16 +00:00
for ( ; i > 0 ; i - - )
2018-08-02 22:52:07 +00:00
{
2018-10-21 12:51:34 +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 21:35:16 +00:00
size_t j = 0 ;
INT32 linescount = 0 ;
2018-08-02 22:52:07 +00:00
2018-12-23 02:17:01 +00:00
while ( msg [ j ] ) // iterate through msg
2018-08-02 22:52:07 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] < HU_FONTSTART ) // don't draw
2018-08-02 22:52:07 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] = = ' \n ' ) // get back down.
2018-07-31 21:35:16 +00:00
{
+ + j ;
2018-08-05 11:08:27 +00:00
if ( ! prev_linereturn )
2018-10-07 14:35:54 +00:00
{
2018-08-05 11:08:27 +00:00
linescount + = 1 ;
dx = 0 ;
2018-10-07 14:35:54 +00:00
}
2018-08-05 11:08:27 +00:00
prev_linereturn = true ;
2018-07-31 21:35:16 +00:00
continue ;
}
else if ( msg [ j ] & 0x80 ) // stolen from video.c, nice.
{
+ + j ;
continue ;
}
2018-08-02 22:52:07 +00:00
+ + j ;
2018-07-31 21:35:16 +00:00
}
else
{
j + + ;
}
2018-08-05 11:08:27 +00:00
prev_linereturn = false ;
2018-07-31 21:35:16 +00:00
dx + = charwidth ;
2018-10-21 12:51:34 +00:00
if ( dx > = boxw )
2018-07-31 21:35:16 +00:00
{
dx = 0 ;
linescount + = 1 ;
}
}
dy = 0 ;
dx = 0 ;
msglines + = linescount + 1 ;
}
2018-08-02 22:52:07 +00:00
2018-10-21 12:51:34 +00:00
y = chaty - charheight * ( msglines + 1 ) ;
2019-01-12 07:53:14 +00:00
# ifdef NETSPLITSCREEN
2018-10-21 12:51:34 +00:00
if ( splitscreen )
{
y - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
y + = 16 ;
}
2019-01-12 07:53:14 +00:00
else
# endif
y - = ( cv_kartspeedometer . value ? 16 : 0 ) ;
2018-10-21 12:51:34 +00:00
2018-08-02 22:52:07 +00:00
dx = 0 ;
2018-07-31 21:35:16 +00:00
dy = 0 ;
i = 0 ;
2018-08-05 11:08:27 +00:00
prev_linereturn = false ;
2018-08-02 22:52:07 +00:00
2018-12-23 02:17:01 +00:00
for ( ; i < = ( chat_nummsg_min - 1 ) ; i + + ) // iterate through our hot messages
2018-06-15 10:20:01 +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-06-15 10:20:01 +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-09-22 17:22:04 +00:00
UINT8 * colormap = NULL ;
2018-08-02 22:52:07 +00:00
2018-12-23 02:17:01 +00:00
while ( msg [ j ] ) // iterate through msg
2018-08-02 22:52:07 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] < HU_FONTSTART ) // don't draw
2018-08-02 22:52:07 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] = = ' \n ' ) // get back down.
2018-06-15 10:20:01 +00:00
{
+ + j ;
2018-08-05 11:08:27 +00:00
if ( ! prev_linereturn )
2018-10-07 14:35:54 +00:00
{
2018-08-05 11:08:27 +00:00
dy + = charheight ;
dx = 0 ;
2018-10-07 14:35:54 +00:00
}
2018-08-05 11:08:27 +00:00
prev_linereturn = true ;
2018-06-15 10:20:01 +00:00
continue ;
}
else if ( msg [ j ] & 0x80 ) // stolen from video.c, nice.
{
clrflag = ( ( msg [ j ] & 0x7f ) < < V_CHARCOLORSHIFT ) & V_CHARCOLORMASK ;
2018-09-22 17:22:04 +00:00
colormap = V_GetStringColormap ( clrflag ) ;
2018-06-15 10:20:01 +00:00
+ + j ;
continue ;
}
2018-08-02 22:52:07 +00:00
+ + j ;
2018-06-15 10:20:01 +00:00
}
else
{
2018-12-23 02:17:01 +00:00
if ( cv_chatbacktint . value ) // on request of wolfy
2018-08-05 15:34:51 +00:00
V_DrawFillConsoleMap ( x + dx + 2 , y + dy , charwidth , charheight , 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT ) ;
2018-07-31 21:35:16 +00:00
V_DrawChatCharacter ( x + dx + 2 , y + dy , msg [ j + + ] | V_SNAPTOBOTTOM | V_SNAPTOLEFT | transflag , ! cv_allcaps . value , colormap ) ;
2018-06-15 10:20:01 +00:00
}
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
dx + = charwidth ;
2018-08-05 11:08:27 +00:00
prev_linereturn = false ;
2018-10-21 12:51:34 +00:00
if ( dx > = boxw )
2018-06-15 10:20:01 +00:00
{
dx = 0 ;
dy + = charheight ;
}
}
dy + = charheight ;
dx = 0 ;
}
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// decrement addy and make that shit smooth:
addy / = 2 ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
}
// HU_DrawChatLog
2018-07-31 21:35:16 +00:00
static void HU_drawChatLog ( INT32 offset )
2018-06-15 10:20:01 +00:00
{
2018-09-10 11:41:53 +00:00
INT32 charwidth = 4 , charheight = 6 ;
2018-10-21 12:51:34 +00:00
INT32 boxw = cv_chatwidth . value , boxh = cv_chatheight . value ;
2018-09-10 11:41:53 +00:00
INT32 x = chatx + 2 , y , dx = 0 , dy = 0 ;
UINT32 i = 0 ;
INT32 chat_topy , chat_bottomy ;
2018-10-25 17:41:14 +00:00
INT32 highlight = HU_GetHighlightColor ( ) ;
2018-09-10 11:41:53 +00:00
boolean atbottom = false ;
2018-08-02 22:52:07 +00:00
2018-09-10 11:41:53 +00:00
// make sure that our scroll position isn't "illegal";
2018-06-15 10:20:01 +00:00
if ( chat_scroll > chat_maxscroll )
chat_scroll = chat_maxscroll ;
2018-08-02 22:52:07 +00:00
2018-12-23 01:52:32 +00:00
# ifdef NETSPLITSCREEN
2018-10-21 12:51:34 +00:00
if ( splitscreen )
{
boxh = max ( 6 , boxh / 2 ) ;
if ( splitscreen > 1 )
boxw = max ( 64 , boxw / 2 ) ;
}
2018-12-23 01:52:32 +00:00
# endif
2018-10-21 12:51:34 +00:00
y = chaty - offset * charheight - ( chat_scroll * charheight ) - boxh * charheight - 12 ;
2018-12-23 01:52:32 +00:00
# ifdef NETSPLITSCREEN
2018-10-21 12:51:34 +00:00
if ( splitscreen )
{
y - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
y + = 16 ;
}
else
2018-12-23 01:52:32 +00:00
# endif
2018-10-21 12:51:34 +00:00
y - = ( cv_kartspeedometer . value ? 16 : 0 ) ;
2018-09-10 11:41:53 +00:00
chat_topy = y + chat_scroll * charheight ;
2018-10-21 12:51:34 +00:00
chat_bottomy = chat_topy + boxh * charheight ;
2018-08-02 22:52:07 +00:00
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-02 22:52:07 +00:00
2018-12-23 02:17:01 +00:00
for ( i = 0 ; i < chat_nummsg_log ; i + + ) // iterate through our chatlog
2018-06-15 10:20:01 +00:00
{
INT32 clrflag = 0 ;
2018-08-02 22:52:07 +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-09-22 17:22:04 +00:00
UINT8 * colormap = NULL ;
2018-12-23 02:17:01 +00:00
while ( msg [ j ] ) // iterate through msg
2018-08-02 22:52:07 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] < HU_FONTSTART ) // don't draw
2018-08-02 22:52:07 +00:00
{
2018-12-23 02:17:01 +00:00
if ( msg [ j ] = = ' \n ' ) // get back down.
2018-06-15 10:20:01 +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-09-22 17:22:04 +00:00
colormap = V_GetStringColormap ( clrflag ) ;
2018-06-15 10:20:01 +00:00
+ + j ;
continue ;
}
2018-08-02 22:52:07 +00:00
+ + j ;
2018-06-15 10:20:01 +00:00
}
else
{
2018-07-31 21:35:16 +00:00
if ( ( y + dy + 2 > = chat_topy ) & & ( y + dy < ( chat_bottomy ) ) )
V_DrawChatCharacter ( x + dx + 2 , y + dy + 2 , msg [ j + + ] | V_SNAPTOBOTTOM | V_SNAPTOLEFT , ! cv_allcaps . value , colormap ) ;
2018-06-15 10:20:01 +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-06-15 10:20:01 +00:00
}
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
dx + = charwidth ;
2018-10-21 12:51:34 +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-06-15 10:20:01 +00:00
{
dx = 0 ;
dy + = charheight ;
}
}
dy + = charheight ;
dx = 0 ;
}
2018-08-02 22:52:07 +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-06-15 10:20:01 +00:00
{
2018-12-23 02:17:01 +00:00
atbottom = true ; // we should scroll
2018-06-15 10:20:01 +00:00
}
chat_scrollmedown = false ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +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-10-21 12:51:34 +00:00
if ( chat_maxscroll < = ( UINT32 ) boxh )
2018-06-15 10:20:01 +00:00
chat_maxscroll = 0 ;
2018-08-02 22:52:07 +00:00
else
2018-10-21 12:51:34 +00:00
chat_maxscroll - = boxh ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// if we're not bound by the time, autoscroll for next frame:
if ( atbottom )
chat_scroll = chat_maxscroll ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// draw arrows to indicate that we can (or not) scroll.
if ( chat_scroll > 0 )
2019-02-16 11:19:03 +00:00
V_DrawCharacter ( chatx - 9 , ( ( justscrolledup ) ? ( chat_topy - 1 ) : ( chat_topy ) ) , V_SNAPTOBOTTOM | V_SNAPTOLEFT | highlight | ' \x1A ' , false ) ; // up arrow
2018-06-15 10:20:01 +00:00
if ( chat_scroll < chat_maxscroll )
2019-02-16 11:19:03 +00:00
V_DrawCharacter ( chatx - 9 , chat_bottomy - ( ( justscrolleddown ) ? 5 : 6 ) , V_SNAPTOBOTTOM | V_SNAPTOLEFT | highlight | ' \x1B ' , false ) ; // down arrow
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
justscrolleddown = false ;
justscrolledup = false ;
2018-08-02 22:52:07 +00:00
}
2018-06-15 10:20:01 +00:00
2014-03-15 16:59:03 +00:00
//
// HU_DrawChat
//
// Draw chat input
//
2018-06-15 10:20:01 +00:00
2014-03-15 16:59:03 +00:00
static void HU_DrawChat ( void )
2018-08-02 22:52:07 +00:00
{
2018-07-31 21:35:16 +00:00
INT32 charwidth = 4 , charheight = 6 ;
2018-10-21 12:51:34 +00:00
INT32 boxw = cv_chatwidth . value ;
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-10-03 18:09:32 +00:00
INT32 cflag = 0 ;
2018-06-15 10:20:01 +00:00
const char * ntalk = " Say: " , * ttalk = " Team: " ;
const char * talk = ntalk ;
2018-10-03 18:09:32 +00:00
const char * mute = " Chat has been muted. " ;
2018-08-02 22:52:07 +00:00
2018-12-23 01:52:32 +00:00
# ifdef NETSPLITSCREEN
2018-10-21 12:51:34 +00:00
if ( splitscreen )
{
y - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
{
y + = 16 ;
boxw = max ( 64 , boxw / 2 ) ;
}
}
else
2018-12-23 01:52:32 +00:00
# endif
2018-10-21 12:51:34 +00:00
y - = ( cv_kartspeedometer . value ? 16 : 0 ) ;
2018-06-15 10:20:01 +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-10-07 14:35:54 +00:00
2018-10-03 18:09:32 +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-10-07 14:35:54 +00:00
}
2018-10-21 12:51:34 +00:00
V_DrawFillConsoleMap ( chatx , y - 1 , boxw , ( typelines * charheight ) , 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT ) ;
2018-10-07 14:35:54 +00:00
2018-06-15 10:20:01 +00:00
while ( talk [ i ] )
{
if ( talk [ i ] < HU_FONTSTART )
+ + i ;
else
2018-10-07 14:35:54 +00:00
{
2018-10-03 18:09:32 +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-10-07 14:35:54 +00:00
}
2018-06-15 10:20:01 +00:00
c + = charwidth ;
}
2018-10-07 14:35:54 +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.
2018-10-03 18:09:32 +00:00
if ( CHAT_MUTE )
2018-10-07 14:35:54 +00:00
{
2018-10-03 18:09:32 +00:00
HU_drawChatLog ( 0 ) ;
return ;
2018-10-07 14:35:54 +00:00
}
2018-06-15 10:20:01 +00:00
i = 0 ;
typelines = 1 ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
if ( ( strlen ( w_chat ) = = 0 | | c_input = = 0 ) & & hu_tick < 4 )
2018-07-31 21:35:16 +00:00
V_DrawChatCharacter ( chatx + 2 + c , y + 1 , ' _ ' | V_SNAPTOBOTTOM | V_SNAPTOLEFT | t , ! cv_allcaps . value , NULL ) ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
while ( w_chat [ i ] )
{
2018-07-31 21:35:16 +00:00
boolean skippedline = false ;
if ( c_input = = ( i + 1 ) )
2018-06-15 10:20:01 +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 21:35:16 +00:00
if ( hu_tick < 4 )
2018-08-02 22:52:07 +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 21:35:16 +00:00
{
typelines + = 1 ;
skippedline = true ;
2018-08-02 22:52:07 +00:00
}
}
2018-06-15 10:20:01 +00:00
//Hurdler: isn't it better like that?
if ( w_chat [ i ] < HU_FONTSTART )
+ + i ;
else
2018-07-31 21:35:16 +00:00
V_DrawChatCharacter ( chatx + c + 2 , y , w_chat [ i + + ] | V_SNAPTOBOTTOM | V_SNAPTOLEFT | t , ! cv_allcaps . value , NULL ) ;
2018-06-15 10:20:01 +00:00
c + = charwidth ;
2018-10-21 12:51:34 +00:00
if ( c > boxw - ( charwidth * 2 ) & & ! skippedline )
2018-06-15 10:20:01 +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-02 22:52:07 +00:00
{
INT32 count = 0 ;
2018-07-31 21:35:16 +00:00
INT32 p_dispy = chaty - charheight - 1 ;
2018-12-23 01:52:32 +00:00
# ifdef NETSPLITSCREEN
2018-10-21 12:51:34 +00:00
if ( splitscreen )
{
p_dispy - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
p_dispy + = 16 ;
}
else
2018-12-23 01:52:32 +00:00
# endif
2018-10-21 12:51:34 +00:00
p_dispy - = ( cv_kartspeedometer . value ? 16 : 0 ) ;
2018-12-17 19:43:59 +00:00
2018-10-07 14:35:54 +00:00
i = 0 ;
2018-06-15 10:20:01 +00:00
for ( i = 0 ; ( i < MAXPLAYERS ) ; i + + )
{
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// filter: (code needs optimization pls help I'm bad with C)
if ( w_chat [ 3 ] )
{
2018-10-07 14:35:54 +00:00
char * nodenum ;
UINT32 n ;
2018-06-15 10:20:01 +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-02 22:52:07 +00:00
2018-10-07 14:35:54 +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-06-15 10:20:01 +00:00
// special cases:
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
if ( ( n = = 0 ) & & ! ( w_chat [ 4 ] = = ' 0 ' ) )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( ! ( i < 10 ) )
2018-08-02 22:52:07 +00:00
continue ;
2018-06-15 10:20:01 +00:00
}
else if ( ( n = = 1 ) & & ! ( w_chat [ 3 ] = = ' 0 ' ) )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( ! ( ( i = = 1 ) | | ( ( i > = 10 ) & & ( i < = 19 ) ) ) )
continue ;
}
else if ( ( n = = 2 ) & & ! ( w_chat [ 3 ] = = ' 0 ' ) )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( ! ( ( i = = 2 ) | | ( ( i > = 20 ) & & ( i < = 29 ) ) ) )
continue ;
}
else if ( ( n = = 3 ) & & ! ( w_chat [ 3 ] = = ' 0 ' ) )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( ! ( ( i = = 3 ) | | ( ( i > = 30 ) & & ( i < = 31 ) ) ) )
continue ;
}
else // general case.
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( i ! = n )
continue ;
}
}
2018-08-02 22:52:07 +00:00
2018-07-31 21:35:16 +00:00
if ( playeringame [ i ] )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +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-10-21 12:51:34 +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-06-15 10:20:01 +00:00
count + + ;
}
}
2018-12-23 02:17:01 +00:00
if ( count = = 0 ) // no results.
2018-06-15 10:20:01 +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. " ) ;
2018-08-02 22:52:07 +00:00
}
2018-06-15 10:20:01 +00:00
}
2018-08-02 22:52:07 +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.
2018-06-15 10:20:01 +00:00
}
2018-10-21 12:51:34 +00:00
2018-12-17 19:43:59 +00:00
// For anyone who, for some godforsaken reason, likes oldchat.
2018-06-15 10:20:01 +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;
V_DrawCharacter ( HU_INPUTX + c , y , talk [ i + + ] | cv_constextsize . value | V_NOSCALESTART , ! cv_allcaps . value ) ;
}
c + = charwidth ;
}
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +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-08-02 22:52:07 +00:00
2014-03-15 16:59:03 +00:00
i = 0 ;
while ( w_chat [ i ] )
{
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +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-08-02 22:52:07 +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;
V_DrawCharacter ( HU_INPUTX + c , y , w_chat [ i + + ] | cv_constextsize . value | V_NOSCALESTART | t , ! cv_allcaps . value ) ;
}
c + = charwidth ;
if ( c > = vid . width )
{
c = 0 ;
y + = charheight ;
}
}
}
2019-01-06 06:00:30 +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
2018-09-27 20:55:21 +00:00
/*static inline void HU_DrawCrosshair(void)
2014-03-15 16:59:03 +00:00
{
2018-01-24 21:53:20 +00:00
INT32 i , x , y ;
2014-03-15 16:59:03 +00:00
i = cv_crosshair . value & 3 ;
if ( ! i )
return ;
if ( ( netgame | | multiplayer ) & & players [ displayplayer ] . spectator )
return ;
# ifdef HWRENDER
if ( rendermode ! = render_soft )
2018-01-24 21:53:20 +00:00
{
x = ( INT32 ) gr_basewindowcenterx ;
2014-03-15 16:59:03 +00:00
y = ( INT32 ) gr_basewindowcentery ;
2018-01-24 21:53:20 +00:00
}
2014-03-15 16:59:03 +00:00
else
# endif
2018-01-24 21:53:20 +00:00
{
x = viewwindowx + ( viewwidth > > 1 ) ;
2014-03-15 16:59:03 +00:00
y = viewwindowy + ( viewheight > > 1 ) ;
2018-01-24 21:53:20 +00:00
}
2014-03-15 16:59:03 +00:00
2018-01-24 21:53:20 +00:00
V_DrawScaledPatch ( x , y , V_NOSCALESTART | V_OFFSET | V_TRANSLUCENT , crosshair [ i - 1 ] ) ;
2014-03-15 16:59:03 +00:00
}
static inline void HU_DrawCrosshair2 ( void )
{
2018-01-24 21:53:20 +00:00
INT32 i , x , y ;
2014-03-15 16:59:03 +00:00
i = cv_crosshair2 . value & 3 ;
if ( ! i )
return ;
if ( ( netgame | | multiplayer ) & & players [ secondarydisplayplayer ] . spectator )
return ;
# ifdef HWRENDER
if ( rendermode ! = render_soft )
2018-01-24 21:53:20 +00:00
{
x = ( INT32 ) gr_basewindowcenterx ;
2014-03-15 16:59:03 +00:00
y = ( INT32 ) gr_basewindowcentery ;
2018-01-24 21:53:20 +00:00
}
2014-03-15 16:59:03 +00:00
else
# endif
2018-01-24 21:53:20 +00:00
{
x = viewwindowx + ( viewwidth > > 1 ) ;
2014-03-15 16:59:03 +00:00
y = viewwindowy + ( viewheight > > 1 ) ;
2018-01-24 21:53:20 +00:00
}
2014-03-15 16:59:03 +00:00
if ( splitscreen )
{
2018-01-24 21:53:20 +00:00
if ( splitscreen > 1 )
2014-03-15 16:59:03 +00:00
# ifdef HWRENDER
2018-01-24 21:53:20 +00:00
if ( rendermode ! = render_soft )
x + = ( INT32 ) gr_viewwidth ;
else
# endif
x + = viewwidth ;
2014-03-15 16:59:03 +00:00
else
2018-01-24 21:53:20 +00:00
{
# ifdef HWRENDER
if ( rendermode ! = render_soft )
y + = ( INT32 ) gr_viewheight ;
else
2014-03-15 16:59:03 +00:00
# endif
2018-01-24 21:53:20 +00:00
y + = viewheight ;
}
2014-03-15 16:59:03 +00:00
2018-01-24 21:53:20 +00:00
V_DrawScaledPatch ( x , y , V_NOSCALESTART | V_OFFSET | V_TRANSLUCENT , crosshair [ i - 1 ] ) ;
2014-03-15 16:59:03 +00:00
}
}
2017-12-18 10:56:38 +00:00
static inline void HU_DrawCrosshair3 ( void )
{
2018-01-24 21:53:20 +00:00
INT32 i , x , y ;
2017-12-18 10:56:38 +00:00
i = cv_crosshair3 . value & 3 ;
if ( ! i )
return ;
if ( ( netgame | | multiplayer ) & & players [ thirddisplayplayer ] . spectator )
return ;
# ifdef HWRENDER
if ( rendermode ! = render_soft )
2018-01-24 21:53:20 +00:00
{
x = ( INT32 ) gr_basewindowcenterx ;
2017-12-18 10:56:38 +00:00
y = ( INT32 ) gr_basewindowcentery ;
2018-01-24 21:53:20 +00:00
}
2017-12-18 10:56:38 +00:00
else
# endif
2018-01-24 21:53:20 +00:00
{
x = viewwindowx + ( viewwidth > > 1 ) ;
2017-12-18 10:56:38 +00:00
y = viewwindowy + ( viewheight > > 1 ) ;
2018-01-24 21:53:20 +00:00
}
2017-12-18 10:56:38 +00:00
2017-12-19 02:11:59 +00:00
if ( splitscreen > 1 )
2017-12-18 10:56:38 +00:00
{
# ifdef HWRENDER
if ( rendermode ! = render_soft )
y + = ( INT32 ) gr_viewheight ;
else
# endif
y + = viewheight ;
2018-01-24 21:53:20 +00:00
V_DrawScaledPatch ( x , y , V_NOSCALESTART | V_OFFSET | V_TRANSLUCENT , crosshair [ i - 1 ] ) ;
2017-12-18 10:56:38 +00:00
}
}
static inline void HU_DrawCrosshair4 ( void )
{
2018-01-24 21:53:20 +00:00
INT32 i , x , y ;
2017-12-18 10:56:38 +00:00
i = cv_crosshair4 . value & 3 ;
if ( ! i )
return ;
if ( ( netgame | | multiplayer ) & & players [ fourthdisplayplayer ] . spectator )
return ;
# ifdef HWRENDER
if ( rendermode ! = render_soft )
2018-01-24 21:53:20 +00:00
{
x = ( INT32 ) gr_basewindowcenterx ;
2017-12-18 10:56:38 +00:00
y = ( INT32 ) gr_basewindowcentery ;
2018-01-24 21:53:20 +00:00
}
2017-12-18 10:56:38 +00:00
else
# endif
2018-01-24 21:53:20 +00:00
{
x = viewwindowx + ( viewwidth > > 1 ) ;
2017-12-18 10:56:38 +00:00
y = viewwindowy + ( viewheight > > 1 ) ;
2018-01-24 21:53:20 +00:00
}
2017-12-18 10:56:38 +00:00
2017-12-19 02:11:59 +00:00
if ( splitscreen > 2 )
2014-03-15 16:59:03 +00:00
{
# ifdef HWRENDER
if ( rendermode ! = render_soft )
2018-01-24 21:53:20 +00:00
{
x + = ( INT32 ) gr_viewwidth ;
2014-03-15 16:59:03 +00:00
y + = ( INT32 ) gr_viewheight ;
2018-01-24 21:53:20 +00:00
}
2014-03-15 16:59:03 +00:00
else
# endif
2018-01-24 21:53:20 +00:00
{
x + = viewwidth ;
2014-03-15 16:59:03 +00:00
y + = viewheight ;
2018-01-24 21:53:20 +00:00
}
2014-03-15 16:59:03 +00:00
2018-01-24 21:53:20 +00:00
V_DrawScaledPatch ( x , y , V_NOSCALESTART | V_OFFSET | V_TRANSLUCENT , crosshair [ i - 1 ] ) ;
2014-03-15 16:59:03 +00:00
}
2018-09-27 20:55:21 +00:00
} */
2014-03-15 16:59:03 +00:00
static void HU_DrawCEcho ( void )
{
INT32 i = 0 ;
INT32 y = ( BASEVIDHEIGHT / 2 ) - 4 ;
INT32 pnumlines = 0 ;
UINT32 realflags = cechoflags ;
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 ) ;
y + = ( ( realflags & V_RETURN8 ) ? 8 : 12 ) ;
echoptr = line ;
echoptr + + ;
}
- - cechotimer ;
}
//
// demo info stuff
//
UINT32 hu_demotime ;
2018-01-22 00:15:26 +00:00
UINT32 hu_demolap ;
2014-03-15 16:59:03 +00:00
static void HU_DrawDemoInfo ( void )
{
2018-10-27 19:43:33 +00:00
V_DrawCenteredString ( ( BASEVIDWIDTH / 2 ) , BASEVIDHEIGHT - 40 , 0 , M_GetText ( " Replay: " ) ) ;
V_DrawCenteredString ( ( BASEVIDWIDTH / 2 ) , BASEVIDHEIGHT - 32 , V_ALLOWLOWERCASE , player_names [ 0 ] ) ;
2014-11-12 00:55:07 +00:00
if ( modeattacking )
{
2018-10-27 19:43:33 +00:00
V_DrawRightAlignedString ( ( BASEVIDWIDTH / 2 ) - 4 , BASEVIDHEIGHT - 24 , V_YELLOWMAP | V_MONOSPACE , " BEST TIME: " ) ;
2014-11-12 00:55:07 +00:00
if ( hu_demotime ! = UINT32_MAX )
2018-10-27 19:43:33 +00:00
V_DrawString ( ( BASEVIDWIDTH / 2 ) + 4 , BASEVIDHEIGHT - 24 , 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-10-27 19:43:33 +00:00
V_DrawString ( ( BASEVIDWIDTH / 2 ) + 4 , BASEVIDHEIGHT - 24 , V_MONOSPACE , " --'-- \" -- " ) ;
2018-01-22 00:15:26 +00:00
2018-10-27 19:43:33 +00:00
V_DrawRightAlignedString ( ( BASEVIDWIDTH / 2 ) - 4 , BASEVIDHEIGHT - 16 , V_YELLOWMAP | V_MONOSPACE , " BEST LAP: " ) ;
2018-01-22 00:15:26 +00:00
if ( hu_demolap ! = UINT32_MAX )
2018-10-27 19:43:33 +00:00
V_DrawString ( ( BASEVIDWIDTH / 2 ) + 4 , BASEVIDHEIGHT - 16 , V_MONOSPACE , va ( " %i'%02i \" %02i " ,
2018-01-22 00:15:26 +00:00
G_TicsToMinutes ( hu_demolap , true ) ,
G_TicsToSeconds ( hu_demolap ) ,
G_TicsToCentiseconds ( hu_demolap ) ) ) ;
else
2018-10-27 19:43:33 +00:00
V_DrawString ( ( BASEVIDWIDTH / 2 ) + 4 , BASEVIDHEIGHT - 16 , V_MONOSPACE , " --'-- \" -- " ) ;
2014-11-12 00:55:07 +00:00
}
2014-03-15 16:59:03 +00:00
}
2019-01-04 09:44:55 +00:00
//
// Song credits
//
static void HU_DrawSongCredits ( void )
{
2019-01-10 02:00:45 +00:00
char * str ;
INT32 len , destx ;
2019-01-04 21:47:03 +00:00
INT32 y = ( splitscreen ? ( BASEVIDHEIGHT / 2 ) - 4 : 32 ) ;
2019-01-04 09:44:55 +00:00
INT32 bgt ;
2019-01-10 02:00:45 +00:00
if ( ! cursongcredit . def ) // No def
return ;
str = va ( " \x1F " " %s " , cursongcredit . def - > source ) ;
len = V_ThinStringWidth ( str , V_ALLOWLOWERCASE | V_6WIDTHSPACE ) ;
destx = ( len + 7 ) ;
2019-01-04 09:44:55 +00:00
if ( cursongcredit . anim )
{
2019-01-04 21:47:03 +00:00
if ( cursongcredit . trans > 0 )
cursongcredit . trans - - ;
if ( cursongcredit . x < destx )
cursongcredit . x + = ( destx - cursongcredit . x ) / 2 ;
if ( cursongcredit . x > destx )
cursongcredit . x = destx ;
2019-01-04 09:44:55 +00:00
cursongcredit . anim - - ;
}
else
{
2019-01-04 21:47:03 +00:00
if ( cursongcredit . trans < NUMTRANSMAPS )
cursongcredit . trans + + ;
if ( cursongcredit . x > 0 )
cursongcredit . x / = 2 ;
if ( cursongcredit . x < 0 )
cursongcredit . x = 0 ;
2019-01-04 09:44:55 +00:00
}
2019-01-04 21:47:03 +00:00
bgt = ( NUMTRANSMAPS / 2 ) + ( cursongcredit . trans / 2 ) ;
2019-01-04 09:44:55 +00:00
if ( bgt < NUMTRANSMAPS )
2019-01-04 21:47:03 +00:00
V_DrawScaledPatch ( cursongcredit . x , y - 2 , V_SNAPTOLEFT | ( bgt < < V_ALPHASHIFT ) , songcreditbg ) ;
if ( cursongcredit . trans < NUMTRANSMAPS )
V_DrawRightAlignedThinString ( cursongcredit . x , y , V_ALLOWLOWERCASE | V_6WIDTHSPACE | V_SNAPTOLEFT | ( cursongcredit . trans < < V_ALPHASHIFT ) , str ) ;
2019-01-04 09:44:55 +00:00
}
2019-02-16 02:51:44 +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-08-02 22:52:07 +00:00
{
// count down the scroll timer.
2018-06-15 10:20:01 +00:00
if ( chat_scrolltime > 0 )
chat_scrolltime - - ;
2018-08-03 22:01:09 +00:00
if ( ! OLDCHAT )
2018-06-15 10:20:01 +00:00
HU_DrawChat ( ) ;
else
2019-01-05 20:59:23 +00:00
HU_DrawChat_Old ( ) ; // why the fuck.........................
2018-06-15 10:20:01 +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-06-15 10:20:01 +00:00
}
2018-08-02 22:52:07 +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-06-15 10:20:01 +00:00
{
size_t i = 0 ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// handle spam while we're at it:
for ( ; ( i < MAXPLAYERS ) ; i + + )
2018-08-02 22:52:07 +00:00
{
2018-12-23 02:17:01 +00:00
if ( stop_spamming [ i ] > 0 )
stop_spamming [ i ] - - ;
2018-08-02 22:52:07 +00:00
}
2018-06-15 10:20:01 +00:00
// handle chat timers
for ( i = 0 ; ( i < chat_nummsg_min ) ; i + + )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +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
2018-01-27 06:17:08 +00:00
| | gamestate = = GS_GAMEEND
2018-07-03 23:58:32 +00:00
| | gamestate = = GS_VOTING | | gamestate = = GS_WAITINGPLAYERS ) // SRB2kart
2014-03-15 16:59:03 +00:00
return ;
// draw multiplayer rankings
if ( hu_showscores )
{
if ( netgame | | multiplayer )
{
# ifdef HAVE_BLUA
if ( LUA_HudEnabled ( hud_rankings ) )
# endif
2018-10-31 14:18:05 +00:00
HU_DrawRankings ( ) ;
2014-03-15 16:59:03 +00:00
# ifdef HAVE_BLUA
LUAh_ScoresHUD ( ) ;
# endif
2018-10-31 16:14:32 +00:00
}
2018-11-17 21:21:10 +00:00
}
2014-03-15 16:59:03 +00:00
if ( gamestate ! = GS_LEVEL )
return ;
// draw the crosshair, not when viewing demos nor with chasecam
2018-09-27 20:55:21 +00:00
/*if (!automapactive && !demoplayback)
2018-01-24 22:00:22 +00:00
{
if ( cv_crosshair . value & & ! camera . chase & & ! players [ displayplayer ] . spectator )
HU_DrawCrosshair ( ) ;
2014-03-15 16:59:03 +00:00
2018-01-24 22:00:22 +00:00
if ( cv_crosshair2 . value & & ! camera2 . chase & & ! players [ secondarydisplayplayer ] . spectator )
HU_DrawCrosshair2 ( ) ;
2018-08-02 22:52:07 +00:00
2018-01-24 22:00:22 +00:00
if ( cv_crosshair3 . value & & ! camera3 . chase & & ! players [ thirddisplayplayer ] . spectator )
HU_DrawCrosshair3 ( ) ;
2017-12-18 10:56:38 +00:00
2018-01-24 22:00:22 +00:00
if ( cv_crosshair4 . value & & ! camera4 . chase & & ! players [ fourthdisplayplayer ] . spectator )
HU_DrawCrosshair4 ( ) ;
2018-09-27 20:55:21 +00:00
} */
2014-03-21 18:42:55 +00:00
2019-01-04 09:44:55 +00:00
// draw song credits
if ( cv_songcredits . value )
HU_DrawSongCredits ( ) ;
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 ) ;
}
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
//======================================================================
2018-06-15 10:20:01 +00:00
//
// HU_drawPing
//
2019-02-16 14:29:48 +00:00
void HU_drawPing ( INT32 x , INT32 y , UINT32 ping , INT32 flags )
2018-06-15 10:20:01 +00:00
{
2019-02-16 03:08:08 +00:00
INT32 gfxnum = 4 ; // gfx to draw
UINT8 const * colormap = R_GetTranslationColormap ( TC_RAINBOW , SKINCOLOR_SALMON , GTC_CACHE ) ;
2018-12-18 00:02:22 +00:00
2019-02-16 03:08:08 +00:00
if ( ping < 76 )
gfxnum = 0 ;
else if ( ping < 137 )
gfxnum = 1 ;
2018-06-15 10:20:01 +00:00
else if ( ping < 256 )
2019-02-16 03:08:08 +00:00
gfxnum = 2 ;
else if ( ping < 500 )
gfxnum = 3 ;
2018-08-02 22:52:07 +00:00
2019-02-16 03:08:08 +00:00
V_DrawScaledPatch ( x , y , flags , pinggfx [ gfxnum ] ) ;
if ( servermaxping & & ping > servermaxping & & hu_tick < 4 ) // flash ping red if too high
V_DrawPingNum ( x , y + 9 , flags , ping , colormap ) ;
else
V_DrawPingNum ( x , y + 9 , flags , ping , NULL ) ;
2018-08-02 22:52:07 +00:00
}
2018-06-15 10:20:01 +00:00
2014-03-15 16:59:03 +00:00
//
2018-11-01 00:10:34 +00:00
// HU_DrawTabRankings -- moved to k_kart.c
2014-03-15 16:59:03 +00:00
//
//
// HU_DrawTeamTabRankings
//
2018-07-18 19:23:46 +00:00
/*void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
2014-03-15 16:59:03 +00:00
{
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 ] ;
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 ;
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 )
| ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT )
| 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
if ( ! players [ tab [ i ] . num ] . powers [ pw_super ]
| | ( ( leveltime / 7 ) & 1 ) )
{
HU_DrawEmeralds ( x - 12 , y + 2 , tab [ i ] . emeralds ) ;
}
if ( players [ tab [ i ] . num ] . powers [ pw_super ] )
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
V_DrawSmallMappedPatch ( x , y - 4 , 0 , facewantprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
2014-03-15 16:59:03 +00:00
}
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 ) ;
if ( players [ tab [ i ] . num ] . health < = 0 )
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
V_DrawSmallTranslucentMappedPatch ( x , y - 4 , 0 , facerankprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
2014-03-15 16:59:03 +00:00
else
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
V_DrawSmallMappedPatch ( x , y - 4 , 0 , facerankprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
2014-03-15 16:59:03 +00:00
}
2018-01-29 01:37:53 +00:00
V_DrawRightAlignedThinString ( x + 120 , y - 1 , ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
2018-06-15 10:20:01 +00:00
if ( ! splitscreen )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( ! ( tab [ i ] . num = = serverplayer ) )
HU_drawPing ( x + 113 , y + 2 , playerpingtable [ tab [ i ] . num ] , false ) ;
2018-08-02 22:52:07 +00:00
}
2018-07-31 09:10:02 +00:00
V_DrawRightAlignedThinString ( x + 100 , y , ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
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 ] ;
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 + + )
{
if ( players [ tab [ i ] . num ] . spectator )
continue ; //ignore them.
2018-07-31 09:10:02 +00:00
strlcpy ( name , tab [ i ] . name , 7 ) ;
2018-06-15 10:20:01 +00:00
if ( ! ( tab [ i ] . num = = serverplayer ) )
HU_drawPing ( x + 113 , y + 2 , playerpingtable [ tab [ i ] . num ] , false ) ;
2018-07-31 09:10:02 +00:00
//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 )
| ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT )
| V_ALLOWLOWERCASE , name ) ;
if ( G_GametypeUsesLives ( ) ) //show lives
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 ) ;
// Draw emeralds
if ( ! players [ tab [ i ] . num ] . powers [ pw_super ]
| | ( ( 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 ;
if ( players [ tab [ i ] . num ] . powers [ pw_super ] )
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
V_DrawSmallScaledPatch ( x , y - 4 , 0 , facewantprefix [ players [ tab [ i ] . num ] . skin ] ) ;
2014-03-15 16:59:03 +00:00
else
{
if ( players [ tab [ i ] . num ] . health < = 0 )
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
V_DrawSmallTranslucentPatch ( x , y - 4 , 0 , facerankprefix [ players [ tab [ i ] . num ] . skin ] ) ;
2014-03-15 16:59:03 +00:00
else
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
V_DrawSmallScaledPatch ( x , y - 4 , 0 , facerankprefix [ players [ tab [ i ] . num ] . skin ] ) ;
2014-03-15 16:59:03 +00:00
}
}
else
{
if ( players [ tab [ i ] . num ] . powers [ pw_super ] )
{
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo ? players [ tab [ i ] . num ] . mo - > color : tab [ i ] . color , GTC_CACHE ) ;
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
V_DrawSmallMappedPatch ( x , y - 4 , 0 , facewantprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
2014-03-15 16:59:03 +00:00
}
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 ) ;
if ( players [ tab [ i ] . num ] . health < = 0 )
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
V_DrawSmallTranslucentMappedPatch ( x , y - 4 , 0 , facerankprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
2014-03-15 16:59:03 +00:00
else
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
V_DrawSmallMappedPatch ( x , y - 4 , 0 , facerankprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
2014-03-15 16:59:03 +00:00
}
}
2014-03-18 17:56:54 +00:00
// All data drawn with thin string for space.
2018-03-04 20:27:52 +00:00
if ( G_RaceGametype ( ) )
2014-03-15 16:59:03 +00:00
{
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
2018-07-31 09:10:02 +00:00
V_DrawRightAlignedThinString ( x + 146 , y , ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
2014-03-15 16:59:03 +00:00
}
else
2018-07-31 09:10:02 +00:00
V_DrawRightAlignedThinString ( x + 146 , y , ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT ) , 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
2018-07-31 09:10:02 +00:00
V_DrawRightAlignedThinString ( x + 100 , y , ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
2014-03-15 16:59:03 +00:00
y + = 16 ;
if ( y > 160 )
{
y = 32 ;
x + = BASEVIDWIDTH / 2 ;
}
}
2019-01-06 06:00:30 +00:00
}
2014-03-15 16:59:03 +00:00
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 ] ;
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 + + )
{
if ( players [ tab [ i ] . num ] . spectator )
continue ; //ignore them.
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 )
| ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT )
| 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
if ( ! players [ tab [ i ] . num ] . powers [ pw_super ]
| | ( ( 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
{
if ( players [ tab [ i ] . num ] . health < = 0 )
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
{
if ( players [ tab [ i ] . num ] . powers [ pw_super ] )
{
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 ) ;
if ( players [ tab [ i ] . num ] . health < = 0 )
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
V_DrawRightAlignedThinString ( x + 128 , y , ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
}
else
V_DrawRightAlignedThinString ( x + 128 , y , ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT ) , va ( " %i:%02i.%02i " , G_TicsToMinutes ( tab [ i ] . count , true ) , G_TicsToSeconds ( tab [ i ] . count ) , G_TicsToCentiseconds ( tab [ i ] . count ) ) ) ;
}
else
V_DrawRightAlignedThinString ( x + 128 , y , ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
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 )
V_DrawSmallScaledPatch ( x , y - 6 , 0 , tinyemeraldpics [ 0 ] ) ;
if ( pemeralds & EMERALD2 )
V_DrawSmallScaledPatch ( x + 4 , y - 3 , 0 , tinyemeraldpics [ 1 ] ) ;
if ( pemeralds & EMERALD3 )
V_DrawSmallScaledPatch ( x + 4 , y + 3 , 0 , tinyemeraldpics [ 2 ] ) ;
if ( pemeralds & EMERALD4 )
V_DrawSmallScaledPatch ( x , y + 6 , 0 , tinyemeraldpics [ 3 ] ) ;
if ( pemeralds & EMERALD5 )
V_DrawSmallScaledPatch ( x - 4 , y + 3 , 0 , tinyemeraldpics [ 4 ] ) ;
if ( pemeralds & EMERALD6 )
V_DrawSmallScaledPatch ( x - 4 , y - 3 , 0 , tinyemeraldpics [ 5 ] ) ;
if ( pemeralds & EMERALD7 )
V_DrawSmallScaledPatch ( x , y , 0 , tinyemeraldpics [ 6 ] ) ;
2019-01-06 06:00:30 +00:00
} */
2014-03-15 16:59:03 +00:00
//
// HU_DrawSpectatorTicker
//
static inline void HU_DrawSpectatorTicker ( void )
{
2018-10-27 19:43:33 +00:00
INT32 i ;
INT32 length = 0 , height = 174 ;
INT32 totallength = 0 , templength = - 8 ;
INT32 dupadjust = ( vid . width / vid . dupx ) , duptweak = ( dupadjust - BASEVIDWIDTH ) / 2 ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
if ( playeringame [ i ] & & players [ i ] . spectator )
totallength + = ( signed ) strlen ( player_names [ i ] ) * 8 + 16 ;
2018-10-27 19:43:33 +00:00
length - = ( leveltime % ( totallength + dupadjust + 8 ) ) ;
length + = dupadjust ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
if ( playeringame [ i ] & & players [ i ] . spectator )
{
char * pos ;
char initial [ MAXPLAYERNAME + 1 ] ;
char current [ MAXPLAYERNAME + 1 ] ;
2018-10-27 19:43:33 +00:00
INT32 len ;
len = ( ( signed ) strlen ( player_names [ i ] ) * 8 + 16 ) ;
2014-03-15 16:59:03 +00:00
strcpy ( initial , player_names [ i ] ) ;
pos = initial ;
2018-10-27 19:43:33 +00:00
if ( length > = - len )
2014-03-15 16:59:03 +00:00
{
2018-10-27 19:43:33 +00:00
if ( length < - 8 )
2014-03-15 16:59:03 +00:00
{
2018-10-27 19:43:33 +00:00
UINT8 eatenchars = ( UINT8 ) ( abs ( length ) / 8 ) ;
2014-03-15 16:59:03 +00:00
if ( eatenchars < = strlen ( initial ) )
{
// Eat one letter off the left side,
// then compensate the drawing position.
pos + = eatenchars ;
strcpy ( current , pos ) ;
2018-10-27 19:43:33 +00:00
templength = ( ( length + 8 ) % 8 ) ;
2014-03-15 16:59:03 +00:00
}
else
{
strcpy ( current , " " ) ;
templength = length ;
}
}
else
{
strcpy ( current , initial ) ;
templength = length ;
}
2018-10-27 19:43:33 +00:00
V_DrawString ( templength - duptweak , height , V_TRANSLUCENT | V_ALLOWLOWERCASE , current ) ;
2014-03-15 16:59:03 +00:00
}
2018-10-27 19:43:33 +00:00
if ( ( length + = len ) > = dupadjust + 8 )
break ;
2014-03-15 16:59:03 +00:00
}
}
//
// HU_DrawRankings
//
static void HU_DrawRankings ( void )
{
patch_t * p ;
playersort_t tab [ MAXPLAYERS ] ;
2018-07-19 14:56:21 +00:00
INT32 i , j , scorelines , hilicol , numplayersingame = 0 ;
2014-03-15 16:59:03 +00:00
boolean completed [ MAXPLAYERS ] ;
2018-07-18 19:23:46 +00:00
UINT32 whiteplayer = MAXPLAYERS ;
2014-03-15 16:59:03 +00:00
2018-10-27 19:43:33 +00:00
V_DrawFadeScreen ( 0xFF00 , 16 ) ; // A little more readable, and prevents cheating the fades under other circumstances.
2018-07-18 20:40:04 +00:00
if ( cons_menuhighlight . value )
hilicol = cons_menuhighlight . value ;
else if ( modeattacking )
hilicol = V_ORANGEMAP ;
else
hilicol = ( ( gametype = = GT_RACE ) ? V_SKYMAP : V_REDMAP ) ;
2014-03-15 16:59:03 +00:00
// draw the current gametype in the lower right
2018-07-18 20:40:04 +00:00
if ( modeattacking )
2018-10-27 19:43:33 +00:00
V_DrawString ( 4 , 188 , hilicol | V_SNAPTOBOTTOM | V_SNAPTOLEFT , " Record Attack " ) ;
2018-07-18 20:40:04 +00:00
else
2018-10-27 19:43:33 +00:00
V_DrawString ( 4 , 188 , hilicol | V_SNAPTOBOTTOM | V_SNAPTOLEFT , gametype_cons_t [ gametype ] . strvalue ) ;
2014-03-15 16:59:03 +00:00
if ( G_GametypeHasTeams ( ) )
{
if ( gametype = = GT_CTF )
p = bflagico ;
else
p = bmatcico ;
V_DrawSmallScaledPatch ( 128 - SHORT ( p - > width ) / 4 , 4 , 0 , p ) ;
V_DrawCenteredString ( 128 , 16 , 0 , va ( " %u " , bluescore ) ) ;
if ( gametype = = GT_CTF )
p = rflagico ;
else
p = rmatcico ;
V_DrawSmallScaledPatch ( 192 - SHORT ( p - > width ) / 4 , 4 , 0 , p ) ;
V_DrawCenteredString ( 192 , 16 , 0 , va ( " %u " , redscore ) ) ;
}
2018-03-04 20:27:52 +00:00
if ( ! G_RaceGametype ( ) )
2014-03-15 16:59:03 +00:00
{
if ( cv_timelimit . value & & timelimitintics > 0 )
{
2018-07-18 20:40:04 +00:00
UINT32 timeval = ( timelimitintics + starttime + 1 - leveltime ) ;
if ( timeval > timelimitintics + 1 )
timeval = timelimitintics ;
timeval / = TICRATE ;
2014-03-15 16:59:03 +00:00
2018-07-19 13:39:46 +00:00
if ( leveltime < = ( timelimitintics + starttime ) )
2014-03-15 16:59:03 +00:00
{
V_DrawCenteredString ( 64 , 8 , 0 , " TIME LEFT " ) ;
2018-07-18 20:40:04 +00:00
V_DrawCenteredString ( 64 , 16 , hilicol , va ( " %u " , timeval ) ) ;
2014-03-15 16:59:03 +00:00
}
// overtime
2018-07-18 20:40:04 +00:00
if ( ! players [ consoleplayer ] . exiting & & ( leveltime > ( timelimitintics + starttime + TICRATE / 2 ) ) & & cv_overtime . value )
2014-03-15 16:59:03 +00:00
{
V_DrawCenteredString ( 64 , 8 , 0 , " TIME LEFT " ) ;
2018-07-18 20:40:04 +00:00
V_DrawCenteredString ( 64 , 16 , hilicol , " OVERTIME " ) ;
2014-03-15 16:59:03 +00:00
}
}
if ( cv_pointlimit . value > 0 )
{
V_DrawCenteredString ( 256 , 8 , 0 , " POINT LIMIT " ) ;
2018-07-18 20:40:04 +00:00
V_DrawCenteredString ( 256 , 16 , hilicol , va ( " %d " , cv_pointlimit . value ) ) ;
2014-03-15 16:59:03 +00:00
}
}
2018-07-18 20:40:04 +00:00
/*else if (gametype == GT_COOP)
2014-03-15 16:59:03 +00:00
{
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 ) ) ;
2018-07-18 20:40:04 +00:00
} */
2014-03-15 16:59:03 +00:00
else
{
if ( circuitmap )
{
2018-07-18 20:40:04 +00:00
V_DrawCenteredString ( 64 , 8 , 0 , " LAP COUNT " ) ;
V_DrawCenteredString ( 64 , 16 , hilicol , va ( " %d " , cv_numlaps . value ) ) ;
2014-03-15 16:59:03 +00:00
}
2018-07-18 20:40:04 +00:00
V_DrawCenteredString ( 256 , 8 , 0 , " GAME SPEED " ) ;
V_DrawCenteredString ( 256 , 16 , hilicol , cv_kartspeed . string ) ;
2014-03-15 16:59:03 +00:00
}
// 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.
2018-07-18 19:23:46 +00:00
if ( ! splitscreen )
whiteplayer = demoplayback ? displayplayer : consoleplayer ;
2014-03-15 16:59:03 +00:00
scorelines = 0 ;
memset ( completed , 0 , sizeof ( completed ) ) ;
memset ( tab , 0 , sizeof ( playersort_t ) * MAXPLAYERS ) ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
tab [ i ] . num = - 1 ;
2018-07-18 19:23:46 +00:00
tab [ i ] . name = NULL ;
tab [ i ] . count = INT32_MAX ;
2018-10-30 23:01:17 +00:00
if ( ! playeringame [ i ] | | players [ i ] . spectator | | ! players [ i ] . mo )
2018-07-18 19:23:46 +00:00
continue ;
2014-03-15 16:59:03 +00:00
2018-07-18 19:23:46 +00:00
numplayersingame + + ;
2014-03-15 16:59:03 +00:00
}
2018-07-18 19:23:46 +00:00
for ( j = 0 ; j < numplayersingame ; j + + )
2014-03-15 16:59:03 +00:00
{
2018-11-18 20:05:02 +00:00
UINT8 lowestposition = MAXPLAYERS + 1 ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2018-10-30 23:01:17 +00:00
if ( completed [ i ] | | ! playeringame [ i ] | | players [ i ] . spectator | | ! players [ i ] . mo )
2018-07-18 19:23:46 +00:00
continue ;
if ( players [ i ] . kartstuff [ k_position ] > = lowestposition )
continue ;
tab [ scorelines ] . num = i ;
lowestposition = players [ i ] . kartstuff [ k_position ] ;
2014-03-15 16:59:03 +00:00
}
2018-07-18 19:23:46 +00:00
i = tab [ scorelines ] . num ;
completed [ i ] = true ;
tab [ scorelines ] . name = player_names [ i ] ;
if ( G_RaceGametype ( ) )
{
if ( circuitmap )
tab [ scorelines ] . count = players [ i ] . laps + 1 ;
else
tab [ scorelines ] . count = players [ i ] . realtime ;
}
else
tab [ scorelines ] . count = players [ i ] . marescore ;
2014-03-15 16:59:03 +00:00
scorelines + + ;
2018-08-24 12:36:03 +00:00
# if MAXPLAYERS > 16
if ( scorelines > 16 )
break ; //dont draw past bottom of screen, show the best only
# endif
}
2014-03-15 16:59:03 +00:00
2018-07-18 19:23:46 +00:00
/*if (G_GametypeHasTeams())
HU_DrawTeamTabRankings ( tab , whiteplayer ) ; //separate function for Spazzo's silly request -- gotta fix this up later
else if ( scorelines > 10 ) */
Stupidbad-big commit, but PLEASE don't ask me to re-do this on another branch, I swear to god. I know it makes things more difficult on you, and I'm sorry for that, but I'm definitely feeling the goddamn crunch right now and I wanna take a step back from this spaghetti nightmare and clear my head.
* Do that thing where the character icons are ALWAYS 1x sized, through having two seperate lumps.
* Revamp the S_SKIN parameters to be `facerank` (rankings - equivalent of half-scale old face), `facewant` (WANTED - equivalent of old face), and `facemmap` (equivalent of old iconprefix).
* Do that thing Oni wanted where it shows two postions above and two positions below your current ranking (and you) to the left of the screen, instead of always the top 4, with some limits to avoid drawing outside of everything.
* Replace the last few shitty Mario numbers (for the left rankings) with cool, new Oni numbers.
* Change a bunch of offsets and things so the tab rankings and the intermission work nicer with 9+ players.
2018-10-28 16:27:55 +00:00
HU_DrawTabRankings ( ( ( scorelines > 8 ) ? 32 : 40 ) , 33 , tab , scorelines , whiteplayer , hilicol ) ;
2018-07-18 19:23:46 +00:00
/*else
HU_DrawDualTabRankings ( 32 , 32 , tab , scorelines , whiteplayer ) ; */
2014-03-15 16:59:03 +00:00
// draw spectators in a ticker across the bottom
2018-10-27 19:43:33 +00:00
if ( netgame & & G_GametypeHasSpectators ( ) )
2014-03-15 16:59:03 +00:00
HU_DrawSpectatorTicker ( ) ;
}
// 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 ;
}