diff --git a/src/Components/Modules/TextRenderer.cpp b/src/Components/Modules/TextRenderer.cpp index ba10f9e0..8c41e8ab 100644 --- a/src/Components/Modules/TextRenderer.cpp +++ b/src/Components/Modules/TextRenderer.cpp @@ -98,8 +98,15 @@ namespace Components return rgb; } - void TextRenderer::DrawAutocompleteBox(const float x, const float y, const float w, const float h, const float* color) + void TextRenderer::DrawAutocompleteBox(const FontIconAutocompleteContext& context, const float x, const float y, const float w, const float h, const float* color) { + static constexpr float colorWhite[4] + { + 1.0f, + 1.0f, + 1.0f, + 1.0f + }; const float borderColor[4] { color[0] * 0.5f, @@ -109,32 +116,61 @@ namespace Components }; Game::R_AddCmdDrawStretchPic(x, y, w, h, 0.0, 0.0, 0.0, 0.0, color, Game::cls->whiteMaterial); - Game::R_AddCmdDrawStretchPic(x, y, 2.0, h, 0.0, 0.0, 0.0, 0.0, borderColor, Game::cls->whiteMaterial); - Game::R_AddCmdDrawStretchPic(x + w - 2.0f, y, 2.0, h, 0.0, 0.0, 0.0, 0.0, borderColor, Game::cls->whiteMaterial); - Game::R_AddCmdDrawStretchPic(x, y, w, 2.0, 0.0, 0.0, 0.0, 0.0, borderColor, Game::cls->whiteMaterial); - Game::R_AddCmdDrawStretchPic(x, y + h - 2.0f, w, 2.0, 0.0, 0.0, 0.0, 0.0, borderColor, Game::cls->whiteMaterial); + Game::R_AddCmdDrawStretchPic(x, y, FONT_ICON_AUTOCOMPLETE_BOX_BORDER, h, 0.0, 0.0, 0.0, 0.0, borderColor, Game::cls->whiteMaterial); + Game::R_AddCmdDrawStretchPic(x + w - FONT_ICON_AUTOCOMPLETE_BOX_BORDER, y, FONT_ICON_AUTOCOMPLETE_BOX_BORDER, h, 0.0, 0.0, 0.0, 0.0, borderColor, Game::cls->whiteMaterial); + Game::R_AddCmdDrawStretchPic(x, y, w, FONT_ICON_AUTOCOMPLETE_BOX_BORDER, 0.0, 0.0, 0.0, 0.0, borderColor, Game::cls->whiteMaterial); + Game::R_AddCmdDrawStretchPic(x, y + h - FONT_ICON_AUTOCOMPLETE_BOX_BORDER, w, FONT_ICON_AUTOCOMPLETE_BOX_BORDER, 0.0, 0.0, 0.0, 0.0, borderColor, Game::cls->whiteMaterial); + + if (context.resultOffset > 0) + { + Game::R_AddCmdDrawStretchPic(x + w - FONT_ICON_AUTOCOMPLETE_BOX_BORDER - FONT_ICON_AUTOCOMPLETE_ARROW_SIZE, + y + FONT_ICON_AUTOCOMPLETE_BOX_BORDER, + FONT_ICON_AUTOCOMPLETE_ARROW_SIZE, + FONT_ICON_AUTOCOMPLETE_ARROW_SIZE, + 1.0f, 1.0f, 0.0f, 0.0f, colorWhite, Game::sharedUiInfo->assets.scrollBarArrowDown); + } + if(context.hasMoreResults) + { + Game::R_AddCmdDrawStretchPic(x + w - FONT_ICON_AUTOCOMPLETE_BOX_BORDER - FONT_ICON_AUTOCOMPLETE_ARROW_SIZE, + y + h - FONT_ICON_AUTOCOMPLETE_BOX_BORDER - FONT_ICON_AUTOCOMPLETE_ARROW_SIZE, + FONT_ICON_AUTOCOMPLETE_ARROW_SIZE, + FONT_ICON_AUTOCOMPLETE_ARROW_SIZE, + 1.0f, 1.0f, 0.0f, 0.0f, colorWhite, Game::sharedUiInfo->assets.scrollBarArrowUp); + } } void TextRenderer::UpdateAutocompleteContextResults(FontIconAutocompleteContext& context, Game::Font_s* font) { context.resultCount = 0; + context.hasMoreResults = false; + context.lastResultOffset = context.resultOffset; const auto* techset2d = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_TECHNIQUE_SET, "2d").techniqueSet; + auto skipCount = context.resultOffset; - Game::DB_EnumXAssetEntries(Game::ASSET_TYPE_MATERIAL, [&context, techset2d](const Game::XAssetEntry* entry) + Game::DB_EnumXAssetEntries(Game::ASSET_TYPE_MATERIAL, [&context, techset2d, &skipCount](const Game::XAssetEntry* entry) { - if (context.resultCount >= FontIconAutocompleteContext::MAX_RESULTS) + if (context.resultCount >= FontIconAutocompleteContext::MAX_RESULTS && context.hasMoreResults) return; const auto* material = entry->asset.header.material; if(material->techniqueSet == techset2d && std::string(material->info.name).rfind(context.lastQuery, 0) == 0) { - context.results[context.resultCount++] = { - std::string(Utils::String::VA(":%s:", material->info.name)), - std::string(material->info.name) - }; + if (skipCount > 0) + { + skipCount--; + } + else if (context.resultCount < FontIconAutocompleteContext::MAX_RESULTS) + { + context.results[context.resultCount++] = { + std::string(Utils::String::VA(":%s:", material->info.name)), + std::string(material->info.name) + }; + } + else + context.hasMoreResults = true; } - }, true, true); + }, false, false); context.maxFontIconWidth = 0; context.maxMaterialNameWidth = 0; @@ -178,17 +214,28 @@ namespace Components return; } + // Update scroll + if(context.selectedOffset < context.resultOffset) + context.resultOffset = context.selectedOffset; + else if(context.selectedOffset >= context.resultOffset + FontIconAutocompleteContext::MAX_RESULTS) + context.resultOffset = context.selectedOffset - (FontIconAutocompleteContext::MAX_RESULTS - 1); + context.autocompleteActive = true; + + // Check if results need updates const auto currentFontIconHash = Game::R_HashString(&edit->buffer[fontIconStart], edit->cursor - fontIconStart); if (currentFontIconHash == context.lastHash && context.lastResultOffset == context.resultOffset) return; + // If query was updated then reset scroll parameters if(currentFontIconHash != context.lastHash) { context.resultOffset = 0; + context.selectedOffset = 0; context.lastHash = currentFontIconHash; } + // Update results for query and scroll context.lastQuery = std::string(&edit->buffer[fontIconStart], edit->cursor - fontIconStart); UpdateAutocompleteContextResults(context, font); } @@ -200,9 +247,11 @@ namespace Components static_cast(Game::R_TextWidth(text, INT_MAX, font))); const auto totalLines = 1u + context.resultCount; - DrawAutocompleteBox(x - FONT_ICON_AUTOCOMPLETE_BOX_PADDING, + const auto arrowPadding = context.resultOffset > 0 || context.hasMoreResults ? FONT_ICON_AUTOCOMPLETE_ARROW_SIZE : 0.0f; + DrawAutocompleteBox(context, + x - FONT_ICON_AUTOCOMPLETE_BOX_PADDING, y - FONT_ICON_AUTOCOMPLETE_BOX_PADDING, - boxWidth + FONT_ICON_AUTOCOMPLETE_BOX_PADDING * 2, + boxWidth + FONT_ICON_AUTOCOMPLETE_BOX_PADDING * 2 + arrowPadding, static_cast(font->pixelHeight * totalLines) + FONT_ICON_AUTOCOMPLETE_BOX_PADDING * 2, (*con_inputBoxColor)->current.vector); @@ -218,11 +267,16 @@ namespace Components Game::R_AddCmdDrawText(text, INT_MAX, font, x, currentY, 1.0f, 1.0f, 0.0, textColor, 0); currentY += static_cast(font->pixelHeight); + const auto selectedIndex = context.selectedOffset - context.resultOffset; for(auto resultIndex = 0u; resultIndex < context.resultCount; resultIndex++) { const auto& result = context.results[resultIndex]; Game::R_AddCmdDrawText(result.fontIconName.c_str(), INT_MAX, font, x, currentY, 1.0f, 1.0f, 0.0, textColor, 0); - Game::R_AddCmdDrawText(result.materialName.c_str(), INT_MAX, font, x + context.maxFontIconWidth + FONT_ICON_AUTOCOMPLETE_COL_SPACING, currentY, 1.0f, 1.0f, 0.0, textColor, 0); + + if(selectedIndex == resultIndex) + Game::R_AddCmdDrawText(Utils::String::VA("^2%s", result.materialName.c_str()), INT_MAX, font, x + context.maxFontIconWidth + FONT_ICON_AUTOCOMPLETE_COL_SPACING, currentY, 1.0f, 1.0f, 0.0, textColor, 0); + else + Game::R_AddCmdDrawText(result.materialName.c_str(), INT_MAX, font, x + context.maxFontIconWidth + FONT_ICON_AUTOCOMPLETE_COL_SPACING, currentY, 1.0f, 1.0f, 0.0, textColor, 0); currentY += static_cast(font->pixelHeight); } } @@ -261,6 +315,116 @@ namespace Components } } + void TextRenderer::AutocompleteUp(FontIconAutocompleteContext& context) + { + if (context.selectedOffset > 0) + context.selectedOffset--; + } + + void TextRenderer::AutocompleteDown(FontIconAutocompleteContext& context) + { + if (context.resultCount < FontIconAutocompleteContext::MAX_RESULTS) + { + if (context.resultCount > 0 && context.selectedOffset < context.resultOffset + context.resultCount - 1) + context.selectedOffset++; + } + else if (context.selectedOffset == context.resultOffset + context.resultCount - 1) + { + if (context.hasMoreResults) + context.selectedOffset++; + } + else + { + context.selectedOffset++; + } + } + + void TextRenderer::AutocompleteFill(const FontIconAutocompleteContext& context, Game::ScreenPlacement* scrPlace, Game::field_t* edit) + { + if (context.selectedOffset >= context.resultOffset + context.resultCount) + return; + + const auto selectedResultIndex = context.selectedOffset - context.resultOffset; + std::string remainingFillData = context.results[selectedResultIndex].materialName.substr(context.lastQuery.size()); + const std::string moveData(&edit->buffer[edit->cursor]); + + const auto remainingBufferCharacters = std::extent_v - edit->cursor - moveData.size() - 1; + if(remainingFillData.size() > remainingBufferCharacters) + remainingFillData = remainingFillData.erase(remainingBufferCharacters); + + if(!remainingFillData.empty()) + { + strncpy(&edit->buffer[edit->cursor], remainingFillData.c_str(), remainingFillData.size()); + strncpy(&edit->buffer[edit->cursor + remainingFillData.size()], moveData.c_str(), moveData.size()); + edit->buffer[std::extent_v - 1] = '\0'; + edit->cursor += static_cast(remainingFillData.size()); + Game::Field_AdjustScroll(scrPlace, edit); + } + } + + bool TextRenderer::AutocompleteHandleKeyDown(FontIconAutocompleteContext& context, const int key, Game::ScreenPlacement* scrPlace, Game::field_t* edit) + { + switch (key) + { + case Game::K_UPARROW: + case Game::K_KP_UPARROW: + AutocompleteUp(context); + return true; + + case Game::K_DOWNARROW: + case Game::K_KP_DOWNARROW: + AutocompleteDown(context); + return true; + + case Game::K_TAB: + AutocompleteFill(context, scrPlace, edit); + return true; + + default: + return false; + } + } + + void TextRenderer::Console_Key_Hk(const int localClientNum, const int key) + { + auto& autocompleteContext = autocompleteContextArray[FONT_ICON_ACI_CONSOLE]; + if (autocompleteContext.autocompleteActive && AutocompleteHandleKeyDown(autocompleteContext, key, Game::scrPlaceFull, Game::g_consoleField)) + return; + + Utils::Hook::Call(0x4311E0)(localClientNum, key); + } + + bool TextRenderer::ChatHandleKeyDown(const int localClientNum, const int key) + { + auto& autocompleteContext = autocompleteContextArray[FONT_ICON_ACI_CHAT]; + return autocompleteContext.autocompleteActive && AutocompleteHandleKeyDown(autocompleteContext, key, &Game::scrPlaceView[localClientNum], &Game::playerKeys[localClientNum].chatField); + } + + constexpr auto Message_Key = 0x5A7E50; + __declspec(naked) void TextRenderer::Message_Key_Stub() + { + __asm + { + pushad + + push eax + push edi + call ChatHandleKeyDown + add esp, 0x8 + test al,al + jnz skipHandling + + popad + call Message_Key + ret + + skipHandling: + popad + mov al, 1 + ret + } + } + float TextRenderer::GetMonospaceWidth(Game::Font_s* font, int rendererFlags) { if(rendererFlags & Game::TEXT_RENDERFLAG_FORCEMONOSPACE) @@ -1124,6 +1288,11 @@ namespace Components Utils::Hook(0x5A50A5, Con_DrawInput_Hk, HOOK_CALL).install()->quick(); Utils::Hook(0x5A50BB, Con_DrawInput_Hk, HOOK_CALL).install()->quick(); + // Handle key inputs for console and chat + Utils::Hook(0x4F685C, Console_Key_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x4F6694, Message_Key_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x4F684C, Message_Key_Stub, HOOK_CALL).install()->quick(); + PatchColorLimit(COLOR_LAST_CHAR); } } \ No newline at end of file diff --git a/src/Components/Modules/TextRenderer.hpp b/src/Components/Modules/TextRenderer.hpp index db54b77f..d7d70e7f 100644 --- a/src/Components/Modules/TextRenderer.hpp +++ b/src/Components/Modules/TextRenderer.hpp @@ -82,8 +82,10 @@ namespace Components std::string lastQuery; FontIconAutocompleteResult results[MAX_RESULTS]; size_t resultCount; + bool hasMoreResults; size_t resultOffset; size_t lastResultOffset; + size_t selectedOffset; float maxFontIconWidth; float maxMaterialNameWidth; }; @@ -93,7 +95,9 @@ namespace Components static constexpr unsigned MY_ALTCOLOR_TWO = 0x0DCE6FFE6; static constexpr unsigned COLOR_MAP_HASH = 0xA0AB1041; static constexpr auto FONT_ICON_AUTOCOMPLETE_BOX_PADDING = 6.0f; + static constexpr auto FONT_ICON_AUTOCOMPLETE_BOX_BORDER = 2.0f; static constexpr auto FONT_ICON_AUTOCOMPLETE_COL_SPACING = 12.0f; + static constexpr auto FONT_ICON_AUTOCOMPLETE_ARROW_SIZE = 12.0f; static constexpr float MY_OFFSETS[4][2] { {-1.0f, -1.0f}, @@ -130,13 +134,21 @@ namespace Components private: static unsigned HsvToRgb(HsvColor hsv); - static void DrawAutocompleteBox(float x, float y, float w, float h, const float* color); + static void DrawAutocompleteBox(const FontIconAutocompleteContext& context, float x, float y, float w, float h, const float* color); static void DrawAutocomplete(const FontIconAutocompleteContext& context, float x, float y, Game::Font_s* font); static void UpdateAutocompleteContextResults(FontIconAutocompleteContext& context, Game::Font_s* font); static void UpdateAutocompleteContext(FontIconAutocompleteContext& context, Game::field_t* edit, Game::Font_s* font); static void Field_Draw_Say(int localClientNum, Game::field_t* edit, int x, int y, int horzAlign, int vertAlign); static void Con_DrawInput_Hk(int localClientNum); + static void AutocompleteUp(FontIconAutocompleteContext& context); + static void AutocompleteDown(FontIconAutocompleteContext& context); + static void AutocompleteFill(const FontIconAutocompleteContext& context, Game::ScreenPlacement* scrPlace, Game::field_t* edit); + static bool AutocompleteHandleKeyDown(FontIconAutocompleteContext& context, int key, Game::ScreenPlacement* scrPlace, Game::field_t* edit); + static void Console_Key_Hk(int localClientNum, int key); + static bool ChatHandleKeyDown(int localClientNum, int key); + static void Message_Key_Stub(); + static int SEH_PrintStrlenWithCursor(const char* string, const Game::field_t* field); static void Field_AdjustScroll_PrintLen_Stub(); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 33bd1995..124c879d 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -341,6 +341,7 @@ namespace Game GetDecayingLetterInfo_t GetDecayingLetterInfo = GetDecayingLetterInfo_t(0x5351C0); Field_Draw_t Field_Draw = Field_Draw_t(0x4F5B40); + Field_AdjustScroll_t Field_AdjustScroll = Field_AdjustScroll_t(0x488C10); XAssetHeader* DB_XAssetPool = reinterpret_cast(0x7998A8); unsigned int* g_poolSize = reinterpret_cast(0x7995E8); @@ -437,6 +438,12 @@ namespace Game clientStatic_t* cls = reinterpret_cast(0xA7FE90); + sharedUiInfo_t* sharedUiInfo = reinterpret_cast(0x62E4B78); + ScreenPlacement* scrPlaceFull = reinterpret_cast(0x10843F0); + ScreenPlacement* scrPlaceView = reinterpret_cast(0x1084378); + + PlayerKeyState* playerKeys = reinterpret_cast(0xA1B7D0); + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize) { int elSize = DB_GetXAssetSizeHandlers[type](); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 41d6723f..a1267ab9 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -794,6 +794,9 @@ namespace Game typedef void(__cdecl * Field_Draw_t)(int localClientNum, field_t* edit, int x, int y, int horzAlign, int vertAlign); extern Field_Draw_t Field_Draw; + + typedef void(__cdecl * Field_AdjustScroll_t)(ScreenPlacement* scrPlace, field_t* edit); + extern Field_AdjustScroll_t Field_AdjustScroll; extern XAssetHeader* DB_XAssetPool; extern unsigned int* g_poolSize; @@ -889,6 +892,12 @@ namespace Game extern clientStatic_t* cls; + extern sharedUiInfo_t* sharedUiInfo; + extern ScreenPlacement* scrPlaceFull; + extern ScreenPlacement* scrPlaceView; + + extern PlayerKeyState* playerKeys; + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize); void Menu_FreeItemMemory(Game::itemDef_s* item); const char* TableLookup(StringTable* stringtable, int row, int column); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 23244457..860a44f2 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -5431,6 +5431,272 @@ namespace Game float subScreenLeft; }; + struct serverStatusInfo_t + { + char address[64]; + const char* lines[128][4]; + char text[1024]; + char pings[54]; + int numLines; + }; + + struct pendingServer_t + { + char adrstr[64]; + char name[64]; + int startTime; + int serverNum; + int valid; + }; + + struct pendingServerStatus_t + { + int num; + pendingServer_t server[16]; + }; + + struct pinglist_t + { + char adrstr[64]; + int start; + }; + + struct serverStatus_s + { + pinglist_t pingList[16]; + int numqueriedservers; + int currentping; + int nextpingtime; + int maxservers; + int refreshtime; + int numServers; + int sortKey; + int sortDir; + int lastCount; + int refreshActive; + int currentServer; + int displayServers[20000]; + int numDisplayServers; + int serverCount; + int numPlayersOnServers; + int nextDisplayRefresh; + int nextSortTime; + int motdLen; + int motdWidth; + int motdPaintX; + int motdPaintX2; + int motdOffset; + int motdTime; + char motd[1024]; + }; + + struct mapInfo + { + char mapName[32]; + char mapLoadName[16]; + char mapDescription[32]; + char mapLoadImage[32]; + char mapCustomKey[32][16]; + char mapCustomValue[32][64]; + int mapCustomCount; + int teamMembers; + int typeBits; + int timeToBeat[32]; + int active; + }; + + struct gameTypeInfo + { + char gameType[12]; + char gameTypeName[32]; + }; + + struct CachedAssets_t + { + Material* scrollBarArrowUp; + Material* scrollBarArrowDown; + Material* scrollBarArrowLeft; + Material* scrollBarArrowRight; + Material* scrollBar; + Material* scrollBarThumb; + Material* sliderBar; + Material* sliderThumb; + Material* whiteMaterial; + Material* cursor; + Material* textDecodeCharacters; + Material* textDecodeCharactersGlow; + Font_s* bigFont; + Font_s* smallFont; + Font_s* consoleFont; + Font_s* boldFont; + Font_s* textFont; + Font_s* extraBigFont; + Font_s* objectiveFont; + Font_s* hudBigFont; + Font_s* hudSmallFont; + snd_alias_list_t* itemFocusSound; + }; + + struct sharedUiInfo_t + { + CachedAssets_t assets; + int playerCount; + char playerNames[18][32]; + char teamNames[18][32]; + int playerClientNums[18]; + volatile int updateGameTypeList; + int numGameTypes; + gameTypeInfo gameTypes[32]; + int numCustomGameTypes; + gameTypeInfo customGameTypes[32]; + char customGameTypeCancelState[2048]; + int numJoinGameTypes; + gameTypeInfo joinGameTypes[32]; + volatile int updateArenas; + int mapCount; + mapInfo mapList[128]; + int mapIndexSorted[128]; + bool mapsAreSorted; + Material* serverHardwareIconList[9]; + unsigned __int64 partyMemberXuid; + Material* talkingIcons[2]; + serverStatus_s serverStatus; + char serverStatusAddress[64]; + serverStatusInfo_t serverStatusInfo; + int nextServerStatusRefresh; + pendingServerStatus_t pendingServerStatus; + }; + + enum keyNum_t + { + K_NONE = 0x0, + K_TAB = 0x9, + K_ENTER = 0xD, + K_ESCAPE = 0x1B, + K_SPACE = 0x20, + K_BACKSPACE = 0x7F, + K_ASCII_FIRST = 0x80, + K_ASCII_181 = 0x80, + K_ASCII_191 = 0x81, + K_ASCII_223 = 0x82, + K_ASCII_224 = 0x83, + K_ASCII_225 = 0x84, + K_ASCII_228 = 0x85, + K_ASCII_229 = 0x86, + K_ASCII_230 = 0x87, + K_ASCII_231 = 0x88, + K_ASCII_232 = 0x89, + K_ASCII_233 = 0x8A, + K_ASCII_236 = 0x8B, + K_ASCII_241 = 0x8C, + K_ASCII_242 = 0x8D, + K_ASCII_243 = 0x8E, + K_ASCII_246 = 0x8F, + K_ASCII_248 = 0x90, + K_ASCII_249 = 0x91, + K_ASCII_250 = 0x92, + K_ASCII_252 = 0x93, + K_END_ASCII_CHARS = 0x94, + K_COMMAND = 0x96, + K_CAPSLOCK = 0x97, + K_POWER = 0x98, + K_PAUSE = 0x99, + K_UPARROW = 0x9A, + K_DOWNARROW = 0x9B, + K_LEFTARROW = 0x9C, + K_RIGHTARROW = 0x9D, + K_ALT = 0x9E, + K_CTRL = 0x9F, + K_SHIFT = 0xA0, + K_INS = 0xA1, + K_DEL = 0xA2, + K_PGDN = 0xA3, + K_PGUP = 0xA4, + K_HOME = 0xA5, + K_END = 0xA6, + K_F1 = 0xA7, + K_F2 = 0xA8, + K_F3 = 0xA9, + K_F4 = 0xAA, + K_F5 = 0xAB, + K_F6 = 0xAC, + K_F7 = 0xAD, + K_F8 = 0xAE, + K_F9 = 0xAF, + K_F10 = 0xB0, + K_F11 = 0xB1, + K_F12 = 0xB2, + K_F13 = 0xB3, + K_F14 = 0xB4, + K_F15 = 0xB5, + K_KP_HOME = 0xB6, + K_KP_UPARROW = 0xB7, + K_KP_PGUP = 0xB8, + K_KP_LEFTARROW = 0xB9, + K_KP_5 = 0xBA, + K_KP_RIGHTARROW = 0xBB, + K_KP_END = 0xBC, + K_KP_DOWNARROW = 0xBD, + K_KP_PGDN = 0xBE, + K_KP_ENTER = 0xBF, + K_KP_INS = 0xC0, + K_KP_DEL = 0xC1, + K_KP_SLASH = 0xC2, + K_KP_MINUS = 0xC3, + K_KP_PLUS = 0xC4, + K_KP_NUMLOCK = 0xC5, + K_KP_STAR = 0xC6, + K_KP_EQUALS = 0xC7, + K_MOUSE1 = 0xC8, + K_MOUSE2 = 0xC9, + K_MOUSE3 = 0xCA, + K_MOUSE4 = 0xCB, + K_MOUSE5 = 0xCC, + K_MWHEELDOWN = 0xCD, + K_MWHEELUP = 0xCE, + K_AUX1 = 0xCF, + K_AUX2 = 0xD0, + K_AUX3 = 0xD1, + K_AUX4 = 0xD2, + K_AUX5 = 0xD3, + K_AUX6 = 0xD4, + K_AUX7 = 0xD5, + K_AUX8 = 0xD6, + K_AUX9 = 0xD7, + K_AUX10 = 0xD8, + K_AUX11 = 0xD9, + K_AUX12 = 0xDA, + K_AUX13 = 0xDB, + K_AUX14 = 0xDC, + K_AUX15 = 0xDD, + K_AUX16 = 0xDE, + K_LAST_KEY = 0xDF, + }; + + struct KeyState + { + int down; + int repeats; + const char* binding; + }; + + enum LocSelInputState + { + LOC_SEL_INPUT_NONE = 0x0, + LOC_SEL_INPUT_CONFIRM = 0x1, + LOC_SEL_INPUT_CANCEL = 0x2, + }; + + struct PlayerKeyState + { + field_t chatField; + int chat_team; + int overstrikeMode; + int anyKeyDown; + KeyState keys[256]; + LocSelInputState locSelInputState; + }; + #pragma endregion #ifndef IDA