[Console] Implemented console tabs

[Console] Implemented console tabs

diff --git a/src/docs/Changelog b/src/docs/Changelog
index fd6ca0d..40dc577 100644
--- a/src/docs/Changelog
+++ b/src/docs/Changelog
@@ -7,6 +7,8 @@
 	* [Engine] Allow sending "userinfo" while the menu is open
 	* [Filesystem] Changed fs_debug logging option
 	* [Client] Small fixes
+	* [Client] Fixed floating-point errors in pitch angle
+	* [Console] Implemented console tabs
 	
 2021-04-03 Dusan Jocic <dusanjocic@msn>
 	* [RenderSystem} Addressed the problem with DPI inside the engine
diff --git a/src/engine/client/cl_console.cpp b/src/engine/client/cl_console.cpp
index adf6d41..e8537ef 100644
--- a/src/engine/client/cl_console.cpp
+++ b/src/engine/client/cl_console.cpp
@@ -41,37 +41,27 @@ sint g_console_field_width = 78;
 
 #define CONSOLE_COLOR '7'
 #define DEFAULT_CONSOLE_WIDTH   78
-#define NUM_CON_TIMES 4
-#define CON_TEXTSIZE 65536
 
-typedef struct
+pointer conTabsNames[NUMBER_TABS] =
 {
-    bool        initialized;
-    
-    schar16         text[CON_TEXTSIZE];
-    sint         current;	// line where next message will be printed
-    sint         x;			// offset in current line for next print
-    sint         display;	// bottom of console displays this line
-    
-    sint         linewidth;	// characters across screen
-    sint         totallines;	// total lines in console scrollback
-    
-    float32         xadjust;	// for wide aspect screens
-    
-    float32         displayFrac;	// aproaches finalFrac at scr_conspeed
-    float32         finalFrac;	// 0.0 to 1.0 lines of console to display
-    float32         desiredFrac;	// ydnar: for variable console heights
-    
-    sint         vislines;	// in scanlines
-    
-    sint         times[NUM_CON_TIMES];	// cls.realtime time the line was generated
-    // for transparent notify lines
-    vec4_t      color;
-    
-    sint          acLength;	// Arnout: autocomplete buffer length
-} console_t;
+    "All Chat",
+    "System Chat",
+    "Player Chat",
+    "Team Chat"
+};
+
+// color indexes in g_color_table
+const sint consoleColors[] =
+{
+    1,
+    3,
+    2,
+    5,
+    6
+};
 
-console_t       con;
+console_t	con[NUMBER_TABS];
+console_t* activeCon = con;
 
 convar_t*         con_debug;
 convar_t*         con_conspeed;
@@ -100,6 +90,133 @@ convar_t*         con_restricted;
 
 vec4_t          console_highlightcolor = { 0.5, 0.5, 0.2, 0.45 };
 
