diff --git a/src/Components/Modules/Console.cpp b/src/Components/Modules/Console.cpp index d7d18992..586d0c42 100644 --- a/src/Components/Modules/Console.cpp +++ b/src/Components/Modules/Console.cpp @@ -324,10 +324,10 @@ namespace Components va_list va; va_start(va, fmt); - _vsnprintf_s(buf, _TRUNCATE, fmt, va); + vsnprintf_s(buf, _TRUNCATE, fmt, va); va_end(va); - Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}", buf); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}\n", buf); Console::RefreshOutput(); @@ -534,10 +534,42 @@ namespace Components return reinterpret_cast(0x471500)(dvarName, r, g, b, a, min, max, flags, description); } + void Console::Con_ToggleConsole() + { + Game::Field_Clear(Game::g_consoleField); + if (Game::conDrawInputGlob->matchIndex >= 0 && Game::conDrawInputGlob->autoCompleteChoice[0] != '\0') + { + Game::conDrawInputGlob->matchIndex = -1; + Game::conDrawInputGlob->autoCompleteChoice[0] = '\0'; + } + + Game::g_consoleField->fixedSize = 1; + Game::con->outputVisible = false; + Game::g_consoleField->widthInPixels = *Game::g_console_field_width; + Game::g_consoleField->charHeight = *Game::g_console_char_height; + + for (std::size_t localClientNum = 0; localClientNum < Game::MAX_LOCAL_CLIENTS; ++localClientNum) + { + assert((Game::clientUIActives[0].keyCatchers & Game::KEYCATCH_CONSOLE) == (Game::clientUIActives[localClientNum].keyCatchers & Game::KEYCATCH_CONSOLE)); + Game::clientUIActives[localClientNum].keyCatchers ^= 1; + } + } + + void Console::AddConsoleCommand() + { + Command::Add("con_echo", [] + { + Console::Con_ToggleConsole(); + Game::I_strncpyz(Game::g_consoleField->buffer, "\\echo ", sizeof(Game::field_t::buffer)); + Game::g_consoleField->cursor = static_cast(std::strlen(Game::g_consoleField->buffer)); + Game::Field_AdjustScroll(Game::ScrPlace_GetFullPlacement(), Game::g_consoleField); + }); + } + Console::Console() { // Console '%s: %s> ' string - Utils::Hook::Set(0x5A44B4, "IW4x: " VERSION "> "); + Utils::Hook::Set(0x5A44B4, "IW4x MP: " VERSION "> "); // Patch console color static float consoleColor[] = { 0.70f, 1.00f, 0.00f, 1.00f }; @@ -577,6 +609,10 @@ namespace Components // Don't resize the console Utils::Hook(0x64DC6B, 0x64DCC2, HOOK_JUMP).install()->quick(); +#ifdef _DEBUG + Console::AddConsoleCommand(); +#endif + if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) { Scheduler::Loop(Console::RefreshStatus, Scheduler::Pipeline::MAIN); diff --git a/src/Components/Modules/Console.hpp b/src/Components/Modules/Console.hpp index baf414b0..baee794c 100644 --- a/src/Components/Modules/Console.hpp +++ b/src/Components/Modules/Console.hpp @@ -66,6 +66,9 @@ namespace Components static void ToggleConsole(); static char** GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType); + static void Con_ToggleConsole(); + static void AddConsoleCommand(); + static Game::dvar_t* RegisterConColor(const char* dvarName, float r, float g, float b, float a, float min, float max, unsigned __int16 flags, const char* description); }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 00d96a3f..f2e71858 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -564,13 +564,19 @@ namespace Game GfxScene* scene = reinterpret_cast(0x6944914); + Console* con = reinterpret_cast(0x9FCCF8); ConDrawInputGlob* conDrawInputGlob = reinterpret_cast(0x9FD6F8); + + int* g_console_field_width = reinterpret_cast(0x79854C); + float* g_console_char_height = reinterpret_cast(0x798550); field_t* g_consoleField = reinterpret_cast(0xA1B6B0); clientStatic_t* cls = reinterpret_cast(0xA7FE90); + clientUIActive_t* clientUIActives = reinterpret_cast(0xB2BB8A); sharedUiInfo_t* sharedUiInfo = reinterpret_cast(0x62E4B78); ScreenPlacement* scrPlaceFull = reinterpret_cast(0x10843F0); + ScreenPlacement* scrPlaceFullUnsafe = reinterpret_cast(0x1084460); ScreenPlacement* scrPlaceView = reinterpret_cast(0x1084378); clientActive_t* clients = reinterpret_cast(0xB2C698); @@ -593,8 +599,6 @@ namespace Game FastCriticalSection* db_hashCritSect = reinterpret_cast(0x16B8A54); - ScreenPlacement* scrPlaceFullUnsafe = reinterpret_cast(0x1084460); - float (*CorrectSolidDeltas)[26][3] = reinterpret_cast(0x739BB8); // Count 26 level_locals_t* level = reinterpret_cast(0x1A831A8); @@ -1164,6 +1168,11 @@ namespace Game return GraphGetValueFromFraction(graph->knotCount, graph->knots, fraction) * graph->scale; } + ScreenPlacement* ScrPlace_GetFullPlacement() + { + return scrPlaceFull; + } + ScreenPlacement* ScrPlace_GetUnsafeFullPlacement() { return scrPlaceFullUnsafe; diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index cc0b30f8..35986f50 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -1231,13 +1231,19 @@ namespace Game extern GfxScene* scene; + extern Console* con; extern ConDrawInputGlob* conDrawInputGlob; + + extern int* g_console_field_width; + extern float* g_console_char_height; extern field_t* g_consoleField; extern clientStatic_t* cls; + extern clientUIActive_t* clientUIActives; extern sharedUiInfo_t* sharedUiInfo; extern ScreenPlacement* scrPlaceFull; + extern ScreenPlacement* scrPlaceFullUnsafe; extern ScreenPlacement* scrPlaceView; extern clientActive_t* clients; @@ -1266,8 +1272,6 @@ namespace Game extern FastCriticalSection* db_hashCritSect; - extern ScreenPlacement* scrPlaceFullUnsafe; - extern level_locals_t* level; extern float (*penetrationDepthTable)[PENETRATE_TYPE_COUNT][SURF_TYPE_COUNT]; @@ -1306,6 +1310,7 @@ namespace Game XModel* G_GetModel(int index); + ScreenPlacement* ScrPlace_GetFullPlacement(); ScreenPlacement* ScrPlace_GetUnsafeFullPlacement(); void UI_FilterStringForButtonAnimation(char* str, unsigned int strMaxSize); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 566baddd..5b29df09 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -8634,6 +8634,160 @@ namespace Game static_assert(sizeof(PartyData) == 0x23D8); + struct MessageLine + { + int messageIndex; + int textBufPos; + int textBufSize; + int typingStartTime; + int lastTypingSoundTime; + int flags; + }; + + struct Message + { + int startTime; + int endTime; + }; + + struct MessageWindow + { + MessageLine* lines; + Message* messages; + char* circularTextBuffer; + int textBufSize; + int lineCount; + int padding; + int scrollTime; + int fadeIn; + int fadeOut; + int textBufPos; + int firstLineIndex; + int activeLineCount; + int messageIndex; + }; + + struct MessageBuffer + { + char gamemsgText[4][2048]; + MessageWindow gamemsgWindows[4]; + MessageLine gamemsgLines[4][12]; + Message gamemsgMessages[4][12]; + char miniconText[4096]; + MessageWindow miniconWindow; + MessageLine miniconLines[100]; + Message miniconMessages[100]; + char errorText[1024]; + MessageWindow errorWindow; + MessageLine errorLines[5]; + Message errorMessages[5]; + }; + + struct Console + { + MessageWindow consoleWindow; + MessageLine consoleLines[1024]; + Message consoleMessages[1024]; + char consoleText[65536]; + char textTempLine[512]; + unsigned int lineOffset; + int displayLineOffset; + int prevChannel; + bool outputVisible; + int fontHeight; + int visibleLineCount; + int visiblePixelWidth; + float screenMin[2]; + float screenMax[2]; + MessageBuffer messageBuffer[1]; + float color[4]; + }; + + enum clientMigState_t + { + CMSTATE_INACTIVE = 0x0, + CMSTATE_OLDHOSTLEAVING = 0x1, + CMSTATE_LIMBO = 0x2, + CMSTATE_NEWHOSTCONNECT = 0x3, + CMSTATE_COUNT = 0x4, + }; + + enum MigrationVerboseState + { + MVSTATE_INACTIVE = 0x0, + MVSTATE_WAITING = 0x1, + MVSTATE_RATING = 0x2, + MVSTATE_SENDING = 0x3, + MVSTATE_MIGRATING = 0x4, + MVSTATE_COUNT = 0x5, + }; + + enum connstate_t + { + CA_DISCONNECTED = 0x0, + CA_CINEMATIC = 0x1, + CA_LOGO = 0x2, + CA_CONNECTING = 0x3, + CA_CHALLENGING = 0x4, + CA_CONNECTED = 0x5, + CA_SENDINGSTATS = 0x6, + CA_LOADING = 0x7, + CA_PRIMED = 0x8, + CA_ACTIVE = 0x9, + }; + + struct MigrationPers + { + int time; + bool stanceHeld; + StanceState stance; + StanceState stancePosition; + int stanceTime; + int cgameUserCmdWeapon; + int cgameUserCmdOffHandIndex; + unsigned int weaponSelect; + int weaponSelectTime; + int weaponForcedSelectTime; + unsigned int weaponLatestPrimaryIdx; + unsigned __int16 primaryWeaponForAlt[1400]; + int holdBreathTime; + int holdBreathInTime; + int holdBreathDelay; + float holdBreathFrac; + }; + + struct clientUIActive_t + { + bool active; + bool isRunning; + bool cgameInitialized; + bool cgameInitCalled; + bool mapPreloaded; + clientMigState_t migrationState; + MigrationPers migrationPers; + MigrationVerboseState verboseMigrationState; + int verboseMigrationData; + int keyCatchers; + bool displayHUDWithKeycatchUI; + connstate_t connectionState; + bool invited; + char itemsUnlocked[256]; + bool itemsUnlockedInited; + bool itemsUnlockedLastGameDirty; + unsigned __int16 itemsUnlockedLastGame[16]; + int itemsUnlockedLastGameCount; + char* itemsUnlockedBuffer; + int itemsUnlockedLocalClientNum; + int itemsUnlockedControllerIndex; + int itemsUnlockedStatsSource; + }; + + enum msgLocErrType_t + { + LOCMSG_SAFE = 0x0, + LOCMSG_NOERR = 0x1, + }; + #pragma endregion #ifndef IDA