// console.c #include "client.h" #include "..\strings\con_text.h" #include "..\qcommon\strip.h" #include "../qcommon/game_version.h" int g_console_field_width = 78; console_t con; cvar_t *con_conspeed; cvar_t *con_notifytime; #define DEFAULT_CONSOLE_WIDTH 78 vec4_t console_color = {1.0, 1.0, 1.0, 1.0}; /* ================ Con_ToggleConsole_f ================ */ void Con_ToggleConsole_f (void) { // closing a full screen console restarts the demo loop if ( cls.state == CA_DISCONNECTED && cls.keyCatchers == KEYCATCH_CONSOLE ) { CL_StartDemoLoop(); return; } Field_Clear( &kg.g_consoleField ); kg.g_consoleField.widthInChars = g_console_field_width; Con_ClearNotify (); cls.keyCatchers ^= KEYCATCH_CONSOLE; } /* ================ Con_MessageMode_f ================ */ void Con_MessageMode_f (void) { //yell chat_playerNum = -1; chat_team = qfalse; Field_Clear( &chatField ); chatField.widthInChars = 30; cls.keyCatchers ^= KEYCATCH_MESSAGE; } /* ================ Con_MessageMode2_f ================ */ void Con_MessageMode2_f (void) { //team chat chat_playerNum = -1; chat_team = qtrue; Field_Clear( &chatField ); chatField.widthInChars = 25; cls.keyCatchers ^= KEYCATCH_MESSAGE; } /* ================ Con_MessageMode3_f ================ */ void Con_MessageMode3_f (void) { //target chat chat_playerNum = VM_Call( cgvm, CG_CROSSHAIR_PLAYER ); if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) { chat_playerNum = -1; return; } chat_team = qfalse; Field_Clear( &chatField ); chatField.widthInChars = 30; cls.keyCatchers ^= KEYCATCH_MESSAGE; } /* ================ Con_MessageMode4_f ================ */ void Con_MessageMode4_f (void) { //attacker chat_playerNum = VM_Call( cgvm, CG_LAST_ATTACKER ); if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) { chat_playerNum = -1; return; } chat_team = qfalse; Field_Clear( &chatField ); chatField.widthInChars = 30; cls.keyCatchers ^= KEYCATCH_MESSAGE; } /* ================ Con_Clear_f ================ */ void Con_Clear_f (void) { int i; for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) { con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' '; } Con_Bottom(); // go to end } /* ================ Con_Dump_f Save the console contents out to a file ================ */ void Con_Dump_f (void) { int l, x, i; short *line; fileHandle_t f; char buffer[1024]; if (Cmd_Argc() != 2) { Com_Printf (SP_GetStringText(CON_TEXT_DUMP_USAGE)); return; } Com_Printf ("Dumped console text to %s.\n", Cmd_Argv(1) ); f = FS_FOpenFileWrite( Cmd_Argv( 1 ) ); if (!f) { Com_Printf (S_COLOR_RED"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; } strcat( buffer, "\n" ); FS_Write(buffer, strlen(buffer), f); } FS_FCloseFile( f ); } /* ================ Con_ClearNotify ================ */ void Con_ClearNotify( void ) { int i; for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) { con.times[i] = 0; } } /* ================ Con_CheckResize If the line width has changed, reformat the buffer. ================ */ void Con_CheckResize (void) { int i, j, width, oldwidth, oldtotallines, numlines, numchars; MAC_STATIC short tbuf[CON_TEXTSIZE]; // width = (SCREEN_WIDTH / SMALLCHAR_WIDTH) - 2; width = (cls.glconfig.vidWidth / SMALLCHAR_WIDTH) - 2; if (width == con.linewidth) return; if (width < 1) // video hasn't been initialized yet { con.xadjust = 1; con.yadjust = 1; width = DEFAULT_CONSOLE_WIDTH; con.linewidth = width; con.totallines = CON_TEXTSIZE / con.linewidth; for(i=0; i= 0) con.times[con.current % NUM_CON_TIMES] = cls.realtime; con.x = 0; if (con.display == con.current) con.display++; con.current++; for(i=0; iinteger ) { return; } if (!con.initialized) { con.color[0] = con.color[1] = con.color[2] = con.color[3] = 1.0f; con.linewidth = -1; Con_CheckResize (); con.initialized = qtrue; } color = ColorIndex(COLOR_WHITE); while ( (c = (unsigned char) *txt) != 0 ) { if ( Q_IsColorString( (unsigned char*) txt ) ) { color = ColorIndex( *(txt+1) ); txt += 2; continue; } // count word length for (l=0 ; l< con.linewidth ; l++) { if ( txt[l] <= ' ') { break; } } // word wrap if (l != con.linewidth && (con.x + l >= con.linewidth) ) { Con_Linefeed(); } txt++; switch (c) { case '\n': Con_Linefeed (); break; case '\r': con.x = 0; break; default: // display character and advance y = con.current % con.totallines; con.text[y*con.linewidth+con.x] = (short) ((color << 8) | c); con.x++; if (con.x >= con.linewidth) { Con_Linefeed(); con.x = 0; } break; } } // mark time for transparent overlay if (con.current >= 0) con.times[con.current % NUM_CON_TIMES] = cls.realtime; } /* ============================================================================== DRAWING ============================================================================== */ /* ================ Con_DrawInput Draw the editline after a ] prompt ================ */ void Con_DrawInput (void) { int y; if ( cls.state != CA_DISCONNECTED && !(cls.keyCatchers & KEYCATCH_CONSOLE ) ) { return; } y = con.vislines - ( SMALLCHAR_HEIGHT * (re.Language_IsAsian() ? 1.5 : 2) ); re.SetColor( con.color ); SCR_DrawSmallChar( (int)(con.xadjust + 1 * SMALLCHAR_WIDTH), y, ']' ); Field_Draw( &kg.g_consoleField, (int)(con.xadjust + 2 * SMALLCHAR_WIDTH), y, SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, qtrue ); } /* ================ Con_DrawNotify Draws the last few lines of output transparently over the game top ================ */ void Con_DrawNotify (void) { int x, v; short *text; int i; int time; int skip; int currentColor; currentColor = 7; re.SetColor( g_color_table[currentColor] ); v = 0; 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; text = con.text + (i % con.totallines)*con.linewidth; if (cl.snap.ps.pm_type != PM_INTERMISSION && cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) { continue; } if (!cl_conXOffset) { cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0); } // asian language needs to use the new font system to print glyphs... // // (ignore colours since we're going to print the whole thing as one string) // if (re.Language_IsAsian()) { static int iFontIndex = re.RegisterFont("ocr_a"); // this seems naughty const float fFontScale = 0.75f*con.yadjust; const int iPixelHeightToAdvance = 2+(1.3/con.yadjust) * re.Font_HeightPixels(iFontIndex, fFontScale); // for asian spacing, since we don't want glyphs to touch. // concat the text to be printed... // char sTemp[4096]={0}; // ott for (x = 0 ; x < con.linewidth ; x++) { if ( ( (text[x]>>8)&7 ) != currentColor ) { currentColor = (text[x]>>8)&7; strcat(sTemp,va("^%i", (text[x]>>8)&7) ); } strcat(sTemp,va("%c",text[x] & 0xFF)); } // // and print... // re.Font_DrawString(cl_conXOffset->integer + con.xadjust*(con.xadjust + (1*SMALLCHAR_WIDTH/*aesthetics*/)), con.yadjust*(v), sTemp, g_color_table[currentColor], iFontIndex, -1, fFontScale); v += iPixelHeightToAdvance; } else { for (x = 0 ; x < con.linewidth ; x++) { if ( ( text[x] & 0xff ) == ' ' ) { continue; } if ( ( (text[x]>>8)&7 ) != currentColor ) { currentColor = (text[x]>>8)&7; re.SetColor( g_color_table[currentColor] ); } if (!cl_conXOffset) { cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0); } SCR_DrawSmallChar( (int)(cl_conXOffset->integer + con.xadjust + (x+1)*SMALLCHAR_WIDTH), v, text[x] & 0xff ); } v += SMALLCHAR_HEIGHT; } } re.SetColor( NULL ); if (cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) { return; } // draw the chat line if ( cls.keyCatchers & KEYCATCH_MESSAGE ) { if (chat_team) { SCR_DrawBigString (8, v, "say_team:", 1.0f ); skip = 11; } else { SCR_DrawBigString (8, v, "say:", 1.0f ); skip = 5; } Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, v, SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, qtrue ); v += BIGCHAR_HEIGHT; } } /* ================ Con_DrawSolidConsole Draws the console with the solid background ================ */ void Con_DrawSolidConsole( float frac ) { int i, x, y; int rows; short *text; int row; int lines; // qhandle_t conShader; int currentColor; lines = (int) (cls.glconfig.vidHeight * frac); if (lines <= 0) return; if (lines > cls.glconfig.vidHeight ) lines = cls.glconfig.vidHeight; // draw the background y = (int) (frac * SCREEN_HEIGHT - 2); if ( y < 1 ) { y = 0; } else { SCR_DrawPic( 0, 0, SCREEN_WIDTH, (float) y, cls.consoleShader ); } const vec4_t color = { 0.509f, 0.609f, 0.847f, 1.0f}; // draw the bottom bar and version number re.SetColor( color ); re.DrawStretchPic( 0, y, SCREEN_WIDTH, 2, 0, 0, 0, 0, cls.whiteShader ); i = strlen( Q3_VERSION ); for (x=0 ; x= con.totallines) { // past scrollback wrap point continue; } text = con.text + (row % con.totallines)*con.linewidth; // asian language needs to use the new font system to print glyphs... // // (ignore colours since we're going to print the whole thing as one string) // if (re.Language_IsAsian()) { // concat the text to be printed... // char sTemp[4096]={0}; // ott for (x = 0 ; x < con.linewidth ; x++) { if ( ( (text[x]>>8)&7 ) != currentColor ) { currentColor = (text[x]>>8)&7; strcat(sTemp,va("^%i", (text[x]>>8)&7) ); } strcat(sTemp,va("%c",text[x] & 0xFF)); } // // and print... // re.Font_DrawString(con.xadjust*(con.xadjust + (1*SMALLCHAR_WIDTH/*(aesthetics)*/)), con.yadjust*(y), sTemp, g_color_table[currentColor], iFontIndexForAsian, -1, fFontScaleForAsian); } else { for (x=0 ; x>8)&7 ) != currentColor ) { currentColor = (text[x]>>8)&7; re.SetColor( g_color_table[currentColor] ); } SCR_DrawSmallChar( (int) (con.xadjust + (x+1)*SMALLCHAR_WIDTH), y, text[x] & 0xff ); } } } // draw the input prompt, user text, and cursor if desired Con_DrawInput (); re.SetColor( NULL ); } /* ================== Con_DrawConsole ================== */ void Con_DrawConsole( void ) { // check for console width changes from a vid mode change Con_CheckResize (); // if disconnected, render console full screen if ( cls.state == CA_DISCONNECTED ) { if ( !( cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME)) ) { Con_DrawSolidConsole( 1.0 ); return; } } if ( con.displayFrac ) { Con_DrawSolidConsole( con.displayFrac ); } else { // draw notify lines if ( cls.state == CA_ACTIVE ) { Con_DrawNotify (); } } } //================================================================ /* ================== Con_RunConsole Scroll it up or down ================== */ void Con_RunConsole (void) { // decide on the destination height of the console if ( cls.keyCatchers & KEYCATCH_CONSOLE ) con.finalFrac = 0.5; // half screen else con.finalFrac = 0; // none visible // scroll towards the destination height if (con.finalFrac < con.displayFrac) { con.displayFrac -= con_conspeed->value*(float)(cls.realFrametime*0.001); if (con.finalFrac > con.displayFrac) con.displayFrac = con.finalFrac; } else if (con.finalFrac > con.displayFrac) { con.displayFrac += con_conspeed->value*(float)(cls.realFrametime*0.001); if (con.finalFrac < con.displayFrac) con.displayFrac = con.finalFrac; } } void Con_PageUp( void ) { con.display -= 2; if ( con.current - con.display >= con.totallines ) { con.display = con.current - con.totallines + 1; } } void Con_PageDown( void ) { con.display += 2; if (con.display > con.current) { con.display = con.current; } } void Con_Top( void ) { con.display = con.totallines; if ( con.current - con.display >= con.totallines ) { con.display = con.current - con.totallines + 1; } } void Con_Bottom( void ) { con.display = con.current; } void Con_Close( void ) { if ( !com_cl_running->integer ) { return; } Field_Clear( &kg.g_consoleField ); Con_ClearNotify (); cls.keyCatchers &= ~KEYCATCH_CONSOLE; con.finalFrac = 0; // none visible con.displayFrac = 0; }