+/*
+================
+Con_LineAccept
+
+When the user enters a command in the console
+================
+*/
+void Con_LineAccept( void )
+{
+    // for commandMode, always use sys-console
+    sint conNum = commandMode ? CON_SYS : activeCon - con;
+    bool isChat = CON_ISCHAT( conNum );
+    
+    // reset if commandMode
+    if( commandMode )
+    {
+        cls.keyCatchers & ~KEYCATCH_CONSOLE;
+        commandMode = false;
+    }
+    
+    // if in sys-console or not in the game explicitly prepend a slash if needed
+    if( ( cls.state != CA_ACTIVE || conNum == CON_SYS ) &&
+            con_autochat->integer &&
+            g_consoleField.buffer[0] &&
+            g_consoleField.buffer[0] != '\\' &&
+            g_consoleField.buffer[0] != '/' )
+    {
+        valueType temp[MAX_EDIT_LINE - 1];
+        
+        Q_strncpyz( temp, g_consoleField.buffer, sizeof( temp ) );
+        Q_vsprintf_s( g_consoleField.buffer, sizeof( g_consoleField.buffer ), sizeof( g_consoleField.buffer ), "\\%s", temp );
+        g_consoleField.cursor++;
+    }
+    
+    // print prompts for non-chat consoles
+    if( !isChat )
+    {
+        Com_Printf( "]%s\n", g_consoleField.buffer );
+    }
+    
+    // leading slash is an explicit command (for non-chat consoles)
+    if( !isChat && ( g_consoleField.buffer[0] == '\\' || g_consoleField.buffer[0] == '/' ) )
+    {
+        cmdBufferSystem->ExecuteText( EXEC_NOW, "reconnect\n" );
+        // valid command
+        cmdBufferSystem->AddText( g_consoleField.buffer + 1 );
+        cmdBufferSystem->AddText( "\n" );
+    }
+    else
+    {
+        // other text will be chat messages if in all-console with con_autochat
+        // or when in chat-console
+        if( !g_consoleField.buffer[0] )
+        {
+            // empty lines just scroll the console without adding to history
+            return;
+        }
+        else
+        {
+            if( ( con_autochat->integer && conNum == CON_ALL ) || conNum == CON_CHAT )
+            {
+                cmdBufferSystem->AddText( "cmd say \"" );
+                cmdBufferSystem->AddText( g_consoleField.buffer );
+                cmdBufferSystem->AddText( "\"" );
+            }
+            else if( conNum == CON_TCHAT )
+            {
+                cmdBufferSystem->AddText( "cmd say_team " );
+                cmdBufferSystem->AddText( g_consoleField.buffer );
+                cmdBufferSystem->AddText( "\"" );
+            }
+            else
+            {
+                cmdBufferSystem->AddText( g_consoleField.buffer );
+                cmdBufferSystem->AddText( "\n" );
+            }
+        }
+    }
+    
+    cmdCompletionSystem->Clear( &g_consoleField );
+    
+    g_consoleField.widthInChars = g_console_field_width;
+    
+    consoleHistorySystem->Save();
+    
+    if( cls.state == CA_DISCONNECTED )
+    {
+        // force an update, because the command may take some time
+        clientScreenSystem->UpdateScreen();
+    }
+}
+
+/*
+================
+Con_ConsoleSwitch
+
+Change to console number n
+================
+*/
+void Con_ConsoleSwitch( sint n )
+{
+    con[n].displayFrac = activeCon->displayFrac;
+    con[n].finalFrac = activeCon->finalFrac;
+    
+    if( n < 0 || n >= NUMBER_TABS )
+    {
+        Com_Printf( "Invalid console number %i\n", n );
+    }
+    else
+    {
+        activeCon = &con[n];
+    }
+}
+
+/*
+================
+Con_ConsoleNext
+
+Change to console n steps relative to current console, will wrap around, n can
+be negative in which case it will switch backwards
+================
+*/
+void Con_ConsoleNext( sint n )
+{
+    Con_ConsoleSwitch( ( NUMBER_TABS + activeCon - con + n ) % NUMBER_TABS );
+}
+
 
 /*
 ================
@@ -108,13 +225,20 @@ Con_ToggleConsole_f
 */
 void Con_ToggleConsole_f( void )
 {
-    con.acLength = 0;
+    activeCon->acLength = 0;
     
     if( con_restricted->integer && ( !keys[K_CTRL].down || !keys[K_SHIFT].down ) )
     {
         return;
     }
     
+    // if in command mode, switch to regular console
+    if( commandMode )
+    {
+        commandMode = false;
+        return;
+    }
+    
     // ydnar: persistent console input is more useful
     // Arnout: added cvar
     if( con_autoclear->integer )
@@ -126,11 +250,14 @@ void Con_ToggleConsole_f( void )
     
     Con_ClearNotify();
     
+    // change to all-console
+    activeCon = &con[CON_ALL];
+    
     // ydnar: multiple console size support
     if( cls.keyCatchers & KEYCATCH_CONSOLE )
     {
         cls.keyCatchers &= ~KEYCATCH_CONSOLE;
-        con.desiredFrac = 0.0;
+        activeCon->desiredFrac = 0.0;
     }
     else
     {
@@ -139,17 +266,17 @@ void Con_ToggleConsole_f( void )
         // schar16 console
         if( keys[K_CTRL].down )
         {
-            con.desiredFrac = ( 5.0 * SMALLCHAR_HEIGHT ) / cls.glconfig.vidHeight;
+            activeCon->desiredFrac = ( 5.0 * SMALLCHAR_HEIGHT ) / cls.glconfig.vidHeight;
         }
         // full console
         else if( keys[K_ALT].down )
         {
-            con.desiredFrac = 1.0;
+            activeCon->desiredFrac = 1.0;
         }
         // normal half-screen console
         else
         {
-            con.desiredFrac = 0.5;
+            activeCon->desiredFrac = 0.5;
         }
     }
 }
@@ -220,6 +347,18 @@ void Con_MessageMode3_f( void )
     cls.keyCatchers ^= KEYCATCH_MESSAGE;
 }
 
+/*
+================
+Con_CommandMode_f
+================
+*/
+void Con_CommandMode_f( void )
+{
+    cmdCompletionSystem->Clear( &g_consoleField );
+    commandMode = true;
+    cls.keyCatchers ^= KEYCATCH_CONSOLE;
+}
+
 void Con_OpenConsole_f( void )
 {

[... diff too long, it was truncated ...]

GitHub
sha: ca0c54e4