diff options
Diffstat (limited to 'src/game/client/components')
32 files changed, 1022 insertions, 369 deletions
diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index ee294dc4..340b9da1 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -41,6 +41,11 @@ void CChat::OnReset() m_PlaceholderOffset = 0; m_PlaceholderLength = 0; m_pHistoryEntry = 0x0; + m_PendingChatCounter = 0; + m_LastChatSend = 0; + + for(int i = 0; i < CHAT_NUM; ++i) + m_aLastSoundPlayed[i] = 0; } void CChat::OnRelease() @@ -107,9 +112,13 @@ bool CChat::OnInput(IInput::CEvent Event) { if(m_Input.GetString()[0]) { - Say(m_Mode == MODE_ALL ? 0 : 1, m_Input.GetString()); - char *pEntry = m_History.Allocate(m_Input.GetLength()+1); - mem_copy(pEntry, m_Input.GetString(), m_Input.GetLength()+1); + if(m_LastChatSend+time_freq() < time_get()) + Say(m_Mode == MODE_ALL ? 0 : 1, m_Input.GetString()); + else + ++m_PendingChatCounter; + CHistoryEntry *pEntry = m_History.Allocate(sizeof(CHistoryEntry)+m_Input.GetLength()); + pEntry->m_Team = m_Mode == MODE_ALL ? 0 : 1; + mem_copy(pEntry->m_aText, m_Input.GetString(), m_Input.GetLength()+1); } m_pHistoryEntry = 0x0; m_Mode = MODE_NONE; @@ -132,17 +141,28 @@ bool CChat::OnInput(IInput::CEvent Event) // find next possible name const char *pCompletionString = 0; - m_CompletionChosen = (m_CompletionChosen+1)%MAX_CLIENTS; - for(int i = 0; i < MAX_CLIENTS; ++i) + m_CompletionChosen = (m_CompletionChosen+1)%(2*MAX_CLIENTS); + for(int i = 0; i < 2*MAX_CLIENTS; ++i) { + int SearchType = ((m_CompletionChosen+i)%(2*MAX_CLIENTS))/MAX_CLIENTS; int Index = (m_CompletionChosen+i)%MAX_CLIENTS; if(!m_pClient->m_Snap.m_paPlayerInfos[Index]) continue; - if(str_find_nocase(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer)) + bool Found = false; + if(SearchType == 1) + { + if(str_comp_nocase_num(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)) && + str_find_nocase(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer)) + Found = true; + } + else if(!str_comp_nocase_num(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer))) + Found = true; + + if(Found) { pCompletionString = m_pClient->m_aClients[Index].m_aName; - m_CompletionChosen = Index; + m_CompletionChosen = Index+SearchType*MAX_CLIENTS; break; } } @@ -151,10 +171,25 @@ bool CChat::OnInput(IInput::CEvent Event) if(pCompletionString) { char aBuf[256]; + // add part before the name str_copy(aBuf, m_Input.GetString(), min(static_cast<int>(sizeof(aBuf)), m_PlaceholderOffset+1)); + + // add the name str_append(aBuf, pCompletionString, sizeof(aBuf)); + + // add seperator + const char *pSeparator = ""; + if(*(m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength) != ' ') + pSeparator = m_PlaceholderOffset == 0 ? ": " : " "; + else if(m_PlaceholderOffset == 0) + pSeparator = ":"; + if(*pSeparator) + str_append(aBuf, pSeparator, sizeof(aBuf)); + + // add part after the name str_append(aBuf, m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength, sizeof(aBuf)); - m_PlaceholderLength = str_length(pCompletionString); + + m_PlaceholderLength = str_length(pSeparator)+str_length(pCompletionString); m_OldChatStringLength = m_Input.GetLength(); m_Input.Set(aBuf); m_Input.SetCursorOffset(m_PlaceholderOffset+m_PlaceholderLength); @@ -173,34 +208,26 @@ bool CChat::OnInput(IInput::CEvent Event) } if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_UP) { - if (m_pHistoryEntry) + if(m_pHistoryEntry) { - char *pTest = m_History.Prev(m_pHistoryEntry); + CHistoryEntry *pTest = m_History.Prev(m_pHistoryEntry); - if (pTest) + if(pTest) m_pHistoryEntry = pTest; } else m_pHistoryEntry = m_History.Last(); - if (m_pHistoryEntry) - { - unsigned int Len = str_length(m_pHistoryEntry); - if (Len < sizeof(m_Input) - 1) // TODO: WTF? - m_Input.Set(m_pHistoryEntry); - } + if(m_pHistoryEntry) + m_Input.Set(m_pHistoryEntry->m_aText); } else if (Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_DOWN) { - if (m_pHistoryEntry) + if(m_pHistoryEntry) m_pHistoryEntry = m_History.Next(m_pHistoryEntry); if (m_pHistoryEntry) - { - unsigned int Len = str_length(m_pHistoryEntry); - if (Len < sizeof(m_Input) - 1) // TODO: WTF? - m_Input.Set(m_pHistoryEntry); - } + m_Input.Set(m_pHistoryEntry->m_aText); else m_Input.Clear(); } @@ -239,13 +266,15 @@ void CChat::OnMessage(int MsgType, void *pRawMsg) void CChat::AddLine(int ClientID, int Team, const char *pLine) { if(ClientID != -1 && (m_pClient->m_aClients[ClientID].m_aName[0] == '\0' || // unknown client - m_pClient->m_aClients[ClientID].m_ChatIgnore)) + m_pClient->m_aClients[ClientID].m_ChatIgnore || + (m_pClient->m_Snap.m_LocalClientID != ClientID && g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[ClientID].m_Friend))) return; bool Highlighted = false; char *p = const_cast<char*>(pLine); while(*p) { + Highlighted = false; pLine = p; // find line seperator and strip multiline while(*p) @@ -264,9 +293,16 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine) m_aLines[m_CurrentLine].m_ClientID = ClientID; m_aLines[m_CurrentLine].m_Team = Team; m_aLines[m_CurrentLine].m_NameColor = -2; - m_aLines[m_CurrentLine].m_Highlighted = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName) != 0; - if(m_aLines[m_CurrentLine].m_Highlighted) - Highlighted = true; + + // check for highlighted name + const char *pHL = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName); + if(pHL) + { + int Length = str_length(m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName); + if((pLine == pHL || pHL[-1] == ' ') && (pHL[Length] == 0 || pHL[Length] == ' ' || (pHL[Length] == ':' && pHL[Length+1] == ' '))) + Highlighted = true; + } + m_aLines[m_CurrentLine].m_Highlighted = Highlighted; if(ClientID == -1) // server message { @@ -292,20 +328,54 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine) char aBuf[1024]; str_format(aBuf, sizeof(aBuf), "%s%s", m_aLines[m_CurrentLine].m_aName, m_aLines[m_CurrentLine].m_aText); - Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "chat", aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_aLines[m_CurrentLine].m_Team?"teamchat":"chat", aBuf); } // play sound + int64 Now = time_get(); if(ClientID == -1) - m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_SERVER, 0, vec2(0,0)); + { + if(Now-m_aLastSoundPlayed[CHAT_SERVER] >= time_freq()*3/10) + { + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_SERVER, 0); + m_aLastSoundPlayed[CHAT_SERVER] = Now; + } + } else if(Highlighted) - m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_HIGHLIGHT, 0, vec2(0.0f, 0.0f)); + { + if(Now-m_aLastSoundPlayed[CHAT_HIGHLIGHT] >= time_freq()*3/10) + { + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0); + m_aLastSoundPlayed[CHAT_HIGHLIGHT] = Now; + } + } else - m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0, vec2(0,0)); + { + if(Now-m_aLastSoundPlayed[CHAT_CLIENT] >= time_freq()*3/10) + { + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_HIGHLIGHT, 0); + m_aLastSoundPlayed[CHAT_CLIENT] = Now; + } + } } void CChat::OnRender() { + // send pending chat messages + if(m_PendingChatCounter > 0 && m_LastChatSend+time_freq() < time_get()) + { + CHistoryEntry *pEntry = m_History.Last(); + for(int i = m_PendingChatCounter-1; pEntry; --i, pEntry = m_History.Prev(pEntry)) + { + if(i == 0) + { + Say(pEntry->m_Team, pEntry->m_aText); + break; + } + } + --m_PendingChatCounter; + } + float Width = 300.0f*Graphics()->ScreenAspect(); Graphics()->MapScreen(0.0f, 0.0f, Width, 300.0f); float x = 5.0f; @@ -354,7 +424,9 @@ void CChat::OnRender() } TextRender()->TextEx(&Cursor, m_Input.GetString()+m_ChatStringOffset, m_Input.GetCursorOffset()-m_ChatStringOffset); + static float MarkerOffset = TextRender()->TextWidth(0, 8.0f, "|", -1)/3; CTextCursor Marker = Cursor; + Marker.m_X -= MarkerOffset; TextRender()->TextEx(&Marker, "|", -1); TextRender()->TextEx(&Cursor, m_Input.GetString()+m_Input.GetCursorOffset(), -1); } @@ -429,6 +501,8 @@ void CChat::OnRender() void CChat::Say(int Team, const char *pLine) { + m_LastChatSend = time_get(); + // send chat message CNetMsg_Cl_Say Msg; Msg.m_Team = Team; diff --git a/src/game/client/components/chat.h b/src/game/client/components/chat.h index 60e18387..09519516 100644 --- a/src/game/client/components/chat.h +++ b/src/game/client/components/chat.h @@ -36,6 +36,11 @@ class CChat : public CComponent MODE_NONE=0, MODE_ALL, MODE_TEAM, + + CHAT_SERVER=0, + CHAT_HIGHLIGHT, + CHAT_CLIENT, + CHAT_NUM, }; int m_Mode; @@ -47,8 +52,17 @@ class CChat : public CComponent char m_aCompletionBuffer[256]; int m_PlaceholderOffset; int m_PlaceholderLength; - char *m_pHistoryEntry; - TStaticRingBuffer<char, 64*1024, CRingBufferBase::FLAG_RECYCLE> m_History; + + struct CHistoryEntry + { + int m_Team; + char m_aText[1]; + }; + CHistoryEntry *m_pHistoryEntry; + TStaticRingBuffer<CHistoryEntry, 64*1024, CRingBufferBase::FLAG_RECYCLE> m_History; + int m_PendingChatCounter; + int64 m_LastChatSend; + int64 m_aLastSoundPlayed[CHAT_NUM]; static void ConSay(IConsole::IResult *pResult, void *pUserData); static void ConSayTeam(IConsole::IResult *pResult, void *pUserData); diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index 33c6db43..d16d56cd 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -51,7 +51,7 @@ CGameConsole::CInstance::CInstance(int Type) m_CompletionChosen = -1; m_CompletionRenderOffset = 0.0f; - m_pCommand = 0x0; + m_IsCommand = false; } void CGameConsole::CInstance::Init(CGameConsole *pGameConsole) @@ -127,11 +127,7 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) m_pHistoryEntry = m_History.Last(); if (m_pHistoryEntry) - { - unsigned int Len = str_length(m_pHistoryEntry); - if (Len < sizeof(m_Input) - 1) // TODO: WTF? - m_Input.Set(m_pHistoryEntry); - } + m_Input.Set(m_pHistoryEntry); Handled = true; } else if (Event.m_Key == KEY_DOWN) @@ -140,11 +136,7 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) m_pHistoryEntry = m_History.Next(m_pHistoryEntry); if (m_pHistoryEntry) - { - unsigned int Len = str_length(m_pHistoryEntry); - if (Len < sizeof(m_Input) - 1) // TODO: WTF? - m_Input.Set(m_pHistoryEntry); - } + m_Input.Set(m_pHistoryEntry); else m_Input.Clear(); Handled = true; @@ -155,14 +147,16 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) { m_CompletionChosen++; m_CompletionEnumerationCount = 0; - m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, PossibleCommandsCompleteCallback, this); + m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL && + m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands(), PossibleCommandsCompleteCallback, this); // handle wrapping if(m_CompletionEnumerationCount && m_CompletionChosen >= m_CompletionEnumerationCount) { m_CompletionChosen %= m_CompletionEnumerationCount; m_CompletionEnumerationCount = 0; - m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, PossibleCommandsCompleteCallback, this); + m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, m_Type != CGameConsole::CONSOLETYPE_LOCAL && + m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands(), PossibleCommandsCompleteCallback, this); } } } @@ -198,7 +192,17 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event) aBuf[i] = *pSrc; aBuf[i] = 0; - m_pCommand = m_pGameConsole->m_pConsole->GetCommandInfo(aBuf, m_CompletionFlagmask); + const IConsole::CCommandInfo *pCommand = m_pGameConsole->m_pConsole->GetCommandInfo(aBuf, m_CompletionFlagmask, + m_Type != CGameConsole::CONSOLETYPE_LOCAL && m_pGameConsole->Client()->RconAuthed() && m_pGameConsole->Client()->UseTempRconCommands()); + if(pCommand) + { + m_IsCommand = true; + str_copy(m_aCommandName, pCommand->m_pName, IConsole::TEMPCMD_NAME_LENGTH); + str_copy(m_aCommandHelp, pCommand->m_pHelp, IConsole::TEMPCMD_HELP_LENGTH); + str_copy(m_aCommandParams, pCommand->m_pParams, IConsole::TEMPCMD_PARAMS_LENGTH); + } + else + m_IsCommand = false; } } } @@ -446,7 +450,9 @@ void CGameConsole::OnRender() } TextRender()->TextEx(&Cursor, aInputString, pConsole->m_Input.GetCursorOffset()); + static float MarkerOffset = TextRender()->TextWidth(0, FontSize, "|", -1)/3; CTextCursor Marker = Cursor; + Marker.m_X -= MarkerOffset; TextRender()->TextEx(&Marker, "|", -1); TextRender()->TextEx(&Cursor, aInputString+pConsole->m_Input.GetCursorOffset(), -1); @@ -455,19 +461,19 @@ void CGameConsole::OnRender() { if(pConsole->m_Input.GetString()[0] != 0) { - m_pConsole->PossibleCommands(pConsole->m_aCompletionBuffer, pConsole->m_CompletionFlagmask, PossibleCommandsRenderCallback, &Info); + m_pConsole->PossibleCommands(pConsole->m_aCompletionBuffer, pConsole->m_CompletionFlagmask, m_ConsoleType != CGameConsole::CONSOLETYPE_LOCAL && + Client()->RconAuthed() && Client()->UseTempRconCommands(), PossibleCommandsRenderCallback, &Info); pConsole->m_CompletionRenderOffset = Info.m_Offset; if(Info.m_EnumCount <= 0) { - if(pConsole->m_pCommand) + if(pConsole->m_IsCommand) { - char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_pCommand->m_pHelp); + str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_aCommandHelp); TextRender()->TextEx(&Info.m_Cursor, aBuf, -1); TextRender()->TextColor(0.75f, 0.75f, 0.75f, 1); - str_format(aBuf, sizeof(aBuf), "Syntax: %s %s", pConsole->m_pCommand->m_pName, pConsole->m_pCommand->m_pParams); + str_format(aBuf, sizeof(aBuf), "Syntax: %s %s", pConsole->m_aCommandName, pConsole->m_aCommandParams); TextRender()->TextEx(&Info.m_Cursor, aBuf, -1); } } @@ -606,16 +612,10 @@ void CGameConsole::Dump(int Type) IOHANDLE io = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE); if(io) { - #if defined(CONF_FAMILY_WINDOWS) - static const char Newline[] = "\r\n"; - #else - static const char Newline[] = "\n"; - #endif - for(CInstance::CBacklogEntry *pEntry = pConsole->m_Backlog.First(); pEntry; pEntry = pConsole->m_Backlog.Next(pEntry)) { io_write(io, pEntry->m_aText, str_length(pEntry->m_aText)); - io_write(io, Newline, sizeof(Newline)-1); + io_write_newline(io); } io_close(io); } @@ -656,6 +656,16 @@ void CGameConsole::ClientConsolePrintCallback(const char *pStr, void *pUserData) ((CGameConsole *)pUserData)->m_LocalConsole.PrintLine(pStr); } +void CGameConsole::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments() == 1) + { + CGameConsole *pThis = static_cast<CGameConsole *>(pUserData); + pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0)); + } +} + void CGameConsole::PrintLine(int Type, const char *pLine) { if(Type == CONSOLETYPE_LOCAL) @@ -673,7 +683,7 @@ void CGameConsole::OnConsoleInit() m_pConsole = Kernel()->RequestInterface<IConsole>(); // - Console()->RegisterPrintCallback(ClientConsolePrintCallback, this); + m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, ClientConsolePrintCallback, this); Console()->Register("toggle_local_console", "", CFGFLAG_CLIENT, ConToggleLocalConsole, this, "Toggle local console"); Console()->Register("toggle_remote_console", "", CFGFLAG_CLIENT, ConToggleRemoteConsole, this, "Toggle remote console"); @@ -681,6 +691,8 @@ void CGameConsole::OnConsoleInit() Console()->Register("clear_remote_console", "", CFGFLAG_CLIENT, ConClearRemoteConsole, this, "Clear remote console"); Console()->Register("dump_local_console", "", CFGFLAG_CLIENT, ConDumpLocalConsole, this, "Dump local console"); Console()->Register("dump_remote_console", "", CFGFLAG_CLIENT, ConDumpRemoteConsole, this, "Dump remote console"); + + Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this); } void CGameConsole::OnStateChange(int NewState, int OldState) diff --git a/src/game/client/components/console.h b/src/game/client/components/console.h index 003a9423..6bcc75a6 100644 --- a/src/game/client/components/console.h +++ b/src/game/client/components/console.h @@ -33,7 +33,10 @@ class CGameConsole : public CComponent int m_CompletionFlagmask; float m_CompletionRenderOffset; - IConsole::CCommandInfo *m_pCommand; + bool m_IsCommand; + char m_aCommandName[IConsole::TEMPCMD_NAME_LENGTH]; + char m_aCommandHelp[IConsole::TEMPCMD_HELP_LENGTH]; + char m_aCommandParams[IConsole::TEMPCMD_PARAMS_LENGTH]; CInstance(int t); void Init(CGameConsole *pGameConsole); @@ -57,6 +60,7 @@ class CGameConsole : public CComponent CInstance *CurrentConsole(); float TimeNow(); + int m_PrintCBIndex; int m_ConsoleType; int m_ConsoleState; @@ -74,6 +78,7 @@ class CGameConsole : public CComponent static void ConClearRemoteConsole(IConsole::IResult *pResult, void *pUserData); static void ConDumpLocalConsole(IConsole::IResult *pResult, void *pUserData); static void ConDumpRemoteConsole(IConsole::IResult *pResult, void *pUserData); + static void ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); public: enum diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp index 81c4d5ae..c0790799 100644 --- a/src/game/client/components/controls.cpp +++ b/src/game/client/components/controls.cpp @@ -199,7 +199,7 @@ int CControls::SnapInput(int *pData) void CControls::OnRender() { // update target pos - if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED || m_pClient->m_Snap.m_SpecInfo.m_Active)) + if(m_pClient->m_Snap.m_pGameInfoObj && !m_pClient->m_Snap.m_SpecInfo.m_Active) m_TargetPos = m_pClient->m_LocalCharacterPos + m_MousePos; else if(m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_UsePosition) m_TargetPos = m_pClient->m_Snap.m_SpecInfo.m_Position + m_MousePos; @@ -209,7 +209,8 @@ void CControls::OnRender() bool CControls::OnMouseMove(float x, float y) { - if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + if((m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) || + (m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_pChat->IsActive())) return false; m_MousePos += vec2(x, y); // TODO: ugly diff --git a/src/game/client/components/countryflags.cpp b/src/game/client/components/countryflags.cpp index 2429ad3f..b63b126f 100644 --- a/src/game/client/components/countryflags.cpp +++ b/src/game/client/components/countryflags.cpp @@ -6,6 +6,8 @@ #include <engine/console.h> #include <engine/graphics.h> #include <engine/storage.h> +#include <engine/textrender.h> +#include <engine/shared/config.h> #include <engine/shared/linereader.h> #include "countryflags.h" @@ -45,28 +47,68 @@ void CCountryFlags::LoadCountryflagsIndexfile() continue; } + int CountryCode = str_toint(pReplacement+3); + if(CountryCode < CODE_LB || CountryCode > CODE_UB) + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "country code '%i' not within valid code range [%i..%i]", CountryCode, CODE_LB, CODE_UB); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf); + continue; + } + // load the graphic file char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "countryflags/%s.png", aOrigin); CImageInfo Info; - if(!Graphics()->LoadPNG(&Info, aBuf, IStorage::TYPE_ALL)) + if(g_Config.m_ClLoadCountryFlags) { - char aMsg[128]; - str_format(aMsg, sizeof(aMsg), "failed to load '%s'", aBuf); - Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aMsg); - continue; + str_format(aBuf, sizeof(aBuf), "countryflags/%s.png", aOrigin); + if(!Graphics()->LoadPNG(&Info, aBuf, IStorage::TYPE_ALL)) + { + char aMsg[128]; + str_format(aMsg, sizeof(aMsg), "failed to load '%s'", aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aMsg); + continue; + } } // add entry CCountryFlag CountryFlag; - CountryFlag.m_CountryCode = str_toint(pReplacement+3); - CountryFlag.m_Texture = Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); - mem_free(Info.m_pData); - str_format(aBuf, sizeof(aBuf), "loaded country flag '%s'", aOrigin); - Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf); - m_aCountryFlags.add(CountryFlag); + CountryFlag.m_CountryCode = CountryCode; + str_copy(CountryFlag.m_aCountryCodeString, aOrigin, sizeof(CountryFlag.m_aCountryCodeString)); + if(g_Config.m_ClLoadCountryFlags) + { + CountryFlag.m_Texture = Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); + mem_free(Info.m_pData); + } + else + CountryFlag.m_Texture = -1; + if(g_Config.m_Debug) + { + str_format(aBuf, sizeof(aBuf), "loaded country flag '%s'", aOrigin); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf); + } + m_aCountryFlags.add_unsorted(CountryFlag); } io_close(File); + m_aCountryFlags.sort_range(); + + // find index of default item + int DefaultIndex = 0, Index = 0; + for(sorted_array<CCountryFlag>::range r = m_aCountryFlags.all(); !r.empty(); r.pop_front(), ++Index) + if(r.front().m_CountryCode == -1) + { + DefaultIndex = Index; + break; + } + + // init LUT + if(DefaultIndex != 0) + for(int i = 0; i < CODE_RANGE; ++i) + m_CodeIndexLUT[i] = DefaultIndex; + else + mem_zero(m_CodeIndexLUT, sizeof(m_CodeIndexLUT)); + for(int i = 0; i < m_aCountryFlags.size(); ++i) + m_CodeIndexLUT[max(0, (m_aCountryFlags[i].m_CountryCode-CODE_LB)%CODE_RANGE)] = i; } void CCountryFlags::OnInit() @@ -80,6 +122,7 @@ void CCountryFlags::OnInit() CCountryFlag DummyEntry; DummyEntry.m_CountryCode = -1; DummyEntry.m_Texture = -1; + mem_zero(DummyEntry.m_aCountryCodeString, sizeof(DummyEntry.m_aCountryCodeString)); m_aCountryFlags.add(DummyEntry); } } @@ -89,17 +132,33 @@ int CCountryFlags::Num() const return m_aCountryFlags.size(); } -const CCountryFlags::CCountryFlag *CCountryFlags::Get(int Index) const +const CCountryFlags::CCountryFlag *CCountryFlags::GetByCountryCode(int CountryCode) const +{ + return GetByIndex(m_CodeIndexLUT[max(0, (CountryCode-CODE_LB)%CODE_RANGE)]); +} + +const CCountryFlags::CCountryFlag *CCountryFlags::GetByIndex(int Index) const { return &m_aCountryFlags[max(0, Index%m_aCountryFlags.size())]; } -int CCountryFlags::Find(int CountryCode) const +void CCountryFlags::Render(int CountryCode, const vec4 *pColor, float x, float y, float w, float h) { - for(int i = 0; i < m_aCountryFlags.size(); ++i) + const CCountryFlag *pFlag = GetByCountryCode(CountryCode); + if(pFlag->m_Texture != -1) + { + Graphics()->TextureSet(pFlag->m_Texture); + Graphics()->QuadsBegin(); + Graphics()->SetColor(pColor->r, pColor->g, pColor->b, pColor->a); + IGraphics::CQuadItem QuadItem(x, y, w, h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + else { - if(m_aCountryFlags[i].m_CountryCode == CountryCode) - return i; + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, x, y, 10.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = w; + TextRender()->TextEx(&Cursor, pFlag->m_aCountryCodeString, -1); } - return -1; } diff --git a/src/game/client/components/countryflags.h b/src/game/client/components/countryflags.h index cd629094..df934821 100644 --- a/src/game/client/components/countryflags.h +++ b/src/game/client/components/countryflags.h @@ -12,19 +12,28 @@ public: struct CCountryFlag { int m_CountryCode; + char m_aCountryCodeString[8]; int m_Texture; - bool operator<(const CCountryFlag &Other) { return m_CountryCode < Other.m_CountryCode; } + bool operator<(const CCountryFlag &Other) { return str_comp(m_aCountryCodeString, Other.m_aCountryCodeString) < 0; } }; void OnInit(); int Num() const; - const CCountryFlag *Get(int Index) const; - int Find(int CountryCode) const; + const CCountryFlag *GetByCountryCode(int CountryCode) const; + const CCountryFlag *GetByIndex(int Index) const; + void Render(int CountryCode, const vec4 *pColor, float x, float y, float w, float h); private: + enum + { + CODE_LB=-1, + CODE_UB=999, + CODE_RANGE=CODE_UB-CODE_LB+1, + }; sorted_array<CCountryFlag> m_aCountryFlags; + int m_CodeIndexLUT[CODE_RANGE]; void LoadCountryflagsIndexfile(); }; diff --git a/src/game/client/components/damageind.cpp b/src/game/client/components/damageind.cpp index cba47666..0f521753 100644 --- a/src/game/client/components/damageind.cpp +++ b/src/game/client/components/damageind.cpp @@ -1,5 +1,6 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ +#include <engine/demo.h> #include <engine/graphics.h> #include <game/generated/protocol.h> #include <game/generated/client_data.h> @@ -38,7 +39,7 @@ void CDamageInd::Create(vec2 Pos, vec2 Dir) if (i) { i->m_Pos = Pos; - i->m_Life = 0.75f; + i->m_StartTime = Client()->LocalTime(); i->m_Dir = Dir*-1; i->m_StartAngle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; } @@ -48,21 +49,39 @@ void CDamageInd::OnRender() { Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); + static float s_LastLocalTime = Client()->LocalTime(); for(int i = 0; i < m_NumItems;) { - vec2 Pos = mix(m_aItems[i].m_Pos+m_aItems[i].m_Dir*75.0f, m_aItems[i].m_Pos, clamp((m_aItems[i].m_Life-0.60f)/0.15f, 0.0f, 1.0f)); + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + { + const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); + if(pInfo->m_Paused) + m_aItems[i].m_StartTime += Client()->LocalTime()-s_LastLocalTime; + else + m_aItems[i].m_StartTime += (Client()->LocalTime()-s_LastLocalTime)*(1.0f-pInfo->m_Speed); + } + else + { + if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + m_aItems[i].m_StartTime += Client()->LocalTime()-s_LastLocalTime; + } - m_aItems[i].m_Life -= Client()->FrameTime(); - if(m_aItems[i].m_Life < 0.0f) + float Life = 0.75f - (Client()->LocalTime() - m_aItems[i].m_StartTime); + if(Life < 0.0f) DestroyI(&m_aItems[i]); else { - Graphics()->SetColor(1.0f,1.0f,1.0f, m_aItems[i].m_Life/0.1f); - Graphics()->QuadsSetRotation(m_aItems[i].m_StartAngle + m_aItems[i].m_Life * 2.0f); + vec2 Pos = mix(m_aItems[i].m_Pos+m_aItems[i].m_Dir*75.0f, m_aItems[i].m_Pos, clamp((Life-0.60f)/0.15f, 0.0f, 1.0f)); + Graphics()->SetColor(1.0f,1.0f,1.0f, Life/0.1f); + Graphics()->QuadsSetRotation(m_aItems[i].m_StartAngle + Life * 2.0f); RenderTools()->SelectSprite(SPRITE_STAR1); RenderTools()->DrawSprite(Pos.x, Pos.y, 48.0f); i++; } } + s_LastLocalTime = Client()->LocalTime(); Graphics()->QuadsEnd(); } + + + diff --git a/src/game/client/components/damageind.h b/src/game/client/components/damageind.h index 2f89e422..4a5975a8 100644 --- a/src/game/client/components/damageind.h +++ b/src/game/client/components/damageind.h @@ -12,7 +12,7 @@ class CDamageInd : public CComponent { vec2 m_Pos; vec2 m_Dir; - float m_Life; + float m_StartTime; float m_StartAngle; }; diff --git a/src/game/client/components/debughud.cpp b/src/game/client/components/debughud.cpp index 7145705c..6adc61b2 100644 --- a/src/game/client/components/debughud.cpp +++ b/src/game/client/components/debughud.cpp @@ -42,11 +42,11 @@ void CDebugHud::RenderNetCorrections() x = Width-10.0f; char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed); + str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed/32); float w = TextRender()->TextWidth(0, Fontsize, aBuf, -1); TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1); y += LineHeight; - str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed*Ramp); + str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed/32*Ramp); w = TextRender()->TextWidth(0, Fontsize, aBuf, -1); TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1); y += LineHeight; @@ -54,11 +54,11 @@ void CDebugHud::RenderNetCorrections() w = TextRender()->TextWidth(0, Fontsize, aBuf, -1); TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1); y += 2*LineHeight; - str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_Snap.m_pLocalCharacter->m_X); + str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_Snap.m_pLocalCharacter->m_X/32); w = TextRender()->TextWidth(0, Fontsize, aBuf, -1); TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1); y += LineHeight; - str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_Snap.m_pLocalCharacter->m_Y); + str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_Snap.m_pLocalCharacter->m_Y/32); w = TextRender()->TextWidth(0, Fontsize, aBuf, -1); TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1); y += 2*LineHeight; diff --git a/src/game/client/components/effects.cpp b/src/game/client/components/effects.cpp index 573ac410..933e7fe6 100644 --- a/src/game/client/components/effects.cpp +++ b/src/game/client/components/effects.cpp @@ -44,7 +44,7 @@ void CEffects::AirJump(vec2 Pos) p.m_Pos = Pos + vec2(6.0f, 16.0f); m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); - m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, Pos); + m_pClient->m_pSounds->PlayAt(CSounds::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, Pos); } void CEffects::DamageIndicator(vec2 Pos, vec2 Dir) @@ -86,7 +86,7 @@ void CEffects::SmokeTrail(vec2 Pos, vec2 Vel) p.m_LifeSpan = 0.5f + frandom()*0.5f; p.m_StartSize = 12.0f + frandom()*8; p.m_EndSize = 0; - p.m_Friction = 0.7; + p.m_Friction = 0.7f; p.m_Gravity = frandom()*-500.0f; m_pClient->m_pParticles->Add(CParticles::GROUP_PROJECTILE_TRAIL, &p); } @@ -147,7 +147,7 @@ void CEffects::PlayerSpawn(vec2 Pos) m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); } - m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_PLAYER_SPAWN, 1.0f, Pos); + m_pClient->m_pSounds->PlayAt(CSounds::CHN_WORLD, SOUND_PLAYER_SPAWN, 1.0f, Pos); } void CEffects::PlayerDeath(vec2 Pos, int ClientID) @@ -242,7 +242,7 @@ void CEffects::HammerHit(vec2 Pos) p.m_EndSize = 0; p.m_Rot = frandom()*pi*2; m_pClient->m_pParticles->Add(CParticles::GROUP_EXPLOSIONS, &p); - m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_HAMMER_HIT, 1.0f, Pos); + m_pClient->m_pSounds->PlayAt(CSounds::CHN_WORLD, SOUND_HAMMER_HIT, 1.0f, Pos); } void CEffects::OnRender() diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp index 741a604f..9e2a80cf 100644 --- a/src/game/client/components/emoticon.cpp +++ b/src/game/client/components/emoticon.cpp @@ -54,6 +54,7 @@ bool CEmoticon::OnMouseMove(float x, float y) if(!m_Active) return false; + UI()->ConvertMouseMove(&x, &y); m_SelectorMouse += vec2(x,y); return true; } @@ -101,16 +102,23 @@ void CEmoticon::OnRender() return; } + if(m_pClient->m_Snap.m_SpecInfo.m_Active) + { + m_Active = false; + m_WasActive = false; + return; + } + m_WasActive = true; - if (length(m_SelectorMouse) > 140) - m_SelectorMouse = normalize(m_SelectorMouse) * 140; + if (length(m_SelectorMouse) > 170.0f) + m_SelectorMouse = normalize(m_SelectorMouse) * 170.0f; float SelectedAngle = GetAngle(m_SelectorMouse) + 2*pi/24; if (SelectedAngle < 0) SelectedAngle += 2*pi; - if (length(m_SelectorMouse) > 100) + if (length(m_SelectorMouse) > 110.0f) m_SelectedEmote = (int)(SelectedAngle / (2*pi) * NUM_EMOTICONS); CUIRect Screen = *UI()->Screen(); @@ -122,7 +130,7 @@ void CEmoticon::OnRender() Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.3f); - DrawCircle(Screen.w/2, Screen.h/2, 160, 64); + DrawCircle(Screen.w/2, Screen.h/2, 190.0f, 64); Graphics()->QuadsEnd(); Graphics()->TextureSet(g_pData->m_aImages[IMAGE_EMOTICONS].m_Id); @@ -136,10 +144,10 @@ void CEmoticon::OnRender() bool Selected = m_SelectedEmote == i; - float Size = Selected ? 80 : 32; + float Size = Selected ? 80.0f : 50.0f; - float NudgeX = 120 * cosf(Angle); - float NudgeY = 120 * sinf(Angle); + float NudgeX = 150.0f * cosf(Angle); + float NudgeY = 150.0f * sinf(Angle); RenderTools()->SelectSprite(SPRITE_OOP + i); IGraphics::CQuadItem QuadItem(Screen.w/2 + NudgeX, Screen.h/2 + NudgeY, Size, Size); Graphics()->QuadsDraw(&QuadItem, 1); diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 188691a1..6881728c 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -35,7 +35,7 @@ void CHud::RenderGameTimer() { char Buf[32]; int Time = 0; - if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit) + if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && !m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer) { Time = m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit*60 - ((Client()->GameTick()-m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick)/Client()->GameTickSpeed()); @@ -49,7 +49,7 @@ void CHud::RenderGameTimer() float FontSize = 10.0f; float w = TextRender()->TextWidth(0, FontSize, Buf, -1); // last 60 sec red, last 10 sec blink - if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && Time <= 60) + if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && Time <= 60 && !m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer) { float Alpha = Time <= 10 && (2*time_get()/time_freq()) % 2 ? 0.5f : 1.0f; TextRender()->TextColor(1.0f, 0.25f, 0.25f, Alpha); @@ -59,6 +59,18 @@ void CHud::RenderGameTimer() } } +void CHud::RenderPauseNotification() +{ + if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED && + !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + const char *pText = Localize("Game paused"); + float FontSize = 20.0f; + float w = TextRender()->TextWidth(0, FontSize,pText, -1); + TextRender()->Text(0, 150.0f*Graphics()->ScreenAspect()+-w/2.0f, 50.0f, FontSize, pText, -1); + } +} + void CHud::RenderSuddenDeath() { if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_SUDDENDEATH) @@ -78,6 +90,7 @@ void CHud::RenderScoreHud() { int GameFlags = m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags; float Whole = 300*Graphics()->ScreenAspect(); + float StartY = 229.0f; if(GameFlags&GAMEFLAG_TEAMS && m_pClient->m_Snap.m_pGameDataObj) { @@ -100,22 +113,24 @@ void CHud::RenderScoreHud() Graphics()->SetColor(1.0f, 0.0f, 0.0f, 0.25f); else Graphics()->SetColor(0.0f, 0.0f, 1.0f, 0.25f); - RenderTools()->DrawRoundRectExt(Whole-ScoreWidthMax-ImageSize-2*Split, 245.0f+t*20, ScoreWidthMax+ImageSize+2*Split, 18.0f, 5.0f, CUI::CORNER_L); + RenderTools()->DrawRoundRectExt(Whole-ScoreWidthMax-ImageSize-2*Split, StartY+t*20, ScoreWidthMax+ImageSize+2*Split, 18.0f, 5.0f, CUI::CORNER_L); Graphics()->QuadsEnd(); // draw score - TextRender()->Text(0, Whole-ScoreWidthMax+(ScoreWidthMax-aScoreTeamWidth[t])/2-Split, 245.0f+t*20, 14.0f, aScoreTeam[t], -1); + TextRender()->Text(0, Whole-ScoreWidthMax+(ScoreWidthMax-aScoreTeamWidth[t])/2-Split, StartY+t*20, 14.0f, aScoreTeam[t], -1); if(GameFlags&GAMEFLAG_FLAGS) { - if(FlagCarrier[t] == FLAG_ATSTAND || (FlagCarrier[t] == FLAG_TAKEN && ((Client()->GameTick()/10)&1))) + int BlinkTimer = (m_pClient->m_FlagDropTick[t] != 0 && + (Client()->GameTick()-m_pClient->m_FlagDropTick[t])/Client()->GameTickSpeed() >= 25) ? 10 : 20; + if(FlagCarrier[t] == FLAG_ATSTAND || (FlagCarrier[t] == FLAG_TAKEN && ((Client()->GameTick()/BlinkTimer)&1))) { // draw flag Graphics()->BlendNormal(); Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); RenderTools()->SelectSprite(t==0?SPRITE_FLAG_RED:SPRITE_FLAG_BLUE); - IGraphics::CQuadItem QuadItem(Whole-ScoreWidthMax-ImageSize, 246.0f+t*20, ImageSize/2, ImageSize); + IGraphics::CQuadItem QuadItem(Whole-ScoreWidthMax-ImageSize, StartY+1.0f+t*20, ImageSize/2, ImageSize); Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } @@ -124,16 +139,17 @@ void CHud::RenderScoreHud() // draw name of the flag holder int ID = FlagCarrier[t]%MAX_CLIENTS; const char *pName = m_pClient->m_aClients[ID].m_aName; - float w = TextRender()->TextWidth(0, 10.0f, pName, -1); - TextRender()->Text(0, Whole-ScoreWidthMax-ImageSize-3*Split-w, 247.0f+t*20, 10.0f, pName, -1); + float w = TextRender()->TextWidth(0, 8.0f, pName, -1); + TextRender()->Text(0, min(Whole-w-1.0f, Whole-ScoreWidthMax-ImageSize-2*Split), StartY+(t+1)*20.0f-3.0f, 8.0f, pName, -1); // draw tee of the flag holder CTeeRenderInfo Info = m_pClient->m_aClients[ID].m_RenderInfo; Info.m_Size = 18.0f; RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1,0), - vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, 246.0f+Info.m_Size/2+t*20)); + vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, StartY+1.0f+Info.m_Size/2+t*20)); } } + StartY += 8.0f; } } else @@ -189,25 +205,33 @@ void CHud::RenderScoreHud() Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.25f); else Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.25f); - RenderTools()->DrawRoundRectExt(Whole-ScoreWidthMax-ImageSize-2*Split-PosSize, 245.0f+t*20, ScoreWidthMax+ImageSize+2*Split+PosSize, 18.0f, 5.0f, CUI::CORNER_L); + RenderTools()->DrawRoundRectExt(Whole-ScoreWidthMax-ImageSize-2*Split-PosSize, StartY+t*20, ScoreWidthMax+ImageSize+2*Split+PosSize, 18.0f, 5.0f, CUI::CORNER_L); Graphics()->QuadsEnd(); // draw score - TextRender()->Text(0, Whole-ScoreWidthMax+(ScoreWidthMax-aScoreWidth[t])/2-Split, 245.0f+t*20, 14.0f, aScore[t], -1); + TextRender()->Text(0, Whole-ScoreWidthMax+(ScoreWidthMax-aScoreWidth[t])/2-Split, StartY+t*20, 14.0f, aScore[t], -1); - // draw tee if(apPlayerInfo[t]) { - CTeeRenderInfo Info = m_pClient->m_aClients[apPlayerInfo[t]->m_ClientID].m_RenderInfo; + // draw name + int ID = apPlayerInfo[t]->m_ClientID; + const char *pName = m_pClient->m_aClients[ID].m_aName; + float w = TextRender()->TextWidth(0, 8.0f, pName, -1); + TextRender()->Text(0, min(Whole-w-1.0f, Whole-ScoreWidthMax-ImageSize-2*Split-PosSize), StartY+(t+1)*20.0f-3.0f, 8.0f, pName, -1); + + // draw tee + CTeeRenderInfo Info = m_pClient->m_aClients[ID].m_RenderInfo; Info.m_Size = 18.0f; RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1,0), - vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, 246.0f+Info.m_Size/2+t*20)); + vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, StartY+1.0f+Info.m_Size/2+t*20)); } // draw position char aBuf[32]; str_format(aBuf, sizeof(aBuf), "%d.", aPos[t]); - TextRender()->Text(0, Whole-ScoreWidthMax-ImageSize-Split-PosSize, 247.0f+t*20, 10.0f, aBuf, -1); + TextRender()->Text(0, Whole-ScoreWidthMax-ImageSize-Split-PosSize, StartY+2.0f+t*20, 10.0f, aBuf, -1); + + StartY += 8.0f; } } } @@ -246,7 +270,7 @@ void CHud::RenderFps() if(g_Config.m_ClShowfps) { // calculate avg. fps - float FPS = 1.0f / Client()->FrameTime(); + float FPS = 1.0f / Client()->RenderFrameTime(); m_AverageFPS = (m_AverageFPS*(1.0f-(1.0f/m_AverageFPS))) + (FPS*(1.0f/m_AverageFPS)); char Buf[512]; str_format(Buf, sizeof(Buf), "%d", (int)m_AverageFPS); @@ -438,6 +462,7 @@ void CHud::OnRender() } RenderGameTimer(); + RenderPauseNotification(); RenderSuddenDeath(); RenderScoreHud(); RenderWarmupTimer(); diff --git a/src/game/client/components/hud.h b/src/game/client/components/hud.h index f0250c7b..34720854 100644 --- a/src/game/client/components/hud.h +++ b/src/game/client/components/hud.h @@ -17,6 +17,7 @@ class CHud : public CComponent void RenderVoting(); void RenderHealthAndAmmo(const CNetObj_Character *pCharacter); void RenderGameTimer(); + void RenderPauseNotification(); void RenderSuddenDeath(); void RenderScoreHud(); void RenderSpectatorHud(); diff --git a/src/game/client/components/items.cpp b/src/game/client/components/items.cpp index 827b28a7..e1032a51 100644 --- a/src/game/client/components/items.cpp +++ b/src/game/client/components/items.cpp @@ -17,12 +17,11 @@ void CItems::OnReset() { - ExtraProjectilesNum = 0; + m_NumExtraProjectiles = 0; } void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID) { - // get positions float Curvature = 0; float Speed = 0; @@ -42,7 +41,10 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID) Speed = m_pClient->m_Tuning.m_GunSpeed; } - float Ct = (Client()->PrevGameTick()-pCurrent->m_StartTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + static float s_LastGameTickTime = Client()->GameTickTime(); + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_LastGameTickTime = Client()->GameTickTime(); + float Ct = (Client()->PrevGameTick()-pCurrent->m_StartTick)/(float)SERVER_TICK_SPEED + s_LastGameTickTime; if(Ct < 0) return; // projectile havn't been shot yet @@ -64,28 +66,27 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID) if(pCurrent->m_Type == WEAPON_GRENADE) { m_pClient->m_pEffects->SmokeTrail(Pos, Vel*-1); - m_pClient->m_pFlow->Add(Pos, Vel*1000*Client()->FrameTime(), 10.0f); + static float s_Time = 0.0f; + static float s_LastLocalTime = Client()->LocalTime(); if(Client()->State() == IClient::STATE_DEMOPLAYBACK) { const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); - static float Time = 0; - static float LastLocalTime = Client()->LocalTime(); - if(!pInfo->m_Paused) - Time += (Client()->LocalTime()-LastLocalTime)*pInfo->m_Speed; - - Graphics()->QuadsSetRotation(Time*pi*2*2 + ItemID); - - LastLocalTime = Client()->LocalTime(); + s_Time += (Client()->LocalTime()-s_LastLocalTime)*pInfo->m_Speed; } else - Graphics()->QuadsSetRotation(Client()->LocalTime()*pi*2*2 + ItemID); + { + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_Time += Client()->LocalTime()-s_LastLocalTime; + } + + Graphics()->QuadsSetRotation(s_Time*pi*2*2 + ItemID); + s_LastLocalTime = Client()->LocalTime(); } else { m_pClient->m_pEffects->BulletTrail(Pos); - m_pClient->m_pFlow->Add(Pos, Vel*1000*Client()->FrameTime(), 10.0f); if(length(Vel) > 0.00001f) Graphics()->QuadsSetRotation(GetAngle(Vel)); @@ -133,26 +134,23 @@ void CItems::RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCu Graphics()->QuadsSetRotation(Angle); + static float s_Time = 0.0f; + static float s_LastLocalTime = Client()->LocalTime(); float Offset = Pos.y/32.0f + Pos.x/32.0f; if(Client()->State() == IClient::STATE_DEMOPLAYBACK) { const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); - static float Time = 0; - static float LastLocalTime = Client()->LocalTime(); - if(!pInfo->m_Paused) - Time += (Client()->LocalTime()-LastLocalTime)*pInfo->m_Speed; - - Pos.x += cosf(Time*2.0f+Offset)*2.5f; - Pos.y += sinf(Time*2.0f+Offset)*2.5f; - - LastLocalTime = Client()->LocalTime(); + s_Time += (Client()->LocalTime()-s_LastLocalTime)*pInfo->m_Speed; } else { - Pos.x += cosf(Client()->LocalTime()*2.0f+Offset)*2.5f; - Pos.y += sinf(Client()->LocalTime()*2.0f+Offset)*2.5f; - } + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_Time += Client()->LocalTime()-s_LastLocalTime; + } + Pos.x += cosf(s_Time*2.0f+Offset)*2.5f; + Pos.y += sinf(s_Time*2.0f+Offset)*2.5f; + s_LastLocalTime = Client()->LocalTime(); RenderTools()->DrawSprite(Pos.x, Pos.y, Size); Graphics()->QuadsEnd(); } @@ -310,23 +308,23 @@ void CItems::OnRender() } // render extra projectiles - for(int i = 0; i < ExtraProjectilesNum; i++) + for(int i = 0; i < m_NumExtraProjectiles; i++) { - if(aExtraProjectiles[i].m_StartTick < Client()->GameTick()) + if(m_aExtraProjectiles[i].m_StartTick < Client()->GameTick()) { - aExtraProjectiles[i] = aExtraProjectiles[ExtraProjectilesNum-1]; - ExtraProjectilesNum--; + m_aExtraProjectiles[i] = m_aExtraProjectiles[m_NumExtraProjectiles-1]; + m_NumExtraProjectiles--; } else - RenderProjectile(&aExtraProjectiles[i], 0); + RenderProjectile(&m_aExtraProjectiles[i], 0); } } void CItems::AddExtraProjectile(CNetObj_Projectile *pProj) { - if(ExtraProjectilesNum != MAX_EXTRA_PROJECTILES) + if(m_NumExtraProjectiles != MAX_EXTRA_PROJECTILES) { - aExtraProjectiles[ExtraProjectilesNum] = *pProj; - ExtraProjectilesNum++; + m_aExtraProjectiles[m_NumExtraProjectiles] = *pProj; + m_NumExtraProjectiles++; } } diff --git a/src/game/client/components/items.h b/src/game/client/components/items.h index 604c1b1c..caf6176b 100644 --- a/src/game/client/components/items.h +++ b/src/game/client/components/items.h @@ -11,8 +11,8 @@ class CItems : public CComponent MAX_EXTRA_PROJECTILES=32, }; - CNetObj_Projectile aExtraProjectiles[MAX_EXTRA_PROJECTILES]; - int ExtraProjectilesNum; + CNetObj_Projectile m_aExtraProjectiles[MAX_EXTRA_PROJECTILES]; + int m_NumExtraProjectiles; void RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID); void RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent); diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 848651ca..db74b165 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -22,6 +22,9 @@ CMapLayers::CMapLayers(int t) { m_Type = t; m_pLayers = 0; + m_CurrentLocalTick = 0; + m_LastLocalTick = 0; + m_EnvelopeUpdate = false; } void CMapLayers::OnInit() @@ -29,6 +32,17 @@ void CMapLayers::OnInit() m_pLayers = Layers(); } +void CMapLayers::EnvelopeUpdate() +{ + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + { + const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); + m_CurrentLocalTick = pInfo->m_CurrentTick; + m_LastLocalTick = pInfo->m_CurrentTick; + m_EnvelopeUpdate = true; + } +} + void CMapLayers::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup) { @@ -63,21 +77,43 @@ void CMapLayers::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void CMapItemEnvelope *pItem = (CMapItemEnvelope *)pThis->m_pLayers->Map()->GetItem(Start+Env, 0, 0); + static float s_Time = 0.0f; + static float s_LastLocalTime = pThis->Client()->LocalTime(); if(pThis->Client()->State() == IClient::STATE_DEMOPLAYBACK) { const IDemoPlayer::CInfo *pInfo = pThis->DemoPlayer()->BaseInfo(); - static float Time = 0; - static float LastLocalTime = pThis->Client()->LocalTime(); - - if(!pInfo->m_Paused) - Time += (pThis->Client()->LocalTime()-LastLocalTime)*pInfo->m_Speed; + + if(!pInfo->m_Paused || pThis->m_EnvelopeUpdate) + { + if(pThis->m_CurrentLocalTick != pInfo->m_CurrentTick) + { + pThis->m_LastLocalTick = pThis->m_CurrentLocalTick; + pThis->m_CurrentLocalTick = pInfo->m_CurrentTick; + } - pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, Time+TimeOffset, pChannels); + s_Time = mix(pThis->m_LastLocalTick / (float)pThis->Client()->GameTickSpeed(), + pThis->m_CurrentLocalTick / (float)pThis->Client()->GameTickSpeed(), + pThis->Client()->IntraGameTick()); + } - LastLocalTime = pThis->Client()->LocalTime(); + pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, s_Time+TimeOffset, pChannels); } else - pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, pThis->Client()->LocalTime()+TimeOffset, pChannels); + { + if(pThis->m_pClient->m_Snap.m_pGameInfoObj && !(pThis->m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + { + if(pItem->m_Version < 2 || pItem->m_Synchronized) + { + s_Time = mix((pThis->Client()->PrevGameTick()-pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)pThis->Client()->GameTickSpeed(), + (pThis->Client()->GameTick()-pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)pThis->Client()->GameTickSpeed(), + pThis->Client()->IntraGameTick()); + } + else + s_Time += pThis->Client()->LocalTime()-s_LastLocalTime; + } + pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, s_Time+TimeOffset, pChannels); + s_LastLocalTime = pThis->Client()->LocalTime(); + } } void CMapLayers::OnRender() @@ -156,16 +192,11 @@ void CMapLayers::OnRender() IOHANDLE File = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE); if(File) { - #if defined(CONF_FAMILY_WINDOWS) - static const char Newline[] = "\r\n"; - #else - static const char Newline[] = "\n"; - #endif for(int y = 0; y < pTMap->m_Height; y++) { for(int x = 0; x < pTMap->m_Width; x++) io_write(File, &(pTiles[y*pTMap->m_Width + x].m_Index), sizeof(pTiles[y*pTMap->m_Width + x].m_Index)); - io_write(File, Newline, sizeof(Newline)-1); + io_write_newline(File); } io_close(File); } @@ -186,9 +217,11 @@ void CMapLayers::OnRender() CTile *pTiles = (CTile *)m_pLayers->Map()->GetData(pTMap->m_Data); Graphics()->BlendNone(); vec4 Color = vec4(pTMap->m_Color.r/255.0f, pTMap->m_Color.g/255.0f, pTMap->m_Color.b/255.0f, pTMap->m_Color.a/255.0f); - RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); + RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE, + EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); Graphics()->BlendNormal(); - RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); + RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, Color, TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT, + EnvelopeEval, this, pTMap->m_ColorEnv, pTMap->m_ColorEnvOffset); } else if(pLayer->m_Type == LAYERTYPE_QUADS) { diff --git a/src/game/client/components/maplayers.h b/src/game/client/components/maplayers.h index 694633ee..d0efcfc7 100644 --- a/src/game/client/components/maplayers.h +++ b/src/game/client/components/maplayers.h @@ -8,6 +8,9 @@ class CMapLayers : public CComponent { CLayers *m_pLayers; // todo refactor: maybe remove it and access it through client* int m_Type; + int m_CurrentLocalTick; + int m_LastLocalTick; + bool m_EnvelopeUpdate; void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup); static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser); @@ -21,6 +24,8 @@ public: CMapLayers(int Type); virtual void OnInit(); virtual void OnRender(); + + void EnvelopeUpdate(); }; #endif diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 53192714..d27307f4 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -27,6 +27,7 @@ #include <game/localization.h> #include <mastersrv/mastersrv.h> +#include "countryflags.h" #include "menus.h" #include "skins.h" @@ -34,11 +35,10 @@ vec4 CMenus::ms_GuiColor; vec4 CMenus::ms_ColorTabbarInactiveOutgame; vec4 CMenus::ms_ColorTabbarActiveOutgame; vec4 CMenus::ms_ColorTabbarInactive; -vec4 CMenus::ms_ColorTabbarActive; +vec4 CMenus::ms_ColorTabbarActive = vec4(0,0,0,0.5f); vec4 CMenus::ms_ColorTabbarInactiveIngame; vec4 CMenus::ms_ColorTabbarActiveIngame; - float CMenus::ms_ButtonHeight = 25.0f; float CMenus::ms_ListheaderHeight = 17.0f; float CMenus::ms_FontmodHeight = 0.8f; @@ -94,14 +94,16 @@ int CMenus::DoButton_Icon(int ImageId, int SpriteId, const CUIRect *pRect) return 0; } -int CMenus::DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect) +int CMenus::DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect, bool Active) { Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GUIBUTTONS].m_Id); Graphics()->QuadsBegin(); + if(!Active) + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f); RenderTools()->SelectSprite(Checked?SPRITE_GUIBUTTON_ON:SPRITE_GUIBUTTON_OFF); IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h); Graphics()->QuadsDrawTL(&QuadItem, 1); - if((UI()->HotItem() == pID)) + if(UI()->HotItem() == pID && Active) { RenderTools()->SelectSprite(SPRITE_GUIBUTTON_HOVER); IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h); @@ -109,7 +111,7 @@ int CMenus::DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect) } Graphics()->QuadsEnd(); - return UI()->DoButtonLogic(pID, "", Checked, pRect); + return Active ? UI()->DoButtonLogic(pID, "", Checked, pRect) : 0; } int CMenus::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect) @@ -209,7 +211,7 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS for(int i = 1; i <= Len; i++) { - if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset + 10 > MxRel) + if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset > MxRel) { s_AtIndex = i - 1; break; @@ -251,6 +253,7 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS { if(!UI()->MouseButton(0)) { + s_AtIndex = min(s_AtIndex, str_length(pStr)); s_DoScroll = false; UI()->SetActiveItem(0); } @@ -626,6 +629,8 @@ int CMenus::RenderMenubar(CUIRect r) void CMenus::RenderLoading() { + // TODO: not supported right now due to separate render thread + static int64 LastLoadRender = 0; float Percent = m_LoadCurrent++/(float)m_LoadTotal; @@ -645,8 +650,6 @@ void CMenus::RenderLoading() RenderBackground(); - float tw; - float w = 700; float h = 200; float x = Screen.w/2-w/2; @@ -663,7 +666,6 @@ void CMenus::RenderLoading() const char *pCaption = Localize("Loading"); - tw = TextRender()->TextWidth(0, 48.0f, pCaption, -1); CUIRect r; r.x = x; r.y = y+20; @@ -737,6 +739,8 @@ void CMenus::OnInit() Console()->Chain("add_favorite", ConchainServerbrowserUpdate, this); Console()->Chain("remove_favorite", ConchainServerbrowserUpdate, this); + Console()->Chain("add_friend", ConchainFriendlistUpdate, this); + Console()->Chain("remove_friend", ConchainFriendlistUpdate, this); // setup load amount m_LoadCurrent = 0; @@ -960,7 +964,7 @@ int CMenus::Render() Box.VMargin(20.f/UI()->Scale(), &Box); if(m_pClient->Editor()->HasUnsavedData()) { - char aBuf[128]; + char aBuf[256]; str_format(aBuf, sizeof(aBuf), "%s\n%s", Localize("There's an unsaved map in the editor, you might want to save it before you quit the game."), Localize("Quit anyway?")); UI()->DoLabelScaled(&Box, aBuf, 20.f, -1, Part.w-20.0f); } @@ -1058,7 +1062,7 @@ int CMenus::Render() // time left const char *pTimeLeftString; - int TimeLeft = m_DownloadSpeed > 0.0f ? (Client()->MapDownloadTotalsize()-Client()->MapDownloadAmount())/m_DownloadSpeed : 0.0f; + int TimeLeft = max(1, m_DownloadSpeed > 0.0f ? static_cast<int>((Client()->MapDownloadTotalsize()-Client()->MapDownloadAmount())/m_DownloadSpeed) : 1); if(TimeLeft >= 60) { TimeLeft /= 60; @@ -1097,6 +1101,65 @@ int CMenus::Render() if(DoButton_Menu(&s_Button, Localize("Ok"), 0, &Part) || m_EscapePressed || m_EnterPressed) m_Popup = POPUP_FIRST_LAUNCH; } + else if(m_Popup == POPUP_COUNTRY) + { + Box = Screen; + Box.VMargin(150.0f, &Box); + Box.HMargin(150.0f, &Box); + Box.HSplitTop(20.f, &Part, &Box); + Box.HSplitBottom(20.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); + Box.HSplitBottom(20.f, &Box, 0); + Box.VMargin(20.0f, &Box); + + static int ActSelection = -2; + if(ActSelection == -2) + ActSelection = g_Config.m_BrFilterCountryIndex; + static float s_ScrollValue = 0.0f; + int OldSelected = -1; + UiDoListboxStart(&s_ScrollValue, &Box, 50.0f, Localize("Country"), "", m_pClient->m_pCountryFlags->Num(), 6, OldSelected, s_ScrollValue); + + for(int i = 0; i < m_pClient->m_pCountryFlags->Num(); ++i) + { + const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_pCountryFlags->GetByIndex(i); + if(pEntry->m_CountryCode == ActSelection) + OldSelected = i; + + CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected == i); + if(Item.m_Visible) + { + CUIRect Label; + Item.m_Rect.Margin(5.0f, &Item.m_Rect); + Item.m_Rect.HSplitBottom(10.0f, &Item.m_Rect, &Label); + float OldWidth = Item.m_Rect.w; + Item.m_Rect.w = Item.m_Rect.h*2; + Item.m_Rect.x += (OldWidth-Item.m_Rect.w)/ 2.0f; + vec4 Color(1.0f, 1.0f, 1.0f, 1.0f); + m_pClient->m_pCountryFlags->Render(pEntry->m_CountryCode, &Color, Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h); + UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, 0); + } + } + + const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); + if(OldSelected != NewSelected) + ActSelection = m_pClient->m_pCountryFlags->GetByIndex(NewSelected)->m_CountryCode; + + Part.VMargin(120.0f, &Part); + + static int s_Button = 0; + if(DoButton_Menu(&s_Button, Localize("Ok"), 0, &Part) || m_EnterPressed) + { + g_Config.m_BrFilterCountryIndex = ActSelection; + Client()->ServerBrowserUpdate(); + m_Popup = POPUP_NONE; + } + + if(m_EscapePressed) + { + ActSelection = g_Config.m_BrFilterCountryIndex; + m_Popup = POPUP_NONE; + } + } else if(m_Popup == POPUP_DELETE_DEMO) { CUIRect Yes, No; @@ -1208,7 +1271,9 @@ int CMenus::Render() // remove friend if(m_FriendlistSelectedIndex >= 0) { - m_pClient->Friends()->RemoveFriend(m_FriendlistSelectedIndex); + m_pClient->Friends()->RemoveFriend(m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName, + m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aClan); + FriendlistOnUpdate(); Client()->ServerBrowserUpdate(); } } @@ -1285,6 +1350,7 @@ bool CMenus::OnMouseMove(float x, float y) if(!m_MenuActive) return false; + UI()->ConvertMouseMove(&x, &y); m_MousePos.x += x; m_MousePos.y += y; if(m_MousePos.x < 0) m_MousePos.x = 0; @@ -1335,7 +1401,8 @@ void CMenus::OnStateChange(int NewState, int OldState) if(NewState == IClient::STATE_OFFLINE) { - m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); + if(OldState >= IClient::STATE_ONLINE && NewState < IClient::STATE_QUITING) + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f); m_Popup = POPUP_NONE; if(Client()->ErrorString() && Client()->ErrorString()[0] != 0) { diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 51b8a1f8..0125278c 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -7,6 +7,7 @@ #include <base/tl/sorted_array.h> #include <engine/demo.h> +#include <engine/friends.h> #include <game/voting.h> #include <game/client/component.h> @@ -39,7 +40,7 @@ class CMenus : public CComponent int DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect); int DoButton_Sprite(const void *pID, int ImageID, int SpriteID, int Checked, const CUIRect *pRect, int Corners); - int DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect); + int DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect, bool Active); int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect); int DoButton_MenuTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Corners); @@ -100,6 +101,7 @@ class CMenus : public CComponent POPUP_DISCONNECTED, POPUP_PURE, POPUP_LANGUAGE, + POPUP_COUNTRY, POPUP_DELETE_DEMO, POPUP_RENAME_DEMO, POPUP_REMOVE_FRIEND, @@ -202,8 +204,34 @@ class CMenus : public CComponent void DemolistPopulate(); static int DemolistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser); + // friends + struct CFriendItem + { + const CFriendInfo *m_pFriendInfo; + int m_NumFound; + + bool operator<(const CFriendItem &Other) + { + if(m_NumFound && !Other.m_NumFound) + return true; + else if(!m_NumFound && Other.m_NumFound) + return false; + else + { + int Result = str_comp(m_pFriendInfo->m_aName, Other.m_pFriendInfo->m_aName); + if(Result) + return Result < 0; + else + return str_comp(m_pFriendInfo->m_aClan, Other.m_pFriendInfo->m_aClan) < 0; + } + } + }; + + sorted_array<CFriendItem> m_lFriends; int m_FriendlistSelectedIndex; + void FriendlistOnUpdate(); + // found in menus.cpp int Render(); //void render_background(); @@ -225,11 +253,13 @@ class CMenus : public CComponent // found in menus_browser.cpp int m_SelectedIndex; + int m_ScrollOffset; void RenderServerbrowserServerList(CUIRect View); void RenderServerbrowserServerDetail(CUIRect View); void RenderServerbrowserFilters(CUIRect View); void RenderServerbrowserFriends(CUIRect View); void RenderServerbrowser(CUIRect MainView); + static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); // found in menus_settings.cpp diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 3ab02db8..a9c434b3 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -154,9 +154,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) int ScrollNum = NumServers-Num+1; if(ScrollNum > 0) { - if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + if(m_ScrollOffset) + { + s_ScrollValue = (float)(m_ScrollOffset)/ScrollNum; + m_ScrollOffset = 0; + } + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && UI()->MouseInside(&View)) s_ScrollValue -= 3.0f/ScrollNum; - if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && UI()->MouseInside(&View)) s_ScrollValue += 3.0f/ScrollNum; } else @@ -213,16 +218,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) m_SelectedIndex = -1; - for (int i = 0; i < NumServers; i++) - { - const CServerInfo *pItem = ServerBrowser()->SortedGet(i); - NumPlayers += pItem->m_NumPlayers; - } + // reset friend counter + for(int i = 0; i < m_lFriends.size(); m_lFriends[i++].m_NumFound = 0); for (int i = 0; i < NumServers; i++) { int ItemIndex = i; const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex); + NumPlayers += pItem->m_NumPlayers; CUIRect Row; CUIRect SelectHitBox; @@ -234,6 +237,29 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) if(Selected) m_SelectedIndex = i; + // update friend counter + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + for(int j = 0; j < pItem->m_NumClients; ++j) + { + if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO) + { + unsigned NameHash = str_quickhash(pItem->m_aClients[j].m_aName); + unsigned ClanHash = str_quickhash(pItem->m_aClients[j].m_aClan); + for(int f = 0; f < m_lFriends.size(); ++f) + { + if(ClanHash == m_lFriends[f].m_pFriendInfo->m_ClanHash && + (!m_lFriends[f].m_pFriendInfo->m_aName[0] || NameHash == m_lFriends[f].m_pFriendInfo->m_NameHash)) + { + m_lFriends[f].m_NumFound++; + if(m_lFriends[f].m_pFriendInfo->m_aName[0]) + break; + } + } + } + } + } + // make sure that only those in view can be selected if(Row.y+Row.h > OriginalView.y && Row.y < OriginalView.y+OriginalView.h) { @@ -353,6 +379,15 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } else if(ID == COL_PLAYERS) { + CUIRect Icon; + Button.VMargin(4.0f, &Button); + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + Button.VSplitLeft(Button.h, &Icon, &Button); + Icon.Margin(2.0f, &Icon); + DoButton_Icon(IMAGE_BROWSEICONS, SPRITE_BROWSE_HEART, &Icon); + } + if(g_Config.m_BrFilterSpectators) str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumPlayers, pItem->m_MaxPlayers); else @@ -463,7 +498,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) g_Config.m_BrFilterFull ^= 1; ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); - if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends"), g_Config.m_BrFilterFriends, &Button)) + if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends only"), g_Config.m_BrFilterFriends, &Button)) g_Config.m_BrFilterFriends ^= 1; ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); @@ -482,6 +517,10 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) if (DoButton_CheckBox((char *)&g_Config.m_BrFilterPureMap, Localize("Standard map"), g_Config.m_BrFilterPureMap, &Button)) g_Config.m_BrFilterPureMap ^= 1; + ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); + if (DoButton_CheckBox((char *)&g_Config.m_BrFilterGametypeStrict, Localize("Strict gametype filter"), g_Config.m_BrFilterGametypeStrict, &Button)) + g_Config.m_BrFilterGametypeStrict ^= 1; + ServerFilter.HSplitTop(5.0f, 0, &ServerFilter); ServerFilter.HSplitTop(19.0f, &Button, &ServerFilter); @@ -515,21 +554,46 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) if(DoEditBox(&g_Config.m_BrFilterServerAddress, &Button, g_Config.m_BrFilterServerAddress, sizeof(g_Config.m_BrFilterServerAddress), FontSize, &OffsetAddr)) Client()->ServerBrowserUpdate(); + // player country + { + CUIRect Rect; + ServerFilter.HSplitTop(3.0f, 0, &ServerFilter); + ServerFilter.HSplitTop(26.0f, &Button, &ServerFilter); + Button.VSplitRight(60.0f, &Button, &Rect); + Button.HMargin(3.0f, &Button); + if(DoButton_CheckBox(&g_Config.m_BrFilterCountry, Localize("Player country:"), g_Config.m_BrFilterCountry, &Button)) + g_Config.m_BrFilterCountry ^= 1; + + float OldWidth = Rect.w; + Rect.w = Rect.h*2; + Rect.x += (OldWidth-Rect.w)/2.0f; + vec4 Color(1.0f, 1.0f, 1.0f, g_Config.m_BrFilterCountry?1.0f: 0.5f); + m_pClient->m_pCountryFlags->Render(g_Config.m_BrFilterCountryIndex, &Color, Rect.x, Rect.y, Rect.w, Rect.h); + + if(g_Config.m_BrFilterCountry && UI()->DoButtonLogic(&g_Config.m_BrFilterCountryIndex, "", 0, &Rect)) + m_Popup = POPUP_COUNTRY; + } + ServerFilter.HSplitBottom(5.0f, &ServerFilter, 0); ServerFilter.HSplitBottom(ms_ButtonHeight-2.0f, &ServerFilter, &Button); static int s_ClearButton = 0; if(DoButton_Menu(&s_ClearButton, Localize("Reset filter"), 0, &Button)) { + g_Config.m_BrFilterString[0] = 0; g_Config.m_BrFilterFull = 0; g_Config.m_BrFilterEmpty = 0; + g_Config.m_BrFilterSpectators = 0; + g_Config.m_BrFilterFriends = 0; + g_Config.m_BrFilterCountry = 0; + g_Config.m_BrFilterCountryIndex = -1; g_Config.m_BrFilterPw = 0; g_Config.m_BrFilterPing = 999; g_Config.m_BrFilterGametype[0] = 0; + g_Config.m_BrFilterGametypeStrict = 0; g_Config.m_BrFilterServerAddress[0] = 0; - g_Config.m_BrFilterCompatversion = 1; - g_Config.m_BrFilterString[0] = 0; g_Config.m_BrFilterPure = 1; g_Config.m_BrFilterPureMap = 1; + g_Config.m_BrFilterCompatversion = 1; Client()->ServerBrowserUpdate(); } } @@ -617,25 +681,38 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) RenderTools()->DrawUIRect(&ServerScoreBoard, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); UI()->DoLabelScaled(&ServerHeader, Localize("Scoreboard"), FontSize+2.0f, 0); - if (pSelectedServer) + if(pSelectedServer) { - ServerScoreBoard.VSplitLeft(5.0f, 0, &ServerScoreBoard); ServerScoreBoard.Margin(3.0f, &ServerScoreBoard); for (int i = 0; i < pSelectedServer->m_NumClients; i++) { CUIRect Name, Clan, Score, Flag; ServerScoreBoard.HSplitTop(25.0f, &Name, &ServerScoreBoard); + if(UI()->DoButtonLogic(&pSelectedServer->m_aClients[i], "", 0, &Name)) + { + if(pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_PLAYER) + m_pClient->Friends()->RemoveFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan); + else + m_pClient->Friends()->AddFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan); + FriendlistOnUpdate(); + Client()->ServerBrowserUpdate(); + } + + vec4 Colour = pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_NO ? vec4(1.0f, 1.0f, 1.0f, (i%2+1)*0.05f) : + vec4(0.5f, 1.0f, 0.5f, 0.15f+(i%2+1)*0.05f); + RenderTools()->DrawUIRect(&Name, Colour, CUI::CORNER_ALL, 4.0f); + Name.VSplitLeft(5.0f, 0, &Name); Name.VSplitLeft(30.0f, &Score, &Name); Name.VSplitRight(34.0f, &Name, &Flag); Flag.HMargin(4.0f, &Flag); - Name.HSplitTop(12.0f, &Name, &Clan); + Name.HSplitTop(11.0f, &Name, &Clan); // score if(pSelectedServer->m_aClients[i].m_Player) { char aTemp[16]; str_format(aTemp, sizeof(aTemp), "%d", pSelectedServer->m_aClients[i].m_Score); - TextRender()->SetCursor(&Cursor, Score.x, Score.y+(Score.h-FontSize)/2.0f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + TextRender()->SetCursor(&Cursor, Score.x, Score.y+(Score.h-FontSize)/4.0f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = Score.w; TextRender()->TextEx(&Cursor, aTemp, -1); } @@ -685,55 +762,108 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) TextRender()->TextEx(&Cursor, pClan, -1); // flag - Graphics()->TextureSet(m_pClient->m_pCountryFlags->Get(pSelectedServer->m_aClients[i].m_Country)->m_Texture); - Graphics()->QuadsBegin(); - Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f); - IGraphics::CQuadItem QuadItem(Flag.x, Flag.y, Flag.w, Flag.h); - Graphics()->QuadsDrawTL(&QuadItem, 1); - Graphics()->QuadsEnd(); + vec4 Color(1.0f, 1.0f, 1.0f, 0.5f); + m_pClient->m_pCountryFlags->Render(pSelectedServer->m_aClients[i].m_Country, &Color, Flag.x, Flag.y, Flag.w, Flag.h); } } } +void CMenus::FriendlistOnUpdate() +{ + m_lFriends.clear(); + for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i) + { + CFriendItem Item; + Item.m_pFriendInfo = m_pClient->Friends()->GetFriend(i); + Item.m_NumFound = 0; + m_lFriends.add_unsorted(Item); + } + m_lFriends.sort_range(); +} + void CMenus::RenderServerbrowserFriends(CUIRect View) { + static int s_Inited = 0; + if(!s_Inited) + { + FriendlistOnUpdate(); + s_Inited = 1; + } + CUIRect ServerFriends = View, FilterHeader; - const float FontSize = 12.0f; + const float FontSize = 10.0f; // header ServerFriends.HSplitTop(ms_ListheaderHeight, &FilterHeader, &ServerFriends); RenderTools()->DrawUIRect(&FilterHeader, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); RenderTools()->DrawUIRect(&ServerFriends, vec4(0,0,0,0.15f), 0, 4.0f); - UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+2.0f, 0); + UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+4.0f, 0); CUIRect Button, List; - ServerFriends.VSplitLeft(5.0f, 0, &ServerFriends); ServerFriends.Margin(3.0f, &ServerFriends); - ServerFriends.VMargin(5.0f, &ServerFriends); + ServerFriends.VMargin(3.0f, &ServerFriends); ServerFriends.HSplitBottom(100.0f, &List, &ServerFriends); // friends list(remove friend) - static int s_FriendList = 0; static float s_ScrollValue = 0; - UiDoListboxStart(&s_FriendList, &List, 40.0f, "", "", m_pClient->Friends()->NumFriends(), 1, m_FriendlistSelectedIndex, s_ScrollValue); + UiDoListboxStart(&m_lFriends, &List, 30.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue); - for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i) + m_lFriends.sort_range(); + for(int i = 0; i < m_lFriends.size(); ++i) { - const CFriendInfo *pFriend = m_pClient->Friends()->GetFriend(i); - CListboxItem Item = UiDoListboxNextItem(pFriend); + CListboxItem Item = UiDoListboxNextItem(&m_lFriends[i]); if(Item.m_Visible) { - Item.m_Rect.Margin(2.5f, &Item.m_Rect); - RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_ALL, 4.0f); - Item.m_Rect.Margin(2.5f, &Item.m_Rect); - Item.m_Rect.HSplitTop(14.0f, &Item.m_Rect, &Button); - UI()->DoLabelScaled(&Item.m_Rect, pFriend->m_aName, FontSize, -1); - UI()->DoLabelScaled(&Button, pFriend->m_aClan, FontSize, -1); + Item.m_Rect.Margin(1.5f, &Item.m_Rect); + CUIRect OnState; + Item.m_Rect.VSplitRight(30.0f, &Item.m_Rect, &OnState); + RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_L, 4.0f); + + Item.m_Rect.VMargin(2.5f, &Item.m_Rect); + Item.m_Rect.HSplitTop(12.0f, &Item.m_Rect, &Button); + UI()->DoLabelScaled(&Item.m_Rect, m_lFriends[i].m_pFriendInfo->m_aName, FontSize, -1); + UI()->DoLabelScaled(&Button, m_lFriends[i].m_pFriendInfo->m_aClan, FontSize, -1); + + RenderTools()->DrawUIRect(&OnState, m_lFriends[i].m_NumFound ? vec4(0.0f, 1.0f, 0.0f, 0.25f) : vec4(1.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_R, 4.0f); + OnState.HMargin((OnState.h-FontSize)/3, &OnState); + OnState.VMargin(5.0f, &OnState); + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%i", m_lFriends[i].m_NumFound); + UI()->DoLabelScaled(&OnState, aBuf, FontSize+2, 1); } } - m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, 0); + bool Activated = false; + m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, &Activated); + + // activate found server with friend + if(Activated && !m_EnterPressed && m_lFriends[m_FriendlistSelectedIndex].m_NumFound) + { + bool Found = false; + int NumServers = ServerBrowser()->NumSortedServers(); + for (int i = 0; i < NumServers && !Found; i++) + { + int ItemIndex = m_SelectedIndex != -1 ? (m_SelectedIndex+i+1)%NumServers : i; + const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex); + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + for(int j = 0; j < pItem->m_NumClients && !Found; ++j) + { + if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO && + str_quickhash(pItem->m_aClients[j].m_aClan) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_ClanHash && + (!m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName[0] || + str_quickhash(pItem->m_aClients[j].m_aName) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_NameHash)) + { + str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress)); + m_ScrollOffset = ItemIndex; + m_SelectedIndex = ItemIndex; + Found = true; + } + } + } + } + } ServerFriends.HSplitTop(2.5f, 0, &ServerFriends); ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends); @@ -768,10 +898,11 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) ServerFriends.HSplitTop(3.0f, 0, &ServerFriends); ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends); - static int s_RemoveButton = 0; - if(DoButton_Menu(&s_RemoveButton, Localize("Add Friend"), 0, &Button)) + static int s_AddButton = 0; + if(DoButton_Menu(&s_AddButton, Localize("Add Friend"), 0, &Button)) { m_pClient->Friends()->AddFriend(s_aName, s_aClan); + FriendlistOnUpdate(); Client()->ServerBrowserUpdate(); } } @@ -910,6 +1041,16 @@ void CMenus::RenderServerbrowser(CUIRect MainView) } } +void CMenus::ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments() == 2 && ((CMenus *)pUserData)->Client()->State() == IClient::STATE_OFFLINE) + { + ((CMenus *)pUserData)->FriendlistOnUpdate(); + ((CMenus *)pUserData)->Client()->ServerBrowserUpdate(); + } +} + void CMenus::ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) { pfnCallback(pResult, pCallbackUserData); diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 7fcfab99..40a9e5b5 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -17,6 +17,7 @@ #include <game/generated/client_data.h> +#include "maplayers.h" #include "menus.h" int CMenus::DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect) @@ -85,15 +86,28 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) void *id = &s_SeekBarID; char aBuffer[128]; + // draw seek bar RenderTools()->DrawUIRect(&SeekBar, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 5.0f); + // draw filled bar float Amount = CurrentTick/(float)TotalTicks; - CUIRect FilledBar = SeekBar; FilledBar.w = 10.0f + (FilledBar.w-10.0f)*Amount; - RenderTools()->DrawUIRect(&FilledBar, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); + // draw markers + for(int i = 0; i < pInfo->m_NumTimelineMarkers; i++) + { + float Ratio = (pInfo->m_aTimelineMarkers[i]-pInfo->m_FirstTick) / (float)TotalTicks; + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + IGraphics::CQuadItem QuadItem(SeekBar.x + (SeekBar.w-10.0f)*Ratio, SeekBar.y, UI()->PixelSize(), SeekBar.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + + // draw time str_format(aBuffer, sizeof(aBuffer), "%d:%02d / %d:%02d", CurrentTick/SERVER_TICK_SPEED/60, (CurrentTick/SERVER_TICK_SPEED)%60, TotalTicks/SERVER_TICK_SPEED/60, (TotalTicks/SERVER_TICK_SPEED)%60); @@ -117,6 +131,8 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) m_pClient->m_SuppressEvents = true; DemoPlayer()->SetPos(Amount); m_pClient->m_SuppressEvents = false; + m_pClient->m_pMapLayersBackGround->EnvelopeUpdate(); + m_pClient->m_pMapLayersForeGround->EnvelopeUpdate(); } } } @@ -200,8 +216,10 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) Client()->Disconnect(); // demo name + char aDemoName[64] = {0}; + DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "Demofile: %s", DemoPlayer()->GetDemoName()); + str_format(aBuf, sizeof(aBuf), Localize("Demofile: %s"), aDemoName); CTextCursor Cursor; TextRender()->SetCursor(&Cursor, NameBar.x, NameBar.y, Button.h*0.5f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = MainView.w; @@ -289,9 +307,9 @@ void CMenus::UiDoListboxStart(const void *pID, const CUIRect *pRect, float RowHe Num = 0; if(Num > 0) { - if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && UI()->MouseInside(&View)) gs_ListBoxScrollValue -= 3.0f/Num; - if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && UI()->MouseInside(&View)) gs_ListBoxScrollValue += 3.0f/Num; if(gs_ListBoxScrollValue < 0.0f) gs_ListBoxScrollValue = 0.0f; @@ -371,7 +389,7 @@ CMenus::CListboxItem CMenus::UiDoListboxNextItem(const void *pId, bool Selected) { gs_ListBoxDoneEvents = 1; - if(m_EnterPressed || (Input()->MouseDoubleClick() && UI()->ActiveItem() == pId)) + if(m_EnterPressed || (UI()->ActiveItem() == pId && Input()->MouseDoubleClick())) { gs_ListBoxItemActivated = true; UI()->SetActiveItem(0); @@ -553,8 +571,8 @@ void CMenus::RenderDemoList(CUIRect MainView) Labels.HSplitTop(20.0f, &Left, &Labels); Left.VSplitLeft(150.0f, &Left, &Right); UI()->DoLabelScaled(&Left, Localize("Length:"), 14.0f, -1); - int Length = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[1]<<16) | - (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[2]<<8) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[3]); + int Length = ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[0]<<24)&0xFF000000) | ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[1]<<16)&0xFF0000) | + ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[2]<<8)&0xFF00) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[3]&0xFF); char aBuf[64]; str_format(aBuf, sizeof(aBuf), "%d:%02d", Length/60, Length%60); UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1); @@ -576,9 +594,9 @@ void CMenus::RenderDemoList(CUIRect MainView) Left.VSplitLeft(20.0f, 0, &Left); Left.VSplitLeft(130.0f, &Left, &Right); UI()->DoLabelScaled(&Left, Localize("Size:"), 14.0f, -1); - Length = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[1]<<16) | + unsigned Size = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[1]<<16) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[2]<<8) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[3]); - str_format(aBuf, sizeof(aBuf), Localize("%d Bytes"), Length); + str_format(aBuf, sizeof(aBuf), Localize("%d Bytes"), Size); UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1); Labels.HSplitTop(5.0f, 0, &Labels); Labels.HSplitTop(20.0f, &Left, &Labels); diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 33aaa14f..a9cf35e9 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -149,7 +149,11 @@ void CMenus::RenderPlayers(CUIRect MainView) static int s_aPlayerIDs[MAX_CLIENTS][2] = {{0}}; for(int i = 0, Count = 0; i < MAX_CLIENTS; ++i) { - if(!m_pClient->m_Snap.m_paPlayerInfos[i] || i == m_pClient->m_Snap.m_LocalClientID) + if(!m_pClient->m_Snap.m_paInfoByTeam[i]) + continue; + + int Index = m_pClient->m_Snap.m_paInfoByTeam[i]->m_ClientID; + if(Index == m_pClient->m_Snap.m_LocalClientID) continue; Options.HSplitTop(28.0f, &ButtonBar, &Options); @@ -159,7 +163,7 @@ void CMenus::RenderPlayers(CUIRect MainView) // player info Player.VSplitLeft(28.0f, &Button, &Player); - CTeeRenderInfo Info = m_pClient->m_aClients[i].m_RenderInfo; + CTeeRenderInfo Info = m_pClient->m_aClients[Index].m_RenderInfo; Info.m_Size = Button.h; RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1.0f, 0.0f), vec2(Button.x+Button.h/2, Button.y+Button.h/2)); @@ -168,30 +172,35 @@ void CMenus::RenderPlayers(CUIRect MainView) CTextCursor Cursor; TextRender()->SetCursor(&Cursor, Player.x, Player.y, 14.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = Player.w; - TextRender()->TextEx(&Cursor, m_pClient->m_aClients[i].m_aName, -1); + TextRender()->TextEx(&Cursor, m_pClient->m_aClients[Index].m_aName, -1); TextRender()->SetCursor(&Cursor, Button.x,Button.y, 14.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = Button.w; - TextRender()->TextEx(&Cursor, m_pClient->m_aClients[i].m_aClan, -1); + TextRender()->TextEx(&Cursor, m_pClient->m_aClients[Index].m_aClan, -1); // ignore button ButtonBar.HMargin(2.0f, &ButtonBar); ButtonBar.VSplitLeft(Width, &Button, &ButtonBar); Button.VSplitLeft((Width-Button.h)/4.0f, 0, &Button); Button.VSplitLeft(Button.h, &Button, 0); - if(DoButton_Toggle(&s_aPlayerIDs[i][0], m_pClient->m_aClients[i].m_ChatIgnore, &Button)) - m_pClient->m_aClients[i].m_ChatIgnore ^= 1; + if(&g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[Index].m_Friend) + DoButton_Toggle(&s_aPlayerIDs[Index][0], 1, &Button, false); + else + if(DoButton_Toggle(&s_aPlayerIDs[Index][0], m_pClient->m_aClients[Index].m_ChatIgnore, &Button, true)) + m_pClient->m_aClients[Index].m_ChatIgnore ^= 1; // friend button ButtonBar.VSplitLeft(20.0f, &Button, &ButtonBar); ButtonBar.VSplitLeft(Width, &Button, &ButtonBar); Button.VSplitLeft((Width-Button.h)/4.0f, 0, &Button); Button.VSplitLeft(Button.h, &Button, 0); - if(DoButton_Toggle(&s_aPlayerIDs[i][1], m_pClient->m_aClients[i].m_Friend, &Button)) - if(m_pClient->m_aClients[i].m_Friend) - m_pClient->Friends()->RemoveFriend(m_pClient->m_aClients[i].m_aName, m_pClient->m_aClients[i].m_aClan); + if(DoButton_Toggle(&s_aPlayerIDs[Index][1], m_pClient->m_aClients[Index].m_Friend, &Button, true)) + { + if(m_pClient->m_aClients[Index].m_Friend) + m_pClient->Friends()->RemoveFriend(m_pClient->m_aClients[Index].m_aName, m_pClient->m_aClients[Index].m_aClan); else - m_pClient->Friends()->AddFriend(m_pClient->m_aClients[i].m_aName, m_pClient->m_aClients[i].m_aClan); + m_pClient->Friends()->AddFriend(m_pClient->m_aClients[Index].m_aName, m_pClient->m_aClients[Index].m_aClan); + } } /* @@ -383,13 +392,15 @@ void CMenus::RenderServerControlKick(CUIRect MainView, bool FilterSpectators) static int aPlayerIDs[MAX_CLIENTS]; for(int i = 0; i < MAX_CLIENTS; i++) { - if(!m_pClient->m_Snap.m_paPlayerInfos[i] || i == m_pClient->m_Snap.m_LocalClientID) + if(!m_pClient->m_Snap.m_paInfoByTeam[i]) continue; - if(FilterSpectators && m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + + int Index = m_pClient->m_Snap.m_paInfoByTeam[i]->m_ClientID; + if(Index == m_pClient->m_Snap.m_LocalClientID || (FilterSpectators && m_pClient->m_Snap.m_paInfoByTeam[i]->m_Team == TEAM_SPECTATORS)) continue; - if(m_CallvoteSelectedPlayer == i) + if(m_CallvoteSelectedPlayer == Index) Selected = NumOptions; - aPlayerIDs[NumOptions++] = i; + aPlayerIDs[NumOptions++] = Index; } static int s_VoteList = 0; diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 28f3559d..553195b1 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -96,6 +96,12 @@ void CMenus::RenderSettingsGeneral(CUIRect MainView) if(DoButton_CheckBox(&g_Config.m_ClShowhud, Localize("Show ingame HUD"), g_Config.m_ClShowhud, &Button)) g_Config.m_ClShowhud ^= 1; + // chat messages + Left.HSplitTop(5.0f, 0, &Left); + Left.HSplitTop(20.0f, &Button, &Left); + if(DoButton_CheckBox(&g_Config.m_ClShowChatFriends, Localize("Show only chat messages from friends"), g_Config.m_ClShowChatFriends, &Button)) + g_Config.m_ClShowChatFriends ^= 1; + // name plates Right.HSplitTop(20.0f, &Button, &Right); if(DoButton_CheckBox(&g_Config.m_ClNameplates, Localize("Show name plates"), g_Config.m_ClNameplates, &Button)) @@ -208,33 +214,30 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) for(int i = 0; i < m_pClient->m_pCountryFlags->Num(); ++i) { - const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_pCountryFlags->Get(i); - if(pEntry == 0) - continue; - + const CCountryFlags::CCountryFlag *pEntry = m_pClient->m_pCountryFlags->GetByIndex(i); if(pEntry->m_CountryCode == g_Config.m_PlayerCountry) OldSelected = i; CListboxItem Item = UiDoListboxNextItem(&pEntry->m_CountryCode, OldSelected == i); if(Item.m_Visible) { - Item.m_Rect.Margin(10.0f, &Item.m_Rect); + CUIRect Label; + Item.m_Rect.Margin(5.0f, &Item.m_Rect); + Item.m_Rect.HSplitBottom(10.0f, &Item.m_Rect, &Label); float OldWidth = Item.m_Rect.w; Item.m_Rect.w = Item.m_Rect.h*2; Item.m_Rect.x += (OldWidth-Item.m_Rect.w)/ 2.0f; - Graphics()->TextureSet(pEntry->m_Texture); - Graphics()->QuadsBegin(); - Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); - IGraphics::CQuadItem QuadItem(Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h); - Graphics()->QuadsDrawTL(&QuadItem, 1); - Graphics()->QuadsEnd(); + vec4 Color(1.0f, 1.0f, 1.0f, 1.0f); + m_pClient->m_pCountryFlags->Render(pEntry->m_CountryCode, &Color, Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h); + if(pEntry->m_Texture != -1) + UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, 0); } } const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); if(OldSelected != NewSelected) { - g_Config.m_PlayerCountry = m_pClient->m_pCountryFlags->Get(NewSelected)->m_CountryCode; + g_Config.m_PlayerCountry = m_pClient->m_pCountryFlags->GetByIndex(NewSelected)->m_CountryCode; m_NeedSendinfo = true; } } @@ -446,13 +449,14 @@ static CKeyInfo gs_aKeys[] = { "Remote console", "toggle_remote_console", 0 }, { "Screenshot", "screenshot", 0 }, { "Scoreboard", "+scoreboard", 0 }, + { "Respawn", "kill", 0 }, }; /* This is for scripts/update_localization.py to work, don't remove! Localize("Move left");Localize("Move right");Localize("Jump");Localize("Fire");Localize("Hook");Localize("Hammer"); Localize("Pistol");Localize("Shotgun");Localize("Grenade");Localize("Rifle");Localize("Next weapon");Localize("Prev. weapon"); Localize("Vote yes");Localize("Vote no");Localize("Chat");Localize("Team chat");Localize("Show chat");Localize("Emoticon"); - Localize("Spectator mode");Localize("Spectate next");Localize("Spectate previous");Localize("Console");Localize("Remote console");Localize("Screenshot");Localize("Scoreboard"); + Localize("Spectator mode");Localize("Spectate next");Localize("Spectate previous");Localize("Console");Localize("Remote console");Localize("Screenshot");Localize("Scoreboard");Localize("Respawn"); */ const int g_KeyCount = sizeof(gs_aKeys) / sizeof(CKeyInfo); @@ -504,10 +508,11 @@ void CMenus::RenderSettingsControls(CUIRect MainView) } CUIRect MovementSettings, WeaponSettings, VotingSettings, ChatSettings, MiscSettings, ResetButton; - MainView.VSplitLeft(MainView.w/2-5.0f, &MovementSettings, &VotingSettings); + MainView.VSplitMid(&MovementSettings, &VotingSettings); // movement settings { + MovementSettings.VMargin(5.0f, &MovementSettings); MovementSettings.HSplitTop(MainView.h/3+60.0f, &MovementSettings, &WeaponSettings); RenderTools()->DrawUIRect(&MovementSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); MovementSettings.Margin(10.0f, &MovementSettings); @@ -543,21 +548,22 @@ void CMenus::RenderSettingsControls(CUIRect MainView) WeaponSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &WeaponSettings); UiDoGetButtons(5, 12, WeaponSettings); } - + // defaults { ResetButton.HSplitTop(10.0f, 0, &ResetButton); RenderTools()->DrawUIRect(&ResetButton, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); ResetButton.HMargin(10.0f, &ResetButton); ResetButton.VMargin(30.0f, &ResetButton); + ResetButton.HSplitTop(20.0f, &ResetButton, 0); static int s_DefaultButton = 0; if(DoButton_Menu((void*)&s_DefaultButton, Localize("Reset to defaults"), 0, &ResetButton)) m_pClient->m_pBinds->SetDefaults(); } - + // voting settings { - VotingSettings.VSplitLeft(10.0f, 0, &VotingSettings); + VotingSettings.VMargin(5.0f, &VotingSettings); VotingSettings.HSplitTop(MainView.h/3-75.0f, &VotingSettings, &ChatSettings); RenderTools()->DrawUIRect(&VotingSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); VotingSettings.Margin(10.0f, &VotingSettings); @@ -590,7 +596,7 @@ void CMenus::RenderSettingsControls(CUIRect MainView) TextRender()->Text(0, MiscSettings.x, MiscSettings.y, 14.0f*UI()->Scale(), Localize("Miscellaneous"), -1); MiscSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &MiscSettings); - UiDoGetButtons(17, 25, MiscSettings); + UiDoGetButtons(17, 26, MiscSettings); } } @@ -627,7 +633,8 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView) // display mode list static float s_ScrollValue = 0; int OldSelected = -1; - str_format(aBuf, sizeof(aBuf), "%s: %dx%d %d bit", Localize("Current"), s_GfxScreenWidth, s_GfxScreenHeight, s_GfxColorDepth); + int G = gcd(s_GfxScreenWidth, s_GfxScreenHeight); + str_format(aBuf, sizeof(aBuf), "%s: %dx%d %d bit (%d:%d)", Localize("Current"), s_GfxScreenWidth, s_GfxScreenHeight, s_GfxColorDepth, s_GfxScreenWidth/G, s_GfxScreenHeight/G); UiDoListboxStart(&s_NumNodes , &ModeList, 24.0f, Localize("Display Modes"), aBuf, s_NumNodes, 1, OldSelected, s_ScrollValue); for(int i = 0; i < s_NumNodes; ++i) @@ -643,7 +650,8 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView) CListboxItem Item = UiDoListboxNextItem(&s_aModes[i], OldSelected == i); if(Item.m_Visible) { - str_format(aBuf, sizeof(aBuf), " %dx%d %d bit", s_aModes[i].m_Width, s_aModes[i].m_Height, Depth); + int G = gcd(s_aModes[i].m_Width, s_aModes[i].m_Height); + str_format(aBuf, sizeof(aBuf), " %dx%d %d bit (%d:%d)", s_aModes[i].m_Width, s_aModes[i].m_Height, Depth, s_aModes[i].m_Width/G, s_aModes[i].m_Height/G); UI()->DoLabelScaled(&Item.m_Rect, aBuf, 16.0f, -1); } } @@ -757,7 +765,10 @@ void CMenus::RenderSettingsSound(CUIRect MainView) { g_Config.m_SndEnable ^= 1; if(g_Config.m_SndEnable) - m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); + { + if(g_Config.m_SndMusic) + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f); + } else m_pClient->m_pSounds->Stop(SOUND_MENU); m_NeedRestartSound = g_Config.m_SndEnable && (!s_SndEnable || s_SndRate != g_Config.m_SndRate); @@ -771,7 +782,7 @@ void CMenus::RenderSettingsSound(CUIRect MainView) { g_Config.m_SndMusic ^= 1; if(g_Config.m_SndMusic) - m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f); else m_pClient->m_pSounds->Stop(SOUND_MENU); } @@ -810,10 +821,11 @@ class CLanguage { public: CLanguage() {} - CLanguage(const char *n, const char *f) : m_Name(n), m_FileName(f) {} + CLanguage(const char *n, const char *f, int Code) : m_Name(n), m_FileName(f), m_CountryCode(Code) {} string m_Name; string m_FileName; + int m_CountryCode; bool operator<(const CLanguage &Other) { return m_Name < Other.m_Name; } }; @@ -828,6 +840,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array< } char aOrigin[128]; + char aReplacement[128]; CLineReader LineReader; LineReader.Init(File); char *pLine; @@ -837,14 +850,32 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array< continue; str_copy(aOrigin, pLine, sizeof(aOrigin)); - char *pReplacement = LineReader.Get(); - if(!pReplacement) + + pLine = LineReader.Get(); + if(!pLine) + { + pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", "unexpected end of index file"); + break; + } + + if(pLine[0] != '=' || pLine[1] != '=' || pLine[2] != ' ') + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "malform replacement for index '%s'", aOrigin); + pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf); + (void)LineReader.Get(); + continue; + } + str_copy(aReplacement, pLine+3, sizeof(aReplacement)); + + pLine = LineReader.Get(); + if(!pLine) { pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", "unexpected end of index file"); break; } - if(pReplacement[0] != '=' || pReplacement[1] != '=' || pReplacement[2] != ' ') + if(pLine[0] != '=' || pLine[1] != '=' || pLine[2] != ' ') { char aBuf[128]; str_format(aBuf, sizeof(aBuf), "malform replacement for index '%s'", aOrigin); @@ -854,7 +885,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array< char aFileName[128]; str_format(aFileName, sizeof(aFileName), "languages/%s.txt", aOrigin); - pLanguages->add(CLanguage(pReplacement+3, aFileName)); + pLanguages->add(CLanguage(aReplacement, aFileName, str_toint(pLine+3))); } io_close(File); } @@ -868,7 +899,7 @@ void CMenus::RenderLanguageSelection(CUIRect MainView) if(s_Languages.size() == 0) { - s_Languages.add(CLanguage("English", "")); + s_Languages.add(CLanguage("English", "", 826)); LoadLanguageIndexfile(Storage(), Console(), &s_Languages); for(int i = 0; i < s_Languages.size(); i++) if(str_comp(s_Languages[i].m_FileName, g_Config.m_ClLanguagefile) == 0) @@ -887,7 +918,16 @@ void CMenus::RenderLanguageSelection(CUIRect MainView) CListboxItem Item = UiDoListboxNextItem(&r.front()); if(Item.m_Visible) - UI()->DoLabelScaled(&Item.m_Rect, r.front().m_Name, 16.0f, -1); + { + CUIRect Rect; + Item.m_Rect.VSplitLeft(Item.m_Rect.h*2.0f, &Rect, &Item.m_Rect); + Rect.VMargin(6.0f, &Rect); + Rect.HMargin(3.0f, &Rect); + vec4 Color(1.0f, 1.0f, 1.0f, 1.0f); + m_pClient->m_pCountryFlags->Render(r.front().m_CountryCode, &Color, Rect.x, Rect.y, Rect.w, Rect.h); + Item.m_Rect.HSplitTop(2.0f, 0, &Item.m_Rect); + UI()->DoLabelScaled(&Item.m_Rect, r.front().m_Name, 16.0f, -1); + } } s_SelectedLanguage = UiDoListboxEnd(&s_ScrollValue, 0); diff --git a/src/game/client/components/particles.cpp b/src/game/client/components/particles.cpp index c4583cb1..66a62f1c 100644 --- a/src/game/client/components/particles.cpp +++ b/src/game/client/components/particles.cpp @@ -43,6 +43,11 @@ void CParticles::Add(int Group, CParticle *pPart) if(pInfo->m_Paused) return; } + else + { + if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + return; + } if (m_FirstFree == -1) return; @@ -142,7 +147,10 @@ void CParticles::OnRender() Update((float)((t-LastTime)/(double)time_freq())*pInfo->m_Speed); } else - Update((float)((t-LastTime)/(double)time_freq())); + { + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + Update((float)((t-LastTime)/(double)time_freq())); + } LastTime = t; } diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 6811c2ad..2eddd8a9 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -95,30 +95,7 @@ void CPlayers::RenderHook( Player = *pPlayerChar; CNetObj_PlayerInfo pInfo = *pPlayerInfo; - CTeeRenderInfo RenderInfo = m_pClient->m_aClients[pInfo.m_ClientID].m_RenderInfo; - - // check for teamplay modes - bool IsTeamplay = false; - if(m_pClient->m_Snap.m_pGameInfoObj) - IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) != 0; - - // check for ninja - if (Player.m_Weapon == WEAPON_NINJA) - { - // change the skin for the player to the ninja - int Skin = m_pClient->m_pSkins->Find("x_ninja"); - if(Skin != -1) - { - if(IsTeamplay) - RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_ColorTexture; - else - { - RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_OrgTexture; - RenderInfo.m_ColorBody = vec4(1,1,1,1); - RenderInfo.m_ColorFeet = vec4(1,1,1,1); - } - } - } + CTeeRenderInfo RenderInfo = m_aRenderInfo[pInfo.m_ClientID]; float IntraTick = Client()->IntraGameTick(); @@ -218,31 +195,9 @@ void CPlayers::RenderPlayer( Player = *pPlayerChar; CNetObj_PlayerInfo pInfo = *pPlayerInfo; - CTeeRenderInfo RenderInfo = m_pClient->m_aClients[pInfo.m_ClientID].m_RenderInfo; + CTeeRenderInfo RenderInfo = m_aRenderInfo[pInfo.m_ClientID]; - // check for teamplay modes - bool IsTeamplay = false; bool NewTick = m_pClient->m_NewTick; - if(m_pClient->m_Snap.m_pGameInfoObj) - IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) != 0; - - // check for ninja - if (Player.m_Weapon == WEAPON_NINJA) - { - // change the skin for the player to the ninja - int Skin = m_pClient->m_pSkins->Find("x_ninja"); - if(Skin != -1) - { - if(IsTeamplay) - RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_ColorTexture; - else - { - RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_OrgTexture; - RenderInfo.m_ColorBody = vec4(1,1,1,1); - RenderInfo.m_ColorFeet = vec4(1,1,1,1); - } - } - } // set size RenderInfo.m_Size = 64.0f; @@ -330,14 +285,17 @@ void CPlayers::RenderPlayer( else if(!WantOtherDir) State.Add(&g_pData->m_aAnimations[ANIM_WALK], WalkTime, 1.0f); + static float s_LastGameTickTime = Client()->GameTickTime(); + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_LastGameTickTime = Client()->GameTickTime(); if (Player.m_Weapon == WEAPON_HAMMER) { - float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + s_LastGameTickTime; State.Add(&g_pData->m_aAnimations[ANIM_HAMMER_SWING], clamp(ct*5.0f,0.0f,1.0f), 1.0f); } if (Player.m_Weapon == WEAPON_NINJA) { - float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + s_LastGameTickTime; State.Add(&g_pData->m_aAnimations[ANIM_NINJA_SWING], clamp(ct*2.0f,0.0f,1.0f), 1.0f); } @@ -347,7 +305,7 @@ void CPlayers::RenderPlayer( static int64 SkidSoundTime = 0; if(time_get()-SkidSoundTime > time_freq()/10) { - m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, Position); + m_pClient->m_pSounds->PlayAt(CSounds::CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, Position); SkidSoundTime = time_get(); } @@ -409,15 +367,22 @@ void CPlayers::RenderPlayer( if ((Client()->GameTick()-Player.m_AttackTick) <= (SERVER_TICK_SPEED / 6) && g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles) { int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles; + static int s_LastIteX = IteX; if(Client()->State() == IClient::STATE_DEMOPLAYBACK) { - static int s_LastIteX = IteX; const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); if(pInfo->m_Paused) IteX = s_LastIteX; else s_LastIteX = IteX; } + else + { + if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + IteX = s_LastIteX; + else + s_LastIteX = IteX; + } if(g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX]) { vec2 Dir = vec2(pPlayerChar->m_X,pPlayerChar->m_Y) - vec2(pPrevChar->m_X, pPrevChar->m_Y); @@ -438,7 +403,11 @@ void CPlayers::RenderPlayer( { // TODO: should be an animation Recoil = 0; - float a = (Client()->GameTick()-Player.m_AttackTick+IntraTick)/5.0f; + static float s_LastIntraTick = IntraTick; + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_LastIntraTick = IntraTick; + + float a = (Client()->GameTick()-Player.m_AttackTick+s_LastIntraTick)/5.0f; if(a < 1) Recoil = sinf(a*pi); p = Position + Dir * g_pData->m_Weapons.m_aId[iw].m_Offsetx - Dir*Recoil*10.0f; @@ -460,6 +429,22 @@ void CPlayers::RenderPlayer( } int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles; + static int s_LastIteX = IteX; + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + { + const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); + if(pInfo->m_Paused) + IteX = s_LastIteX; + else + s_LastIteX = IteX; + } + else + { + if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + IteX = s_LastIteX; + else + s_LastIteX = IteX; + } if (Alpha > 0.0f && g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX]) { float OffsetY = -g_pData->m_Weapons.m_aId[iw].m_Muzzleoffsety; @@ -546,6 +531,31 @@ void CPlayers::RenderPlayer( void CPlayers::OnRender() { + // update RenderInfo for ninja + bool IsTeamplay = false; + if(m_pClient->m_Snap.m_pGameInfoObj) + IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) != 0; + for(int i = 0; i < MAX_CLIENTS; ++i) + { + m_aRenderInfo[i] = m_pClient->m_aClients[i].m_RenderInfo; + if(m_pClient->m_Snap.m_aCharacters[i].m_Cur.m_Weapon == WEAPON_NINJA) + { + // change the skin for the player to the ninja + int Skin = m_pClient->m_pSkins->Find("x_ninja"); + if(Skin != -1) + { + if(IsTeamplay) + m_aRenderInfo[i].m_Texture = m_pClient->m_pSkins->Get(Skin)->m_ColorTexture; + else + { + m_aRenderInfo[i].m_Texture = m_pClient->m_pSkins->Get(Skin)->m_OrgTexture; + m_aRenderInfo[i].m_ColorBody = vec4(1,1,1,1); + m_aRenderInfo[i].m_ColorFeet = vec4(1,1,1,1); + } + } + } + } + // render other players in two passes, first pass we render the other, second pass we render our self for(int p = 0; p < 4; p++) { diff --git a/src/game/client/components/players.h b/src/game/client/components/players.h index cedad0ff..9f23a0e6 100644 --- a/src/game/client/components/players.h +++ b/src/game/client/components/players.h @@ -6,6 +6,7 @@ class CPlayers : public CComponent { + CTeeRenderInfo m_aRenderInfo[MAX_CLIENTS]; void RenderHand(class CTeeRenderInfo *pInfo, vec2 CenterPos, vec2 Dir, float AngleOffset, vec2 PostRotOffset); void RenderPlayer( const CNetObj_Character *pPrevChar, diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp index 2cec5e62..935f7bad 100644 --- a/src/game/client/components/scoreboard.cpp +++ b/src/game/client/components/scoreboard.cpp @@ -273,12 +273,9 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aClan, -1); // country flag - Graphics()->TextureSet(m_pClient->m_pCountryFlags->Get(m_pClient->m_aClients[pInfo->m_ClientID].m_Country)->m_Texture); - Graphics()->QuadsBegin(); - Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f); - IGraphics::CQuadItem QuadItem(CountryOffset, y+(Spacing+TeeSizeMod*5.0f)/2.0f, CountryLength, LineHeight-Spacing-TeeSizeMod*5.0f); - Graphics()->QuadsDrawTL(&QuadItem, 1); - Graphics()->QuadsEnd(); + vec4 Color(1.0f, 1.0f, 1.0f, 0.5f); + m_pClient->m_pCountryFlags->Render(m_pClient->m_aClients[pInfo->m_ClientID].m_Country, &Color, + CountryOffset, y+(Spacing+TeeSizeMod*5.0f)/2.0f, CountryLength, LineHeight-Spacing-TeeSizeMod*5.0f); // ping str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Latency, 0, 1000)); diff --git a/src/game/client/components/skins.cpp b/src/game/client/components/skins.cpp index dd38e9ea..babf49bb 100644 --- a/src/game/client/components/skins.cpp +++ b/src/game/client/components/skins.cpp @@ -7,6 +7,7 @@ #include <engine/graphics.h> #include <engine/storage.h> +#include <engine/shared/config.h> #include "skins.h" @@ -103,8 +104,11 @@ int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser) // set skin data str_copy(Skin.m_aName, pName, min((int)sizeof(Skin.m_aName),l-3)); - str_format(aBuf, sizeof(aBuf), "load skin %s", Skin.m_aName); - pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); + if(g_Config.m_Debug) + { + str_format(aBuf, sizeof(aBuf), "load skin %s", Skin.m_aName); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); + } pSelf->m_aSkins.add(Skin); return 0; diff --git a/src/game/client/components/sounds.cpp b/src/game/client/components/sounds.cpp index ffafa128..be63415c 100644 --- a/src/game/client/components/sounds.cpp +++ b/src/game/client/components/sounds.cpp @@ -34,6 +34,30 @@ static int LoadSoundsThread(void *pUser) return 0; } + +int CSounds::GetSampleId(int SetId) +{ + if(!g_Config.m_SndEnable || !Sound()->IsSoundEnabled() || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) + return -1; + + CDataSoundset *pSet = &g_pData->m_aSounds[SetId]; + if(!pSet->m_NumSounds) + return -1; + + if(pSet->m_NumSounds == 1) + return pSet->m_aSounds[0].m_Id; + + // return random one + int Id; + do + { + Id = rand() % pSet->m_NumSounds; + } + while(Id == pSet->m_Last); + pSet->m_Last = Id; + return pSet->m_aSounds[Id].m_Id; +} + void CSounds::OnInit() { // setup sound channels @@ -65,8 +89,17 @@ void CSounds::OnInit() void CSounds::OnReset() { - Sound()->StopAll(); - ClearQueue(); + if(Client()->State() >= IClient::STATE_ONLINE) + { + Sound()->StopAll(); + ClearQueue(); + } +} + +void CSounds::OnStateChange(int NewState, int OldState) +{ + if(NewState == IClient::STATE_ONLINE || NewState == IClient::STATE_DEMOPLAYBACK) + OnReset(); } void CSounds::OnRender() @@ -89,7 +122,7 @@ void CSounds::OnRender() int64 Now = time_get(); if(m_QueueWaitTime <= Now) { - Play(m_aQueue[0].m_Channel, m_aQueue[0].m_SetId, 1.0f, vec2(0,0)); + Play(m_aQueue[0].m_Channel, m_aQueue[0].m_SetId, 1.0f); m_QueueWaitTime = Now+time_freq()*3/10; // wait 300ms before playing the next one if(--m_QueuePos > 0) mem_move(m_aQueue, m_aQueue+1, m_QueuePos*sizeof(QueueEntry)); @@ -123,47 +156,48 @@ void CSounds::PlayAndRecord(int Chn, int SetId, float Vol, vec2 Pos) Msg.m_SoundID = SetId; Client()->SendPackMsg(&Msg, MSGFLAG_NOSEND|MSGFLAG_RECORD); - Play(Chn, SetId, Vol, Pos); + Play(Chn, SetId, Vol); } -void CSounds::Play(int Chn, int SetId, float Vol, vec2 Pos) +void CSounds::Play(int Chn, int SetId, float Vol) { - if(!g_Config.m_SndEnable || (Chn == CHN_MUSIC && !g_Config.m_SndMusic) || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) + if(Chn == CHN_MUSIC && !g_Config.m_SndMusic) return; - SOUNDSET *pSet = &g_pData->m_aSounds[SetId]; - - if(!pSet->m_NumSounds) + int SampleId = GetSampleId(SetId); + if(SampleId == -1) return; - + int Flags = 0; if(Chn == CHN_MUSIC) Flags = ISound::FLAG_LOOP; - if(pSet->m_NumSounds == 1) - { - Sound()->PlayAt(Chn, pSet->m_aSounds[0].m_Id, Flags, Pos.x, Pos.y); + Sound()->Play(Chn, SampleId, Flags); +} + +void CSounds::PlayAt(int Chn, int SetId, float Vol, vec2 Pos) +{ + if(Chn == CHN_MUSIC && !g_Config.m_SndMusic) + return; + + int SampleId = GetSampleId(SetId); + if(SampleId == -1) return; - } - // play a random one - int Id; - do - { - Id = rand() % pSet->m_NumSounds; - } - while(Id == pSet->m_Last); - Sound()->PlayAt(Chn, pSet->m_aSounds[Id].m_Id, Flags, Pos.x, Pos.y); - pSet->m_Last = Id; + int Flags = 0; + if(Chn == CHN_MUSIC) + Flags = ISound::FLAG_LOOP; + + Sound()->PlayAt(Chn, SampleId, Flags, Pos.x, Pos.y); } void CSounds::Stop(int SetId) { if(m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) return; - - SOUNDSET *pSet = &g_pData->m_aSounds[SetId]; - + + CDataSoundset *pSet = &g_pData->m_aSounds[SetId]; + for(int i = 0; i < pSet->m_NumSounds; i++) Sound()->Stop(pSet->m_aSounds[i].m_Id); } diff --git a/src/game/client/components/sounds.h b/src/game/client/components/sounds.h index 2670f793..9d647398 100644 --- a/src/game/client/components/sounds.h +++ b/src/game/client/components/sounds.h @@ -19,6 +19,8 @@ class CSounds : public CComponent int64 m_QueueWaitTime; class CJob m_SoundJob; bool m_WaitForSoundJob; + + int GetSampleId(int SetId); public: // sound channels @@ -32,11 +34,13 @@ public: virtual void OnInit(); virtual void OnReset(); + virtual void OnStateChange(int NewState, int OldState); virtual void OnRender(); void ClearQueue(); void Enqueue(int Channel, int SetId); - void Play(int Channel, int SetId, float Vol, vec2 Pos); + void Play(int Channel, int SetId, float Vol); + void PlayAt(int Channel, int SetId, float Vol, vec2 Pos); void PlayAndRecord(int Channel, int SetId, float Vol, vec2 Pos); void Stop(int SetId); }; diff --git a/src/game/client/components/spectator.cpp b/src/game/client/components/spectator.cpp index 41c7b48f..c09b2ee2 100644 --- a/src/game/client/components/spectator.cpp +++ b/src/game/client/components/spectator.cpp @@ -64,8 +64,8 @@ void CSpectator::ConSpectateNext(IConsole::IResult *pResult, void *pUserData) if(!pSelf->m_pClient->m_Snap.m_paPlayerInfos[i] || pSelf->m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) continue; - NewSpectatorID = i; - GotNewSpectatorID = true; + NewSpectatorID = i; + GotNewSpectatorID = true; break; } } @@ -139,6 +139,7 @@ bool CSpectator::OnMouseMove(float x, float y) if(!m_Active) return false; + UI()->ConvertMouseMove(&x, &y); m_SelectorMouse += vec2(x,y); return true; } @@ -161,6 +162,13 @@ void CSpectator::OnRender() return; } + if(!m_pClient->m_Snap.m_SpecInfo.m_Active) + { + m_Active = false; + m_WasActive = false; + return; + } + m_WasActive = true; m_SelectedSpectatorID = NO_SELECTION; @@ -236,6 +244,23 @@ void CSpectator::OnRender() TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected?1.0f:0.5f); TextRender()->Text(0, Width/2.0f+x+50.0f, Height/2.0f+y+5.0f, FontSize, m_pClient->m_aClients[i].m_aName, 220.0f); + // flag + if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS && + m_pClient->m_Snap.m_pGameDataObj && (m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierRed == m_pClient->m_Snap.m_paPlayerInfos[i]->m_ClientID || + m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierBlue == m_pClient->m_Snap.m_paPlayerInfos[i]->m_ClientID)) + { + Graphics()->BlendNormal(); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); + Graphics()->QuadsBegin(); + + RenderTools()->SelectSprite(m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team==TEAM_RED ? SPRITE_FLAG_BLUE : SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); + + float Size = LineHeight; + IGraphics::CQuadItem QuadItem(Width/2.0f+x-LineHeight/5.0f, Height/2.0f+y-LineHeight/3.0f, Size/2.0f, Size); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + CTeeRenderInfo TeeInfo = m_pClient->m_aClients[i].m_RenderInfo; RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), vec2(Width/2.0f+x+20.0f, Height/2.0f+y+20.0f)); |