/* =========================================================================== Copyright (C) 1997-2001 Id Software, Inc. This file is part of Quake 2 source code. Quake 2 source code 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. Quake 2 source code 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 Quake 2 source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // console.c #include "client.h" console_t con; cvar_t *con_notifytime; qboolean newconback_found = false; // whether to draw Q3-style console extern char key_lines[32][MAXCMDLINE]; extern int edit_line; extern int key_linepos; /* ================ Con_DrawString ================ */ void Con_DrawString (int x, int y, char *string, fontslot_t font, int alpha) { CL_DrawStringGeneric (x, y, string, font, alpha, FONT_SIZE, SCALETYPE_CONSOLE, false); } /* ================ Key_ClearTyping ================ */ void Key_ClearTyping (void) { key_lines[edit_line][1] = 0; // clear any typing key_linepos = 1; con.backedit = 0; } /* ================ Con_ToggleConsole_f ================ */ void Con_ToggleConsole_f (void) { // SCR_EndLoadingPlaque (); // get rid of loading plaque // Knightmare- allow disconnected menu if (cls.state == ca_disconnected && cls.key_dest != key_menu) { // start the demo loop again Cbuf_AddText ("d1\n"); return; } Key_ClearTyping (); Con_ClearNotify (); if (cls.consoleActive) // Knightmare changed //if (cls.key_dest == key_console) { cls.consoleActive = false; // Knightmare added //UI_ForceMenuOff (); //if (cls.key_dest != key_menu) if (Cvar_VariableValue ("maxclients") == 1 && Com_ServerState () && cls.key_dest != key_menu) Cvar_Set ("paused", "0"); } else { cls.consoleActive = true; // Knightmare added //UI_ForceMenuOff (); //cls.key_dest = key_console; if (Cvar_VariableValue ("maxclients") == 1 && Com_ServerState () && cls.key_dest != key_menu) Cvar_Set ("paused", "1"); } } /* ================ Con_ToggleChat_f ================ */ void Con_ToggleChat_f (void) { Key_ClearTyping (); if (cls.consoleActive) // Knightmare added //if (cls.key_dest == key_console) { if (cls.state == ca_active) { UI_ForceMenuOff (); cls.consoleActive = false; // Knightmare added cls.key_dest = key_game; } } else //cls.key_dest = key_console; cls.consoleActive = true; // Knightmare added Con_ClearNotify (); } /* ================ Con_Clear_f ================ */ void Con_Clear_f (void) { memset (con.text, ' ', CON_TEXTSIZE); } /* ================ Con_Dump_f Save the console contents out to a file ================ */ 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 \n"); return; } Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Savegamedir(), Cmd_Argv(1)); // was FS_Gamedir() Com_Printf ("Dumped console text to %s.\n", name); FS_CreatePath (name); f = fopen (name, "w"); if (!f) { Com_Printf ("ERROR: couldn't open.\n"); return; } // skip empty lines for (l = con.current - con.totallines + 1 ; l <= con.current ; l++) { line = con.text + (l%con.totallines)*con.linewidth; for (x=0 ; x=0 ; x--) { if (buffer[x] == ' ') buffer[x] = 0; else break; } for (x=0; buffer[x]; x++) buffer[x] &= 0x7f; fprintf (f, "%s\n", buffer); } fclose (f); } /* ================ Con_ClearNotify ================ */ void Con_ClearNotify (void) { int i; for (i=0 ; i> 3) - 2; width = min((MAXCMDLINE - 2), width); // clamp width to MAXCMDLINE - 2 to prevent buffer overflow if (width == con.linewidth) return; if (width < 1) // video hasn't been initialized yet { width = 78; // was 38 con.linewidth = width; con.backedit = 0; con.totallines = CON_TEXTSIZE / con.linewidth; memset (con.text, ' ', CON_TEXTSIZE); } else { oldwidth = con.linewidth; con.linewidth = width; con.backedit = 0; oldtotallines = con.totallines; con.totallines = CON_TEXTSIZE / con.linewidth; numlines = oldtotallines; if (con.totallines < numlines) numlines = con.totallines; numchars = oldwidth; if (con.linewidth < numchars) numchars = con.linewidth; memcpy (tbuf, con.text, CON_TEXTSIZE); memset (con.text, ' ', CON_TEXTSIZE); for (i=0 ; i con.linewidth) ) { con.x = 0; } txt++; if (cr) { con.current--; cr = false; } if (!con.x) { Con_Linefeed (colorCode, bold, shadow, italic, alt); // mark time for transparent overlay if (con.current >= 0) con.times[con.current % NUM_CON_TIMES] = cls.realtime; } // track formatting codes for word wrap modifier = (c & ~128); if (nextCharModifierCheck) // last char was a ^ { Con_StringSetParams (modifier, &colorCode, &bold, &shadow, &italic, &alt); nextCharModifierCheck = false; } else { // set var to check modifier if char is ^ nextCharModifierCheck = (modifier == '^') ? true : false; } switch (c) { case '\n': con.x = 0; colorCode = 0; bold = shadow = italic = alt = false; break; case '\r': con.x = 0; cr = 1; colorCode = 0; bold = shadow = italic = alt = false; break; default: // display character and advance y = con.current % con.totallines; con.text[y*con.linewidth+con.x] = c | mask | con.ormask; con.x++; if (con.x >= con.linewidth) con.x = 0; break; } } } /* ============== Con_CenteredPrint ============== */ void Con_CenteredPrint (char *text) { int l; char buffer[1024]; l = (int)strlen(text); l = (con.linewidth-l)/2; if (l < 0) l = 0; memset (buffer, ' ', l); // strncpy (buffer+l, text); // strncat (buffer, "\n"); Q_strncpyz (buffer+l, sizeof(buffer)-1, text); Q_strncatz (buffer, sizeof(buffer), "\n"); Con_Print (buffer); } /* ============================================================================== DRAWING ============================================================================== */ /* ================ Con_DrawInput The input line scrolls horizontally if typing goes beyond the right edge ================ */ void Con_DrawInput (void) { int y; int i; char *text, output[2048], addch[8]; float conLeft = 0; if (!cls.consoleActive && cls.state == ca_active) return; // don't draw anything (always draw if not active) SCR_ScaleCoords (&conLeft, NULL, NULL, NULL, ALIGN_STRETCH); /*if (cls.key_dest == key_menu) return; if (cls.key_dest != key_console && cls.state == ca_active) return;*/ // don't draw anything (always draw if not active) text = key_lines[edit_line]; // add the cursor frame if (con.backedit) text[key_linepos] = ' '; else text[key_linepos] = 10+((int)(cls.realtime>>8)&1); // fill out remainder with spaces for (i=key_linepos+1 ; i< con.linewidth ; i++) text[i] = ' '; // prestep if horizontally scrolling if (key_linepos >= con.linewidth) text += 1 + key_linepos - con.linewidth; // draw it y = con.vislines-FONT_SIZE*2; // was 16 Com_sprintf (output, sizeof(output), ""); for (i=0; i>8)&1)) { // Com_sprintf (output, sizeof(output), "%s%c", output, 11 ); addch[0] = 11; addch[1] = '\0'; Q_strncatz (output, sizeof(output), addch); } else { // Com_sprintf (output, sizeof(output), "%s%c", output, text[i]); addch[0] = text[i]; addch[1] = '\0'; Q_strncatz (output, sizeof(output), addch); } } Con_DrawString ( (int)conLeft + FONT_SIZE/2, con.vislines - (int)(2.75*FONT_SIZE), output, FONT_CONSOLE, 255); // remove cursor key_lines[edit_line][key_linepos] = 0; } /* ================ Con_DrawNotify Draws the last few lines of output transparently over the game top ================ */ void Con_DrawNotify (void) { int x; char *text, output[2048], addch[8]; int i, j; // int time; char *s; int alpha, lines; float v, time, conLeft, conWidth; lines = 0; v = 0; conLeft = 0; conWidth = SCREEN_WIDTH; SCR_ScaleCoords (&conLeft, NULL, &conWidth, NULL, ALIGN_STRETCH); Com_sprintf (output, sizeof(output), ""); // this is the say msg while typeing... if (cls.key_dest == key_message) { if (chat_team) Com_sprintf (output, sizeof(output), "%s", " say_team: "); else Com_sprintf (output, sizeof(output), "%s", " say: "); s = chat_buffer; x = 0; if (chat_bufferlen > (viddef.width/FONT_SIZE)-(strlen(output)+1)) x += chat_bufferlen - (int)((viddef.width/FONT_SIZE)-(strlen(output)+1)); while (s[x]) { if ( chat_backedit && (chat_backedit == chat_bufferlen-x) && ((int)(cls.realtime>>8)&1) ) { // Com_sprintf (output, sizeof(output), "%s%c", output, 11 ); addch[0] = 11; addch[1] = '\0'; Q_strncatz (output, sizeof(output), addch); } else { // Com_sprintf (output, sizeof(output), "%s%c", output, (char)s[x]); addch[0] = s[x]; addch[1] = '\0'; Q_strncatz (output, sizeof(output), addch); } x++; } if (!chat_backedit) { // Com_sprintf (output, sizeof(output), "%s%c", output, 10+((int)(cls.realtime>>8)&1) ); addch[0] = 10+((int)(cls.realtime>>8)&1); addch[1] = '\0'; Q_strncatz (output, sizeof(output), addch); } Con_DrawString ((int)conLeft, v, output, FONT_SCREEN, 255); v += FONT_SIZE*2; // make extra space so we have room } for (i = con.current-NUM_CON_TIMES+1; i <= con.current; i++) { if (i < 0) continue; time = con.times[i % NUM_CON_TIMES]; if (time == 0) continue; time = cls.realtime - time; if (time > con_notifytime->value*1000) continue; // vertical offset set by closest to dissapearing lines++; } // v = 0; if (lines) for (j = 0, i = con.current-NUM_CON_TIMES+1; i <= con.current; i++, j++) { if (i < 0) continue; time = con.times[i % NUM_CON_TIMES]; if (time == 0) continue; time = cls.realtime - time; if (time > con_notifytime->value*1000) continue; text = con.text + (i % con.totallines)*con.linewidth; alpha = 255 * sqrt( (1.0-time/(con_notifytime->value*1000.0+1.0)) * (((float)v+8.0)) / (8.0*lines) ); // alpha = (1-time/(con_notifytime->value*1000.0+2)) * (255*(v+16)) / (4*lines); if (alpha < 0) alpha = 0; if (alpha > 255) alpha = 255; Com_sprintf (output, sizeof(output), ""); for (x = 0; x < con.linewidth; x++) { // Com_sprintf (output, sizeof(output), "%s%c", output, (char)text[x]); addch[0] = (char)text[x]; addch[1] = '\0'; Q_strncatz (output, sizeof(output), addch); } Con_DrawString ((int)conLeft + FONT_SIZE/2, v, output, FONT_SCREEN, alpha); v += FONT_SIZE; } } /* ================ Con_DrawDownloadProgress Draws a download progress bar ================ */ void Con_DrawDownloadProgress (float conLeft, float conWidth, float conLineHeight, int verLen, int red, int green, int blue) { int i, j, x, y, len; char *text; char dlbar[1024]; int graph_x, graph_y, graph_h, graph_w; if ((text = strrchr(cls.downloadname, '/')) != NULL) text++; else text = cls.downloadname; memset (dlbar, 0, sizeof(dlbar)); // clear dlbar // make this a little shorter in case of longer version string x = con.linewidth - ((con.linewidth * 7) / 40) - (verLen-14); if (cls.downloadrate > 0.0f) y = x - (int)strlen(text) - 19; else y = x - (int)strlen(text) - 8; i = con.linewidth/3; if (strlen(text) > i) { y = x - i - 11; strncpy(dlbar, text, i); dlbar[i] = 0; // strncat(dlbar, "..."); Q_strncatz(dlbar, sizeof(dlbar), "..."); } else // strncpy(dlbar, text); Q_strncpyz(dlbar, sizeof(dlbar), text); // strncat(dlbar, ": "); Q_strncatz(dlbar, sizeof(dlbar), ": "); i = (int)strlen(dlbar); // init solid color download bar graph_x = (i+1)*FONT_SIZE; graph_y = con.vislines - (int)(FONT_SIZE*1.5) - (int)conLineHeight; // was -12 graph_w = y*FONT_SIZE; graph_h = FONT_SIZE; for (j = 0; j < y; j++) // add blank spaces Com_sprintf(dlbar + strlen(dlbar), sizeof(dlbar)-strlen(dlbar), " "); if (cls.downloadrate > 0.0f) Com_sprintf(dlbar + strlen(dlbar), sizeof(dlbar)-strlen(dlbar), " %2d%% (%4.2fKB/s)", cls.downloadpercent, cls.downloadrate); else Com_sprintf(dlbar + strlen(dlbar), sizeof(dlbar)-strlen(dlbar), " %2d%%", cls.downloadpercent); // draw it //y = graph_y; len = (int)strlen(dlbar); for (i = 0; i < len; i++) if (dlbar[i] != ' ') R_DrawChar((int)conLeft + (i+1)*FONT_SIZE, graph_y, dlbar[i], FONT_CONSOLE, CON_FONT_SCALE, 255, 255, 255, 255, false, (i==(len-1)) ); // new solid color download bar graph_x--; graph_y--; graph_w+=2; graph_h+=2; R_DrawFill ((int)conLeft + graph_x, graph_y, graph_w, graph_h, 255, 255, 255, 90); R_DrawFill ((int)conLeft + (int)(graph_x+graph_h*0.2), (int)(graph_y+graph_h*0.2), (int)((graph_w-graph_h*0.4)*cls.downloadpercent*0.01), (int)(graph_h*0.6), red, green, blue, 255); } /* ================ Con_DrawConsole Draws the console with the solid background ================ */ void Con_DrawConsole (float frac, qboolean trans) { int i, x, y; int rows; char *text, output[1024], addch[8]; int row; int lines; char version[64]; float alpha, barheight, conLeft, conWidth, picLeft, picWidth, picHeight, pboxWidth; // changeable download bar color int red, green, blue; vec4_t picColor; drawStruct_t ds = { 0 }; // CL_TextColor ((int)alt_text_color->value, &red, &green, &blue); CL_TextColor (alt_text_color->integer, &red, &green, &blue); // Q3-style console bottom bar conLeft = picLeft = 0; conWidth = picWidth = SCREEN_WIDTH; picHeight = SCREEN_HEIGHT; SCR_ScaleCoords (&picLeft, NULL, &picWidth, &picHeight, ALIGN_CENTER); // if ( (newconback_found && scr_newconback->value) || scr_oldconbar->value ) { if ( (newconback_found && scr_newconback->integer) || scr_oldconbar->integer ) { barheight = 2; SCR_ScaleCoords (&conLeft, NULL, &conWidth, &barheight, ALIGN_STRETCH); } else { barheight = 0; SCR_ScaleCoords (&conLeft, NULL, &conWidth, NULL, ALIGN_STRETCH); } lines = viddef.height * frac; if (lines <= 0) return; if (lines > viddef.height) lines = viddef.height; // Psychospaz's transparent console //alpha = (trans) ? ((frac/ (newconback_found?0.5:scr_conheight->value) )*scr_conalpha->value) : 1; // alpha = trans ? ((newconback_found && scr_newconback->value) ? scr_conalpha->value : 2*frac*scr_conalpha->value) : 1; alpha = trans ? ((newconback_found && scr_newconback->integer) ? scr_conalpha->value : 2*frac*scr_conalpha->value) : 1; Vector4Set (picColor, 1.0f, 1.0f, 1.0f, alpha); // draw the background //i = newconback_found ? lines - barheight : lines*(1/scr_conheight->value); //j = newconback_found ? 0 : (scr_conheight->value-1)*i - barheight; y = lines - barheight; Vector2Copy (vec2_origin, ds.offset); Vector4Copy (picColor, ds.color); if (y < 1) y = 0; /* else if (newconback_found && scr_newconback->integer) // Q3-style console R_DrawStretchPic ((int)picLeft, 0, picWidth, lines-barheight, "/gfx/ui/newconback.pcx", alpha); else R_DrawStretchPic ((int)picLeft, (lines-(int)picHeight-(int)barheight), picWidth, (int)picHeight, "conback", alpha); */ else if (newconback_found && scr_newconback->integer) { // Q3-style console ds.pic = "/gfx/ui/newconback.pcx"; // ds.x = (int)conLeft; ds.w = conWidth; ds.x = (int)picLeft; ds.w = picWidth; ds.y = 0; ds.h = lines-(int)barheight; R_DrawPic (ds); } else { ds.pic = "conback"; // ds.x = (int)conLeft; ds.w = conWidth; ds.x = (int)picLeft; ds.w = picWidth; ds.y = (lines-(int)picHeight-(int)barheight); ds.h = (int)picHeight; R_DrawPic (ds); } // pillarbox sides if console is wider than scaled pic if (conWidth > picWidth) { pboxWidth = picLeft - conLeft; R_DrawFill ((int)conLeft, 0, (int)pboxWidth, lines-(int)barheight, 0, 0, 0, (int)(alpha*255.0f)); R_DrawFill ((int)picLeft + (int)picWidth, 0, (int)pboxWidth, lines-(int)barheight, 0, 0, 0, (int)(alpha*255.0f)); } // changed to "KMQuake2 vx.xx" #ifdef ERASER_COMPAT_BUILD Com_sprintf (version, sizeof(version), S_COLOR_BOLD S_COLOR_SHADOW S_COLOR_ALT"KMQuake2 v%4.2fu%d (Eraser compatible)", VERSION, VERSION_UPDATE); #else // ERASER_COMPAT_BUILD Com_sprintf (version, sizeof(version), S_COLOR_BOLD S_COLOR_SHADOW S_COLOR_ALT"KMQuake2 v%4.2fu%d", VERSION, VERSION_UPDATE); #endif // NEW_ENTITY_STATE_MEMBERS Con_DrawString ((int)(conLeft+conWidth)-FONT_SIZE*(stringLen((const char *)&version))-3, y-(int)(1.25*FONT_SIZE), version, FONT_CONSOLE, 255); // if ( (newconback_found && scr_newconback->value) || scr_oldconbar->value ) // Q3-style console bottom bar if ( (newconback_found && scr_newconback->integer) || scr_oldconbar->integer ) // Q3-style console bottom bar R_DrawFill ((int)conLeft, y, conWidth, barheight, red, green, blue, 255); // draw the text con.vislines = lines; rows = (lines-(int)(2.75*FONT_SIZE))/FONT_SIZE; // rows of text to draw y = lines - (int)(3.75*FONT_SIZE); // draw from the bottom up if (con.display != con.current) { // draw arrows to show the buffer is backscrolled for (x = 0; x < con.linewidth; x+=4) R_DrawChar ((int)conLeft + (x+1)*FONT_SIZE, y, '^', FONT_CONSOLE, CON_FONT_SCALE, 255, 0, 0, 255, false, ((x+4)>=con.linewidth) ); y -= FONT_SIZE; rows--; } row = con.display; for (i=0; i= con.totallines) break; // past scrollback wrap point text = con.text + (row % con.totallines)*con.linewidth; Com_sprintf (output, sizeof(output), ""); for (x=0; x