mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
The original written comment by Callum remarks that it was good for writing IP addresses, and this makes it apply to the IP address Online Menu field... Also adds better comments.
1958 lines
45 KiB
C
1958 lines
45 KiB
C
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
|
// Copyright (C) 1999-2020 by Sonic Team Junior.
|
|
//
|
|
// 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 console.c
|
|
/// \brief Console drawing and input
|
|
|
|
#ifdef __GNUC__
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "doomdef.h"
|
|
#include "console.h"
|
|
#include "g_game.h"
|
|
#include "g_input.h"
|
|
#include "hu_stuff.h"
|
|
#include "keys.h"
|
|
#include "r_main.h"
|
|
#include "r_defs.h"
|
|
#include "sounds.h"
|
|
#include "st_stuff.h"
|
|
#include "s_sound.h"
|
|
#include "v_video.h"
|
|
#include "i_video.h"
|
|
#include "z_zone.h"
|
|
#include "i_system.h"
|
|
#include "i_threads.h"
|
|
#include "d_main.h"
|
|
#include "k_menu.h"
|
|
#include "filesrch.h"
|
|
#include "m_misc.h"
|
|
|
|
#ifdef HWRENDER
|
|
#include "hardware/hw_main.h"
|
|
#endif
|
|
|
|
#define MAXHUDLINES 20
|
|
|
|
#ifdef HAVE_THREADS
|
|
I_mutex con_mutex;
|
|
|
|
# define Lock_state() I_lock_mutex(&con_mutex)
|
|
# define Unlock_state() I_unlock_mutex(con_mutex)
|
|
#else/*HAVE_THREADS*/
|
|
# define Lock_state()
|
|
# define Unlock_state()
|
|
#endif/*HAVE_THREADS*/
|
|
|
|
static boolean con_started = false; // console has been initialised
|
|
boolean con_startup = false; // true at game startup
|
|
|
|
con_loadprogress_t con_startup_loadprogress = 0; // Progress for startup load bar
|
|
|
|
static boolean con_forcepic = true; // at startup toggle console translucency when first off
|
|
boolean con_recalc; // set true when screen size has changed
|
|
|
|
static tic_t con_tick; // console ticker for anim or blinking prompt cursor
|
|
// con_scrollup should use time (currenttime - lasttime)..
|
|
|
|
static boolean consoletoggle; // true when console key pushed, ticker will handle
|
|
static boolean consoleready; // console prompt is ready
|
|
|
|
INT32 con_destlines; // vid lines used by console at final position
|
|
static INT32 con_curlines; // vid lines currently used by console
|
|
|
|
INT32 con_clipviewtop; // (useless)
|
|
|
|
static INT32 con_hudlines; // number of console heads up message lines
|
|
static INT32 con_hudtime[MAXHUDLINES]; // remaining time of display for hud msg lines
|
|
|
|
INT32 con_clearlines; // top screen lines to refresh when view reduced
|
|
boolean con_hudupdate; // when messages scroll, we need a backgrnd refresh
|
|
|
|
// console text output
|
|
static char *con_line; // console text output current line
|
|
static size_t con_cx; // cursor position in current line
|
|
static size_t con_cy; // cursor line number in con_buffer, is always
|
|
// increasing, and wrapped around in the text
|
|
// buffer using modulo.
|
|
|
|
static size_t con_totallines; // lines of console text into the console buffer
|
|
static size_t con_width; // columns of chars, depend on vid mode width
|
|
|
|
static size_t con_scrollup; // how many rows of text to scroll up (pgup/pgdn)
|
|
UINT32 con_scalefactor; // text size scale factor
|
|
|
|
// hold 32 last lines of input for history
|
|
#define CON_MAXPROMPTCHARS 256
|
|
#define CON_PROMPTCHAR '$'
|
|
|
|
static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
|
|
|
|
static INT32 inputline; // current input line number
|
|
static INT32 inputhist; // line number of history input line to restore
|
|
static size_t input_cur; // position of cursor in line
|
|
static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
|
|
static size_t input_len; // length of current line, used to bound cursor and such
|
|
// notice: input does NOT include the "$" at the start of the line. - 11/3/16
|
|
|
|
// protos.
|
|
static void CON_InputInit(void);
|
|
static void CON_RecalcSize(void);
|
|
static void CON_ChangeHeight(void);
|
|
|
|
static void CON_DrawBackpic(void);
|
|
static void CONS_hudlines_Change(void);
|
|
static void CONS_backcolor_Change(void);
|
|
|
|
//======================================================================
|
|
// CONSOLE VARS AND COMMANDS
|
|
//======================================================================
|
|
#ifdef macintosh
|
|
#define CON_BUFFERSIZE 4096 // my compiler can't handle local vars >32k
|
|
#else
|
|
#define CON_BUFFERSIZE 16384
|
|
#endif
|
|
|
|
static char con_buffer[CON_BUFFERSIZE];
|
|
|
|
// how many seconds the hud messages lasts on the screen
|
|
static consvar_t cons_msgtimeout = CVAR_INIT ("con_hudtime", "5", CV_SAVE, CV_Unsigned, NULL);
|
|
|
|
// number of lines displayed on the HUD
|
|
static consvar_t cons_hudlines = CVAR_INIT ("con_hudlines", "5", CV_CALL|CV_SAVE, CV_Unsigned, CONS_hudlines_Change);
|
|
|
|
// number of lines console move per frame
|
|
// (con_speed needs a limit, apparently)
|
|
static CV_PossibleValue_t speed_cons_t[] = {{0, "MIN"}, {64, "MAX"}, {0, NULL}};
|
|
static consvar_t cons_speed = CVAR_INIT ("con_speed", "8", CV_SAVE, speed_cons_t, NULL);
|
|
|
|
// percentage of screen height to use for console
|
|
static consvar_t cons_height = CVAR_INIT ("con_height", "50", CV_SAVE, CV_Unsigned, NULL);
|
|
|
|
static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}, {0, NULL}};
|
|
// whether to use console background picture, or translucent mode
|
|
static consvar_t cons_backpic = CVAR_INIT ("con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL);
|
|
|
|
static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Black"}, {2, "Sepia"},
|
|
{3, "Brown"}, {4, "Pink"}, {5, "Red"},
|
|
{6, "Orange"}, {7, "Gold"}, {8, "Yellow"},
|
|
{9, "Peridot"}, {10,"Green"}, {11,"Aquamarine"},
|
|
{12,"Cyan"}, {13,"Steel"}, {14,"Blue"},
|
|
{15,"Purple"}, {16,"Magenta"}, {17,"Lavender"},
|
|
{18,"Rose"},
|
|
{0, NULL}};
|
|
consvar_t cons_backcolor = CVAR_INIT ("con_backcolor", "Black", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change);
|
|
|
|
static void CON_Print(char *msg);
|
|
|
|
//
|
|
//
|
|
static void CONS_hudlines_Change(void)
|
|
{
|
|
INT32 i;
|
|
|
|
Lock_state();
|
|
|
|
// Clear the currently displayed lines
|
|
for (i = 0; i < con_hudlines; i++)
|
|
con_hudtime[i] = 0;
|
|
|
|
if (cons_hudlines.value < 1)
|
|
cons_hudlines.value = 1;
|
|
else if (cons_hudlines.value > MAXHUDLINES)
|
|
cons_hudlines.value = MAXHUDLINES;
|
|
|
|
con_hudlines = cons_hudlines.value;
|
|
|
|
Unlock_state();
|
|
|
|
CONS_Printf(M_GetText("Number of console HUD lines is now %d\n"), con_hudlines);
|
|
}
|
|
|
|
// Clear console text buffer
|
|
//
|
|
static void CONS_Clear_f(void)
|
|
{
|
|
Lock_state();
|
|
|
|
memset(con_buffer, 0, CON_BUFFERSIZE);
|
|
|
|
con_cx = 0;
|
|
con_cy = con_totallines-1;
|
|
con_line = &con_buffer[con_cy*con_width];
|
|
con_scrollup = 0;
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
// Choose english keymap
|
|
//
|
|
/*static void CONS_English_f(void)
|
|
{
|
|
shiftxform = english_shiftxform;
|
|
CONS_Printf(M_GetText("%s keymap.\n"), M_GetText("English"));
|
|
}*/
|
|
|
|
static char *bindtable[NUMINPUTS];
|
|
|
|
static void CONS_Bind_f(void)
|
|
{
|
|
size_t na;
|
|
INT32 key;
|
|
|
|
na = COM_Argc();
|
|
|
|
if (na != 2 && na != 3)
|
|
{
|
|
CONS_Printf(M_GetText("bind <keyname> [<command>]: create shortcut keys to command(s)\n"));
|
|
CONS_Printf("\x82%s", M_GetText("Bind table :\n"));
|
|
na = 0;
|
|
for (key = 0; key < NUMINPUTS; key++)
|
|
if (bindtable[key])
|
|
{
|
|
CONS_Printf("%s : \"%s\"\n", G_KeynumToString(key), bindtable[key]);
|
|
na = 1;
|
|
}
|
|
if (!na)
|
|
CONS_Printf(M_GetText("(empty)\n"));
|
|
return;
|
|
}
|
|
|
|
key = G_KeyStringtoNum(COM_Argv(1));
|
|
if (key <= 0 || key >= NUMINPUTS)
|
|
{
|
|
CONS_Alert(CONS_NOTICE, M_GetText("Invalid key name\n"));
|
|
return;
|
|
}
|
|
|
|
Z_Free(bindtable[key]);
|
|
bindtable[key] = NULL;
|
|
|
|
if (na == 3)
|
|
bindtable[key] = Z_StrDup(COM_Argv(2));
|
|
}
|
|
|
|
//======================================================================
|
|
// CONSOLE SETUP
|
|
//======================================================================
|
|
|
|
// Font colormap colors
|
|
// TODO: This could probably be improved somehow...
|
|
// These colormaps are 99% identical, with just a few changed bytes
|
|
// This could EASILY be handled by modifying a centralised colormap
|
|
// for software depending on the prior state - but yknow, OpenGL...
|
|
UINT8 *yellowmap, *purplemap, *greenmap, *bluemap, *graymap, *redmap, *orangemap,\
|
|
*skymap, *goldmap, *lavendermap, *aquamap, *magentamap, *pinkmap, *brownmap, *tanmap;
|
|
|
|
// Console BG color
|
|
UINT8 *consolebgmap = NULL;
|
|
UINT8 *promptbgmap = NULL;
|
|
static UINT8 promptbgcolor = UINT8_MAX;
|
|
|
|
void CON_SetupBackColormapEx(INT32 color, boolean prompt)
|
|
{
|
|
UINT16 i, palsum;
|
|
UINT8 j, palindex;
|
|
UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
|
|
INT32 shift = 6;
|
|
|
|
if (color == INT32_MAX)
|
|
color = cons_backcolor.value;
|
|
|
|
shift = 6; // 12 colors -- shift of 7 means 6 colors
|
|
|
|
switch (color)
|
|
{
|
|
case 0: palindex = 15; break; // White
|
|
case 1: palindex = 31; break; // Black
|
|
case 2: palindex = 251; break; // Sepia
|
|
case 3: palindex = 239; break; // Brown
|
|
case 4: palindex = 214; shift = 7; break; // Pink
|
|
case 5: palindex = 47; break; // Red
|
|
case 6: palindex = 63; break; // Orange
|
|
case 7: palindex = 71; shift = 7; break; // Gold
|
|
case 8: palindex = 79; shift = 7; break; // Yellow
|
|
case 9: palindex = 191; shift = 8; break; // Peridot
|
|
case 10: palindex = 111; break; // Green
|
|
case 11: palindex = 127; shift = 7; break; // Aquamarine
|
|
case 12: palindex = 139; break; // Cyan
|
|
case 13: palindex = 175; shift = 7; break; // Steel
|
|
case 14: palindex = 159; break; // Blue
|
|
case 15: palindex = 168; shift = 7; break; // Purple
|
|
case 16: palindex = 187; break; // Magenta
|
|
case 17: palindex = 199; shift = 7; break; // Lavender
|
|
case 18: palindex = 207; shift = 7; break; // Rose
|
|
// Default black
|
|
default: palindex = 31; break;
|
|
}
|
|
|
|
if (prompt)
|
|
{
|
|
if (!promptbgmap)
|
|
promptbgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
|
|
|
|
if (color == promptbgcolor)
|
|
return;
|
|
else
|
|
promptbgcolor = color;
|
|
}
|
|
else if (!consolebgmap)
|
|
consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
|
|
|
|
// setup background colormap
|
|
for (i = 0, j = 0; i < 768; i += 3, j++)
|
|
{
|
|
palsum = (pal[i] + pal[i+1] + pal[i+2]) >> shift;
|
|
if (prompt)
|
|
promptbgmap[j] = (UINT8)(palindex - palsum);
|
|
else
|
|
consolebgmap[j] = (UINT8)(palindex - palsum);
|
|
}
|
|
}
|
|
|
|
void CON_SetupBackColormap(void)
|
|
{
|
|
CON_SetupBackColormapEx(cons_backcolor.value, false);
|
|
CON_SetupBackColormapEx(1, true); // default to gray
|
|
}
|
|
|
|
static void CONS_backcolor_Change(void)
|
|
{
|
|
CON_SetupBackColormapEx(cons_backcolor.value, false);
|
|
}
|
|
|
|
static void CON_SetupColormaps(void)
|
|
{
|
|
INT32 i;
|
|
UINT8 *memorysrc = (UINT8 *)Z_Malloc((256*15), PU_STATIC, NULL);
|
|
|
|
purplemap = memorysrc;
|
|
yellowmap = (purplemap+256);
|
|
greenmap = (yellowmap+256);
|
|
bluemap = (greenmap+256);
|
|
redmap = (bluemap+256);
|
|
graymap = (redmap+256);
|
|
orangemap = (graymap+256);
|
|
skymap = (orangemap+256);
|
|
lavendermap = (skymap+256);
|
|
goldmap = (lavendermap+256);
|
|
aquamap = (goldmap+256);
|
|
magentamap = (aquamap+256);
|
|
pinkmap = (magentamap+256);
|
|
brownmap = (pinkmap+256);
|
|
tanmap = (brownmap+256);
|
|
|
|
// setup the other colormaps, for console text
|
|
|
|
// these don't need to be aligned, unless you convert the
|
|
// V_DrawMappedPatch() into optimised asm.
|
|
|
|
for (i = 0; i < (256*15); i++, ++memorysrc)
|
|
*memorysrc = (UINT8)(i & 0xFF); // remap each color to itself...
|
|
|
|
purplemap[0] = (UINT8)163;
|
|
|
|
yellowmap[0] = (UINT8)73;
|
|
yellowmap[1] = (UINT8)73;
|
|
yellowmap[3] = (UINT8)74;
|
|
yellowmap[6] = (UINT8)74;
|
|
yellowmap[7] = (UINT8)190;
|
|
yellowmap[8] = (UINT8)190;
|
|
yellowmap[10] = (UINT8)190;
|
|
yellowmap[12] = (UINT8)190;
|
|
yellowmap[14] = (UINT8)149;
|
|
yellowmap[15] = (UINT8)149;
|
|
yellowmap[16] = (UINT8)149;
|
|
yellowmap[21] = (UINT8)152;
|
|
yellowmap[23] = (UINT8)173;
|
|
yellowmap[24] = (UINT8)167;
|
|
|
|
greenmap[0] = (UINT8)98;
|
|
bluemap[0] = (UINT8)148;
|
|
redmap[0] = (UINT8)34; // battle
|
|
graymap[0] = (UINT8)10;
|
|
orangemap[0] = (UINT8)52; // record attack
|
|
skymap[0] = (UINT8)132; // race
|
|
lavendermap[0] = (UINT8)192;
|
|
goldmap[0] = (UINT8)65;
|
|
aquamap[0] = (UINT8)121;
|
|
magentamap[0] = (UINT8)182;
|
|
pinkmap[0] = (UINT8)210;
|
|
brownmap[0] = (UINT8)224;
|
|
tanmap[0] = (UINT8)217; // no longer nice :(
|
|
|
|
// Init back colormap
|
|
CON_SetupBackColormap();
|
|
}
|
|
|
|
// Setup the console text buffer
|
|
//
|
|
void CON_Init(void)
|
|
{
|
|
INT32 i;
|
|
|
|
for (i = 0; i < NUMINPUTS; i++)
|
|
bindtable[i] = NULL;
|
|
|
|
Lock_state();
|
|
|
|
// clear all lines
|
|
memset(con_buffer, 0, CON_BUFFERSIZE);
|
|
|
|
// make sure it is ready for the loading screen
|
|
con_width = 0;
|
|
|
|
Unlock_state();
|
|
|
|
CON_RecalcSize();
|
|
|
|
CON_SetupColormaps();
|
|
|
|
Lock_state();
|
|
|
|
//note: CON_Ticker should always execute at least once before D_Display()
|
|
con_clipviewtop = -1; // -1 does not clip
|
|
|
|
con_hudlines = atoi(cons_hudlines.defaultvalue);
|
|
|
|
Unlock_state();
|
|
|
|
// setup console input filtering
|
|
CON_InputInit();
|
|
|
|
// register our commands
|
|
//
|
|
COM_AddCommand("cls", CONS_Clear_f);
|
|
//COM_AddCommand("english", CONS_English_f);
|
|
// set console full screen for game startup MAKE SURE VID_Init() done !!!
|
|
Lock_state();
|
|
|
|
con_destlines = vid.height;
|
|
con_curlines = vid.height;
|
|
|
|
Unlock_state();
|
|
|
|
if (!dedicated)
|
|
{
|
|
Lock_state();
|
|
|
|
con_started = true;
|
|
con_startup = true;
|
|
consoletoggle = false;
|
|
|
|
Unlock_state();
|
|
|
|
CV_RegisterVar(&cons_msgtimeout);
|
|
CV_RegisterVar(&cons_hudlines);
|
|
CV_RegisterVar(&cons_speed);
|
|
CV_RegisterVar(&cons_height);
|
|
CV_RegisterVar(&cons_backpic);
|
|
CV_RegisterVar(&cons_backcolor);
|
|
COM_AddCommand("bind", CONS_Bind_f);
|
|
}
|
|
else
|
|
{
|
|
Lock_state();
|
|
|
|
con_started = true;
|
|
con_startup = false;
|
|
consoletoggle = true;
|
|
|
|
Unlock_state();
|
|
}
|
|
}
|
|
// Console input initialization
|
|
//
|
|
static void CON_InputInit(void)
|
|
{
|
|
Lock_state();
|
|
|
|
// prepare the first prompt line
|
|
memset(inputlines, 0, sizeof (inputlines));
|
|
inputline = 0;
|
|
input_cur = input_sel = input_len = 0;
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
//======================================================================
|
|
// CONSOLE EXECUTION
|
|
//======================================================================
|
|
|
|
// Called at screen size change to set the rows and line size of the
|
|
// console text buffer.
|
|
//
|
|
static void CON_RecalcSize(void)
|
|
{
|
|
size_t conw, oldcon_width, oldnumlines, i, oldcon_cy;
|
|
char *tmp_buffer;
|
|
char *string;
|
|
|
|
Lock_state();
|
|
|
|
switch (cv_constextsize.value)
|
|
{
|
|
case V_NOSCALEPATCH:
|
|
con_scalefactor = 1;
|
|
break;
|
|
case V_SMALLSCALEPATCH:
|
|
con_scalefactor = vid.smalldupx;
|
|
break;
|
|
case V_MEDSCALEPATCH:
|
|
con_scalefactor = vid.meddupx;
|
|
break;
|
|
default: // Full scaling
|
|
con_scalefactor = vid.dupx;
|
|
break;
|
|
}
|
|
|
|
con_recalc = false;
|
|
|
|
if (dedicated)
|
|
conw = 1;
|
|
else
|
|
conw = (vid.width>>3) / con_scalefactor - 2;
|
|
|
|
if (con_curlines == vid.height) // first init
|
|
{
|
|
con_curlines = vid.height;
|
|
con_destlines = vid.height;
|
|
}
|
|
|
|
if (con_destlines > 0) // Resize console if already open
|
|
{
|
|
CON_ChangeHeight();
|
|
con_curlines = con_destlines;
|
|
}
|
|
|
|
// check for change of video width
|
|
if (conw == con_width)
|
|
{
|
|
Unlock_state();
|
|
return; // didn't change
|
|
}
|
|
|
|
Unlock_state();
|
|
|
|
tmp_buffer = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL);
|
|
string = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL); // BP: it is a line but who know
|
|
|
|
Lock_state();
|
|
|
|
oldcon_width = con_width;
|
|
oldnumlines = con_totallines;
|
|
oldcon_cy = con_cy;
|
|
M_Memcpy(tmp_buffer, con_buffer, CON_BUFFERSIZE);
|
|
|
|
if (conw < 1)
|
|
con_width = (BASEVIDWIDTH>>3) - 2;
|
|
else
|
|
con_width = conw;
|
|
|
|
con_width += 11; // Graue 06-19-2004 up to 11 control chars per line
|
|
|
|
con_totallines = CON_BUFFERSIZE / con_width;
|
|
memset(con_buffer, ' ', CON_BUFFERSIZE);
|
|
|
|
con_cx = 0;
|
|
con_cy = con_totallines-1;
|
|
con_line = &con_buffer[con_cy*con_width];
|
|
con_scrollup = 0;
|
|
|
|
Unlock_state();
|
|
|
|
// re-arrange console text buffer to keep text
|
|
if (oldcon_width) // not the first time
|
|
{
|
|
for (i = oldcon_cy + 1; i < oldcon_cy + oldnumlines; i++)
|
|
{
|
|
if (tmp_buffer[(i%oldnumlines)*oldcon_width])
|
|
{
|
|
M_Memcpy(string, &tmp_buffer[(i%oldnumlines)*oldcon_width], oldcon_width);
|
|
conw = oldcon_width - 1;
|
|
while (string[conw] == ' ' && conw)
|
|
conw--;
|
|
string[conw+1] = '\n';
|
|
string[conw+2] = '\0';
|
|
CON_Print(string);
|
|
}
|
|
}
|
|
}
|
|
|
|
Z_Free(string);
|
|
Z_Free(tmp_buffer);
|
|
}
|
|
|
|
static void CON_ChangeHeight(void)
|
|
{
|
|
INT32 minheight;
|
|
|
|
Lock_state();
|
|
|
|
minheight = 20 * con_scalefactor; // 20 = 8+8+4
|
|
|
|
// toggle console in
|
|
con_destlines = (cons_height.value*vid.height)/100;
|
|
if (con_destlines < minheight)
|
|
con_destlines = minheight;
|
|
else if (con_destlines > vid.height)
|
|
con_destlines = vid.height;
|
|
|
|
con_destlines &= ~0x3; // multiple of text row height
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
// Handles Console moves in/out of screen (per frame)
|
|
//
|
|
static void CON_MoveConsole(void)
|
|
{
|
|
fixed_t conspeed;
|
|
|
|
Lock_state();
|
|
|
|
conspeed = FixedDiv(cons_speed.value*vid.fdupy, FRACUNIT);
|
|
|
|
// instant
|
|
if (!cons_speed.value)
|
|
{
|
|
con_curlines = con_destlines;
|
|
return;
|
|
}
|
|
|
|
// up/down move to dest
|
|
if (con_curlines < con_destlines)
|
|
{
|
|
con_curlines += FixedInt(conspeed);
|
|
if (con_curlines > con_destlines)
|
|
con_curlines = con_destlines;
|
|
}
|
|
else if (con_curlines > con_destlines)
|
|
{
|
|
con_curlines -= FixedInt(conspeed);
|
|
if (con_curlines < con_destlines)
|
|
con_curlines = con_destlines;
|
|
}
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
INT32 CON_ShiftChar(INT32 ch)
|
|
{
|
|
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
|
|
{
|
|
// Standard Latin-script uppercase translation
|
|
if (shiftdown ^ capslock)
|
|
ch = shiftxform[ch];
|
|
}
|
|
else if (ch >= KEY_KEYPAD7 && ch <= KEY_KPADDEL)
|
|
{
|
|
// Numpad keycodes mapped to printable equivalent
|
|
const char keypad_translation[] =
|
|
{
|
|
'7','8','9','-',
|
|
'4','5','6','+',
|
|
'1','2','3',
|
|
'0','.'
|
|
};
|
|
|
|
ch = keypad_translation[ch - KEY_KEYPAD7];
|
|
}
|
|
else if (ch == KEY_KPADSLASH)
|
|
{
|
|
// Ditto, but non-contiguous keycode
|
|
ch = '/';
|
|
}
|
|
else
|
|
{
|
|
// QWERTY keycode translation
|
|
if (shiftdown)
|
|
ch = shiftxform[ch];
|
|
}
|
|
|
|
return ch;
|
|
}
|
|
|
|
// Clear time of console heads up messages
|
|
//
|
|
void CON_ClearHUD(void)
|
|
{
|
|
INT32 i;
|
|
|
|
Lock_state();
|
|
|
|
for (i = 0; i < con_hudlines; i++)
|
|
con_hudtime[i] = 0;
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
// Force console to move out immediately
|
|
// note: con_ticker will set consoleready false
|
|
void CON_ToggleOff(void)
|
|
{
|
|
Lock_state();
|
|
|
|
if (!con_destlines)
|
|
{
|
|
Unlock_state();
|
|
return;
|
|
}
|
|
|
|
con_destlines = 0;
|
|
con_curlines = 0;
|
|
CON_ClearHUD();
|
|
con_forcepic = 0;
|
|
con_clipviewtop = -1; // remove console clipping of view
|
|
|
|
I_UpdateMouseGrab();
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
boolean CON_Ready(void)
|
|
{
|
|
boolean ready;
|
|
Lock_state();
|
|
{
|
|
ready = consoleready;
|
|
}
|
|
Unlock_state();
|
|
return ready;
|
|
}
|
|
|
|
// Console ticker: handles console move in/out, cursor blinking
|
|
//
|
|
void CON_Ticker(void)
|
|
{
|
|
INT32 i;
|
|
INT32 minheight;
|
|
|
|
Lock_state();
|
|
|
|
minheight = 20 * con_scalefactor; // 20 = 8+8+4
|
|
|
|
// cursor blinking
|
|
con_tick++;
|
|
con_tick &= 7;
|
|
|
|
// console key was pushed
|
|
if (consoletoggle)
|
|
{
|
|
consoletoggle = false;
|
|
|
|
// toggle off console
|
|
if (con_destlines > 0)
|
|
{
|
|
con_destlines = 0;
|
|
CON_ClearHUD();
|
|
I_UpdateMouseGrab();
|
|
}
|
|
else
|
|
CON_ChangeHeight();
|
|
}
|
|
|
|
// console movement
|
|
if (con_destlines != con_curlines)
|
|
CON_MoveConsole();
|
|
|
|
// clip the view, so that the part under the console is not drawn
|
|
con_clipviewtop = -1;
|
|
if (cons_backpic.value) // clip only when using an opaque background
|
|
{
|
|
if (con_curlines > 0)
|
|
con_clipviewtop = con_curlines - viewwindowy - 1 - 10;
|
|
// NOTE: BIG HACK::SUBTRACT 10, SO THAT WATER DON'T COPY LINES OF THE CONSOLE
|
|
// WINDOW!!! (draw some more lines behind the bottom of the console)
|
|
if (con_clipviewtop < 0)
|
|
con_clipviewtop = -1; // maybe not necessary, provided it's < 0
|
|
}
|
|
|
|
// check if console ready for prompt
|
|
if (con_destlines >= minheight)
|
|
consoleready = true;
|
|
else
|
|
consoleready = false;
|
|
|
|
// make overlay messages disappear after a while
|
|
for (i = 0; i < con_hudlines; i++)
|
|
{
|
|
con_hudtime[i]--;
|
|
if (con_hudtime[i] < 0)
|
|
con_hudtime[i] = 0;
|
|
}
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
//
|
|
// ----
|
|
//
|
|
// Shortcuts for adding and deleting characters, strings, and sections
|
|
// Necessary due to moving cursor
|
|
//
|
|
|
|
static void CON_InputClear(void)
|
|
{
|
|
Lock_state();
|
|
|
|
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
|
|
input_cur = input_sel = input_len = 0;
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
static void CON_InputSetString(const char *c)
|
|
{
|
|
Lock_state();
|
|
|
|
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
|
|
strcpy(inputlines[inputline], c);
|
|
input_cur = input_sel = input_len = strlen(c);
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
static void CON_InputAddString(const char *c)
|
|
{
|
|
size_t csize = strlen(c);
|
|
|
|
Lock_state();
|
|
|
|
if (input_len + csize > CON_MAXPROMPTCHARS-1)
|
|
{
|
|
Unlock_state();
|
|
return;
|
|
}
|
|
if (input_cur != input_len)
|
|
memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
|
|
memcpy(&inputlines[inputline][input_cur], c, csize);
|
|
input_len += csize;
|
|
input_sel = (input_cur += csize);
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
static void CON_InputDelSelection(void)
|
|
{
|
|
size_t start, end, len;
|
|
|
|
Lock_state();
|
|
|
|
if (!input_cur)
|
|
{
|
|
Unlock_state();
|
|
return;
|
|
}
|
|
|
|
if (input_cur > input_sel)
|
|
{
|
|
start = input_sel;
|
|
end = input_cur;
|
|
}
|
|
else
|
|
{
|
|
start = input_cur;
|
|
end = input_sel;
|
|
}
|
|
len = (end - start);
|
|
|
|
if (end != input_len)
|
|
memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
|
|
memset(&inputlines[inputline][input_len - len], 0, len);
|
|
|
|
input_len -= len;
|
|
input_sel = input_cur = start;
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
static void CON_InputAddChar(char c)
|
|
{
|
|
if (input_len >= CON_MAXPROMPTCHARS-1)
|
|
return;
|
|
|
|
Lock_state();
|
|
|
|
if (input_cur != input_len)
|
|
memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
|
|
inputlines[inputline][input_cur++] = c;
|
|
inputlines[inputline][++input_len] = 0;
|
|
input_sel = input_cur;
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
static void CON_InputDelChar(void)
|
|
{
|
|
if (!input_cur)
|
|
return;
|
|
|
|
Lock_state();
|
|
|
|
if (input_cur != input_len)
|
|
memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
|
|
inputlines[inputline][--input_len] = 0;
|
|
input_sel = --input_cur;
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
//
|
|
// ----
|
|
//
|
|
|
|
// Handles console key input
|
|
//
|
|
boolean CON_Responder(event_t *ev)
|
|
{
|
|
static UINT8 consdown = false; // console is treated differently due to rare usage
|
|
|
|
// sequential completions a la 4dos
|
|
static char completion[80];
|
|
|
|
static INT32 skips;
|
|
|
|
static INT32 com_skips;
|
|
static INT32 var_skips;
|
|
static INT32 alias_skips;
|
|
|
|
const char *cmd = NULL;
|
|
INT32 key;
|
|
|
|
if (chat_on)
|
|
return false;
|
|
|
|
// let go keyup events, don't eat them
|
|
if (ev->type != ev_keydown && ev->type != ev_console)
|
|
{
|
|
if (ev->data1 == gamecontrol[0][gc_console][0] || ev->data1 == gamecontrol[0][gc_console][1]
|
|
|| ev->data1 == gamecontrol[0][gc_console][2] || ev->data1 == gamecontrol[0][gc_console][3])
|
|
consdown = false;
|
|
return false;
|
|
}
|
|
|
|
key = ev->data1;
|
|
|
|
// check for console toggle key
|
|
if (ev->type != ev_console)
|
|
{
|
|
#ifndef DEVELOP // I have driven this course 45 times and I just want to give myself rocketsneakers
|
|
if (modeattacking || metalrecording || marathonmode)
|
|
return false;
|
|
#endif
|
|
|
|
if (ev->data1 >= NUMKEYS) // See also: HUD_Responder
|
|
{
|
|
INT32 i;
|
|
for (i = 0; i < num_gamecontrols; i++)
|
|
{
|
|
if (gamecontrol[0][i][0] == ev->data1 || gamecontrol[0][i][1] == ev->data1
|
|
|| gamecontrol[0][i][2] == ev->data1 || gamecontrol[0][i][3] == ev->data1)
|
|
break;
|
|
}
|
|
|
|
if (i == num_gamecontrols)
|
|
return false;
|
|
}
|
|
|
|
if (key == gamecontrol[0][gc_console][0] || key == gamecontrol[0][gc_console][1]
|
|
|| key == gamecontrol[0][gc_console][2] || key == gamecontrol[0][gc_console][3])
|
|
{
|
|
if (consdown) // ignore repeat
|
|
return true;
|
|
consoletoggle = true;
|
|
consdown = true;
|
|
return true;
|
|
}
|
|
|
|
// check other keys only if console prompt is active
|
|
if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!!
|
|
{
|
|
if (! menuactive && bindtable[key])
|
|
{
|
|
COM_BufAddText(bindtable[key]);
|
|
COM_BufAddText("\n");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// escape key toggle off console
|
|
if (key == KEY_ESCAPE)
|
|
{
|
|
consoletoggle = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
|
|
if (key == KEY_LSHIFT || key == KEY_RSHIFT
|
|
|| key == KEY_LCTRL || key == KEY_RCTRL
|
|
|| key == KEY_LALT || key == KEY_RALT)
|
|
return true;
|
|
|
|
if (key == KEY_LEFTARROW)
|
|
{
|
|
if (input_cur != 0)
|
|
{
|
|
if (ctrldown)
|
|
input_cur = M_JumpWordReverse(inputlines[inputline], input_cur);
|
|
else
|
|
--input_cur;
|
|
}
|
|
if (!shiftdown)
|
|
input_sel = input_cur;
|
|
return true;
|
|
}
|
|
else if (key == KEY_RIGHTARROW)
|
|
{
|
|
if (input_cur < input_len)
|
|
{
|
|
if (ctrldown)
|
|
input_cur += M_JumpWord(&inputlines[inputline][input_cur]);
|
|
else
|
|
++input_cur;
|
|
}
|
|
if (!shiftdown)
|
|
input_sel = input_cur;
|
|
return true;
|
|
}
|
|
|
|
// backspace and delete command prompt
|
|
if (input_sel != input_cur)
|
|
{
|
|
if (key == KEY_BACKSPACE || key == KEY_DEL)
|
|
{
|
|
CON_InputDelSelection();
|
|
return true;
|
|
}
|
|
}
|
|
else if (key == KEY_BACKSPACE)
|
|
{
|
|
if (ctrldown)
|
|
{
|
|
input_sel = M_JumpWordReverse(inputlines[inputline], input_cur);
|
|
CON_InputDelSelection();
|
|
}
|
|
else
|
|
CON_InputDelChar();
|
|
return true;
|
|
}
|
|
else if (key == KEY_DEL)
|
|
{
|
|
if (input_cur == input_len)
|
|
return true;
|
|
|
|
if (ctrldown)
|
|
{
|
|
input_sel = input_cur + M_JumpWord(&inputlines[inputline][input_cur]);
|
|
CON_InputDelSelection();
|
|
}
|
|
else
|
|
{
|
|
++input_cur;
|
|
CON_InputDelChar();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ctrl modifier -- changes behavior, adds shortcuts
|
|
if (ctrldown)
|
|
{
|
|
// show all cvars/commands that match what we have inputted
|
|
if (key == KEY_TAB)
|
|
{
|
|
size_t i, len;
|
|
|
|
if (!completion[0])
|
|
{
|
|
if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
|
|
return true;
|
|
strcpy(completion, inputlines[inputline]);
|
|
}
|
|
len = strlen(completion);
|
|
|
|
//first check commands
|
|
CONS_Printf("\nCommands:\n");
|
|
for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i))
|
|
CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
|
|
if (i == 0) CONS_Printf(" (none)\n");
|
|
|
|
//now we move on to CVARs
|
|
CONS_Printf("Variables:\n");
|
|
for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i))
|
|
CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
|
|
if (i == 0) CONS_Printf(" (none)\n");
|
|
|
|
//and finally aliases
|
|
CONS_Printf("Aliases:\n");
|
|
for (i = 0, cmd = COM_CompleteAlias(completion, i); cmd; cmd = COM_CompleteAlias(completion, ++i))
|
|
CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
|
|
if (i == 0) CONS_Printf(" (none)\n");
|
|
|
|
completion[0] = 0;
|
|
|
|
return true;
|
|
}
|
|
// ---
|
|
|
|
if (key == KEY_HOME) // oldest text in buffer
|
|
{
|
|
con_scrollup = (con_totallines-((con_curlines-16)>>3));
|
|
return true;
|
|
}
|
|
else if (key == KEY_END) // most recent text in buffer
|
|
{
|
|
con_scrollup = 0;
|
|
return true;
|
|
}
|
|
|
|
if (key == 'x' || key == 'X')
|
|
{
|
|
if (input_sel > input_cur)
|
|
I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
|
|
else
|
|
I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
|
|
CON_InputDelSelection();
|
|
completion[0] = 0;
|
|
return true;
|
|
}
|
|
else if (key == 'c' || key == 'C')
|
|
{
|
|
if (input_sel > input_cur)
|
|
I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
|
|
else
|
|
I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
|
|
return true;
|
|
}
|
|
else if (key == 'v' || key == 'V')
|
|
{
|
|
const char *paste = I_ClipboardPaste();
|
|
if (input_sel != input_cur)
|
|
CON_InputDelSelection();
|
|
if (paste != NULL)
|
|
CON_InputAddString(paste);
|
|
completion[0] = 0;
|
|
return true;
|
|
}
|
|
|
|
// Select all
|
|
if (key == 'a' || key == 'A')
|
|
{
|
|
input_sel = 0;
|
|
input_cur = input_len;
|
|
return true;
|
|
}
|
|
|
|
// ...why shouldn't it eat the key? if it doesn't, it just means you
|
|
// can control Sonic from the console, which is silly
|
|
return true;//return false;
|
|
}
|
|
|
|
// command completion forward (tab) and backward (shift-tab)
|
|
if (key == KEY_TAB)
|
|
{
|
|
// sequential command completion forward and backward
|
|
|
|
// remember typing for several completions (a-la-4dos)
|
|
if (!completion[0])
|
|
{
|
|
if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
|
|
return true;
|
|
strcpy(completion, inputlines[inputline]);
|
|
skips = 0;
|
|
com_skips = 0;
|
|
var_skips = 0;
|
|
alias_skips = 0;
|
|
}
|
|
else
|
|
{
|
|
if (shiftdown)
|
|
{
|
|
if (skips > 0)
|
|
skips--;
|
|
}
|
|
else
|
|
{
|
|
skips++;
|
|
}
|
|
}
|
|
|
|
if (skips <= com_skips)
|
|
{
|
|
cmd = COM_CompleteCommand(completion, skips);
|
|
|
|
if (cmd && skips == com_skips)
|
|
{
|
|
com_skips ++;
|
|
var_skips ++;
|
|
alias_skips++;
|
|
}
|
|
}
|
|
|
|
if (!cmd && skips <= var_skips)
|
|
{
|
|
cmd = CV_CompleteVar(completion, skips - com_skips);
|
|
|
|
if (cmd && skips == var_skips)
|
|
{
|
|
var_skips ++;
|
|
alias_skips++;
|
|
}
|
|
}
|
|
|
|
if (!cmd && skips <= alias_skips)
|
|
{
|
|
cmd = COM_CompleteAlias(completion, skips - var_skips);
|
|
|
|
if (cmd && skips == alias_skips)
|
|
{
|
|
alias_skips++;
|
|
}
|
|
}
|
|
|
|
if (cmd)
|
|
{
|
|
CON_InputSetString(va("%s ", cmd));
|
|
}
|
|
else
|
|
{
|
|
skips--;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// move up (backward) in console textbuffer
|
|
if (key == KEY_PGUP)
|
|
{
|
|
if (con_scrollup < (con_totallines-((con_curlines-16)>>3)))
|
|
con_scrollup++;
|
|
return true;
|
|
}
|
|
else if (key == KEY_PGDN)
|
|
{
|
|
if (con_scrollup > 0)
|
|
con_scrollup--;
|
|
return true;
|
|
}
|
|
else if (key == KEY_HOME)
|
|
{
|
|
input_cur = 0;
|
|
if (!shiftdown)
|
|
input_sel = input_cur;
|
|
return true;
|
|
}
|
|
else if (key == KEY_END)
|
|
{
|
|
input_cur = input_len;
|
|
if (!shiftdown)
|
|
input_sel = input_cur;
|
|
return true;
|
|
}
|
|
|
|
// At this point we're messing with input
|
|
// Clear completion
|
|
completion[0] = 0;
|
|
|
|
// command enter
|
|
if (key == KEY_ENTER)
|
|
{
|
|
if (!input_len)
|
|
return true;
|
|
|
|
// push the command
|
|
COM_BufAddText(inputlines[inputline]);
|
|
COM_BufAddText("\n");
|
|
|
|
CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]);
|
|
|
|
inputline = (inputline+1) & 31;
|
|
inputhist = inputline;
|
|
CON_InputClear();
|
|
|
|
return true;
|
|
}
|
|
|
|
// move back in input history
|
|
if (key == KEY_UPARROW)
|
|
{
|
|
// copy one of the previous inputlines to the current
|
|
do
|
|
inputhist = (inputhist - 1) & 31; // cycle back
|
|
while (inputhist != inputline && !inputlines[inputhist][0]);
|
|
|
|
// stop at the last history input line, which is the
|
|
// current line + 1 because we cycle through the 32 input lines
|
|
if (inputhist == inputline)
|
|
inputhist = (inputline + 1) & 31;
|
|
|
|
CON_InputSetString(inputlines[inputhist]);
|
|
return true;
|
|
}
|
|
|
|
// move forward in input history
|
|
if (key == KEY_DOWNARROW)
|
|
{
|
|
if (inputhist == inputline)
|
|
return true;
|
|
do
|
|
inputhist = (inputhist + 1) & 31;
|
|
while (inputhist != inputline && !inputlines[inputhist][0]);
|
|
|
|
// back to currentline
|
|
if (inputhist == inputline)
|
|
CON_InputClear();
|
|
else
|
|
CON_InputSetString(inputlines[inputhist]);
|
|
return true;
|
|
}
|
|
|
|
key = CON_ShiftChar(key);
|
|
|
|
// enter a char into the command prompt
|
|
if (key < 32 || key > 127)
|
|
return true;
|
|
|
|
if (input_sel != input_cur)
|
|
CON_InputDelSelection();
|
|
CON_InputAddChar(key);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Insert a new line in the console text buffer
|
|
//
|
|
static void CON_Linefeed(void)
|
|
{
|
|
// set time for heads up messages
|
|
con_hudtime[con_cy%con_hudlines] = cons_msgtimeout.value*TICRATE;
|
|
|
|
con_cy++;
|
|
con_cx = 0;
|
|
|
|
con_line = &con_buffer[(con_cy%con_totallines)*con_width];
|
|
memset(con_line, ' ', con_width);
|
|
|
|
// make sure the view borders are refreshed if hud messages scroll
|
|
con_hudupdate = true; // see HU_Erase()
|
|
}
|
|
|
|
// Outputs text into the console text buffer
|
|
static void CON_Print(char *msg)
|
|
{
|
|
size_t l;
|
|
INT32 controlchars = 0; // for color changing
|
|
char color = '\x80'; // keep color across lines
|
|
|
|
if (msg == NULL)
|
|
return;
|
|
|
|
if (*msg == '\3') // chat text, makes ding sound
|
|
S_StartSound(NULL, sfx_radio);
|
|
else if (*msg == '\4') // chat action, dings and is in yellow
|
|
{
|
|
*msg = '\x82'; // yellow
|
|
S_StartSound(NULL, sfx_radio);
|
|
}
|
|
|
|
Lock_state();
|
|
|
|
if (!(*msg & 0x80))
|
|
{
|
|
con_line[con_cx++] = '\x80';
|
|
controlchars = 1;
|
|
}
|
|
|
|
while (*msg)
|
|
{
|
|
// skip non-printable characters and white spaces
|
|
while (*msg && *msg <= ' ')
|
|
{
|
|
if (*msg & 0x80)
|
|
{
|
|
color = con_line[con_cx++] = *(msg++);
|
|
controlchars++;
|
|
continue;
|
|
}
|
|
else if (*msg == '\r') // carriage return
|
|
{
|
|
con_cy--;
|
|
CON_Linefeed();
|
|
color = '\x80';
|
|
controlchars = 0;
|
|
}
|
|
else if (*msg == '\n') // linefeed
|
|
{
|
|
CON_Linefeed();
|
|
con_line[con_cx++] = color;
|
|
controlchars = 1;
|
|
}
|
|
else if (*msg == ' ') // space
|
|
{
|
|
con_line[con_cx++] = ' ';
|
|
if (con_cx - controlchars >= con_width-11)
|
|
{
|
|
CON_Linefeed();
|
|
con_line[con_cx++] = color;
|
|
controlchars = 1;
|
|
}
|
|
}
|
|
else if (*msg == '\t')
|
|
{
|
|
// adds tab spaces for nice layout in console
|
|
|
|
do
|
|
{
|
|
con_line[con_cx++] = ' ';
|
|
} while ((con_cx - controlchars) % 4 != 0);
|
|
|
|
if (con_cx - controlchars >= con_width-11)
|
|
{
|
|
CON_Linefeed();
|
|
con_line[con_cx++] = color;
|
|
controlchars = 1;
|
|
}
|
|
}
|
|
msg++;
|
|
}
|
|
|
|
if (*msg == '\0')
|
|
{
|
|
Unlock_state();
|
|
return;
|
|
}
|
|
|
|
// printable character
|
|
for (l = 0; l < (con_width-11) && msg[l] > ' '; l++)
|
|
;
|
|
|
|
// word wrap
|
|
if ((con_cx - controlchars) + l > con_width-11)
|
|
{
|
|
CON_Linefeed();
|
|
con_line[con_cx++] = color;
|
|
controlchars = 1;
|
|
}
|
|
|
|
// a word at a time
|
|
for (; l > 0; l--)
|
|
con_line[con_cx++] = *(msg++);
|
|
}
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
void CON_LogMessage(const char *msg)
|
|
{
|
|
char txt[8192], *t;
|
|
const char *p = msg, *e = txt+sizeof (txt)-2;
|
|
|
|
for (t = txt; *p != '\0'; p++)
|
|
{
|
|
if (*p == '\n' || *p >= ' ') // don't log or console print CON_Print's control characters
|
|
*t++ = *p;
|
|
|
|
if (t >= e)
|
|
{
|
|
*t = '\0'; //end of string
|
|
I_OutputMsg("%s", txt); //print string
|
|
t = txt; //reset t pointer
|
|
memset(txt,'\0', sizeof (txt)); //reset txt
|
|
}
|
|
}
|
|
*t = '\0'; //end of string
|
|
I_OutputMsg("%s", txt);
|
|
}
|
|
|
|
// Console print! Wahooo! Lots o fun!
|
|
//
|
|
|
|
void CONS_Printf(const char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
static char *txt = NULL;
|
|
|
|
if (txt == NULL)
|
|
txt = malloc(8192);
|
|
|
|
va_start(argptr, fmt);
|
|
vsprintf(txt, fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
// echo console prints to log file
|
|
DEBFILE(txt);
|
|
|
|
// write message in con text buffer
|
|
if (con_started)
|
|
CON_Print(txt);
|
|
|
|
CON_LogMessage(txt);
|
|
|
|
Lock_state();
|
|
|
|
// make sure new text is visible
|
|
con_scrollup = 0;
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
void CONS_Alert(alerttype_t level, const char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
static char *txt = NULL;
|
|
|
|
if (txt == NULL)
|
|
txt = malloc(8192);
|
|
|
|
va_start(argptr, fmt);
|
|
vsprintf(txt, fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
switch (level)
|
|
{
|
|
case CONS_NOTICE:
|
|
// no notice for notices, hehe
|
|
CONS_Printf("\x83" "%s" "\x80 ", M_GetText("NOTICE:"));
|
|
break;
|
|
case CONS_WARNING:
|
|
refreshdirmenu |= REFRESHDIR_WARNING;
|
|
CONS_Printf("\x82" "%s" "\x80 ", M_GetText("WARNING:"));
|
|
break;
|
|
case CONS_ERROR:
|
|
refreshdirmenu |= REFRESHDIR_ERROR;
|
|
CONS_Printf("\x85" "%s" "\x80 ", M_GetText("ERROR:"));
|
|
break;
|
|
}
|
|
|
|
// I am lazy and I feel like just letting CONS_Printf take care of things.
|
|
// Is that okay?
|
|
CONS_Printf("%s", txt);
|
|
}
|
|
|
|
void CONS_Debug(UINT32 debugflags, const char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
static char *txt = NULL;
|
|
|
|
if ((cht_debug & debugflags) != debugflags)
|
|
return;
|
|
|
|
if (txt == NULL)
|
|
txt = malloc(8192);
|
|
|
|
va_start(argptr, fmt);
|
|
vsprintf(txt, fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
// Again I am lazy, oh well
|
|
CONS_Printf("%s", txt);
|
|
}
|
|
|
|
|
|
// Print an error message, and wait for ENTER key to continue.
|
|
// To make sure the user has seen the message
|
|
//
|
|
void CONS_Error(const char *msg)
|
|
{
|
|
CONS_Printf("\x82%s", msg); // write error msg in different colour
|
|
CONS_Printf(M_GetText("Press ENTER to continue\n"));
|
|
|
|
// dirty quick hack, but for the good cause
|
|
while (I_GetKey() != KEY_ENTER)
|
|
I_OsPolling();
|
|
}
|
|
|
|
//======================================================================
|
|
// CONSOLE DRAW
|
|
//======================================================================
|
|
|
|
// draw console prompt line
|
|
//
|
|
static void CON_DrawInput(void)
|
|
{
|
|
INT32 charwidth = (INT32)con_scalefactor << 3;
|
|
const char *p = inputlines[inputline];
|
|
size_t c, clen, cend;
|
|
UINT8 lellip = 0, rellip = 0;
|
|
INT32 x, y, i;
|
|
|
|
y = con_curlines - 12 * con_scalefactor;
|
|
x = charwidth*2;
|
|
|
|
clen = con_width-13;
|
|
|
|
if (input_len <= clen)
|
|
{
|
|
c = 0;
|
|
clen = input_len;
|
|
}
|
|
else // input line scrolls left if it gets too long
|
|
{
|
|
clen -= 2; // There will always be some extra truncation -- but where is what we'll find out
|
|
|
|
if (input_cur <= clen/2)
|
|
{
|
|
// Close enough to right edge to show all
|
|
c = 0;
|
|
// Always will truncate right side from this position, so always draw right ellipsis
|
|
rellip = 1;
|
|
}
|
|
else
|
|
{
|
|
// Cursor in the middle (or right side) of input
|
|
// Move over for the ellipsis
|
|
c = input_cur - (clen/2) + 2;
|
|
x += charwidth*2;
|
|
lellip = 1;
|
|
|
|
if (c + clen >= input_len)
|
|
{
|
|
// Cursor in the right side of input
|
|
// We were too far over, so move back
|
|
c = input_len - clen;
|
|
}
|
|
else
|
|
{
|
|
// Cursor in the middle -- ellipses on both sides
|
|
clen -= 2;
|
|
rellip = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lellip)
|
|
{
|
|
x -= charwidth*3;
|
|
if (input_sel < c)
|
|
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
|
|
for (i = 0; i < 3; ++i, x += charwidth)
|
|
V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
|
|
}
|
|
else
|
|
V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
|
|
|
|
for (cend = c + clen; c < cend; ++c, x += charwidth)
|
|
{
|
|
if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c))
|
|
{
|
|
V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 77 | V_NOSCALESTART);
|
|
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, true);
|
|
}
|
|
else
|
|
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, true);
|
|
|
|
if (c == input_cur && con_tick >= 4)
|
|
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
|
|
}
|
|
if (cend == input_cur && con_tick >= 4)
|
|
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
|
|
if (rellip)
|
|
{
|
|
if (input_sel > cend)
|
|
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
|
|
for (i = 0; i < 3; ++i, x += charwidth)
|
|
V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
|
|
}
|
|
}
|
|
|
|
// draw the last lines of console text to the top of the screen
|
|
static void CON_DrawHudlines(void)
|
|
{
|
|
UINT8 *p;
|
|
size_t i;
|
|
INT32 y;
|
|
INT32 charflags = 0;
|
|
INT32 charwidth = 8 * con_scalefactor;
|
|
INT32 charheight = 8 * con_scalefactor;
|
|
|
|
if (con_hudlines <= 0)
|
|
return;
|
|
|
|
if (chat_on && OLDCHAT)
|
|
y = charheight; // leave place for chat input in the first row of text (only do it if consolechat is on.)
|
|
else
|
|
y = 0;
|
|
|
|
for (i = con_cy - con_hudlines+1; i <= con_cy; i++)
|
|
{
|
|
size_t c;
|
|
INT32 x;
|
|
|
|
if ((signed)i < 0)
|
|
continue;
|
|
if (con_hudtime[i%con_hudlines] == 0)
|
|
continue;
|
|
|
|
p = (UINT8 *)&con_buffer[(i%con_totallines)*con_width];
|
|
|
|
for (c = 0, x = 0; c < con_width; c++, x += charwidth, p++)
|
|
{
|
|
while (*p & 0x80) // Graue 06-19-2004
|
|
{
|
|
charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
|
|
p++;
|
|
c++;
|
|
}
|
|
if (c >= con_width)
|
|
break;
|
|
if (*p < HU_FONTSTART)
|
|
;//charwidth = 4 * con_scalefactor;
|
|
else
|
|
{
|
|
//charwidth = (hu_font['A'-HU_FONTSTART]->width) * con_scalefactor;
|
|
V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
|
|
}
|
|
}
|
|
|
|
//V_DrawCharacter(x, y, (p[c]&0xff) | cv_constextsize.value | V_NOSCALESTART, true);
|
|
y += charheight;
|
|
}
|
|
|
|
// top screen lines that might need clearing when view is reduced
|
|
con_clearlines = y; // this is handled by HU_Erase();
|
|
}
|
|
|
|
// Lactozilla: Draws the console's background picture.
|
|
static void CON_DrawBackpic(void)
|
|
{
|
|
patch_t *con_backpic;
|
|
lumpnum_t piclump;
|
|
int x, w, h;
|
|
|
|
// Get the lumpnum for CONSBACK, STARTUP (Only during game startup) or fallback into MISSING.
|
|
if (con_startup)
|
|
piclump = W_CheckNumForName("STARTUP");
|
|
else
|
|
piclump = W_CheckNumForName("KARTKREW");
|
|
|
|
if (piclump == LUMPERROR)
|
|
piclump = W_GetNumForName("MISSING");
|
|
|
|
// Cache the patch.
|
|
con_backpic = W_CachePatchNum(piclump, PU_PATCH);
|
|
|
|
// Center the backpic, and draw a vertically cropped patch.
|
|
w = (BASEVIDWIDTH * vid.dupx);
|
|
x = (vid.width / 2) - (w / 2);
|
|
h = con_curlines/vid.dupy;
|
|
|
|
// If the patch doesn't fill the entire screen,
|
|
// then fill the sides with a solid color.
|
|
if (x > 0)
|
|
{
|
|
column_t *column = (column_t *)((UINT8 *)(con_backpic->columns) + (con_backpic->columnofs[0]));
|
|
if (!column->topdelta)
|
|
{
|
|
UINT8 *source = (UINT8 *)(column) + 3;
|
|
INT32 color = (source[0] | V_NOSCALESTART);
|
|
// left side
|
|
V_DrawFill(0, 0, x, con_curlines, color);
|
|
// right side
|
|
V_DrawFill((x + w), 0, (vid.width - w), con_curlines, color);
|
|
}
|
|
}
|
|
|
|
// Draw the patch.
|
|
V_DrawCroppedPatch(x << FRACBITS, 0, FRACUNIT, V_NOSCALESTART, con_backpic,
|
|
0, ( BASEVIDHEIGHT - h ), BASEVIDWIDTH, h);
|
|
|
|
// Unlock the cached patch.
|
|
W_UnlockCachedPatch(con_backpic);
|
|
}
|
|
|
|
// draw the console background, text, and prompt if enough place
|
|
//
|
|
static void CON_DrawConsole(void)
|
|
{
|
|
UINT8 *p;
|
|
size_t i;
|
|
INT32 y;
|
|
INT32 charflags = 0;
|
|
INT32 charwidth = (INT32)con_scalefactor << 3;
|
|
INT32 charheight = charwidth;
|
|
INT32 minheight = 20 * con_scalefactor; // 20 = 8+8+4
|
|
|
|
if (con_curlines <= 0)
|
|
return;
|
|
|
|
//FIXME: refresh borders only when console bg is translucent
|
|
con_clearlines = con_curlines; // clear console draw from view borders
|
|
con_hudupdate = true; // always refresh while console is on
|
|
|
|
// draw console background
|
|
if (cons_backpic.value || con_forcepic)
|
|
CON_DrawBackpic();
|
|
else
|
|
{
|
|
// inu: no more width (was always 0 and vid.width)
|
|
if (rendermode != render_none)
|
|
V_DrawFadeConsBack(con_curlines); // translucent background
|
|
}
|
|
|
|
// draw console text lines from top to bottom
|
|
if (con_curlines < minheight)
|
|
return;
|
|
|
|
i = con_cy - con_scrollup;
|
|
|
|
// skip the last empty line due to the cursor being at the start of a new line
|
|
i--;
|
|
|
|
i -= (con_curlines - minheight) / charheight;
|
|
|
|
if (rendermode == render_none) return;
|
|
|
|
for (y = (con_curlines-minheight) % charheight; y <= con_curlines-minheight; y += charheight, i++)
|
|
{
|
|
INT32 x;
|
|
size_t c;
|
|
|
|
p = (UINT8 *)&con_buffer[((i > 0 ? i : 0)%con_totallines)*con_width];
|
|
|
|
for (c = 0, x = charwidth; c < con_width; c++, x += charwidth, p++)
|
|
{
|
|
while (*p & 0x80)
|
|
{
|
|
charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
|
|
p++;
|
|
c++;
|
|
}
|
|
if (c >= con_width)
|
|
break;
|
|
V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
|
|
}
|
|
}
|
|
|
|
// draw prompt if enough place (not while game startup)
|
|
if ((con_curlines == con_destlines) && (con_curlines >= minheight) && !con_startup)
|
|
CON_DrawInput();
|
|
}
|
|
|
|
// Console refresh drawer, call each frame
|
|
//
|
|
static boolean CON_GamestateDrawHudLines(void)
|
|
{
|
|
switch (gamestate)
|
|
{
|
|
case GS_LEVEL:
|
|
case GS_INTERMISSION:
|
|
case GS_VOTING:
|
|
case GS_CUTSCENE:
|
|
case GS_CREDITS:
|
|
case GS_EVALUATION:
|
|
case GS_WAITINGPLAYERS:
|
|
case GS_CEREMONY:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CON_Drawer(void)
|
|
{
|
|
Lock_state();
|
|
|
|
if (!con_started || !graphics_started)
|
|
{
|
|
Unlock_state();
|
|
return;
|
|
}
|
|
|
|
if (con_recalc)
|
|
{
|
|
CON_RecalcSize();
|
|
if (con_curlines <= 0)
|
|
CON_ClearHUD();
|
|
}
|
|
|
|
if (con_curlines > 0)
|
|
CON_DrawConsole();
|
|
else if (CON_GamestateDrawHudLines() == true)
|
|
CON_DrawHudlines();
|
|
|
|
Unlock_state();
|
|
}
|
|
|
|
static const char *CON_LoadingStrings[LOADED_ALLDONE+1] =
|
|
{
|
|
"Init zone memory...", //LOADED_ZINIT
|
|
"Init game timing...", //LOADED_ISTARTUPTIMER
|
|
"Loading main assets...", //LOADED_IWAD
|
|
"Loading add-ons...", //LOADED_PWAD
|
|
"Init graphics subsystem...", //LOADED_ISTARTUPGRAPHICS
|
|
"Cache fonts...", //LOADED_HUINIT
|
|
"Load settings...", //LOADED_CONFIG
|
|
"Cache textures...", //LOADED_INITTEXTUREDATA
|
|
"Cache sprites...", //LOADED_INITSPIRTES
|
|
"Load characters...", //LOADED_INITSKINS
|
|
"Init rendering daemon...", //LOADED_RINIT
|
|
"Init audio subsystem...", //LOADED_SINITSFXCHANNELS
|
|
"Cache HUD...", //LOADED_STINIT
|
|
"Init ACSVM...", //LOADED_ACSINIT
|
|
"Check game status...", //LOADED_DCHECKNETGAME
|
|
"Now starting..."
|
|
}; // see also con_loadprogress_t in console.h
|
|
|
|
//
|
|
// Error handling for the loading bar, to ensure it doesn't skip any steps.
|
|
//
|
|
void CON_SetLoadingProgress(con_loadprogress_t newStep)
|
|
{
|
|
const con_loadprogress_t expectedStep = con_startup_loadprogress + 1;
|
|
|
|
if (newStep != expectedStep)
|
|
{
|
|
I_Error("Something is wrong with the loading bar! (got %d, expected %d)\n", newStep, expectedStep);
|
|
return;
|
|
}
|
|
|
|
con_startup_loadprogress = newStep;
|
|
|
|
if (con_startup_loadprogress <= LOADED_ALLDONE)
|
|
CONS_Printf("LOADING UPDATE - %s\n", CON_LoadingStrings[con_startup_loadprogress]);
|
|
|
|
if (con_startup_loadprogress < LOADED_ISTARTUPGRAPHICS) // rendering not possible?
|
|
return;
|
|
CON_DrawLoadBar(); // here we display the console text
|
|
I_OsPolling();
|
|
I_UpdateNoBlit();
|
|
I_FinishUpdate(); // page flip or blit buffer
|
|
}
|
|
|
|
//
|
|
// Draws a simple white fill at the bottom of startup for load progress
|
|
//
|
|
void CON_DrawLoadBar(void)
|
|
{
|
|
const INT16 barheight = 2;
|
|
INT16 barwidth = 0;
|
|
|
|
Lock_state();
|
|
|
|
if (!con_started || !graphics_started)
|
|
{
|
|
Unlock_state();
|
|
return;
|
|
}
|
|
|
|
CON_DrawBackpic();
|
|
|
|
barwidth = (BASEVIDWIDTH * con_startup_loadprogress) / LOADED_ALLDONE;
|
|
V_DrawFill(0, BASEVIDHEIGHT - barheight, barwidth, barheight, 0);
|
|
#ifdef DEVELOP
|
|
if (con_startup_loadprogress <= LOADED_ALLDONE)
|
|
V_DrawString(4, BASEVIDHEIGHT - (barheight + 8 + 4), 0, CON_LoadingStrings[con_startup_loadprogress]);
|
|
#endif
|
|
|
|
Unlock_state();
|
|
}
|