yquake2remaster/src/client/cl_console.c

760 lines
12 KiB
C
Raw Normal View History

/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*
* =======================================================================
*
* This file implements the console
*
* =======================================================================
*/
#include "header/client.h"
#include <time.h>
2012-07-22 13:34:45 +00:00
console_t con;
cvar_t *con_notifytime;
#define MAXCMDLINE 256
extern char key_lines[32][MAXCMDLINE];
extern int edit_line;
extern int key_linepos;
void
DrawString(int x, int y, char *s)
{
while (*s)
{
re.DrawChar(x, y, *s);
x += 8;
s++;
}
}
2012-07-22 13:34:45 +00:00
void
DrawAltString(int x, int y, char *s)
{
while (*s)
{
re.DrawChar(x, y, *s ^ 0x80);
x += 8;
s++;
}
}
2012-07-22 13:34:45 +00:00
void
Key_ClearTyping(void)
{
key_lines[edit_line][1] = 0; /* clear any typing */
key_linepos = 1;
}
2012-07-22 13:34:45 +00:00
void
Con_ToggleConsole_f(void)
{
SCR_EndLoadingPlaque(); /* get rid of loading plaque */
2012-07-22 13:34:45 +00:00
if (cl.attractloop)
{
Cbuf_AddText("killserver\n");
return;
}
2012-07-22 13:34:45 +00:00
if (cls.state == ca_disconnected)
{
/* start the demo loop again */
2012-07-22 13:34:45 +00:00
Cbuf_AddText("d1\n");
return;
}
2012-07-22 13:34:45 +00:00
Key_ClearTyping();
Con_ClearNotify();
2012-07-22 13:34:45 +00:00
if (cls.key_dest == key_console)
{
M_ForceMenuOff();
Cvar_Set("paused", "0");
}
else
{
M_ForceMenuOff();
cls.key_dest = key_console;
2012-07-22 13:34:45 +00:00
if ((Cvar_VariableValue("maxclients") == 1) &&
Com_ServerState())
{
Cvar_Set("paused", "1");
}
}
}
2012-07-22 13:34:45 +00:00
void
Con_ToggleChat_f(void)
{
Key_ClearTyping();
2012-07-22 13:34:45 +00:00
if (cls.key_dest == key_console)
{
if (cls.state == ca_active)
{
M_ForceMenuOff();
cls.key_dest = key_game;
}
2012-07-22 13:34:45 +00:00
}
else
{
cls.key_dest = key_console;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
Con_ClearNotify();
}
2012-07-22 13:34:45 +00:00
void
Con_Clear_f(void)
{
memset(con.text, ' ', CON_TEXTSIZE);
}
/*
* Save the console contents out to a file
*/
2012-07-22 13:34:45 +00:00
void
Con_Dump_f(void)
{
int l, x;
char *line;
FILE *f;
char buffer[1024];
char name[MAX_OSPATH];
if (Cmd_Argc() != 2)
{
Com_Printf("usage: condump <filename>\n");
return;
}
if (con.linewidth >= 1024)
2012-07-22 13:34:45 +00:00
{
Com_Printf("con.linewidth too large!\n");
2012-06-01 16:13:30 +00:00
return;
}
2012-07-22 13:34:45 +00:00
Com_sprintf(name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1));
2012-07-22 13:34:45 +00:00
Com_Printf("Dumped console text to %s.\n", name);
FS_CreatePath(name);
f = fopen(name, "w");
2012-07-22 13:34:45 +00:00
if (!f)
{
Com_Printf("ERROR: couldn't open.\n");
return;
}
/* skip empty lines */
2012-07-22 13:34:45 +00:00
for (l = con.current - con.totallines + 1; l <= con.current; l++)
{
line = con.text + (l % con.totallines) * con.linewidth;
2012-07-22 13:34:45 +00:00
for (x = 0; x < con.linewidth; x++)
{
if (line[x] != ' ')
2012-07-22 13:34:45 +00:00
{
break;
2012-07-22 13:34:45 +00:00
}
}
if (x != con.linewidth)
2012-07-22 13:34:45 +00:00
{
break;
2012-07-22 13:34:45 +00:00
}
}
/* write the remaining lines */
buffer[con.linewidth] = 0;
2012-07-22 13:34:45 +00:00
for ( ; l <= con.current; l++)
{
line = con.text + (l % con.totallines) * con.linewidth;
memcpy(buffer, line, con.linewidth);
2012-07-22 13:34:45 +00:00
for (x = con.linewidth - 1; x >= 0; x--)
{
if (buffer[x] == ' ')
2012-07-22 13:34:45 +00:00
{
buffer[x] = 0;
2012-07-22 13:34:45 +00:00
}
else
2012-07-22 13:34:45 +00:00
{
break;
2012-07-22 13:34:45 +00:00
}
}
2012-07-22 13:34:45 +00:00
for (x = 0; buffer[x]; x++)
{
buffer[x] &= 0x7f;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
fprintf(f, "%s\n", buffer);
}
2012-07-22 13:34:45 +00:00
fclose(f);
}
2012-07-22 13:34:45 +00:00
void
Con_ClearNotify(void)
{
int i;
2012-07-22 13:34:45 +00:00
for (i = 0; i < NUM_CON_TIMES; i++)
{
con.times[i] = 0;
2012-07-22 13:34:45 +00:00
}
}
2012-07-22 13:34:45 +00:00
void
Con_MessageMode_f(void)
{
chat_team = false;
cls.key_dest = key_message;
}
2012-07-22 13:34:45 +00:00
void
Con_MessageMode2_f(void)
{
chat_team = true;
cls.key_dest = key_message;
}
/*
* If the line width has changed, reformat the buffer.
*/
2012-07-22 13:34:45 +00:00
void
Con_CheckResize(void)
{
int i, j, width, oldwidth, oldtotallines, numlines, numchars;
char tbuf[CON_TEXTSIZE];
width = (viddef.width >> 3) - 2;
if (width == con.linewidth)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
/* video hasn't been initialized yet */
2012-07-22 13:34:45 +00:00
if (width < 1)
{
width = 38;
con.linewidth = width;
con.totallines = CON_TEXTSIZE / con.linewidth;
2012-07-22 13:34:45 +00:00
memset(con.text, ' ', CON_TEXTSIZE);
}
else
{
oldwidth = con.linewidth;
con.linewidth = width;
oldtotallines = con.totallines;
con.totallines = CON_TEXTSIZE / con.linewidth;
numlines = oldtotallines;
if (con.totallines < numlines)
2012-07-22 13:34:45 +00:00
{
numlines = con.totallines;
2012-07-22 13:34:45 +00:00
}
numchars = oldwidth;
if (con.linewidth < numchars)
2012-07-22 13:34:45 +00:00
{
numchars = con.linewidth;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
memcpy(tbuf, con.text, CON_TEXTSIZE);
memset(con.text, ' ', CON_TEXTSIZE);
2012-07-22 13:34:45 +00:00
for (i = 0; i < numlines; i++)
{
for (j = 0; j < numchars; j++)
{
con.text[(con.totallines - 1 - i) * con.linewidth + j] =
2012-07-22 13:34:45 +00:00
tbuf[((con.current - i + oldtotallines) %
oldtotallines) * oldwidth + j];
}
}
2012-07-22 13:34:45 +00:00
Con_ClearNotify();
}
con.current = con.totallines - 1;
con.display = con.current;
}
2012-07-22 13:34:45 +00:00
void
Con_Init(void)
{
con.linewidth = -1;
2012-07-22 13:34:45 +00:00
Con_CheckResize();
2012-07-22 13:34:45 +00:00
Com_Printf("Console initialized.\n");
/* register our commands */
2012-07-22 13:34:45 +00:00
con_notifytime = Cvar_Get("con_notifytime", "3", 0);
Cmd_AddCommand("toggleconsole", Con_ToggleConsole_f);
Cmd_AddCommand("togglechat", Con_ToggleChat_f);
Cmd_AddCommand("messagemode", Con_MessageMode_f);
Cmd_AddCommand("messagemode2", Con_MessageMode2_f);
Cmd_AddCommand("clear", Con_Clear_f);
Cmd_AddCommand("condump", Con_Dump_f);
con.initialized = true;
}
2012-07-22 13:34:45 +00:00
void
Con_Linefeed(void)
{
con.x = 0;
if (con.display == con.current)
2012-07-22 13:34:45 +00:00
{
con.display++;
2012-07-22 13:34:45 +00:00
}
con.current++;
2012-07-22 13:34:45 +00:00
memset(&con.text[(con.current % con.totallines) * con.linewidth],
' ', con.linewidth);
}
/*
* Handles cursor positioning, line wrapping, etc All console printing
* must go through this in order to be logged to disk If no console is
* visible, the text will appear at the top of the game window
*/
2012-07-22 13:34:45 +00:00
void
Con_Print(char *txt)
{
int y;
int c, l;
static int cr;
int mask;
if (!con.initialized)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
if ((txt[0] == 1) || (txt[0] == 2))
{
mask = 128; /* go to colored text */
txt++;
2012-07-22 13:34:45 +00:00
}
else
{
mask = 0;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
while ((c = *txt))
{
/* count word length */
2012-07-22 13:34:45 +00:00
for (l = 0; l < con.linewidth; l++)
{
if (txt[l] <= ' ')
{
break;
2012-07-22 13:34:45 +00:00
}
}
/* word wrap */
2012-07-22 13:34:45 +00:00
if ((l != con.linewidth) && (con.x + l > con.linewidth))
{
con.x = 0;
2012-07-22 13:34:45 +00:00
}
txt++;
2012-07-22 13:34:45 +00:00
if (cr)
{
con.current--;
cr = false;
}
2012-07-22 13:34:45 +00:00
if (!con.x)
{
Con_Linefeed();
/* mark time for transparent overlay */
if (con.current >= 0)
2012-07-22 13:34:45 +00:00
{
con.times[con.current % NUM_CON_TIMES] = cls.realtime;
2012-07-22 13:34:45 +00:00
}
}
2012-07-22 13:34:45 +00:00
switch (c)
{
case '\n':
con.x = 0;
break;
case '\r':
con.x = 0;
cr = 1;
break;
default: /* display character and advance */
y = con.current % con.totallines;
2012-07-22 13:34:45 +00:00
con.text[y * con.linewidth + con.x] = c | mask | con.ormask;
con.x++;
if (con.x >= con.linewidth)
2012-07-22 13:34:45 +00:00
{
con.x = 0;
2012-07-22 13:34:45 +00:00
}
break;
}
}
}
2012-07-22 13:34:45 +00:00
void
Con_CenteredPrint(char *text)
{
int l;
char buffer[1024];
l = strlen(text);
2012-07-22 13:34:45 +00:00
l = (con.linewidth - l) / 2;
if (l < 0)
2012-07-22 13:34:45 +00:00
{
l = 0;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
memset(buffer, ' ', l);
strcpy(buffer + l, text);
strcat(buffer, "\n");
2012-07-22 13:34:45 +00:00
Con_Print(buffer);
}
/*
2012-04-29 13:57:33 +00:00
* The input line scrolls horizontally if
* typing goes beyond the right edge
*/
2012-07-22 13:34:45 +00:00
void
Con_DrawInput(void)
{
int i;
char *text;
if (cls.key_dest == key_menu)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
/* don't draw anything (always draw if not active) */
2012-07-22 13:34:45 +00:00
if ((cls.key_dest != key_console) && (cls.state == ca_active))
{
return;
2012-07-22 13:34:45 +00:00
}
text = key_lines[edit_line];
/* add the cursor frame */
2012-07-22 13:34:45 +00:00
text[key_linepos] = 10 + ((int)(cls.realtime >> 8) & 1);
/* fill out remainder with spaces */
2012-07-22 13:34:45 +00:00
for (i = key_linepos + 1; i < con.linewidth; i++)
{
text[i] = ' ';
2012-07-22 13:34:45 +00:00
}
/* prestep if horizontally scrolling */
if (key_linepos >= con.linewidth)
2012-07-22 13:34:45 +00:00
{
text += 1 + key_linepos - con.linewidth;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
for (i = 0; i < con.linewidth; i++)
{
re.DrawChar((i + 1) << 3, con.vislines - 22, text[i]);
}
/* remove cursor */
key_lines[edit_line][key_linepos] = 0;
}
/*
* Draws the last few lines of output transparently over the game top
*/
2012-07-22 13:34:45 +00:00
void
Con_DrawNotify(void)
{
int x, v;
char *text;
int i;
int time;
char *s;
int skip;
v = 0;
2012-07-22 13:34:45 +00:00
for (i = con.current - NUM_CON_TIMES + 1; i <= con.current; i++)
{
if (i < 0)
2012-07-22 13:34:45 +00:00
{
continue;
2012-07-22 13:34:45 +00:00
}
time = con.times[i % NUM_CON_TIMES];
if (time == 0)
2012-07-22 13:34:45 +00:00
{
continue;
2012-07-22 13:34:45 +00:00
}
time = cls.realtime - time;
2012-07-22 13:34:45 +00:00
if (time > con_notifytime->value * 1000)
{
continue;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
text = con.text + (i % con.totallines) * con.linewidth;
2012-07-22 13:34:45 +00:00
for (x = 0; x < con.linewidth; x++)
{
re.DrawChar((x + 1) << 3, v, text[x]);
}
v += 8;
}
2012-07-22 13:34:45 +00:00
if (cls.key_dest == key_message)
{
if (chat_team)
{
DrawString(8, v, "say_team:");
skip = 11;
2012-07-22 13:34:45 +00:00
}
else
{
DrawString(8, v, "say:");
skip = 5;
}
s = chat_buffer;
2012-07-22 13:34:45 +00:00
if (chat_bufferlen > (viddef.width >> 3) - (skip + 1))
{
s += chat_bufferlen - ((viddef.width >> 3) - (skip + 1));
}
x = 0;
2012-07-22 13:34:45 +00:00
while (s[x])
{
re.DrawChar((x + skip) << 3, v, s[x]);
x++;
}
2012-07-22 13:34:45 +00:00
re.DrawChar((x + skip) << 3, v, 10 + ((cls.realtime >> 8) & 1));
v += 8;
}
2012-07-22 13:34:45 +00:00
if (v)
{
SCR_AddDirtyPoint(0, 0);
SCR_AddDirtyPoint(viddef.width - 1, v);
}
}
/*
* Draws the console with the solid background
*/
2012-07-22 13:34:45 +00:00
void
Con_DrawConsole(float frac)
{
int i, j, x, y, n;
int rows;
char *text;
int row;
int lines;
char version[48];
char dlbar[1024];
char timebuf[48];
char tmpbuf[48];
time_t t;
struct tm *today;
lines = viddef.height * frac;
if (lines <= 0)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
if (lines > viddef.height)
2012-07-22 13:34:45 +00:00
{
lines = viddef.height;
2012-07-22 13:34:45 +00:00
}
/* draw the background */
2012-07-22 13:34:45 +00:00
re.DrawStretchPic(0, -viddef.height + lines, viddef.width,
viddef.height, "conback");
SCR_AddDirtyPoint(0, 0);
SCR_AddDirtyPoint(viddef.width - 1, lines - 1);
Com_sprintf(version, sizeof(version), "Yamagi Quake II v%s", VERSION);
2012-07-22 13:34:45 +00:00
for (x = 0; x < 21; x++)
{
re.DrawChar(viddef.width - 173 + x * 8, lines - 35, 128 + version[x]);
}
t = time(NULL);
today = localtime(&t);
2012-07-22 13:34:45 +00:00
strftime(timebuf, sizeof(timebuf), "%H:%M:%S - %m/%d/%Y", today);
2009-03-02 23:42:57 +00:00
2012-07-22 13:34:45 +00:00
Com_sprintf(tmpbuf, sizeof(tmpbuf), "%s", timebuf);
2012-07-22 13:34:45 +00:00
for (x = 0; x < 21; x++)
{
re.DrawChar(viddef.width - 173 + x * 8, lines - 25, 128 + tmpbuf[x]);
}
/* draw the text */
con.vislines = lines;
2012-07-22 13:34:45 +00:00
rows = (lines - 22) >> 3; /* rows of text to draw */
y = lines - 30;
/* draw from the bottom up */
2012-07-22 13:34:45 +00:00
if (con.display != con.current)
{
/* draw arrows to show the buffer is backscrolled */
2012-07-22 13:34:45 +00:00
for (x = 0; x < con.linewidth; x += 4)
{
re.DrawChar((x + 1) << 3, y, '^');
}
y -= 8;
rows--;
}
row = con.display;
2012-07-22 13:34:45 +00:00
for (i = 0; i < rows; i++, y -= 8, row--)
{
if (row < 0)
2012-07-22 13:34:45 +00:00
{
break;
2012-07-22 13:34:45 +00:00
}
if (con.current - row >= con.totallines)
2012-07-22 13:34:45 +00:00
{
break; /* past scrollback wrap point */
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
text = con.text + (row % con.totallines) * con.linewidth;
2012-07-22 13:34:45 +00:00
for (x = 0; x < con.linewidth; x++)
{
re.DrawChar((x + 1) << 3, y, text[x]);
}
}
/* draw the download bar, figure out width */
2012-07-22 13:34:45 +00:00
if (cls.download)
{
if ((text = strrchr(cls.downloadname, '/')) != NULL)
2012-07-22 13:34:45 +00:00
{
text++;
2012-07-22 13:34:45 +00:00
}
else
2012-07-22 13:34:45 +00:00
{
text = cls.downloadname;
2012-07-22 13:34:45 +00:00
}
x = con.linewidth - ((con.linewidth * 7) / 40);
y = x - strlen(text) - 8;
2012-07-22 13:34:45 +00:00
i = con.linewidth / 3;
2012-07-22 13:34:45 +00:00
if (strlen(text) > i)
{
y = x - i - 11;
memcpy(dlbar, text, i);
dlbar[i] = 0;
strcat(dlbar, "...");
2012-07-22 13:34:45 +00:00
}
else
{
strcpy(dlbar, text);
2012-07-22 13:34:45 +00:00
}
strcat(dlbar, ": ");
i = strlen(dlbar);
dlbar[i++] = '\x80';
/* where's the dot gone? */
if (cls.downloadpercent == 0)
2012-07-22 13:34:45 +00:00
{
n = 0;
2012-07-22 13:34:45 +00:00
}
else
2012-07-22 13:34:45 +00:00
{
n = y * cls.downloadpercent / 100;
2012-07-22 13:34:45 +00:00
}
for (j = 0; j < y; j++)
2012-07-22 13:34:45 +00:00
{
if (j == n)
2012-07-22 13:34:45 +00:00
{
dlbar[i++] = '\x83';
2012-07-22 13:34:45 +00:00
}
else
2012-07-22 13:34:45 +00:00
{
dlbar[i++] = '\x81';
2012-07-22 13:34:45 +00:00
}
}
dlbar[i++] = '\x82';
dlbar[i] = 0;
sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent);
/* draw it */
2012-07-22 13:34:45 +00:00
y = con.vislines - 12;
for (i = 0; i < strlen(dlbar); i++)
2012-07-22 13:34:45 +00:00
{
re.DrawChar((i + 1) << 3, y, dlbar[i]);
}
}
/* draw the input prompt, user text, and cursor if desired */
2012-07-22 13:34:45 +00:00
Con_DrawInput();
}
2012-07-22 13:34:45 +00:00