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.
2016-07-06 04:09:17 +00:00
// Copyright (C) 1999-2016 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
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 ] ;
static player_t * plr ;
boolean chat_on ; // entering a chat message?
static char w_chat [ HU_MAXMSGLEN ] ;
2018-08-02 22:52:07 +00:00
static UINT32 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
// -------
// protos.
// -------
static void HU_DrawRankings ( void ) ;
2018-10-27 19:43:33 +00:00
//static void HU_DrawCoopOverlay(void);
//static void HU_DrawNetplayCoopOverlay(void);
2014-03-15 16:59:03 +00:00
//======================================================================
// 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 ) ;
}
// 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 ) ;
}
// 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-08-02 22:52:07 +00:00
static UINT32 chat_maxscroll = 0 ; // how far can we scroll?
2018-06-15 10:20: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.
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
static tic_t chat_timers [ 8 ] ;
static boolean chat_scrollmedown = false ; // force instant scroll down on the chat log. Happens when you open it / send a message.
// remove text from minichat table
2018-08-02 22:52:07 +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-08-02 22:52:07 +00:00
UINT32 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 ] ;
}
chat_nummsg_min - - ; // lost 1 msg.
// 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-08-02 22:52:07 +00:00
UINT32 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 ] ) ;
}
chat_nummsg_log - - ; // lost 1 msg.
}
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-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-06-15 10:20:01 +00:00
if ( chat_nummsg_log > = CHAT_BUFSIZE )
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-10-10 19:24:55 +00:00
if ( OLDCHAT ) // if we're using oldchat, print directly in console
CONS_Printf ( " %s \n " , text ) ;
else // if we aren't, still save the message to log.txt
CON_LogMessage ( va ( " %s \n " , text ) ) ;
2018-06-15 10:20:01 +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 >
*/
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-10-03 18:09:32 +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-06-15 10:20:01 +00:00
if ( strlen ( msg ) > 4 & & strnicmp ( msg , " /pm " , 3 ) = = 0 ) // used /pm
{
// 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-06-15 10:20:01 +00:00
int spc = 1 ; // used if nodenum[1] is a space.
char * nodenum = ( char * ) malloc ( 3 ) ;
strncpy ( nodenum , msg + 3 , 5 ) ;
// 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-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-06-15 10:20:01 +00:00
return ;
}
}
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
target = atoi ( ( const char * ) nodenum ) ; // turn that into a number
//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!
if ( playeringame [ target ] ) // player exists
target + + ; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
else
{
2018-10-10 19:24:55 +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-08-02 22:52:07 +00:00
memcpy ( 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-06-15 10:20:01 +00:00
static tic_t stop_spamming_you_cunt [ 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-10-07 14:35:54 +00:00
int 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-06-15 10:20:01 +00:00
//if (stop_spamming_you_cunt[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY))
if ( stop_spamming_you_cunt [ 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 ] ) ;
stop_spamming_you_cunt [ playernum ] = 4 ;
spam_eatmsg = 1 ;
}
else
stop_spamming_you_cunt [ 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 )
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
}
else if ( target = = - 1 ) // say team
{
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
{
const UINT8 color = players [ playernum ] . skincolor ;
2018-09-22 19:56:44 +00:00
if ( color < = SKINCOLOR_SILVER | | color = = SKINCOLOR_SLATE )
2018-09-22 17:22:04 +00:00
cstart = " \x80 " ; // white
2018-10-04 22:07:56 +00:00
else if ( color < = SKINCOLOR_BLACK | | color = = SKINCOLOR_JET )
2018-09-22 17:22:04 +00:00
cstart = " \x86 " ; // V_GRAYMAP
else if ( color < = SKINCOLOR_LEATHER )
2018-10-04 22:07:56 +00:00
cstart = " \x8e " ; // V_BROWNMAP
2018-09-22 19:56:44 +00:00
else if ( color < = SKINCOLOR_ROSE | | color = = SKINCOLOR_LILAC )
2018-09-22 17:22:04 +00:00
cstart = " \x8d " ; // V_PINKMAP
2018-09-22 13:59:30 +00:00
else if ( color < = SKINCOLOR_KETCHUP )
2018-09-22 17:22:04 +00:00
cstart = " \x85 " ; // V_REDMAP
else if ( color < = SKINCOLOR_TANGERINE )
cstart = " \x87 " ; // V_ORANGEMAP
2018-09-22 13:59:30 +00:00
else if ( color < = SKINCOLOR_CARAMEL )
2018-09-22 17:22:04 +00:00
cstart = " \x8f " ; // V_PEACHMAP
else if ( color < = SKINCOLOR_BRONZE )
cstart = " \x8A " ; // V_GOLDMAP
2018-10-04 18:28:38 +00:00
else if ( color < = SKINCOLOR_OLIVE )
2018-09-22 17:22:04 +00:00
cstart = " \x82 " ; // V_YELLOWMAP
else if ( color < = SKINCOLOR_PISTACHIO )
cstart = " \x8b " ; // V_TEAMAP
2018-10-04 18:28:38 +00:00
else if ( color < = SKINCOLOR_DREAM | | color = = SKINCOLOR_LIME )
2018-09-22 17:22:04 +00:00
cstart = " \x83 " ; // V_GREENMAP
else if ( color < = SKINCOLOR_NAVY | | color = = SKINCOLOR_SAPPHIRE )
cstart = " \x88 " ; // V_SKYMAP
else if ( color < = SKINCOLOR_STEEL )
cstart = " \x8c " ; // V_STEELMAP
else if ( color < = SKINCOLOR_BLUEBERRY )
cstart = " \x84 " ; // V_BLUEMAP
else if ( color = = SKINCOLOR_PURPLE )
cstart = " \x81 " ; // V_PURPLEMAP
else //if (color <= SKINCOLOR_POMEGRANATE)
cstart = " \x89 " ; // V_LAVENDERMAP
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
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
fmt = " \3 %s<%s%s> \x80 %s %s%s \n " ;
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
}
# endif
// 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-06-15 10:20:01 +00:00
if ( c_input > = strlen ( s ) ) // don't do anything complicated
{
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-08-02 22:52:07 +00:00
UINT32 m = HU_MAXMSGLEN - 1 ;
2018-06-15 10:20:01 +00:00
for ( ; ( m > = c_input ) ; m - - )
{
if ( s [ m ] )
s [ m + 1 ] = ( s [ m ] ) ;
2018-10-07 14:35:54 +00:00
2018-09-16 09:01:00 +00:00
if ( m < 1 )
break ; // fix the chat going ham if your replace the first character. (For whatever reason this didn't happen in vanilla????)
2018-06-15 10:20:01 +00:00
}
s [ c_input ] = ch ; // and replace this.
}
c_input + + ;
2014-03-15 16:59:03 +00:00
return true ;
}
return false ;
}
else if ( ch = = KEY_BACKSPACE )
{
2018-10-07 14:35:54 +00:00
size_t i ;
2018-06-15 10:20:01 +00:00
if ( c_input < = 0 )
return false ;
2018-10-07 14:35:54 +00:00
i = c_input ;
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
}
//
//
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 ;
}
static boolean teamtalk = false ;
2018-06-15 10:20:01 +00:00
// WHY DO YOU OVERCOMPLICATE EVERYTHING?????????
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 ] ;
size_t ci = 2 ;
2018-06-15 10:20:01 +00:00
char * msg = & buf [ 2 ] ;
2018-10-07 14:35:54 +00:00
size_t i ;
INT32 target = 0 ;
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
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-06-15 10:20:01 +00:00
if ( strlen ( msg ) > 4 & & strnicmp ( msg , " /pm " , 3 ) = = 0 ) // used /pm
{
2018-10-07 14:35:54 +00:00
int spc ;
char * nodenum ;
const char * newmsg ;
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-10-07 14:35:54 +00:00
spc = 1 ; // used if nodenum[1] is a space.
nodenum = ( char * ) malloc ( 3 ) ;
2018-06-15 10:20:01 +00:00
strncpy ( nodenum , msg + 3 , 5 ) ;
// 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-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-06-15 10:20:01 +00:00
return ;
}
}
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
target = atoi ( ( const char * ) nodenum ) ; // turn that into a number
//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!
if ( playeringame [ target ] ) // player exists
target + + ; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
else
{
2018-10-10 19:24:55 +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 ;
}
// we need to get rid of the /pm<node>
2018-10-07 14:35:54 +00:00
newmsg = msg + 5 + spc ;
2018-06-15 10:20:01 +00:00
memcpy ( 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 ;
}
}
void HU_clearChatChars ( void )
{
2018-06-15 10:20:01 +00:00
size_t i = 0 ;
for ( ; i < HU_MAXMSGLEN ; i + + )
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-06-15 10:20:01 +00:00
static boolean justscrolleddown ;
static boolean justscrolledup ;
2014-03-15 16:59:03 +00:00
//
// Returns true if key eaten
//
boolean HU_Responder ( event_t * ev )
{
2018-08-02 22:52:07 +00:00
INT32 c = 0 ;
2018-10-07 14:35:54 +00:00
2014-03-15 16:59:03 +00:00
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 ;
}
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-10-03 18:09:32 +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 ;
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 ;
2014-03-15 16:59:03 +00:00
return true ;
}
}
else // if chat_on
{
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 ;
2018-08-02 22:52:07 +00:00
c = ( INT32 ) ev - > data1 ;
2018-06-15 10:20:01 +00:00
// capslock
if ( c & & c = = KEY_CAPSLOCK ) // it's a toggle.
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( capslock )
capslock = false ;
2018-08-02 22:52:07 +00:00
else
2018-06-15 10:20:01 +00:00
capslock = true ;
return true ;
2018-08-02 22:52:07 +00:00
}
// use console translations
2018-06-15 10:20:01 +00:00
if ( shiftdown ^ capslock )
2014-03-15 16:59:03 +00:00
c = shiftxform [ c ] ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
// TODO: make chat behave like the console, so that we can go back and edit stuff when we fuck up.
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-06-15 10:20:01 +00:00
if ( c_input > = strlen ( w_chat ) ) // add it at the end of the string.
{
memcpy ( & w_chat [ chatlen ] , paste , pastelen ) ; // copy all of that.
c_input + = pastelen ;
/*size_t i = 0;
for ( ; i < pastelen ; i + + )
{
HU_queueChatChar ( paste [ i ] ) ; // queue it so that it's actually sent. (this chat write thing is REALLY messy.)
} */
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 ;
for ( ; i > = c_input ; i - - )
{
if ( w_chat [ i ] )
w_chat [ i + pastelen ] = w_chat [ i ] ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
}
memcpy ( & w_chat [ c_input ] , paste , pastelen ) ; // copy all of that.
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-06-15 10:20:01 +00:00
c_input = 0 ; // reset input cursor
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-06-15 10:20:01 +00:00
c_input = 0 ; // reset input cursor
2018-08-02 22:52:07 +00:00
}
2018-10-03 18:09:32 +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-10-03 18:09:32 +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-10-03 18:09:32 +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 ;
}
return false ;
}
//======================================================================
// HEADS UP DRAWING
//======================================================================
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
{
int c ;
size_t chw , i , lastusablespace = 0 ;
size_t slen ;
char * newstring = Z_StrDup ( string ) ;
2018-08-05 11:08:27 +00:00
INT32 charwidth = 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-08-05 11:08:27 +00:00
chw = charwidth ;
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-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.
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-08-05 11:08:27 +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 )
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-07-31 21:35:16 +00:00
while ( msg [ j ] ) // iterate through msg
2018-08-02 22:52:07 +00:00
{
2018-07-31 21:35:16 +00:00
if ( msg [ j ] < HU_FONTSTART ) // don't draw
2018-08-02 22:52:07 +00:00
{
2018-07-31 21:35:16 +00:00
if ( msg [ j ] = = ' \n ' ) // get back down.
{
+ + 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 ) ;
if ( splitscreen )
{
y - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
y + = 16 ;
}
else
y - = ( cv_kartspeedometer . value ? 16 : 0 ) ;
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-07-31 21:35:16 +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-08-02 22:52:07 +00:00
INT32 timer = ( ( cv_chattime . value * TICRATE ) - chat_timers [ i ] ) - cv_chattime . value * TICRATE + 9 ; // see below...
2018-06-15 10:20:01 +00:00
INT32 transflag = ( timer > = 0 & & timer < = 9 ) ? ( timer * V_10TRANS ) : 0 ; // you can make bad jokes out of this one.
size_t j = 0 ;
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 ] ) ; // 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-06-15 10:20:01 +00:00
while ( msg [ j ] ) // iterate through msg
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( msg [ j ] < HU_FONTSTART ) // don't draw
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( msg [ j ] = = ' \n ' ) // get back down.
{
+ + 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-08-05 15:34:51 +00:00
if ( cv_chatbacktint . value ) // on request of wolfy
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
// TODO: fix dumb word wrapping issues
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-10-21 12:51:34 +00:00
if ( splitscreen )
{
boxh = max ( 6 , boxh / 2 ) ;
if ( splitscreen > 1 )
boxw = max ( 64 , boxw / 2 ) ;
}
y = chaty - offset * charheight - ( chat_scroll * charheight ) - boxh * charheight - 12 ;
if ( splitscreen )
{
y - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
y + = 16 ;
}
else
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-10-21 12:51:34 +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-06-15 10:20:01 +00:00
for ( i = 0 ; i < chat_nummsg_log ; i + + ) // iterate through our chatlog
{
INT32 clrflag = 0 ;
2018-08-02 22:52:07 +00:00
INT32 j = 0 ;
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_log [ i ] ) ; // get the current message, and word wrap it.
2018-09-22 17:22:04 +00:00
UINT8 * colormap = NULL ;
2018-06-15 10:20:01 +00:00
while ( msg [ j ] ) // iterate through msg
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( msg [ j ] < HU_FONTSTART ) // don't draw
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
if ( msg [ j ] = = ' \n ' ) // get back down.
{
+ + 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
j + + ; // don't forget to increment this or we'll get stuck in the limbo.
}
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-06-15 10:20: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.
{
atbottom = true ; // we should scroll
}
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-08-02 22:52:07 +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.
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
if ( chat_scroll > 0 )
2018-09-10 11:41:53 +00:00
{
V_DrawCharacter ( chatx - 9 , ( ( justscrolledup ) ? ( chat_topy - 1 ) : ( chat_topy ) ) ,
2018-10-25 17:41:14 +00:00
' \x1A ' | V_SNAPTOBOTTOM | V_SNAPTOLEFT | highlight , false ) ; // up arrow
2018-09-10 11:41:53 +00:00
}
2018-06-15 10:20:01 +00:00
if ( chat_scroll < chat_maxscroll )
2018-09-10 11:41:53 +00:00
{
V_DrawCharacter ( chatx - 9 , chat_bottomy - ( ( justscrolleddown ) ? 5 : 6 ) ,
2018-10-25 17:41:14 +00:00
' \x1B ' | V_SNAPTOBOTTOM | V_SNAPTOLEFT | highlight , false ) ; // down arrow
2018-09-10 11:41:53 +00:00
}
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
static INT16 typelines = 1 ; // number of drawfill lines we need. it's some weird hack and might be one frame off but I'm lazy to make another loop.
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-09-22 17:22:04 +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-10-21 12:51:34 +00:00
if ( splitscreen )
{
y - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
{
y + = 16 ;
boxw = max ( 64 , boxw / 2 ) ;
}
}
else
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 ;
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-10-03 18:09:32 +00:00
// if chat is muted, just draw the log and get it over with:
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-10-21 12:51:34 +00:00
int cursorx = ( c + charwidth < boxw - charwidth ) ? ( chatx + 2 + c + charwidth ) : ( chatx + 1 ) ; // we may have to go down.
2018-07-31 21:35:16 +00:00
int cursory = ( cursorx ! = chatx + 1 ) ? ( y ) : ( y + charheight ) ;
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-09-21 15:03:09 +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 ;
}
}
// handle /pm list.
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-10-21 12:51:34 +00:00
if ( splitscreen )
{
p_dispy - = BASEVIDHEIGHT / 2 ;
if ( splitscreen > 1 )
p_dispy + = 16 ;
}
else
p_dispy - = ( cv_kartspeedometer . value ? 16 : 0 ) ;
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-06-15 10:20:01 +00:00
strncpy ( nodenum , w_chat + 3 , 4 ) ;
2018-10-07 14:35:54 +00:00
n = atoi ( ( const char * ) nodenum ) ; // turn that into a number
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 ] ;
strlcpy ( name , player_names [ i ] , 7 ) ; // shorten name to 7 characters.
2018-10-21 12:51:34 +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.
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 + + ;
}
}
if ( count = = 0 ) // no results.
{
2018-10-21 12:51:34 +00:00
V_DrawFillConsoleMap ( chatx - 50 , 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.
V_DrawSmallString ( chatx - 48 , 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-07-31 21:35:16 +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-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
}
2018-10-21 12:51:34 +00:00
2018-06-15 10:20:01 +00:00
// why the fuck would you use this...
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 )
{
int cursorx = ( HU_INPUTX + c + charwidth < vid . width ) ? ( HU_INPUTX + c + charwidth ) : ( HU_INPUTX ) ; // we may have to go down.
int 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 ;
}
}
}
// 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
}
// Heads up displays drawer, call each frame
//
void HU_Drawer ( void )
{
// 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
HU_DrawChat_Old ( ) ; // why the fuck.........................
}
else
{
2018-10-01 18:23:56 +00:00
if ( netgame ) // Don't draw it outside, I know it leads to stupid stuff.
{
2018-08-03 22:01:09 +00:00
chat_scrolltime = 0 ; // do scroll anyway.
typelines = 1 ; // make sure that the chat doesn't have a weird blinking huge ass square if we typed a lot last time.
2018-10-10 19:24:55 +00:00
if ( ! OLDCHAT & & cv_consolechat . value < 2 ) // Don't display minimized chat if you set the mode to Window (Hidden)
2018-06-15 10:20:01 +00:00
HU_drawMiniChat ( ) ; // draw messages in a cool fashion.
2018-10-01 18:23:56 +00:00
}
2018-06-15 10:20:01 +00:00
}
2018-08-02 22:52:07 +00:00
2018-06-15 10:20: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)
{
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-06-15 10:20:01 +00:00
if ( stop_spamming_you_cunt [ i ] > 0 )
stop_spamming_you_cunt [ 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 ( ) ;
}
}
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
HU_DrawRankings ( ) ;
2018-10-27 19:43:33 +00:00
//if (gametype == GT_COOP)
//HU_DrawNetplayCoopOverlay();
2014-03-15 16:59:03 +00:00
}
2018-10-27 19:43:33 +00:00
//else
//HU_DrawCoopOverlay();
2014-03-15 16:59:03 +00:00
# ifdef HAVE_BLUA
LUAh_ScoresHUD ( ) ;
# endif
}
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
// 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 ;
if ( chat_on )
if ( bottomline < 8 )
bottomline = 8 ;
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
//
void HU_drawPing ( INT32 x , INT32 y , INT32 ping , boolean notext )
{
UINT8 numbars = 1 ; // how many ping bars do we draw?
UINT8 barcolor = 128 ; // color we use for the bars (green, yellow or red)
SINT8 i = 0 ;
SINT8 yoffset = 6 ;
2018-10-07 14:35:54 +00:00
INT32 dx ;
2018-06-15 10:20:01 +00:00
if ( ping < 128 )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
numbars = 3 ;
barcolor = 184 ;
2018-08-02 22:52:07 +00:00
}
2018-06-15 10:20:01 +00:00
else if ( ping < 256 )
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
numbars = 2 ; // Apparently ternaries w/ multiple statements don't look good in C so I decided against it.
barcolor = 103 ;
2018-08-02 22:52:07 +00:00
}
2018-10-07 14:35:54 +00:00
dx = x + 1 - ( V_SmallStringWidth ( va ( " %dms " , ping ) , V_ALLOWLOWERCASE ) / 2 ) ;
2018-06-15 10:20:01 +00:00
if ( ! notext | | vid . width > = 640 ) // how sad, we're using a shit resolution.
V_DrawSmallString ( dx , y + 4 , V_ALLOWLOWERCASE , va ( " %dms " , ping ) ) ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
for ( i = 0 ; ( i < 3 ) ; i + + ) // Draw the ping bar
2018-08-02 22:52:07 +00:00
{
2018-06-15 10:20:01 +00:00
V_DrawFill ( x + 2 * ( i - 1 ) , y + yoffset - 4 , 2 , 8 - yoffset , 31 ) ;
if ( i < numbars )
V_DrawFill ( x + 2 * ( i - 1 ) , y + yoffset - 3 , 1 , 8 - yoffset - 1 , barcolor ) ;
2018-08-02 22:52:07 +00:00
2018-06-15 10:20:01 +00:00
yoffset - = 2 ;
}
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_DrawTabRankings
//
2018-07-18 20:40:04 +00:00
void HU_DrawTabRankings ( INT32 x , INT32 y , playersort_t * tab , INT32 scorelines , INT32 whiteplayer , INT32 hilicol )
2014-03-15 16:59:03 +00:00
{
2018-10-28 16:29:56 +00:00
INT32 i , rightoffset = 240 ;
2014-03-15 16:59:03 +00:00
const UINT8 * colormap ;
2018-10-27 19:43:33 +00:00
INT32 dupadjust = ( vid . width / vid . dupx ) , duptweak = ( dupadjust - BASEVIDWIDTH ) / 2 ;
2014-03-15 16:59:03 +00:00
//this function is designed for 9 or less score lines only
2018-08-24 12:36:03 +00:00
//I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up
2014-03-15 16:59:03 +00:00
2018-10-27 19:43:33 +00:00
V_DrawFill ( 1 - duptweak , 26 , dupadjust - 2 , 1 , 0 ) ; // Draw a horizontal line because it looks nice!
2018-08-24 12:36:03 +00:00
if ( scorelines > 8 )
2018-07-18 19:23:46 +00:00
{
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_DrawFill ( 160 , 26 , 1 , 147 , 0 ) ; // Draw a vertical line to separate the two sides.
V_DrawFill ( 1 , 173 , 318 , 1 , 0 ) ; // And a horizontal line near the bottom.
rightoffset = ( BASEVIDWIDTH / 2 ) - 4 - x ;
2018-07-18 19:23:46 +00:00
}
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < scorelines ; i + + )
{
2018-08-24 12:36:03 +00:00
char strtime [ MAXPLAYERNAME + 1 ] ;
2018-07-18 19:23:46 +00:00
if ( players [ tab [ i ] . num ] . spectator | | ! players [ tab [ i ] . num ] . mo )
2014-03-15 16:59:03 +00:00
continue ; //ignore them.
2018-08-02 22:52:07 +00:00
2018-10-29 10:48:18 +00:00
if ( netgame // don't draw it offline
& & tab [ i ] . num ! = serverplayer )
HU_drawPing ( x + rightoffset + ( ( scorelines > 8 ) ? 13 : - 27 ) , y + 2 , playerpingtable [ tab [ i ] . num ] , false ) ;
2018-08-02 22:52:07 +00:00
2018-08-24 12:36:03 +00:00
if ( scorelines > 8 )
strlcpy ( strtime , tab [ i ] . name , 6 ) ;
else
STRBUFCPY ( strtime , tab [ i ] . name ) ;
2014-03-18 17:56:54 +00:00
V_DrawString ( x + 20 , y ,
2018-07-18 19:23:46 +00:00
( ( tab [ i ] . num = = whiteplayer )
? hilicol | V_ALLOWLOWERCASE
: V_ALLOWLOWERCASE ) ,
2018-08-24 12:36:03 +00:00
strtime ) ;
2014-03-15 16:59:03 +00:00
2018-07-18 19:23:46 +00:00
if ( players [ tab [ i ] . num ] . mo - > color )
2014-03-15 16:59:03 +00:00
{
2018-07-18 19:23:46 +00:00
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo - > color , GTC_CACHE ) ;
if ( players [ tab [ i ] . num ] . mo - > colorized )
colormap = R_GetTranslationColormap ( TC_RAINBOW , players [ tab [ i ] . num ] . mo - > color , GTC_CACHE ) ;
2014-03-15 16:59:03 +00:00
else
2018-07-18 19:23:46 +00:00
colormap = R_GetTranslationColormap ( players [ tab [ i ] . num ] . skin , players [ tab [ i ] . num ] . mo - > 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_DrawMappedPatch ( x , y - 4 , 0 , facerankprefix [ players [ tab [ i ] . num ] . skin ] , colormap ) ;
/*if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] > 0) -- doesn't physically fit...
2014-03-15 16:59:03 +00:00
{
2018-07-18 19:23:46 +00:00
INT32 bumperx = x - 5 ;
for ( j = 0 ; j < players [ tab [ i ] . num ] . kartstuff [ k_bumper ] ; j + + )
{
bumperx - = 3 ;
V_DrawSmallMappedPatch ( bumperx , y + 6 , 0 , W_CachePatchName ( " K_BLNICO " , PU_CACHE ) , colormap ) ;
}
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
} */
2014-03-15 16:59:03 +00:00
}
2018-07-18 19:23:46 +00:00
if ( G_BattleGametype ( ) & & players [ tab [ i ] . num ] . kartstuff [ k_bumper ] < = 0 )
V_DrawSmallScaledPatch ( x - 2 , y - 4 , 0 , W_CachePatchName ( " K_NOBLNS " , PU_CACHE ) ) ;
2014-03-15 16:59:03 +00:00
2018-03-04 20:27:52 +00:00
if ( G_RaceGametype ( ) )
2014-03-15 16:59:03 +00:00
{
2018-10-01 11:52:54 +00:00
# define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time))
2018-07-18 19:23:46 +00:00
if ( players [ tab [ i ] . num ] . exiting )
V_DrawRightAlignedString ( x + rightoffset , y , hilicol , timestring ( players [ tab [ i ] . num ] . realtime ) ) ;
2018-07-30 21:07:28 +00:00
else if ( players [ tab [ i ] . num ] . pflags & PF_TIMEOVER )
2018-10-01 11:52:54 +00:00
V_DrawRightAlignedThinString ( x + rightoffset , y - 1 , 0 , " NO CONTEST. " ) ;
2018-07-18 19:23:46 +00:00
else if ( circuitmap )
2018-10-01 11:52:54 +00:00
V_DrawRightAlignedString ( x + rightoffset , y , 0 , va ( " Lap %d " , tab [ i ] . count ) ) ;
2018-07-18 19:23:46 +00:00
# undef timestring
2014-03-15 16:59:03 +00:00
}
else
2018-07-30 22:17:01 +00:00
V_DrawRightAlignedString ( x + rightoffset , y , 0 , va ( " %u " , tab [ i ] . count ) ) ;
2014-03-15 16:59:03 +00:00
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
y + = 18 ;
2018-08-24 12:36:03 +00:00
if ( i = = 7 )
2018-07-18 19:23:46 +00:00
{
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
y = 33 ;
x = ( BASEVIDWIDTH / 2 ) + 4 ;
2018-07-18 19:23:46 +00:00
}
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 ;
const UINT8 * colormap ;
char name [ MAXPLAYERNAME + 1 ] ;
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.
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 ;
strlcpy ( name , tab [ i ] . name , 9 ) ;
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
}
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.
strlcpy ( name , tab [ i ] . name , 9 ) ;
2018-06-15 10:20:01 +00:00
if ( ! ( tab [ i ] . num = = serverplayer ) )
HU_drawPing ( x + 113 , y + 2 , playerpingtable [ tab [ i ] . num ] , false ) ;
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-01-29 01:37:53 +00:00
V_DrawRightAlignedThinString ( x + 156 , y - 1 , 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-01-29 01:37:53 +00:00
V_DrawRightAlignedThinString ( x + 156 , y - 1 , ( ( players [ tab [ i ] . num ] . health > 0 ) ? 0 : V_TRANSLUCENT ) , va ( " %u " , tab [ i ] . count ) ) ;
2014-03-15 16:59:03 +00:00
}
else
2018-01-29 01:37:53 +00:00
V_DrawRightAlignedThinString ( x + 156 , y - 1 , ( ( 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-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 ) ) ;
2014-03-15 16:59:03 +00:00
y + = 16 ;
if ( y > 160 )
{
y = 32 ;
x + = BASEVIDWIDTH / 2 ;
}
}
2018-07-18 19:23:46 +00:00
} */
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 ] ) ;
}
//
// 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 ;
if ( ! playeringame [ i ] | | players [ i ] . spectator )
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-07-19 14:56:21 +00:00
UINT8 lowestposition = MAXPLAYERS ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2018-07-18 19:23:46 +00:00
if ( ! playeringame [ i ] | | players [ i ] . spectator | | completed [ i ] )
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-28 01:45:04 +00:00
if ( netgame & & G_GametypeHasSpectators ( ) )
2014-03-15 16:59:03 +00:00
HU_DrawSpectatorTicker ( ) ;
}
2018-10-27 19:43:33 +00:00
/*static void HU_DrawCoopOverlay(void)
2014-03-15 16:59:03 +00:00
{
if ( token
# ifdef HAVE_BLUA
& & LUA_HudEnabled ( hud_tokens )
# endif
)
{
V_DrawString ( 168 , 176 , 0 , va ( " - %d " , token ) ) ;
V_DrawSmallScaledPatch ( 148 , 172 , 0 , tokenicon ) ;
}
# ifdef HAVE_BLUA
if ( LUA_HudEnabled ( hud_tabemblems ) )
# endif
if ( ! modifiedgame | | savemoddata )
{
V_DrawString ( 160 , 144 , 0 , va ( " - %d/%d " , M_CountEmblems ( ) , numemblems + numextraemblems ) ) ;
V_DrawScaledPatch ( 128 , 144 - SHORT ( emblemicon - > height ) / 4 , 0 , emblemicon ) ;
}
# ifdef HAVE_BLUA
if ( ! LUA_HudEnabled ( hud_coopemeralds ) )
return ;
# endif
if ( emeralds & EMERALD1 )
2014-03-23 16:00:29 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 , ( BASEVIDHEIGHT / 3 ) - 32 , 0 , emeraldpics [ 0 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD2 )
2014-03-23 16:00:29 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 + 24 , ( BASEVIDHEIGHT / 3 ) - 16 , 0 , emeraldpics [ 1 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD3 )
2014-03-23 16:00:29 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 + 24 , ( BASEVIDHEIGHT / 3 ) + 16 , 0 , emeraldpics [ 2 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD4 )
2014-03-23 16:00:29 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 , ( BASEVIDHEIGHT / 3 ) + 32 , 0 , emeraldpics [ 3 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD5 )
2014-03-23 16:00:29 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 - 24 , ( BASEVIDHEIGHT / 3 ) + 16 , 0 , emeraldpics [ 4 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD6 )
2014-03-23 16:00:29 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 - 24 , ( BASEVIDHEIGHT / 3 ) - 16 , 0 , emeraldpics [ 5 ] ) ;
2014-03-15 16:59:03 +00:00
if ( emeralds & EMERALD7 )
2014-03-23 16:00:29 +00:00
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - 8 , ( BASEVIDHEIGHT / 3 ) , 0 , emeraldpics [ 6 ] ) ;
2014-03-15 16:59:03 +00:00
}
static void HU_DrawNetplayCoopOverlay ( void )
{
int i ;
# ifdef HAVE_BLUA
if ( ! LUA_HudEnabled ( hud_coopemeralds ) )
return ;
# endif
for ( i = 0 ; i < 7 ; + + i )
{
if ( emeralds & ( 1 < < i ) )
2014-03-23 16:00:29 +00:00
V_DrawScaledPatch ( 20 + ( i * 20 ) , 6 , 0 , emeraldpics [ i ] ) ;
2014-03-15 16:59:03 +00:00
}
2018-10-27 19:43:33 +00:00
} */
2014-03-15 16:59:03 +00:00
// Interface to CECHO settings for the outside world, avoiding the
// expense (and security problems) of going via the console buffer.
void HU_ClearCEcho ( void )
{
cechotimer = 0 ;
}
void HU_SetCEchoDuration ( INT32 seconds )
{
cechoduration = seconds * TICRATE ;
}
void HU_SetCEchoFlags ( INT32 flags )
{
// Don't allow cechoflags to contain any bits in V_PARAMMASK
cechoflags = ( flags & ~ V_PARAMMASK ) ;
}
void HU_DoCEcho ( const char * msg )
{
I_OutputMsg ( " %s \n " , msg ) ; // print to log
strncpy ( cechotext , msg , sizeof ( cechotext ) ) ;
strncat ( cechotext , " \\ " , sizeof ( cechotext ) - strlen ( cechotext ) - 1 ) ;
cechotext [ sizeof ( cechotext ) - 1 ] = ' \0 ' ;
cechotimer = cechoduration ;
}