diff options
Diffstat (limited to 'src/game')
61 files changed, 2453 insertions, 604 deletions
diff --git a/src/game/client/animstate.cpp b/src/game/client/animstate.cpp index 1289126b..ce595359 100644 --- a/src/game/client/animstate.cpp +++ b/src/game/client/animstate.cpp @@ -7,7 +7,7 @@ #include "animstate.h" -static void AnimSeqEval(ANIM_SEQUENCE *pSeq, float Time, ANIM_KEYFRAME *pFrame) +static void AnimSeqEval(CAnimSequence *pSeq, float Time, CAnimKeyframe *pFrame) { if(pSeq->m_NumFrames == 0) { @@ -23,8 +23,8 @@ static void AnimSeqEval(ANIM_SEQUENCE *pSeq, float Time, ANIM_KEYFRAME *pFrame) else { //time = max(0.0f, min(1.0f, time / duration)); // TODO: use clamp - ANIM_KEYFRAME *pFrame1 = 0; - ANIM_KEYFRAME *pFrame2 = 0; + CAnimKeyframe *pFrame1 = 0; + CAnimKeyframe *pFrame2 = 0; float Blend = 0.0f; // TODO: make this smarter.. binary search @@ -49,7 +49,7 @@ static void AnimSeqEval(ANIM_SEQUENCE *pSeq, float Time, ANIM_KEYFRAME *pFrame) } } -static void AnimAddKeyframe(ANIM_KEYFRAME *pSeq, ANIM_KEYFRAME *pAdded, float Amount) +static void AnimAddKeyframe(CAnimKeyframe *pSeq, CAnimKeyframe *pAdded, float Amount) { pSeq->m_X += pAdded->m_X*Amount; pSeq->m_Y += pAdded->m_Y*Amount; @@ -65,7 +65,7 @@ static void AnimAdd(CAnimState *pState, CAnimState *pAdded, float Amount) } -void CAnimState::Set(ANIMATION *pAnim, float Time) +void CAnimState::Set(CAnimation *pAnim, float Time) { AnimSeqEval(&pAnim->m_Body, Time, &m_Body); AnimSeqEval(&pAnim->m_BackFoot, Time, &m_BackFoot); @@ -73,7 +73,7 @@ void CAnimState::Set(ANIMATION *pAnim, float Time) AnimSeqEval(&pAnim->m_Attach, Time, &m_Attach); } -void CAnimState::Add(ANIMATION *pAnim, float Time, float Amount) +void CAnimState::Add(CAnimation *pAnim, float Time, float Amount) { CAnimState Add; Add.Set(pAnim, Time); diff --git a/src/game/client/animstate.h b/src/game/client/animstate.h index 63b6a80a..fbc0a2f8 100644 --- a/src/game/client/animstate.h +++ b/src/game/client/animstate.h @@ -5,18 +5,18 @@ class CAnimState { - ANIM_KEYFRAME m_Body; - ANIM_KEYFRAME m_BackFoot; - ANIM_KEYFRAME m_FrontFoot; - ANIM_KEYFRAME m_Attach; + CAnimKeyframe m_Body; + CAnimKeyframe m_BackFoot; + CAnimKeyframe m_FrontFoot; + CAnimKeyframe m_Attach; public: - ANIM_KEYFRAME *GetBody() { return &m_Body; }; - ANIM_KEYFRAME *GetBackFoot() { return &m_BackFoot; }; - ANIM_KEYFRAME *GetFrontFoot() { return &m_FrontFoot; }; - ANIM_KEYFRAME *GetAttach() { return &m_Attach; }; - void Set(ANIMATION *pAnim, float Time); - void Add(ANIMATION *pAdded, float Time, float Amount); + CAnimKeyframe *GetBody() { return &m_Body; }; + CAnimKeyframe *GetBackFoot() { return &m_BackFoot; }; + CAnimKeyframe *GetFrontFoot() { return &m_FrontFoot; }; + CAnimKeyframe *GetAttach() { return &m_Attach; }; + void Set(CAnimation *pAnim, float Time); + void Add(CAnimation *pAdded, float Time, float Amount); static CAnimState *GetIdle(); }; diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index ee294dc4..aba38bf6 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -132,17 +132,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 +162,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); @@ -184,11 +210,7 @@ bool CChat::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); } else if (Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_DOWN) { @@ -196,11 +218,7 @@ bool CChat::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(); } @@ -246,6 +264,7 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine) char *p = const_cast<char*>(pLine); while(*p) { + Highlighted = false; pLine = p; // find line seperator and strip multiline while(*p) @@ -264,9 +283,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,7 +318,7 @@ 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 @@ -354,7 +380,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); } diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index 33c6db43..f2e9e65d 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); } } @@ -656,6 +662,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 +689,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 +697,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/countryflags.cpp b/src/game/client/components/countryflags.cpp index 2429ad3f..ef350cd7 100644 --- a/src/game/client/components/countryflags.cpp +++ b/src/game/client/components/countryflags.cpp @@ -45,6 +45,15 @@ 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); @@ -59,7 +68,8 @@ void CCountryFlags::LoadCountryflagsIndexfile() // add entry CCountryFlag CountryFlag; - CountryFlag.m_CountryCode = str_toint(pReplacement+3); + CountryFlag.m_CountryCode = CountryCode; + str_copy(CountryFlag.m_aCountryCodeString, aOrigin, sizeof(CountryFlag.m_aCountryCodeString)); 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); @@ -67,6 +77,10 @@ void CCountryFlags::LoadCountryflagsIndexfile() m_aCountryFlags.add(CountryFlag); } io_close(File); + + 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+1; } void CCountryFlags::OnInit() @@ -89,17 +103,12 @@ 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 &m_aCountryFlags[max(0, Index%m_aCountryFlags.size())]; + return GetByIndex(m_CodeIndexLUT[max(0, (CountryCode-CODE_LB)%CODE_RANGE)]-1); } -int CCountryFlags::Find(int CountryCode) const +const CCountryFlags::CCountryFlag *CCountryFlags::GetByIndex(int Index) const { - for(int i = 0; i < m_aCountryFlags.size(); ++i) - { - if(m_aCountryFlags[i].m_CountryCode == CountryCode) - return i; - } - return -1; + return &m_aCountryFlags[max(0, Index%m_aCountryFlags.size())]; } diff --git a/src/game/client/components/countryflags.h b/src/game/client/components/countryflags.h index cd629094..ad24a762 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; + //int Find(int CountryCode) const; 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/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..8554b7ba 100644 --- a/src/game/client/components/effects.cpp +++ b/src/game/client/components/effects.cpp @@ -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); } diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp index 741a604f..b2f48b80 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,6 +102,13 @@ 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) diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 188691a1..11343912 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); @@ -108,7 +108,9 @@ void CHud::RenderScoreHud() 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(); diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 848651ca..096f9cc5 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -63,21 +63,25 @@ void CMapLayers::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void CMapItemEnvelope *pItem = (CMapItemEnvelope *)pThis->m_pLayers->Map()->GetItem(Start+Env, 0, 0); + static float Time = 0; if(pThis->Client()->State() == IClient::STATE_DEMOPLAYBACK) { const IDemoPlayer::CInfo *pInfo = pThis->DemoPlayer()->BaseInfo(); - static float Time = 0; - static float LastLocalTime = pThis->Client()->LocalTime(); + static int LastLocalTick = pInfo->m_CurrentTick; if(!pInfo->m_Paused) - Time += (pThis->Client()->LocalTime()-LastLocalTime)*pInfo->m_Speed; + Time += (pInfo->m_CurrentTick-LastLocalTick) / (float)pThis->Client()->GameTickSpeed() * pInfo->m_Speed; pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, Time+TimeOffset, pChannels); - LastLocalTime = pThis->Client()->LocalTime(); + LastLocalTick = pInfo->m_CurrentTick; } 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) + Time = (pThis->Client()->GameTick()-pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)pThis->Client()->GameTickSpeed(); + pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, Time+TimeOffset, pChannels); + } } void CMapLayers::OnRender() @@ -186,9 +190,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/menus.cpp b/src/game/client/components/menus.cpp index 53192714..8f330f78 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" @@ -209,7 +210,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 +252,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); } @@ -645,8 +647,6 @@ void CMenus::RenderLoading() RenderBackground(); - float tw; - float w = 700; float h = 200; float x = Screen.w/2-w/2; @@ -663,7 +663,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 +736,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 +961,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 +1059,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 +1098,69 @@ 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; + 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(); + 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 +1272,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 +1351,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 +1402,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) + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); 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..585fb91f 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> @@ -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..8501c67d 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); @@ -481,6 +516,10 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); 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); @@ -515,21 +554,50 @@ 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; + Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(g_Config.m_BrFilterCountryIndex)->m_Texture); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, g_Config.m_BrFilterCountry?1.0f: 0.5f); + IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + + 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 +685,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,7 +766,7 @@ 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()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(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); @@ -695,45 +776,102 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) } } +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 +906,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 +1049,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..f3a75f0c 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -200,8 +200,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 +291,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 +373,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 +555,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 +578,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..1afef922 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -188,10 +188,12 @@ void CMenus::RenderPlayers(CUIRect MainView) 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); else m_pClient->Friends()->AddFriend(m_pClient->m_aClients[i].m_aName, m_pClient->m_aClients[i].m_aClan); + } } /* diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 28f3559d..51fdbd29 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -208,17 +208,16 @@ 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; @@ -228,13 +227,14 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) 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(); + 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; } } @@ -627,7 +627,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 +644,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 +759,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, vec2(0, 0)); + } else m_pClient->m_pSounds->Stop(SOUND_MENU); m_NeedRestartSound = g_Config.m_SndEnable && (!s_SndEnable || s_SndRate != g_Config.m_SndRate); @@ -810,10 +815,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 +834,7 @@ void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, sorted_array< } char aOrigin[128]; + char aReplacement[128]; CLineReader LineReader; LineReader.Init(File); char *pLine; @@ -837,14 +844,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(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); + 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(pLine[0] != '=' || pLine[1] != '=' || pLine[2] != ' ') { char aBuf[128]; str_format(aBuf, sizeof(aBuf), "malform replacement for index '%s'", aOrigin); @@ -854,7 +879,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 +893,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 +912,19 @@ 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); + Graphics()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(r.front().m_CountryCode)->m_Texture); + Graphics()->QuadsBegin(); + IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + 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/scoreboard.cpp b/src/game/client/components/scoreboard.cpp index 2cec5e62..ae11c7ea 100644 --- a/src/game/client/components/scoreboard.cpp +++ b/src/game/client/components/scoreboard.cpp @@ -273,7 +273,7 @@ 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()->TextureSet(m_pClient->m_pCountryFlags->GetByCountryCode(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); diff --git a/src/game/client/components/sounds.cpp b/src/game/client/components/sounds.cpp index ffafa128..c4ade00e 100644 --- a/src/game/client/components/sounds.cpp +++ b/src/game/client/components/sounds.cpp @@ -65,8 +65,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() @@ -128,10 +137,10 @@ void CSounds::PlayAndRecord(int Chn, int SetId, float Vol, vec2 Pos) void CSounds::Play(int Chn, int SetId, float Vol, vec2 Pos) { - if(!g_Config.m_SndEnable || (Chn == CHN_MUSIC && !g_Config.m_SndMusic) || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) + if(!g_Config.m_SndEnable || !Sound()->IsSoundEnabled() || (Chn == CHN_MUSIC && !g_Config.m_SndMusic) || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) return; - SOUNDSET *pSet = &g_pData->m_aSounds[SetId]; + CDataSoundset *pSet = &g_pData->m_aSounds[SetId]; if(!pSet->m_NumSounds) return; @@ -162,7 +171,7 @@ 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..ab9cc8e6 100644 --- a/src/game/client/components/sounds.h +++ b/src/game/client/components/sounds.h @@ -32,6 +32,7 @@ public: virtual void OnInit(); virtual void OnReset(); + virtual void OnStateChange(int NewState, int OldState); virtual void OnRender(); void ClearQueue(); 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)); diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 511cf894..7b6b1192 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -90,17 +90,6 @@ void CGameClient::CStack::Add(class CComponent *pComponent) { m_paComponents[m_N const char *CGameClient::Version() { return GAME_VERSION; } const char *CGameClient::NetVersion() { return GAME_NETVERSION; } const char *CGameClient::GetItemName(int Type) { return m_NetObjHandler.GetObjName(Type); } -int CGameClient::GetCountryIndex(int Code) -{ - int Index = g_GameClient.m_pCountryFlags->Find(Code); - if(Index < 0) - { - Index = g_GameClient.m_pCountryFlags->Find(-1); - if(Index < 0) - Index = 0; - } - return Index; -} void CGameClient::OnConsoleInit() { @@ -196,7 +185,7 @@ void CGameClient::OnConsoleInit() Console()->Register("restart", "?i", CFGFLAG_SERVER, 0, 0, "Restart in x seconds"); Console()->Register("broadcast", "r", CFGFLAG_SERVER, 0, 0, "Broadcast message"); Console()->Register("say", "r", CFGFLAG_SERVER, 0, 0, "Say in chat"); - Console()->Register("set_team", "ii", CFGFLAG_SERVER, 0, 0, "Set team of player to team"); + Console()->Register("set_team", "ii?i", CFGFLAG_SERVER, 0, 0, "Set team of player to team"); Console()->Register("set_team_all", "i", CFGFLAG_SERVER, 0, 0, "Set team of all players to team"); Console()->Register("add_vote", "sr", CFGFLAG_SERVER, 0, 0, "Add a voting option"); Console()->Register("remove_vote", "s", CFGFLAG_SERVER, 0, 0, "remove a voting option"); @@ -352,6 +341,9 @@ void CGameClient::OnReset() m_All.m_paComponents[i]->OnReset(); m_DemoSpecID = SPEC_FREEVIEW; + m_FlagDropTick[TEAM_RED] = 0; + m_FlagDropTick[TEAM_BLUE] = 0; + m_Tuning = CTuningParams(); } @@ -602,32 +594,32 @@ void CGameClient::ProcessEvents() if(Item.m_Type == NETEVENTTYPE_DAMAGEIND) { - NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)pData; + CNetEvent_DamageInd *ev = (CNetEvent_DamageInd *)pData; g_GameClient.m_pEffects->DamageIndicator(vec2(ev->m_X, ev->m_Y), GetDirection(ev->m_Angle)); } else if(Item.m_Type == NETEVENTTYPE_EXPLOSION) { - NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)pData; + CNetEvent_Explosion *ev = (CNetEvent_Explosion *)pData; g_GameClient.m_pEffects->Explosion(vec2(ev->m_X, ev->m_Y)); } else if(Item.m_Type == NETEVENTTYPE_HAMMERHIT) { - NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)pData; + CNetEvent_HammerHit *ev = (CNetEvent_HammerHit *)pData; g_GameClient.m_pEffects->HammerHit(vec2(ev->m_X, ev->m_Y)); } else if(Item.m_Type == NETEVENTTYPE_SPAWN) { - NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)pData; + CNetEvent_Spawn *ev = (CNetEvent_Spawn *)pData; g_GameClient.m_pEffects->PlayerSpawn(vec2(ev->m_X, ev->m_Y)); } else if(Item.m_Type == NETEVENTTYPE_DEATH) { - NETEVENT_DEATH *ev = (NETEVENT_DEATH *)pData; + CNetEvent_Death *ev = (CNetEvent_Death *)pData; g_GameClient.m_pEffects->PlayerDeath(vec2(ev->m_X, ev->m_Y), ev->m_ClientID); } else if(Item.m_Type == NETEVENTTYPE_SOUNDWORLD) { - NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)pData; + CNetEvent_SoundWorld *ev = (CNetEvent_SoundWorld *)pData; g_GameClient.m_pSounds->Play(CSounds::CHN_WORLD, ev->m_SoundID, 1.0f, vec2(ev->m_X, ev->m_Y)); } } @@ -696,7 +688,7 @@ void CGameClient::OnNewSnapshot() int ClientID = Item.m_ID; IntsToStr(&pInfo->m_Name0, 4, m_aClients[ClientID].m_aName); IntsToStr(&pInfo->m_Clan0, 3, m_aClients[ClientID].m_aClan); - m_aClients[ClientID].m_Country = GetCountryIndex(pInfo->m_Country); + m_aClients[ClientID].m_Country = pInfo->m_Country; IntsToStr(&pInfo->m_Skin0, 6, m_aClients[ClientID].m_aSkinName); m_aClients[ClientID].m_UseCustomColor = pInfo->m_UseCustomColor; @@ -794,6 +786,20 @@ void CGameClient::OnNewSnapshot() { m_Snap.m_pGameDataObj = (const CNetObj_GameData *)pData; m_Snap.m_GameDataSnapID = Item.m_ID; + if(m_Snap.m_pGameDataObj->m_FlagCarrierRed == FLAG_TAKEN) + { + if(m_FlagDropTick[TEAM_RED] == 0) + m_FlagDropTick[TEAM_RED] = Client()->GameTick(); + } + else if(m_FlagDropTick[TEAM_RED] != 0) + m_FlagDropTick[TEAM_RED] = 0; + if(m_Snap.m_pGameDataObj->m_FlagCarrierBlue == FLAG_TAKEN) + { + if(m_FlagDropTick[TEAM_BLUE] == 0) + m_FlagDropTick[TEAM_BLUE] = Client()->GameTick(); + } + else if(m_FlagDropTick[TEAM_BLUE] != 0) + m_FlagDropTick[TEAM_BLUE] = 0; } else if(Item.m_Type == NETOBJTYPE_FLAG) m_Snap.m_paFlags[Item.m_ID%2] = (const CNetObj_Flag *)pData; diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index a89f4e86..4783f8b4 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -89,6 +89,7 @@ public: bool m_SuppressEvents; bool m_NewTick; bool m_NewPredictedTick; + int m_FlagDropTick[2]; // TODO: move this CTuningParams m_Tuning; @@ -211,7 +212,6 @@ public: virtual void OnStartGame(); virtual const char *GetItemName(int Type); - virtual int GetCountryIndex(int Code); virtual const char *Version(); virtual const char *NetVersion(); diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp index 29b891c2..2de85d66 100644 --- a/src/game/client/lineinput.cpp +++ b/src/game/client/lineinput.cpp @@ -42,7 +42,7 @@ bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *p if (Len < StrMaxSize - CharSize && CursorPos < StrMaxSize - CharSize) { - mem_move(pStr + CursorPos + CharSize, pStr + CursorPos, Len - CursorPos + CharSize); + mem_move(pStr + CursorPos + CharSize, pStr + CursorPos, Len-CursorPos+1); // +1 == null term for(int i = 0; i < CharSize; i++) pStr[CursorPos+i] = Tmp[i]; CursorPos += CharSize; diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp index 5dbc3842..93909d0c 100644 --- a/src/game/client/render.cpp +++ b/src/game/client/render.cpp @@ -37,7 +37,7 @@ static void layershot_end() config.cl_layershot++; }*/ -void CRenderTools::SelectSprite(SPRITE *pSpr, int Flags, int sx, int sy) +void CRenderTools::SelectSprite(CDataSprite *pSpr, int Flags, int sx, int sy) { int x = pSpr->m_X+sx; int y = pSpr->m_Y+sy; @@ -232,7 +232,7 @@ void CRenderTools::RenderTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote } // draw feet - ANIM_KEYFRAME *pFoot = f ? pAnim->GetFrontFoot() : pAnim->GetBackFoot(); + CAnimKeyframe *pFoot = f ? pAnim->GetFrontFoot() : pAnim->GetBackFoot(); float w = BaseSize; float h = BaseSize/2; diff --git a/src/game/client/render.h b/src/game/client/render.h index 10705e56..d3d7fc40 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -39,6 +39,7 @@ enum TILERENDERFLAG_EXTEND=4, }; +typedef void (*ENVELOPE_EVAL)(float TimeOffset, int Env, float *pChannels, void *pUser); class CRenderTools { @@ -51,7 +52,7 @@ public: //typedef struct SPRITE; - void SelectSprite(struct SPRITE *pSprite, int Flags=0, int sx=0, int sy=0); + void SelectSprite(struct CDataSprite *pSprite, int Flags=0, int sx=0, int sy=0); void SelectSprite(int id, int Flags=0, int sx=0, int sy=0); void DrawSprite(float x, float y, float size); @@ -70,8 +71,8 @@ public: // map render methods (gc_render_map.cpp) static void RenderEvalEnvelope(CEnvPoint *pPoints, int NumPoints, int Channels, float Time, float *pResult); - void RenderQuads(CQuad *pQuads, int NumQuads, int Flags, void (*pfnEval)(float TimeOffset, int Env, float *pChannels, void *pUser), void *pUser); - void RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int Flags); + void RenderQuads(CQuad *pQuads, int NumQuads, int Flags, ENVELOPE_EVAL pfnEval, void *pUser); + void RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset); // helpers void MapscreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, diff --git a/src/game/client/render_map.cpp b/src/game/client/render_map.cpp index 33cc1c7d..23fa42e0 100644 --- a/src/game/client/render_map.cpp +++ b/src/game/client/render_map.cpp @@ -78,7 +78,7 @@ static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y); } -void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, void (*pfnEval)(float TimeOffset, int Env, float *pChannels, void *pUser), void *pUser) +void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser) { Graphics()->QuadsBegin(); float Conv = 1/255.0f; @@ -162,7 +162,8 @@ void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, voi Graphics()->QuadsEnd(); } -void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags) +void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags, + ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset) { //Graphics()->TextureSet(img_get(tmap->image)); float ScreenX0, ScreenY0, ScreenX1, ScreenY1; @@ -174,8 +175,19 @@ void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 float FinalTileSize = Scale/(ScreenX1-ScreenX0) * Graphics()->ScreenWidth(); float FinalTilesetScale = FinalTileSize/TilePixelSize; + float r=1, g=1, b=1, a=1; + if(ColorEnv >= 0) + { + float aChannels[4]; + pfnEval(ColorEnvOffset/1000.0f, ColorEnv, aChannels, pUser); + r = aChannels[0]; + g = aChannels[1]; + b = aChannels[2]; + a = aChannels[3]; + } + Graphics()->QuadsBegin(); - Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a); + Graphics()->SetColor(Color.r*r, Color.g*g, Color.b*b, Color.a*a); int StartY = (int)(ScreenY0/Scale)-1; int StartX = (int)(ScreenX0/Scale)-1; diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 2161bc77..00a30c15 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -53,6 +53,13 @@ int CUI::MouseInside(const CUIRect *r) return 0; } +void CUI::ConvertMouseMove(float *x, float *y) +{ + float Fac = (float)(g_Config.m_UiMousesens)/g_Config.m_InpMousesens; + *x = *x*Fac; + *y = *y*Fac; +} + CUIRect *CUI::Screen() { float Aspect = Graphics()->ScreenAspect(); diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 017abf7c..7cd78d6f 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -79,6 +79,7 @@ public: const void *LastActiveItem() const { return m_pLastActiveItem; } int MouseInside(const CUIRect *pRect); + void ConvertMouseMove(float *x, float *y); CUIRect *Screen(); void ClipEnable(const CUIRect *pRect); diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp new file mode 100644 index 00000000..528e459b --- /dev/null +++ b/src/game/editor/auto_map.cpp @@ -0,0 +1,202 @@ +#include <stdio.h> // sscanf + +#include <engine/console.h> +#include <engine/storage.h> +#include <engine/shared/linereader.h> + +#include "auto_map.h" +#include "editor.h" + +CAutoMapper::CAutoMapper(CEditor *pEditor) +{ + m_pEditor = pEditor; + m_FileLoaded = false; +} + +void CAutoMapper::Load(const char* pTileName) +{ + char aPath[256]; + str_format(aPath, sizeof(aPath), "editor/%s.rules", pTileName); + IOHANDLE RulesFile = m_pEditor->Storage()->OpenFile(aPath, IOFLAG_READ, IStorage::TYPE_ALL); + if(!RulesFile) + return; + + CLineReader LineReader; + LineReader.Init(RulesFile); + + CConfiguration *pCurrentConf = 0; + CIndexRule *pCurrentIndex = 0; + + char aBuf[256]; + + // read each line + while(char *pLine = LineReader.Get()) + { + // skip blank/empty lines as well as comments + if(str_length(pLine) > 0 && pLine[0] != '#' && pLine[0] != '\n' && pLine[0] != '\r' + && pLine[0] != '\t' && pLine[0] != '\v' && pLine[0] != ' ') + { + if(pLine[0]== '[') + { + // new configuration, get the name + pLine++; + + CConfiguration NewConf; + int ID = m_lConfigs.add(NewConf); + pCurrentConf = &m_lConfigs[ID]; + + str_copy(pCurrentConf->m_aName, pLine, str_length(pLine)); + } + else + { + if(!str_comp_num(pLine, "Index", 5)) + { + // new index + int ID = 0; + char aFlip[128] = ""; + + sscanf(pLine, "Index %d %127s", &ID, aFlip); + + CIndexRule NewIndexRule; + NewIndexRule.m_ID = ID; + NewIndexRule.m_Flag = 0; + NewIndexRule.m_RandomValue = 0; + NewIndexRule.m_BaseTile = false; + + if(str_length(aFlip) > 0) + { + if(!str_comp(aFlip, "XFLIP")) + NewIndexRule.m_Flag = TILEFLAG_VFLIP; + else if(!str_comp(aFlip, "YFLIP")) + NewIndexRule.m_Flag = TILEFLAG_HFLIP; + } + + // add the index rule object and make it current + int ArrayID = pCurrentConf->m_aIndexRules.add(NewIndexRule); + pCurrentIndex = &pCurrentConf->m_aIndexRules[ArrayID]; + } + else if(!str_comp_num(pLine, "BaseTile", 8) && pCurrentIndex) + { + pCurrentIndex->m_BaseTile = true; + } + else if(!str_comp_num(pLine, "Pos", 3) && pCurrentIndex) + { + int x = 0, y = 0; + char aValue[128]; + int Value = CPosRule::EMPTY; + bool IndexValue = false; + + sscanf(pLine, "Pos %d %d %127s", &x, &y, aValue); + + if(!str_comp(aValue, "FULL")) + Value = CPosRule::FULL; + else if(!str_comp_num(aValue, "INDEX", 5)) + { + sscanf(pLine, "Pos %*d %*d INDEX %d", &Value); + IndexValue = true; + } + + CPosRule NewPosRule = {x, y, Value, IndexValue}; + pCurrentIndex->m_aRules.add(NewPosRule); + } + else if(!str_comp_num(pLine, "Random", 6) && pCurrentIndex) + { + sscanf(pLine, "Random %d", &pCurrentIndex->m_RandomValue); + } + } + } + } + + io_close(RulesFile); + + str_format(aBuf, sizeof(aBuf),"loaded %s", aPath); + m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor", aBuf); + + m_FileLoaded = true; +} + +const char* CAutoMapper::GetConfigName(int Index) +{ + if(Index < 0 || Index >= m_lConfigs.size()) + return ""; + + return m_lConfigs[Index].m_aName; +} + +void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID) +{ + if(!m_FileLoaded || pLayer->m_Readonly || ConfigID < 0 || ConfigID >= m_lConfigs.size()) + return; + + CConfiguration *pConf = &m_lConfigs[ConfigID]; + + if(!pConf->m_aIndexRules.size()) + return; + + int BaseTile = 1; + + // find base tile if there is one + for(int i = 0; i < pConf->m_aIndexRules.size(); ++i) + { + if(pConf->m_aIndexRules[i].m_BaseTile) + { + BaseTile = pConf->m_aIndexRules[i].m_ID; + break; + } + } + + // auto map ! + int MaxIndex = pLayer->m_Width*pLayer->m_Height; + for(int y = 0; y < pLayer->m_Height; y++) + for(int x = 0; x < pLayer->m_Width; x++) + { + CTile *pTile = &(pLayer->m_pTiles[y*pLayer->m_Width+x]); + if(pTile->m_Index == 0) + continue; + + pTile->m_Index = BaseTile; + m_pEditor->m_Map.m_Modified = true; + + if(y == 0 || y == pLayer->m_Height-1 || x == 0 || x == pLayer->m_Width-1) + continue; + + for(int i = 0; i < pConf->m_aIndexRules.size(); ++i) + { + if(pConf->m_aIndexRules[i].m_BaseTile) + continue; + + bool RespectRules = true; + for(int j = 0; j < pConf->m_aIndexRules[i].m_aRules.size() && RespectRules; ++j) + { + CPosRule *pRule = &pConf->m_aIndexRules[i].m_aRules[j]; + int CheckIndex = (y+pRule->m_Y)*pLayer->m_Width+(x+pRule->m_X); + + if(CheckIndex < 0 || CheckIndex >= MaxIndex) + RespectRules = false; + else + { + if(pRule->m_IndexValue) + { + if(pLayer->m_pTiles[CheckIndex].m_Index != pRule->m_Value) + RespectRules = false; + } + else + { + if(pLayer->m_pTiles[CheckIndex].m_Index > 0 && pRule->m_Value == CPosRule::EMPTY) + RespectRules = false; + + if(pLayer->m_pTiles[CheckIndex].m_Index == 0 && pRule->m_Value == CPosRule::FULL) + RespectRules = false; + } + } + } + + if(RespectRules && + (pConf->m_aIndexRules[i].m_RandomValue <= 1 || (int)((float)rand() / ((float)RAND_MAX + 1) * pConf->m_aIndexRules[i].m_RandomValue) == 1)) + { + pTile->m_Index = pConf->m_aIndexRules[i].m_ID; + pTile->m_Flags = pConf->m_aIndexRules[i].m_Flag; + } + } + } +} diff --git a/src/game/editor/auto_map.h b/src/game/editor/auto_map.h new file mode 100644 index 00000000..ee570378 --- /dev/null +++ b/src/game/editor/auto_map.h @@ -0,0 +1,54 @@ +#ifndef GAME_EDITOR_AUTO_MAP_H +#define GAME_EDITOR_AUTO_MAP_H + +#include <base/tl/array.h> + +class CAutoMapper +{ + struct CPosRule + { + int m_X; + int m_Y; + int m_Value; + bool m_IndexValue; + + enum + { + EMPTY=0, + FULL + }; + }; + + struct CIndexRule + { + int m_ID; + array<CPosRule> m_aRules; + int m_Flag; + int m_RandomValue; + bool m_BaseTile; + }; + + struct CConfiguration + { + array<CIndexRule> m_aIndexRules; + char m_aName[128]; + }; + +public: + CAutoMapper(class CEditor *pEditor); + + void Load(const char* pTileName); + void Proceed(class CLayerTiles *pLayer, int ConfigID); + + int ConfigNamesNum() { return m_lConfigs.size(); } + const char* GetConfigName(int Index); + + const bool IsLoaded() { return m_FileLoaded; } +private: + array<CConfiguration> m_lConfigs; + class CEditor *m_pEditor; + bool m_FileLoaded; +}; + + +#endif diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/editor.cpp index 1163dc4a..500e600b 100644 --- a/src/game/editor/ed_editor.cpp +++ b/src/game/editor/editor.cpp @@ -8,20 +8,20 @@ #include <engine/client.h> #include <engine/console.h> #include <engine/graphics.h> -#include <engine/textrender.h> #include <engine/input.h> #include <engine/keys.h> #include <engine/storage.h> +#include <engine/textrender.h> -#include <game/client/ui.h> #include <game/gamecore.h> +#include <game/localization.h> +#include <game/client/lineinput.h> #include <game/client/render.h> +#include <game/client/ui.h> #include <game/generated/client_data.h> -#include "ed_editor.h" -#include <game/client/lineinput.h> - -#include <game/localization.h> +#include "auto_map.h" +#include "editor.h" int CEditor::ms_CheckerTexture; int CEditor::ms_BackgroundTexture; @@ -41,9 +41,10 @@ CEditorImage::~CEditorImage() CLayerGroup::CLayerGroup() { - m_pName = ""; + m_aName[0] = 0; m_Visible = true; m_SaveToMap = true; + m_Collapse = false; m_GameGroup = false; m_OffsetX = 0; m_OffsetY = 0; @@ -189,6 +190,24 @@ void CEditorImage::AnalyseTileFlags() } +void CEditor::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser) +{ + CEditor *pThis = (CEditor *)pUser; + if(Env < 0 || Env >= pThis->m_Map.m_lEnvelopes.size()) + { + pChannels[0] = 0; + pChannels[1] = 0; + pChannels[2] = 0; + pChannels[3] = 0; + return; + } + + CEnvelope *e = pThis->m_Map.m_lEnvelopes[Env]; + float t = pThis->m_AnimateTime+TimeOffset; + t *= pThis->m_AnimateSpeed; + e->Eval(t, pChannels); +} + /******************************************************** OTHER *********************************************************/ @@ -196,32 +215,59 @@ void CEditorImage::AnalyseTileFlags() // copied from gc_menu.cpp, should be more generalized //extern int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false); -int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden) +int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden, int Corners) { - int Inside = UI()->MouseInside(pRect); + int Inside = UI()->MouseInside(pRect); bool ReturnValue = false; + bool UpdateOffset = false; static int s_AtIndex = 0; + static bool s_DoScroll = false; + static float s_ScrollStart = 0.0f; + + FontSize *= UI()->Scale(); if(UI()->LastActiveItem() == pID) { int Len = str_length(pStr); + if(Len == 0) + s_AtIndex = 0; if(Inside && UI()->MouseButton(0)) { + s_DoScroll = true; + s_ScrollStart = UI()->MouseX(); int MxRel = (int)(UI()->MouseX() - pRect->x); - for (int i = 1; i <= Len; i++) + for(int i = 1; i <= Len; i++) { - if (TextRender()->TextWidth(0, FontSize, pStr, i) + 10 > MxRel) + if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset > MxRel) { s_AtIndex = i - 1; break; } - if (i == Len) + if(i == Len) s_AtIndex = Len; } } + else if(!UI()->MouseButton(0)) + s_DoScroll = false; + else if(s_DoScroll) + { + // do scrolling + if(UI()->MouseX() < pRect->x && s_ScrollStart-UI()->MouseX() > 10.0f) + { + s_AtIndex = max(0, s_AtIndex-1); + s_ScrollStart = UI()->MouseX(); + UpdateOffset = true; + } + else if(UI()->MouseX() > pRect->x+pRect->w && UI()->MouseX()-s_ScrollStart > 10.0f) + { + s_AtIndex = min(Len, s_AtIndex+1); + s_ScrollStart = UI()->MouseX(); + UpdateOffset = true; + } + } for(int i = 0; i < Input()->NumEvents(); i++) { @@ -235,7 +281,11 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str if(UI()->ActiveItem() == pID) { if(!UI()->MouseButton(0)) + { + s_AtIndex = min(s_AtIndex, str_length(pStr)); + s_DoScroll = false; UI()->SetActiveItem(0); + } } else if(UI()->HotItem() == pID) { @@ -251,8 +301,8 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str UI()->SetHotItem(pID); CUIRect Textbox = *pRect; - RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 3.0f); - Textbox.VMargin(3.0f, &Textbox); + RenderTools()->DrawUIRect(&Textbox, vec4(1, 1, 1, 0.5f), Corners, 3.0f); + Textbox.VMargin(2.0f, &Textbox); const char *pDisplayStr = pStr; char aStars[128]; @@ -268,19 +318,47 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str pDisplayStr = aStars; } + // check if the text has to be moved + if(UI()->LastActiveItem() == pID && !JustGotActive && (UpdateOffset || Input()->NumEvents())) + { + float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex); + if(w-*Offset > Textbox.w) + { + // move to the left + float wt = TextRender()->TextWidth(0, FontSize, pDisplayStr, -1); + do + { + *Offset += min(wt-*Offset-Textbox.w, Textbox.w/3); + } + while(w-*Offset > Textbox.w); + } + else if(w-*Offset < 0.0f) + { + // move to the right + do + { + *Offset = max(0.0f, *Offset-Textbox.w/3); + } + while(w-*Offset < 0.0f); + } + } + UI()->ClipEnable(pRect); + Textbox.x -= *Offset; + UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1); - - //TODO: make it blink + + // render the cursor if(UI()->LastActiveItem() == pID && !JustGotActive) { float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex); Textbox = *pRect; Textbox.VSplitLeft(2.0f, 0, &Textbox); - Textbox.x += w*UI()->Scale(); - Textbox.y -= FontSize/10.f; - - UI()->DoLabel(&Textbox, "|", FontSize*1.1f, -1); + Textbox.x += (w-*Offset-TextRender()->TextWidth(0, FontSize, "|", -1)/2); + + if((2*time_get()/time_freq()) % 2) // make it blink + UI()->DoLabel(&Textbox, "|", FontSize, -1); } + UI()->ClipDisable(); return ReturnValue; } @@ -443,12 +521,12 @@ int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners) +int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize) { RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), Corners, 3.0f); CUIRect NewRect = *pRect; - NewRect.y += NewRect.h/2.0f-7.0f; - UI()->DoLabel(&NewRect, pText, 10, 0, -1); + NewRect.HMargin(NewRect.h/2.0f-FontSize/2.0f-1.0f, &NewRect); + UI()->DoLabel(&NewRect, pText, FontSize, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } @@ -466,6 +544,49 @@ int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } +void CEditor::RenderGrid(CLayerGroup *pGroup) +{ + if(!m_GridActive) + return; + + float aGroupPoints[4]; + pGroup->Mapping(aGroupPoints); + + float w = UI()->Screen()->w; + float h = UI()->Screen()->h; + + int LineDistance = GetLineDistance(); + + int XOffset = aGroupPoints[0]/LineDistance; + int YOffset = aGroupPoints[1]/LineDistance; + int XGridOffset = XOffset % m_GridFactor; + int YGridOffset = YOffset % m_GridFactor; + + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); + + for(int i = 0; i < (int)w; i++) + { + if((i+YGridOffset) % m_GridFactor == 0) + Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f); + else + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f); + + IGraphics::CLineItem Line = IGraphics::CLineItem(LineDistance*XOffset, LineDistance*i+LineDistance*YOffset, w+aGroupPoints[2], LineDistance*i+LineDistance*YOffset); + Graphics()->LinesDraw(&Line, 1); + + if((i+XGridOffset) % m_GridFactor == 0) + Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f); + else + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f); + + Line = IGraphics::CLineItem(LineDistance*i+LineDistance*XOffset, LineDistance*YOffset, LineDistance*i+LineDistance*XOffset, h+aGroupPoints[3]); + Graphics()->LinesDraw(&Line, 1); + } + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + Graphics()->LinesEnd(); +} + void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brightness) { Graphics()->TextureSet(Texture); @@ -482,15 +603,12 @@ int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, in { // logic static float s_Value; - int Ret = 0; int Inside = UI()->MouseInside(pRect); if(UI()->ActiveItem() == pID) { if(!UI()->MouseButton(0)) { - if(Inside) - Ret = 1; m_LockMouse = false; UI()->SetActiveItem(0); } @@ -741,14 +859,6 @@ void CEditor::DoToolbar(CUIRect ToolBar) m_AnimateSpeed -= 0.5f; } - if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && m_Dialog == DIALOG_NONE) - m_ZoomLevel -= 20; - - if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && m_Dialog == DIALOG_NONE) - m_ZoomLevel += 20; - - if(m_ZoomLevel < 50) - m_ZoomLevel = 50; m_WorldZoom = m_ZoomLevel/100.0f; TB_Top.VSplitLeft(10.0f, &Button, &TB_Top); @@ -861,6 +971,41 @@ void CEditor::DoToolbar(CUIRect ToolBar) m_WorldOffsetX = 0; m_WorldOffsetY = 0; } + + TB_Bottom.VSplitLeft(5.0f, 0, &TB_Bottom); + + // grid button + TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom); + static int s_GridButton = 0; + if(DoButton_Editor(&s_GridButton, "Grid", m_GridActive, &Button, 0, "Toggle Grid")) + { + m_GridActive = !m_GridActive; + } + + TB_Bottom.VSplitLeft(30.0f, 0, &TB_Bottom); + + // grid zoom + TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom); + static int s_GridIncreaseButton = 0; + if(DoButton_Ex(&s_GridIncreaseButton, "G-", 0, &Button, 0, "Decrease grid", CUI::CORNER_L)) + { + if(m_GridFactor > 1) + m_GridFactor--; + } + + TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom); + static int s_GridNormalButton = 0; + if(DoButton_Ex(&s_GridNormalButton, "1", 0, &Button, 0, "Normal grid", 0)) + m_GridFactor = 1; + + TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom); + + static int s_GridDecreaseButton = 0; + if(DoButton_Ex(&s_GridDecreaseButton, "G+", 0, &Button, 0, "Increase grid", CUI::CORNER_R)) + { + if(m_GridFactor < 15) + m_GridFactor++; + } } static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) @@ -920,10 +1065,41 @@ void CEditor::DoQuad(CQuad *q, int Index) else if(s_Operation == OP_MOVE_ALL) { // move all points including pivot - for(int v = 0; v < 5; v++) + if(m_GridActive) { - q->m_aPoints[v].x += f2fx(wx-s_LastWx); - q->m_aPoints[v].y += f2fx(wy-s_LastWy); + int LineDistance = GetLineDistance(); + + float x = 0.0f; + float y = 0.0f; + if(wx >= 0) + x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + if(wy >= 0) + y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + + int OldX = q->m_aPoints[4].x; + int OldY = q->m_aPoints[4].y; + q->m_aPoints[4].x = f2fx(x); + q->m_aPoints[4].y = f2fx(y); + int DiffX = q->m_aPoints[4].x - OldX; + int DiffY = q->m_aPoints[4].y - OldY; + + for(int v = 0; v < 4; v++) + { + q->m_aPoints[v].x += DiffX; + q->m_aPoints[v].y += DiffY; + } + } + else + { + for(int v = 0; v < 5; v++) + { + q->m_aPoints[v].x += f2fx(wx-s_LastWx); + q->m_aPoints[v].y += f2fx(wy-s_LastWy); + } } } else if(s_Operation == OP_ROTATE) @@ -1054,12 +1230,37 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) { if(s_Operation == OP_MOVEPOINT) { - for(int m = 0; m < 4; m++) - if(m_SelectedPoints&(1<<m)) - { - pQuad->m_aPoints[m].x += f2fx(dx); - pQuad->m_aPoints[m].y += f2fx(dy); - } + if(m_GridActive) + { + for(int m = 0; m < 4; m++) + if(m_SelectedPoints&(1<<m)) + { + int LineDistance = GetLineDistance(); + + float x = 0.0f; + float y = 0.0f; + if(wx >= 0) + x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + if(wy >= 0) + y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + else + y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor); + + pQuad->m_aPoints[m].x = f2fx(x); + pQuad->m_aPoints[m].y = f2fx(y); + } + } + else + { + for(int m = 0; m < 4; m++) + if(m_SelectedPoints&(1<<m)) + { + pQuad->m_aPoints[m].x += f2fx(dx); + pQuad->m_aPoints[m].y += f2fx(dy); + } + } } else if(s_Operation == OP_MOVEUV) { @@ -1159,12 +1360,8 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) { - //UI()->ClipEnable(&view); - - bool ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE; - // render all good stuff - if(!ShowPicker) + if(!m_ShowPicker) { for(int g = 0; g < m_Map.m_lGroups.size(); g++) { @@ -1182,7 +1379,10 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) CLayerTiles *pT = static_cast<CLayerTiles *>(GetSelectedLayerType(0, LAYERTYPE_TILES)); if(m_ShowTileInfo && pT && pT->m_Visible && m_ZoomLevel <= 300) + { + GetSelectedGroup()->MapScreen(); pT->ShowInfo(); + } } static void *s_pEditorID = (void *)&s_pEditorID; @@ -1196,8 +1396,6 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) static float s_StartWx = 0; static float s_StartWy = 0; - static float s_StartMx = 0; - static float s_StartMy = 0; enum { @@ -1210,7 +1408,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) }; // remap the screen so it can display the whole tileset - if(ShowPicker) + if(m_ShowPicker) { CUIRect Screen = *UI()->Screen(); float Size = 32.0*16.0f; @@ -1239,7 +1437,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) int NumEditLayers = 0; NumEditLayers = 0; - if(ShowPicker) + if(m_ShowPicker) { pEditLayers[0] = &m_TilesetPicker; NumEditLayers++; @@ -1255,6 +1453,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) { g->MapScreen(); + RenderGrid(g); + for(int i = 0; i < NumEditLayers; i++) { if(pEditLayers[i]->m_Type != LAYERTYPE_TILES) @@ -1285,8 +1485,6 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) { s_StartWx = wx; s_StartWy = wy; - s_StartMx = mx; - s_StartMy = my; if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL) || UI()->MouseButton(2)) { @@ -1445,7 +1643,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) // quad editing { - if(!ShowPicker && m_Brush.IsEmpty()) + if(!m_ShowPicker && m_Brush.IsEmpty()) { // fetch layers CLayerGroup *g = GetSelectedGroup(); @@ -1772,22 +1970,18 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) CUIRect Slot, Button; char aBuf[64]; - int ValidGroup = 0; - int ValidLayer = 0; - if(m_SelectedGroup >= 0 && m_SelectedGroup < m_Map.m_lGroups.size()) - ValidGroup = 1; - - if(ValidGroup && m_SelectedLayer >= 0 && m_SelectedLayer < m_Map.m_lGroups[m_SelectedGroup]->m_lLayers.size()) - ValidLayer = 1; - float LayersHeight = 12.0f; // Height of AddGroup button static int s_ScrollBar = 0; static float s_ScrollValue = 0; for(int g = 0; g < m_Map.m_lGroups.size(); g++) + { // Each group is 19.0f // Each layer is 14.0f - LayersHeight += 19.0f + m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f; + LayersHeight += 19.0f; + if(!m_Map.m_lGroups[g]->m_Collapse) + LayersHeight += m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f; + } float ScrollDifference = LayersHeight - LayersBox.h; @@ -1798,6 +1992,20 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) LayersBox.VSplitRight(3.0f, &LayersBox, 0); // extra spacing Scroll.HMargin(5.0f, &Scroll); s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue); + + if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&LayersBox)) + { + int ScrollNum = (int)((LayersHeight-LayersBox.h)/15.0f)+1; + if(ScrollNum > 0) + { + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f); + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f); + } + else + ScrollNum = 0; + } } float LayerStartAt = ScrollDifference * s_ScrollValue; @@ -1824,7 +2032,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) { LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); Slot.VSplitLeft(12, &VisibleToggle, &Slot); - if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L)) + if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", m_Map.m_lGroups[g]->m_Collapse ? 1 : 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L)) m_Map.m_lGroups[g]->m_Visible = !m_Map.m_lGroups[g]->m_Visible; Slot.VSplitRight(12.0f, &Slot, &SaveCheck); @@ -1832,16 +2040,22 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) if(!m_Map.m_lGroups[g]->m_GameGroup) m_Map.m_lGroups[g]->m_SaveToMap = !m_Map.m_lGroups[g]->m_SaveToMap; - str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_pName); + str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_aName); + float FontSize = 10.0f; + while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Slot.w) + FontSize--; if(int Result = DoButton_Ex(&m_Map.m_lGroups[g], aBuf, g==m_SelectedGroup, &Slot, - BUTTON_CONTEXT, "Select group. Right click for properties.", 0)) + BUTTON_CONTEXT, m_Map.m_lGroups[g]->m_Collapse ? "Select group. Double click to expand." : "Select group. Double click to collapse.", 0, FontSize)) { m_SelectedGroup = g; m_SelectedLayer = 0; static int s_GroupPopupId = 0; if(Result == 2) - UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, PopupGroup); + UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 220, PopupGroup); + + if(m_Map.m_lGroups[g]->m_lLayers.size() && Input()->MouseDoubleClick()) + m_Map.m_lGroups[g]->m_Collapse ^= 1; } LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); } @@ -1857,6 +2071,9 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) continue; } + if(m_Map.m_lGroups[g]->m_Collapse) + continue; + //visible LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); Slot.VSplitLeft(12.0f, 0, &Button); @@ -1870,15 +2087,24 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) if(m_Map.m_lGroups[g]->m_lLayers[i] != m_Map.m_pGameLayer) m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap = !m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap; - str_format(aBuf, sizeof(aBuf),"#%d %s ", i, m_Map.m_lGroups[g]->m_lLayers[i]->m_pTypeName); + if(m_Map.m_lGroups[g]->m_lLayers[i]->m_aName[0]) + str_format(aBuf, sizeof(aBuf), "%s", m_Map.m_lGroups[g]->m_lLayers[i]->m_aName); + else if(m_Map.m_lGroups[g]->m_lLayers[i]->m_Type == LAYERTYPE_TILES) + str_copy(aBuf, "Tiles", sizeof(aBuf)); + else + str_copy(aBuf, "Quads", sizeof(aBuf)); + + float FontSize = 10.0f; + while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Button.w) + FontSize--; if(int Result = DoButton_Ex(m_Map.m_lGroups[g]->m_lLayers[i], aBuf, g==m_SelectedGroup&&i==m_SelectedLayer, &Button, - BUTTON_CONTEXT, "Select layer. Right click for properties.", 0)) + BUTTON_CONTEXT, "Select layer.", 0, FontSize)) { m_SelectedLayer = i; m_SelectedGroup = g; static int s_LayerPopupID = 0; if(Result == 2) - UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 220, PopupLayer); + UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 245, PopupLayer); } LayerCur += 14.0f; @@ -1916,6 +2142,7 @@ void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser) *pImg = ImgInfo; pImg->m_External = External; pEditor->ExtractName(pFileName, pImg->m_aName, sizeof(pImg->m_aName)); + pImg->m_AutoMapper.Load(pImg->m_aName); pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); pEditor->SortImages(); for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i) @@ -1947,6 +2174,7 @@ void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser) pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); pImg->m_External = 1; // external by default str_copy(pImg->m_aName, aBuf, sizeof(pImg->m_aName)); + pImg->m_AutoMapper.Load(pImg->m_aName); pEditor->m_Map.m_lImages.add(pImg); pEditor->SortImages(); if(pEditor->m_SelectedImage > -1 && pEditor->m_SelectedImage < pEditor->m_Map.m_lImages.size()) @@ -2080,6 +2308,20 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) ToolBox.VSplitRight(3.0f, &ToolBox, 0); // extra spacing Scroll.HMargin(5.0f, &Scroll); s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue); + + if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&ToolBox)) + { + int ScrollNum = (int)((ImagesHeight-ToolBox.h)/14.0f)+1; + if(ScrollNum > 0) + { + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f); + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f); + } + else + ScrollNum = 0; + } } float ImageStartAt = ScrollDifference * s_ScrollValue; @@ -2217,7 +2459,7 @@ void CEditor::AddFileDialogEntry(int Index, CUIRect *pView) Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); - if(DoButton_File((void*)(10+(int)Button.y), m_FileList[Index].m_aName, m_FilesSelectedIndex == Index, &Button, 0, 0)) + if(DoButton_File(&m_FileList[Index], m_FileList[Index].m_aName, m_FilesSelectedIndex == Index, &Button, 0, 0)) { if(!m_FileList[Index].m_IsDir) str_copy(m_aFileDialogFileName, m_FileList[Index].m_aFilename, sizeof(m_aFileDialogFileName)); @@ -2261,9 +2503,9 @@ void CEditor::RenderFileDialog() // filebox if(m_FileDialogStorageType == IStorage::TYPE_SAVE) { - static int s_FileBoxID = 0; + static float s_FileBoxID = 0; UI()->DoLabel(&FileBoxLabel, "Filename:", 10.0f, -1, -1); - if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f)) + if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f, &s_FileBoxID)) { // remove '/' and '\' for(int i = 0; m_aFileDialogFileName[i]; ++i) @@ -2612,8 +2854,8 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) ToolBar.VSplitLeft(80.0f, &Button, &ToolBar); - static int s_NameBox = 0; - if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f)) + static float s_NameBox = 0; + if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f, &s_NameBox)) m_Map.m_Modified = true; } } @@ -2811,8 +3053,6 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) // render handles { - static bool s_Move = false; - int CurrentValue = 0, CurrentTime = 0; Graphics()->TextureSet(-1); @@ -2846,7 +3086,6 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) if(!UI()->MouseButton(0)) { UI()->SetActiveItem(0); - s_Move = false; } else { @@ -3050,6 +3289,7 @@ void CEditor::Render() RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f); CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, ToolBox; + m_ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE; if(m_GuiActive) { @@ -3059,7 +3299,7 @@ void CEditor::Render() View.VSplitLeft(100.0f, &ToolBox, &View); View.HSplitBottom(16.0f, &View, &StatusBar); - if(m_ShowEnvelopeEditor) + if(m_ShowEnvelopeEditor && !m_ShowPicker) { float size = 125.0f; if(m_ShowEnvelopeEditor == 2) @@ -3074,6 +3314,18 @@ void CEditor::Render() if(m_Mode == MODE_LAYERS) DoMapEditor(View, ToolBar); + // do the scrolling + if(m_Dialog == DIALOG_NONE && UI()->MouseInside(&View)) + { + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + m_ZoomLevel -= 20; + + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + m_ZoomLevel += 20; + + m_ZoomLevel = clamp(m_ZoomLevel, 50, 2000); + } + if(m_GuiActive) { float Brightness = 0.25f; @@ -3171,7 +3423,6 @@ void CEditor::Render() Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } - } void CEditor::Reset(bool CreateDefault) @@ -3209,6 +3460,24 @@ void CEditor::Reset(bool CreateDefault) m_Map.m_Modified = false; } +int CEditor::GetLineDistance() +{ + int LineDistance = 512; + + if(m_ZoomLevel <= 100) + LineDistance = 16; + else if(m_ZoomLevel <= 250) + LineDistance = 32; + else if(m_ZoomLevel <= 450) + LineDistance = 64; + else if(m_ZoomLevel <= 850) + LineDistance = 128; + else if(m_ZoomLevel <= 1550) + LineDistance = 256; + + return LineDistance; +} + void CEditorMap::DeleteEnvelope(int Index) { if(Index < 0 || Index >= m_lEnvelopes.size()) @@ -3249,7 +3518,7 @@ void CEditorMap::MakeGameGroup(CLayerGroup *pGroup) { m_pGameGroup = pGroup; m_pGameGroup->m_GameGroup = true; - m_pGameGroup->m_pName = "Game"; + str_copy(m_pGameGroup->m_aName, "Game", sizeof(m_pGameGroup->m_aName)); } @@ -3356,6 +3625,7 @@ void CEditor::UpdateAndRender() float rx, ry; { Input()->MouseRelative(&rx, &ry); + UI()->ConvertMouseMove(&rx, &ry); m_MouseDeltaX = rx; m_MouseDeltaY = ry; @@ -3402,18 +3672,6 @@ void CEditor::UpdateAndRender() if(Input()->KeyDown(KEY_TAB)) m_GuiActive = !m_GuiActive; - if(Input()->KeyDown(KEY_F5)) - Save("maps/debug_test2.map"); - - if(Input()->KeyDown(KEY_F6)) - Load("maps/debug_test2.map", IStorage::TYPE_ALL); - - if(Input()->KeyDown(KEY_F8)) - Load("maps/debug_test.map", IStorage::TYPE_ALL); - - if(Input()->KeyDown(KEY_F7)) - Save("maps/quicksave.map"); - if(Input()->KeyDown(KEY_F10)) m_ShowMousePointer = false; diff --git a/src/game/editor/ed_editor.h b/src/game/editor/editor.h index aab757e1..1a904953 100644 --- a/src/game/editor/ed_editor.h +++ b/src/game/editor/editor.h @@ -1,25 +1,28 @@ /* (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. */ -#ifndef GAME_EDITOR_ED_EDITOR_H -#define GAME_EDITOR_ED_EDITOR_H +#ifndef GAME_EDITOR_EDITOR_H +#define GAME_EDITOR_EDITOR_H + +#include <math.h> -#include <base/system.h> #include <base/math.h> -#include <base/tl/array.h> +#include <base/system.h> + #include <base/tl/algorithm.h> +#include <base/tl/array.h> #include <base/tl/sorted_array.h> #include <base/tl/string.h> -#include <math.h> +#include <game/client/ui.h> #include <game/mapitems.h> #include <game/client/render.h> -#include <engine/shared/datafile.h> #include <engine/shared/config.h> +#include <engine/shared/datafile.h> #include <engine/editor.h> #include <engine/graphics.h> -#include <game/client/ui.h> +#include "auto_map.h" typedef void (*INDEX_MODIFY_FUNC)(int *pIndex); @@ -123,7 +126,7 @@ public: CLayer() { m_Type = LAYERTYPE_INVALID; - m_pTypeName = "(invalid)"; + str_copy(m_aName, "(invalid)", sizeof(m_aName)); m_Visible = true; m_Readonly = false; m_SaveToMap = true; @@ -153,7 +156,7 @@ public: virtual void GetSize(float *w, float *h) { *w = 0; *h = 0;} - const char *m_pTypeName; + char m_aName[12]; int m_Type; int m_Flags; @@ -181,10 +184,11 @@ public: int m_ClipW; int m_ClipH; - const char *m_pName; + char m_aName[12]; bool m_GameGroup; bool m_Visible; bool m_SaveToMap; + bool m_Collapse; CLayerGroup(); ~CLayerGroup(); @@ -230,6 +234,7 @@ public: CEditor *m_pEditor; CEditorImage(CEditor *pEditor) + : m_AutoMapper(pEditor) { m_pEditor = pEditor; m_TexID = -1; @@ -249,6 +254,7 @@ public: int m_External; char m_aName[128]; unsigned char m_aTileFlags[256]; + class CAutoMapper m_AutoMapper; }; class CEditorMap @@ -400,6 +406,8 @@ public: int m_Width; int m_Height; CColor m_Color; + int m_ColorEnv; + int m_ColorEnvOffset; CTile *m_pTiles; }; @@ -470,6 +478,9 @@ public: m_Dialog = 0; m_pTooltip = 0; + m_GridActive = false; + m_GridFactor = 1; + m_aFileName[0] = 0; m_aFileSaveName[0] = 0; m_ValidSaveFilename = false; @@ -551,6 +562,9 @@ public: int m_Dialog; const char *m_pTooltip; + bool m_GridActive; + int m_GridFactor; + char m_aFileName[512]; char m_aFileSaveName[512]; bool m_ValidSaveFilename; @@ -630,6 +644,7 @@ public: float m_AnimateSpeed; int m_ShowEnvelopeEditor; + bool m_ShowPicker; int m_SelectedLayer; int m_SelectedGroup; @@ -650,12 +665,14 @@ public: CEditorMap m_Map; + static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser); + void DoMapBorder(); int DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); int DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); int DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - int DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners); + int DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize=10.0f); int DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); int DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); @@ -664,10 +681,12 @@ public: int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); int DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags=0, const char *pToolTip=0); - int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false); + int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden=false, int Corners=CUI::CORNER_ALL); void RenderBackground(CUIRect View, int Texture, float Size, float Brightness); + void RenderGrid(CLayerGroup *pGroup); + void UiInvokePopupMenu(void *pID, int Flags, float X, float Y, float W, float H, int (*pfnFunc)(CEditor *pEditor, CUIRect Rect), void *pExtra=0); void UiDoPopupMenu(); @@ -683,6 +702,7 @@ public: static int PopupSelectGametileOp(CEditor *pEditor, CUIRect View); static int PopupImage(CEditor *pEditor, CUIRect View); static int PopupMenuFile(CEditor *pEditor, CUIRect View); + static int PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View); static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser); static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser); @@ -693,6 +713,9 @@ public: void PopupSelectGametileOpInvoke(float x, float y); int PopupSelectGameTileOpResult(); + + void PopupSelectConfigAutoMapInvoke(float x, float y); + int PopupSelectConfigAutoMapResult(); vec4 ButtonColorMul(const void *pID); @@ -732,6 +755,8 @@ public: int Length = pEnd > pExtractedName ? min(BufferSize, (int)(pEnd-pExtractedName+1)) : BufferSize; str_copy(pName, pExtractedName, Length); } + + int GetLineDistance(); }; // make sure to inline this function diff --git a/src/game/editor/ed_io.cpp b/src/game/editor/io.cpp index a5ead97a..68330f03 100644 --- a/src/game/editor/ed_io.cpp +++ b/src/game/editor/io.cpp @@ -6,7 +6,7 @@ #include <engine/serverbrowser.h> #include <engine/storage.h> #include <game/gamecore.h> -#include "ed_editor.h" +#include "editor.h" template<typename T> static int MakeVersion(int i, const T &v) @@ -266,6 +266,9 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) GItem.m_StartLayer = LayerCount; GItem.m_NumLayers = 0; + // save group name + StrToInts(GItem.m_aName, sizeof(GItem.m_aName)/sizeof(int), pGroup->m_aName); + for(int l = 0; l < pGroup->m_lLayers.size(); l++) { if(!pGroup->m_lLayers[l]->m_SaveToMap) @@ -278,23 +281,24 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) pLayer->PrepareForSave(); CMapItemLayerTilemap Item; - Item.m_Version = 2; + Item.m_Version = 3; Item.m_Layer.m_Flags = pLayer->m_Flags; Item.m_Layer.m_Type = pLayer->m_Type; - Item.m_Color.r = pLayer->m_Color.r; - Item.m_Color.g = pLayer->m_Color.g; - Item.m_Color.b = pLayer->m_Color.b; - Item.m_Color.a = pLayer->m_Color.a; - Item.m_ColorEnv = -1; // not in use right now - Item.m_ColorEnvOffset = 0; + Item.m_Color = pLayer->m_Color; + Item.m_ColorEnv = pLayer->m_ColorEnv; + Item.m_ColorEnvOffset = pLayer->m_ColorEnvOffset; Item.m_Width = pLayer->m_Width; Item.m_Height = pLayer->m_Height; - Item.m_Flags = pLayer->m_Game; + Item.m_Flags = pLayer->m_Game ? TILESLAYERFLAG_GAME : 0; Item.m_Image = pLayer->m_Image; Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), pLayer->m_pTiles); + + // save layer name + StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), pLayer->m_aName); + df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item); GItem.m_NumLayers++; @@ -307,7 +311,7 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) if(pLayer->m_lQuads.size()) { CMapItemLayerQuads Item; - Item.m_Version = 1; + Item.m_Version = 2; Item.m_Layer.m_Flags = pLayer->m_Flags; Item.m_Layer.m_Type = pLayer->m_Type; Item.m_Image = pLayer->m_Image; @@ -315,6 +319,10 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) // add the data Item.m_NumQuads = pLayer->m_lQuads.size(); Item.m_Data = df.AddDataSwapped(pLayer->m_lQuads.size()*sizeof(CQuad), pLayer->m_lQuads.base_ptr()); + + // save layer name + StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), pLayer->m_aName); + df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item); // clean up @@ -449,6 +457,9 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag if(pName) str_copy(pImg->m_aName, pName, 128); + // load auto mapper file + pImg->m_AutoMapper.Load(pImg->m_aName); + m_lImages.add(pImg); // unload image @@ -486,6 +497,10 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag pGroup->m_ClipH = pGItem->m_ClipH; } + // load group name + if(pGItem->m_Version >= 3) + IntsToStr(pGItem->m_aName, sizeof(pGroup->m_aName)/sizeof(int), pGroup->m_aName); + for(int l = 0; l < pGItem->m_NumLayers; l++) { CLayer *pLayer = 0; @@ -498,7 +513,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag CMapItemLayerTilemap *pTilemapItem = (CMapItemLayerTilemap *)pLayerItem; CLayerTiles *pTiles = 0; - if(pTilemapItem->m_Flags&1) + if(pTilemapItem->m_Flags&TILESLAYERFLAG_GAME) { pTiles = new CLayerGame(pTilemapItem->m_Width, pTilemapItem->m_Height); MakeGameLayer(pTiles); @@ -508,10 +523,9 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag { pTiles = new CLayerTiles(pTilemapItem->m_Width, pTilemapItem->m_Height); pTiles->m_pEditor = m_pEditor; - pTiles->m_Color.r = pTilemapItem->m_Color.r; - pTiles->m_Color.g = pTilemapItem->m_Color.g; - pTiles->m_Color.b = pTilemapItem->m_Color.b; - pTiles->m_Color.a = pTilemapItem->m_Color.a; + pTiles->m_Color = pTilemapItem->m_Color; + pTiles->m_ColorEnv = pTilemapItem->m_ColorEnv; + pTiles->m_ColorEnvOffset = pTilemapItem->m_ColorEnvOffset; } pLayer = pTiles; @@ -519,7 +533,11 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag pGroup->AddLayer(pTiles); void *pData = DataFile.GetData(pTilemapItem->m_Data); pTiles->m_Image = pTilemapItem->m_Image; - pTiles->m_Game = pTilemapItem->m_Flags&1; + pTiles->m_Game = pTilemapItem->m_Flags&TILESLAYERFLAG_GAME; + + // load layer name + if(pTilemapItem->m_Version >= 3) + IntsToStr(pTilemapItem->m_aName, sizeof(pTiles->m_aName)/sizeof(int), pTiles->m_aName); mem_copy(pTiles->m_pTiles, pData, pTiles->m_Width*pTiles->m_Height*sizeof(CTile)); @@ -543,6 +561,11 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag pQuads->m_Image = pQuadsItem->m_Image; if(pQuads->m_Image < -1 || pQuads->m_Image >= m_lImages.size()) pQuads->m_Image = -1; + + // load layer name + if(pQuadsItem->m_Version >= 2) + IntsToStr(pQuadsItem->m_aName, sizeof(pQuads->m_aName)/sizeof(int), pQuads->m_aName); + void *pData = DataFile.GetDataSwapped(pQuadsItem->m_Data); pGroup->AddLayer(pQuads); pQuads->m_lQuads.set_size(pQuadsItem->m_NumQuads); diff --git a/src/game/editor/ed_layer_game.cpp b/src/game/editor/layer_game.cpp index cf48845e..7e879c3e 100644 --- a/src/game/editor/ed_layer_game.cpp +++ b/src/game/editor/layer_game.cpp @@ -1,12 +1,12 @@ /* (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 "ed_editor.h" +#include "editor.h" CLayerGame::CLayerGame(int w, int h) : CLayerTiles(w, h) { - m_pTypeName = "Game"; + str_copy(m_aName, "Game", sizeof(m_aName)); m_Game = 1; } diff --git a/src/game/editor/ed_layer_quads.cpp b/src/game/editor/layer_quads.cpp index 680a54cd..d0b66405 100644 --- a/src/game/editor/ed_layer_quads.cpp +++ b/src/game/editor/layer_quads.cpp @@ -5,7 +5,7 @@ #include <engine/console.h> #include <engine/graphics.h> -#include "ed_editor.h" +#include "editor.h" #include <game/generated/client_data.h> #include <game/client/render.h> #include <game/localization.h> @@ -13,7 +13,7 @@ CLayerQuads::CLayerQuads() { m_Type = LAYERTYPE_QUADS; - m_pTypeName = "Quads"; + str_copy(m_aName, "Quads", sizeof(m_aName)); m_Image = -1; } @@ -21,31 +21,13 @@ CLayerQuads::~CLayerQuads() { } -static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser) -{ - CEditor *pEditor = (CEditor *)pUser; - if(Env < 0 || Env > pEditor->m_Map.m_lEnvelopes.size()) - { - pChannels[0] = 0; - pChannels[1] = 0; - pChannels[2] = 0; - pChannels[3] = 0; - return; - } - - CEnvelope *e = pEditor->m_Map.m_lEnvelopes[Env]; - float t = pEditor->m_AnimateTime+TimeOffset; - t *= pEditor->m_AnimateSpeed; - e->Eval(t, pChannels); -} - void CLayerQuads::Render() { Graphics()->TextureSet(-1); if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size()) Graphics()->TextureSet(m_pEditor->m_Map.m_lImages[m_Image]->m_TexID); - m_pEditor->RenderTools()->RenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, EnvelopeEval, m_pEditor); + m_pEditor->RenderTools()->RenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, m_pEditor->EnvelopeEval, m_pEditor); } CQuad *CLayerQuads::NewQuad() diff --git a/src/game/editor/ed_layer_tiles.cpp b/src/game/editor/layer_tiles.cpp index d0c9041c..5662613c 100644 --- a/src/game/editor/ed_layer_tiles.cpp +++ b/src/game/editor/layer_tiles.cpp @@ -8,14 +8,14 @@ #include <game/generated/client_data.h> #include <game/client/render.h> -#include "ed_editor.h" +#include "editor.h" #include <game/localization.h> CLayerTiles::CLayerTiles(int w, int h) { m_Type = LAYERTYPE_TILES; - m_pTypeName = "Tiles"; + str_copy(m_aName, "Tiles", sizeof(m_aName)); m_Width = w; m_Height = h; m_Image = -1; @@ -25,6 +25,8 @@ CLayerTiles::CLayerTiles(int w, int h) m_Color.g = 255; m_Color.b = 255; m_Color.a = 255; + m_ColorEnv = -1; + m_ColorEnvOffset = 0; m_pTiles = new CTile[m_Width*m_Height]; mem_zero(m_pTiles, m_Width*m_Height*sizeof(CTile)); @@ -62,7 +64,8 @@ void CLayerTiles::Render() m_TexID = m_pEditor->m_Map.m_lImages[m_Image]->m_TexID; Graphics()->TextureSet(m_TexID); vec4 Color = vec4(m_Color.r/255.0f, m_Color.g/255.0f, m_Color.b/255.0f, m_Color.a/255.0f); - m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT); + m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, + m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset); } int CLayerTiles::ConvertX(float x) const { return (int)(x/32.0f); } @@ -123,7 +126,7 @@ void CLayerTiles::BrushSelecting(CUIRect Rect) m_pEditor->Graphics()->QuadsEnd(); char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d,%d", ConvertX(Rect.w), ConvertY(Rect.h)); - TextRender()->Text(0, Rect.x+3.0f, Rect.y+3.0f, 15.0f*m_pEditor->m_WorldZoom, aBuf, -1); + TextRender()->Text(0, Rect.x+3.0f, Rect.y+3.0f, m_pEditor->m_ShowPicker?15.0f:15.0f*m_pEditor->m_WorldZoom, aBuf, -1); } int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect) @@ -353,19 +356,39 @@ void CLayerTiles::ShowInfo() } x += m_pTiles[c].m_Skip; } + + Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } int CLayerTiles::RenderProperties(CUIRect *pToolBox) { CUIRect Button; - pToolBox->HSplitBottom(12.0f, pToolBox, &Button); - + bool InGameGroup = !find_linear(m_pEditor->m_Map.m_pGameGroup->m_lLayers.all(), this).empty(); - if(m_pEditor->m_Map.m_pGameLayer == this) + if(m_pEditor->m_Map.m_pGameLayer != this) + { + if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size() && m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.IsLoaded()) + { + static int s_AutoMapperButton = 0; + pToolBox->HSplitBottom(12.0f, pToolBox, &Button); + if(m_pEditor->DoButton_Editor(&s_AutoMapperButton, "Auto map", 0, &Button, 0, "")) + m_pEditor->PopupSelectConfigAutoMapInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY()); + + int Result = m_pEditor->PopupSelectConfigAutoMapResult(); + if(Result > -1) + { + m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.Proceed(this, Result); + return 1; + } + } + } + else InGameGroup = false; if(InGameGroup) { + pToolBox->HSplitBottom(2.0f, pToolBox, 0); + pToolBox->HSplitBottom(12.0f, pToolBox, &Button); static int s_ColclButton = 0; if(m_pEditor->DoButton_Editor(&s_ColclButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer")) m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY()); @@ -392,6 +415,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) PROP_SHIFT, PROP_IMAGE, PROP_COLOR, + PROP_COLOR_ENV, + PROP_COLOR_ENV_OFFSET, NUM_PROPS, }; @@ -407,6 +432,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) {"Shift", 0, PROPTYPE_SHIFT, 0, 0}, {"Image", m_Image, PROPTYPE_IMAGE, 0, 0}, {"Color", Color, PROPTYPE_COLOR, 0, 0}, + {"Color Env", m_ColorEnv+1, PROPTYPE_INT_STEP, 0, m_pEditor->m_Map.m_lEnvelopes.size()+1}, + {"Color TO", m_ColorEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {0}, }; @@ -445,6 +472,10 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) m_Color.b = (NewVal>>8)&0xff; m_Color.a = NewVal&0xff; } + if(Prop == PROP_COLOR_ENV) + m_ColorEnv = clamp(NewVal-1, -1, m_pEditor->m_Map.m_lEnvelopes.size()-1); + if(Prop == PROP_COLOR_ENV_OFFSET) + m_ColorEnvOffset = NewVal; return 0; } diff --git a/src/game/editor/ed_popups.cpp b/src/game/editor/popups.cpp index f572e43c..f29ae7e2 100644 --- a/src/game/editor/ed_popups.cpp +++ b/src/game/editor/popups.cpp @@ -1,11 +1,15 @@ /* (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 <base/tl/array.h> + #include <engine/console.h> #include <engine/graphics.h> #include <engine/input.h> #include <engine/keys.h> #include <engine/storage.h> -#include "ed_editor.h" + +#include "editor.h" // popup menu handling @@ -147,6 +151,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) l->m_pEditor = pEditor; pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l); pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1; + pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false; return 1; } @@ -160,9 +165,22 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) l->m_pEditor = pEditor; pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l); pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1; + pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false; return 1; } + // group name + if(!pEditor->GetSelectedGroup()->m_GameGroup) + { + View.HSplitBottom(5.0f, &View, &Button); + View.HSplitBottom(12.0f, &View, &Button); + static float s_Name = 0; + pEditor->UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1); + Button.VSplitLeft(40.0f, 0, &Button); + if(pEditor->DoEditBox(&s_Name, &Button, pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_aName, sizeof(pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_aName), 10.0f, &s_Name)) + pEditor->m_Map.m_Modified = true; + } + enum { PROP_ORDER=0, @@ -239,6 +257,18 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View) return 1; } + // layer name + if(pEditor->m_Map.m_pGameLayer != pEditor->GetSelectedLayer(0)) + { + View.HSplitBottom(5.0f, &View, &Button); + View.HSplitBottom(12.0f, &View, &Button); + static float s_Name = 0; + pEditor->UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1); + Button.VSplitLeft(40.0f, 0, &Button); + if(pEditor->DoEditBox(&s_Name, &Button, pEditor->GetSelectedLayer(0)->m_aName, sizeof(pEditor->GetSelectedLayer(0)->m_aName), 10.0f, &s_Name)) + pEditor->m_Map.m_Modified = true; + } + View.HSplitBottom(10.0f, &View, 0); CLayerGroup *pCurrentGroup = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]; @@ -528,8 +558,8 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View) View.HSplitBottom(40.0f, &View, 0); View.VMargin(40.0f, &View); View.HSplitBottom(20.0f, &View, &Label); - static int s_FolderBox = 0; - pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_FileDialogNewFolderName, sizeof(pEditor->m_FileDialogNewFolderName), 15.0f); + static float s_FolderBox = 0; + pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_FileDialogNewFolderName, sizeof(pEditor->m_FileDialogNewFolderName), 15.0f, &s_FolderBox); View.HSplitBottom(20.0f, &View, &Label); pEditor->UI()->DoLabel(&Label, "Name:", 10.0f, -1); @@ -737,3 +767,43 @@ int CEditor::PopupSelectGameTileOpResult() s_GametileOpSelected = -1; return Result; } + +static int s_AutoMapConfigSelected = -1; + +int CEditor::PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View) +{ + CLayerTiles *pLayer = static_cast<CLayerTiles*>(pEditor->GetSelectedLayer(0)); + CUIRect Button; + static int s_AutoMapperConfigButtons[256]; + CAutoMapper *pAutoMapper = &pEditor->m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper; + + for(int i = 0; i < pAutoMapper->ConfigNamesNum(); ++i) + { + View.HSplitTop(2.0f, 0, &View); + View.HSplitTop(12.0f, &Button, &View); + if(pEditor->DoButton_Editor(&s_AutoMapperConfigButtons[i], pAutoMapper->GetConfigName(i), 0, &Button, 0, 0)) + s_AutoMapConfigSelected = i; + } + + return 0; +} + +void CEditor::PopupSelectConfigAutoMapInvoke(float x, float y) +{ + static int s_AutoMapConfigSelectID = 0; + s_AutoMapConfigSelected = -1; + CLayerTiles *pLayer = static_cast<CLayerTiles*>(GetSelectedLayer(0)); + if(pLayer && pLayer->m_Image >= 0 && pLayer->m_Image < m_Map.m_lImages.size() && + m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper.ConfigNamesNum()) + UiInvokePopupMenu(&s_AutoMapConfigSelectID, 0, x, y, 120.0f, 12.0f+14.0f*m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper.ConfigNamesNum(), PopupSelectConfigAutoMap); +} + +int CEditor::PopupSelectConfigAutoMapResult() +{ + if(s_AutoMapConfigSelected < 0) + return -1; + + int Result = s_AutoMapConfigSelected; + s_AutoMapConfigSelected = -1; + return Result; +} diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index d2a1652c..af086df2 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -362,35 +362,37 @@ void CCharacterCore::Move() m_Vel.x = m_Vel.x*RampValue; - vec2 NewPos = m_Pos; + vec2 NewPos = m_Pos; m_pCollision->MoveBox(&NewPos, &m_Vel, vec2(28.0f, 28.0f), 0); m_Vel.x = m_Vel.x*(1.0f/RampValue); if(m_pWorld && m_pWorld->m_Tuning.m_PlayerCollision) { - // check player collision - float Distance = distance(m_Pos, NewPos); - int End = Distance+1; - for(int i = 0; i < End; i++) - { - float a = i/Distance; - vec2 Pos = mix(m_Pos, NewPos, a); - for(int p = 0; p < MAX_CLIENTS; p++) - { - CCharacterCore *pCharCore = m_pWorld->m_apCharacters[p]; - if(!pCharCore || pCharCore == this) - continue; - float D = distance(Pos, pCharCore->m_Pos); - if(D < 28.0f*1.25f && D > 0.0f) - { - if(a > 0.0f) - m_Pos = Pos; - else - m_Pos = NewPos; - return; - } - } + // check player collision + float Distance = distance(m_Pos, NewPos); + int End = Distance+1; + vec2 LastPos = m_Pos; + for(int i = 0; i < End; i++) + { + float a = i/Distance; + vec2 Pos = mix(m_Pos, NewPos, a); + for(int p = 0; p < MAX_CLIENTS; p++) + { + CCharacterCore *pCharCore = m_pWorld->m_apCharacters[p]; + if(!pCharCore || pCharCore == this) + continue; + float D = distance(Pos, pCharCore->m_Pos); + if(D < 28.0f && D > 0.0f) + { + if(a > 0.0f) + m_Pos = LastPos; + else if(distance(NewPos, pCharCore->m_Pos) > D) + m_Pos = NewPos; + return; + } + } + LastPos = Pos; } } diff --git a/src/game/mapitems.h b/src/game/mapitems.h index d99d6724..fb66e12d 100644 --- a/src/game/mapitems.h +++ b/src/game/mapitems.h @@ -53,6 +53,7 @@ enum TILEFLAG_ROTATE=8, LAYERFLAG_DETAIL=1, + TILESLAYERFLAG_GAME=1, ENTITY_OFFSET=255-16*4, }; @@ -114,13 +115,15 @@ struct CMapItemGroup_v1 struct CMapItemGroup : public CMapItemGroup_v1 { - enum { CURRENT_VERSION=2 }; + enum { CURRENT_VERSION=3 }; int m_UseClipping; int m_ClipX; int m_ClipY; int m_ClipW; int m_ClipH; + + int m_aName[3]; } ; struct CMapItemLayer @@ -145,6 +148,8 @@ struct CMapItemLayerTilemap int m_Image; int m_Data; + + int m_aName[3]; } ; struct CMapItemLayerQuads @@ -155,6 +160,8 @@ struct CMapItemLayerQuads int m_NumQuads; int m_Data; int m_Image; + + int m_aName[3]; } ; struct CMapItemVersion diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 821aade5..06c1c913 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -56,8 +56,30 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos) { m_EmoteStop = -1; m_LastAction = -1; - m_ActiveWeapon = WEAPON_GUN; - m_LastWeapon = WEAPON_HAMMER; + + /*zCatch */ + if(GameServer()->m_pController->IsZCatch() && g_Config.m_SvMode == 1) + { + m_ActiveWeapon = WEAPON_RIFLE; + m_LastWeapon = WEAPON_RIFLE; + } + else if(GameServer()->m_pController->IsZCatch() && g_Config.m_SvMode == 3) + { + m_ActiveWeapon = WEAPON_HAMMER; + m_LastWeapon = WEAPON_HAMMER; + } + else if(GameServer()->m_pController->IsZCatch() && g_Config.m_SvMode == 4) + { + m_ActiveWeapon = WEAPON_GRENADE; + m_LastWeapon = WEAPON_GRENADE; + } + else + { + m_ActiveWeapon = WEAPON_GUN; + m_LastWeapon = WEAPON_HAMMER; + } + /* end zCatch */ + m_QueuedWeapon = -1; m_pPlayer = pPlayer; @@ -115,20 +137,21 @@ void CCharacter::HandleNinja() if(m_ActiveWeapon != WEAPON_NINJA) return; - vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); - - if ((Server()->Tick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000)) + /* zCatch */ + if(GameServer()->m_pController->IsZCatch() == false || (GameServer()->m_pController->IsZCatch() && g_Config.m_SvMode == 0)) { - // time's up, return - m_aWeapons[WEAPON_NINJA].m_Got = false; - m_ActiveWeapon = m_LastWeapon; - if(m_ActiveWeapon == WEAPON_NINJA) - m_ActiveWeapon = WEAPON_GUN; + if ((Server()->Tick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000)) + { + // time's up, return + m_aWeapons[WEAPON_NINJA].m_Got = false; + m_ActiveWeapon = m_LastWeapon; - SetWeapon(m_ActiveWeapon); - return; + SetWeapon(m_ActiveWeapon); + return; + } } - + /* zCatch end*/ + // force ninja Weapon SetWeapon(WEAPON_NINJA); @@ -183,7 +206,7 @@ void CCharacter::HandleNinja() if(m_NumObjectsHit < 10) m_apHitObjects[m_NumObjectsHit++] = aEnts[i]; - aEnts[i]->TakeDamage(vec2(0, 10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA); + aEnts[i]->TakeDamage(vec2(0, -10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA); } } @@ -436,8 +459,6 @@ void CCharacter::HandleWeapons() //ninja HandleNinja(); - vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); - // check reload timer if(m_ReloadTimer) { @@ -490,7 +511,8 @@ void CCharacter::GiveNinja() m_Ninja.m_ActivationTick = Server()->Tick(); m_aWeapons[WEAPON_NINJA].m_Got = true; m_aWeapons[WEAPON_NINJA].m_Ammo = -1; - m_LastWeapon = m_ActiveWeapon; + if (m_ActiveWeapon != WEAPON_NINJA) + m_LastWeapon = m_ActiveWeapon; m_ActiveWeapon = WEAPON_NINJA; GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA); @@ -531,6 +553,18 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); } +void CCharacter::ResetInput() +{ + m_Input.m_Direction = 0; + m_Input.m_Hook = 0; + // simulate releasing the fire button + if((m_Input.m_Fire&1) != 0) + m_Input.m_Fire++; + m_Input.m_Fire &= INPUT_STATE_MASK; + m_Input.m_Jump = 0; + m_LatestPrevInput = m_LatestInput = m_Input; +} + void CCharacter::Tick() { if(m_pPlayer->m_ForceBalanced) @@ -700,11 +734,18 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) if(GameServer()->m_pController->IsFriendlyFire(m_pPlayer->GetCID(), From) && !g_Config.m_SvTeamdamage) return false; - - // m_pPlayer only inflicts half damage on self + + /* zCatch */ + bool Is_zCatch = GameServer()->m_pController->IsZCatch(); + if(From == m_pPlayer->GetCID()) - Dmg = max(1, Dmg/2); - + if(Is_zCatch && g_Config.m_SvMode != 1) + Dmg = 0; //No selfdamage, except in vanilla-mode + // m_pPlayer only inflicts half damage on self + else + Dmg = max(1, Dmg/2); + /* end zCatch */ + m_DamageTaken++; // create healthmod indicator @@ -718,8 +759,15 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) m_DamageTaken = 0; GameServer()->CreateDamageInd(m_Pos, 0, Dmg); } - - if(Dmg) + /* zCatch*/ + //One-Shot-One-Kill + if(Is_zCatch && (g_Config.m_SvMode != 0 && g_Config.m_SvMode != 2)) // all except vanilla-mode and all weapons + { + m_Health = 0; + m_Armor = 0; + } + /* end zCatch*/ + else if(Dmg) { if(m_Armor) { @@ -748,7 +796,15 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) // do damage Hit sound if(From >= 0 && From != m_pPlayer->GetCID() && GameServer()->m_apPlayers[From]) - GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, CmaskOne(From)); + { + int Mask = CmaskOne(From); + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS && GameServer()->m_apPlayers[i]->m_SpectatorID == From) + Mask |= CmaskOne(i); + } + GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, Mask); + } // check for death if(m_Health <= 0) @@ -821,7 +877,8 @@ void CCharacter::Snap(int SnappingClient) pCharacter->m_Direction = m_Input.m_Direction; - if(m_pPlayer->GetCID() == SnappingClient || SnappingClient == -1 || m_pPlayer->GetCID() == GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID) + if(m_pPlayer->GetCID() == SnappingClient || SnappingClient == -1 || + (!g_Config.m_SvStrictSpectateMode && m_pPlayer->GetCID() == GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID)) { pCharacter->m_Health = m_Health; pCharacter->m_Armor = m_Armor; diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 611dc427..a311a6f9 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -11,6 +11,7 @@ enum { + WEAPON_ANTICAMPER = -4, WEAPON_GAME = -3, // team switching etc WEAPON_SELF = -2, // console kill command WEAPON_WORLD = -1, // death tiles etc @@ -43,6 +44,7 @@ public: void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnDirectInput(CNetObj_PlayerInput *pNewInput); + void ResetInput(); void FireWeapon(); void Die(int Killer, int Weapon); diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index 30ed0a9b..af66fe0c 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -44,7 +44,6 @@ void CLaser::DoBounce() } vec2 To = m_Pos + m_Dir * m_Energy; - vec2 OrgTo = To; if(GameServer()->Collision()->IntersectLine(m_Pos, To, 0x0, &To)) { diff --git a/src/game/server/eventhandler.cpp b/src/game/server/eventhandler.cpp index deb1ca4e..354bd4ab 100644 --- a/src/game/server/eventhandler.cpp +++ b/src/game/server/eventhandler.cpp @@ -46,7 +46,7 @@ void CEventHandler::Snap(int SnappingClient) { if(SnappingClient == -1 || CmaskIsSet(m_aClientMasks[i], SnappingClient)) { - NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&m_aData[m_aOffsets[i]]; + CNetEvent_Common *ev = (CNetEvent_Common *)&m_aData[m_aOffsets[i]]; if(SnappingClient == -1 || distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, vec2(ev->m_X, ev->m_Y)) < 1500.0f) { void *d = GameServer()->Server()->SnapNewItem(m_aTypes[i], i, m_aSizes[i]); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 82adcbef..a34890e5 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -13,6 +13,7 @@ #include "gamemodes/tdm.h" #include "gamemodes/ctf.h" #include "gamemodes/mod.h" +#include "gamemodes/zcatch.hpp" enum { @@ -36,6 +37,9 @@ void CGameContext::Construct(int Resetting) if(Resetting==NO_RESET) m_pVoteOptionHeap = new CHeap(); + + for(int i = 0; i < MAX_MUTES; i++) + m_aMutes[i].m_IP[0] = 0; } CGameContext::CGameContext(int Resetting) @@ -93,7 +97,7 @@ void CGameContext::CreateDamageInd(vec2 Pos, float Angle, int Amount) for(int i = 0; i < Amount; i++) { float f = mix(s, e, float(i+1)/float(Amount+2)); - NETEVENT_DAMAGEIND *pEvent = (NETEVENT_DAMAGEIND *)m_Events.Create(NETEVENTTYPE_DAMAGEIND, sizeof(NETEVENT_DAMAGEIND)); + CNetEvent_DamageInd *pEvent = (CNetEvent_DamageInd *)m_Events.Create(NETEVENTTYPE_DAMAGEIND, sizeof(CNetEvent_DamageInd)); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -106,7 +110,7 @@ void CGameContext::CreateDamageInd(vec2 Pos, float Angle, int Amount) void CGameContext::CreateHammerHit(vec2 Pos) { // create the event - NETEVENT_HAMMERHIT *pEvent = (NETEVENT_HAMMERHIT *)m_Events.Create(NETEVENTTYPE_HAMMERHIT, sizeof(NETEVENT_HAMMERHIT)); + CNetEvent_HammerHit *pEvent = (CNetEvent_HammerHit *)m_Events.Create(NETEVENTTYPE_HAMMERHIT, sizeof(CNetEvent_HammerHit)); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -118,7 +122,7 @@ void CGameContext::CreateHammerHit(vec2 Pos) void CGameContext::CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamage) { // create the event - NETEVENT_EXPLOSION *pEvent = (NETEVENT_EXPLOSION *)m_Events.Create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION)); + CNetEvent_Explosion *pEvent = (CNetEvent_Explosion *)m_Events.Create(NETEVENTTYPE_EXPLOSION, sizeof(CNetEvent_Explosion)); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -162,7 +166,7 @@ void create_smoke(vec2 Pos) void CGameContext::CreatePlayerSpawn(vec2 Pos) { // create the event - NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)m_Events.Create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN)); + CNetEvent_Spawn *ev = (CNetEvent_Spawn *)m_Events.Create(NETEVENTTYPE_SPAWN, sizeof(CNetEvent_Spawn)); if(ev) { ev->m_X = (int)Pos.x; @@ -173,7 +177,7 @@ void CGameContext::CreatePlayerSpawn(vec2 Pos) void CGameContext::CreateDeath(vec2 Pos, int ClientID) { // create the event - NETEVENT_DEATH *pEvent = (NETEVENT_DEATH *)m_Events.Create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH)); + CNetEvent_Death *pEvent = (CNetEvent_Death *)m_Events.Create(NETEVENTTYPE_DEATH, sizeof(CNetEvent_Death)); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -188,7 +192,7 @@ void CGameContext::CreateSound(vec2 Pos, int Sound, int Mask) return; // create a sound - NETEVENT_SOUNDWORLD *pEvent = (NETEVENT_SOUNDWORLD *)m_Events.Create(NETEVENTTYPE_SOUNDWORLD, sizeof(NETEVENT_SOUNDWORLD), Mask); + CNetEvent_SoundWorld *pEvent = (CNetEvent_SoundWorld *)m_Events.Create(NETEVENTTYPE_SOUNDWORLD, sizeof(CNetEvent_SoundWorld), Mask); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -225,7 +229,7 @@ void CGameContext::SendChat(int ChatterClientID, int Team, const char *pText) str_format(aBuf, sizeof(aBuf), "%d:%d:%s: %s", ChatterClientID, Team, Server()->ClientName(ChatterClientID), pText); else str_format(aBuf, sizeof(aBuf), "*** %s", pText); - Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "chat", aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, Team!=CHAT_ALL?"teamchat":"chat", aBuf); if(Team == CHAT_ALL) { @@ -343,7 +347,8 @@ void CGameContext::SendVoteStatus(int ClientID, int Total, int Yes, int No) void CGameContext::AbortVoteKickOnDisconnect(int ClientID) { - if(m_VoteCloseTime && !str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientID) + if(m_VoteCloseTime && ((!str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientID) || + (!str_comp_num(m_aVoteCommand, "set_team ", 9) && str_toint(&m_aVoteCommand[9]) == ClientID))) m_VoteCloseTime = -1; } @@ -421,7 +426,10 @@ void CGameContext::OnTick() bool aVoteChecked[MAX_CLIENTS] = {0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(!m_apPlayers[i] || m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS || aVoteChecked[i]) // don't count in votes by spectators + /* zCatch - Allow voting from players in spectators (needed or the last 2 players ingame can kick the whole server), + * but deny votes from players who are explicit in spec + */ + if(!m_apPlayers[i] || m_apPlayers[i]->m_SpecExplicit == 1 || aVoteChecked[i]) // don't count in votes by spectators continue; int ActVote = m_apPlayers[i]->m_Vote; @@ -507,6 +515,74 @@ void CGameContext::OnClientEnter(int ClientID) { //world.insert_entity(&players[client_id]); m_apPlayers[ClientID]->Respawn(); + + /* begin zCatch */ + int leader_id = -1; + int StartTeam = m_pController->ClampTeam(1); + + if(m_pController->IsZCatch()) + { + int num = 0; + + for(int i=0; i<MAX_CLIENTS; i++) + { + if(IsClientReady(i)) + num++; + } + if(num < 3) + m_pController->EndRound(); + + if(g_Config.m_SvAllowJoin == 1) + { + m_apPlayers[ClientID]->m_CatchedBy = ZCATCH_NOT_CATCHED; + m_apPlayers[ClientID]->m_SpecExplicit = (num < 3) ? 0 : 1; + StartTeam = (num < 3) ? m_pController->ClampTeam(1) : TEAM_SPECTATORS; + SendBroadcast("You can join the game", ClientID); + + } + else if(g_Config.m_SvAllowJoin == 2) + { + int num2 = 0, num_prev = 0; + + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_apPlayers[i]) + { + num2 = 0; + for(int j = 0; j < MAX_CLIENTS; j++) + { + if(m_apPlayers[j] && m_apPlayers[j]->m_CatchedBy == i) + num2++; + } + if(num2 > num_prev) + { + leader_id = i; + num_prev = num2; + } + } + } + + if(leader_id > -1) + { + m_apPlayers[ClientID]->m_CatchedBy = leader_id; + m_apPlayers[ClientID]->m_SpecExplicit = 0; + m_apPlayers[ClientID]->m_SpectatorID = leader_id; + StartTeam = TEAM_SPECTATORS; + } + else + { + m_apPlayers[ClientID]->m_CatchedBy = ZCATCH_NOT_CATCHED; + m_apPlayers[ClientID]->m_SpecExplicit = 0; + } + } + else + StartTeam = m_pController->GetAutoTeam(ClientID); + } + + m_apPlayers[ClientID]->SetTeamDirect(StartTeam); + + /* end zCatch */ + char aBuf[512]; str_format(aBuf, sizeof(aBuf), "'%s' entered and joined the %s", Server()->ClientName(ClientID), m_pController->GetTeamName(m_apPlayers[ClientID]->GetTeam())); SendChat(-1, CGameContext::CHAT_ALL, aBuf); @@ -515,6 +591,21 @@ void CGameContext::OnClientEnter(int ClientID) Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); m_VoteUpdate = true; + + /* zCatch begin */ + if(m_pController->IsZCatch()) + { + SendChatTarget(ClientID, "Welcome to zCatch!"); + SendChatTarget(ClientID, "type /cmdlist to get all commands"); + SendChatTarget(ClientID, "type /help for instructions"); + if(g_Config.m_SvAllowJoin == 2 && leader_id > -1) + { + char buf[128]; + str_format(buf, sizeof(buf), "You will join the game when %s dies", Server()->ClientName(leader_id)); + SendChatTarget(ClientID, buf); + } + } + /* zCatch end */ } void CGameContext::OnClientConnected(int ClientID) @@ -599,8 +690,66 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) *pMessage = ' '; pMessage++; } + + //Check if the player is muted + char aAddrStr[NETADDR_MAXSTRSIZE] = {0}; + Server()->GetClientAddr(ClientID, aAddrStr, sizeof(aAddrStr)); + int Pos; + if((Pos = Muted(aAddrStr)) > -1) + { + char aBuf[128]; + int Expires = (m_aMutes[Pos].m_Expires - Server()->Tick())/Server()->TickSpeed(); + str_format(aBuf, sizeof(aBuf), "You are muted for %d minutes and %d seconds.", Expires/60, Expires%60); + SendChatTarget(ClientID, aBuf); + return; + } + else if(g_Config.m_SvMuteDuration && ((pPlayer->m_ChatTicks += g_Config.m_SvChatValue) > g_Config.m_SvChatThreshold)) //is he spamming? + { + AddMute(ClientID, g_Config.m_SvMuteDuration); + pPlayer->m_ChatTicks = 0; + return; + } - SendChat(ClientID, Team, pMsg->m_pMessage); + /* begin zCatch*/ + if(!str_comp("/info", pMsg->m_pMessage) || !str_comp("/about", pMsg->m_pMessage)) + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "zCatch version %s by erd and Teetime. Type /cmdlist for all commands.", ZCATCH_VERSION); + SendChatTarget(ClientID, " "); + SendChatTarget(ClientID, aBuf); + } + else if(!str_comp("/cmdlist", pMsg->m_pMessage)) + { + SendChatTarget(ClientID, " "); + SendChatTarget(ClientID, "/info or /about - see information about author."); + SendChatTarget(ClientID, "/help - learn how to play."); + SendChatTarget(ClientID, "/follow 1 or /follow 0 - Enables/Disables following of the catcher."); + } + else if(!str_comp("/help", pMsg->m_pMessage)) + { + SendChatTarget(ClientID, " "); + SendChatTarget(ClientID, "The winner is the tee which is left over at the end."); + SendChatTarget(ClientID, "If you die, all players that you killed will respawn."); + SendChatTarget(ClientID, "So the only way to win is to kill every player without beeing killed."); + SendChatTarget(ClientID, "Have fun!"); + } + else if(!str_comp("/follow 0", pMsg->m_pMessage)) + { + pPlayer->m_PlayerWantToFollowCatcher = 0; + pPlayer->m_SpectatorID = SPEC_FREEVIEW; + SendChatTarget(ClientID, "Follow of catcher disabled."); + } + else if(!str_comp("/follow 1", pMsg->m_pMessage)) + { + pPlayer->m_PlayerWantToFollowCatcher = 1; + SendChatTarget(ClientID, "Follow of catcher enabled."); + } + else if(!str_comp_num("/", pMsg->m_pMessage, 1)) + SendChatTarget(ClientID, "Unknown command."); + else + SendChat(ClientID, Team, pMsg->m_pMessage); + + /* end zCatch */ } else if(MsgID == NETMSGTYPE_CL_CALLVOTE) { @@ -609,7 +758,8 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) int64 Now = Server()->Tick(); pPlayer->m_LastVoteTry = Now; - if(pPlayer->GetTeam() == TEAM_SPECTATORS) + // zCatch - Only People who are explicit in Spectators can't vote! + if(pPlayer->m_SpecExplicit == 1) //zCatch { SendChatTarget(ClientID, "Spectators aren't allowed to start a vote."); return; @@ -672,7 +822,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) { int PlayerNum = 0; for(int i = 0; i < MAX_CLIENTS; ++i) - if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + if(m_apPlayers[i] && m_apPlayers[i]->m_SpecExplicit != 1) // zCatch - Count all Players who are not explicit in spectator ++PlayerNum; if(PlayerNum < g_Config.m_SvVoteKickMin) @@ -691,12 +841,12 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } if(KickID == ClientID) { - SendChatTarget(ClientID, "You cant kick yourself"); + SendChatTarget(ClientID, "You can't kick yourself"); return; } if(Server()->IsAuthed(KickID)) { - SendChatTarget(ClientID, "You cant kick admins"); + SendChatTarget(ClientID, "You can't kick admins"); char aBufKick[128]; str_format(aBufKick, sizeof(aBufKick), "'%s' called for vote to kick you", Server()->ClientName(ClientID)); SendChatTarget(KickID, aBufKick); @@ -731,13 +881,13 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } if(SpectateID == ClientID) { - SendChatTarget(ClientID, "You cant move yourself"); + SendChatTarget(ClientID, "You can't move yourself"); return; } str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to move '%s' to spectators (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), pReason); str_format(aDesc, sizeof(aDesc), "move '%s' to spectators", Server()->ClientName(SpectateID)); - str_format(aCmd, sizeof(aCmd), "set_team %d -1", SpectateID); + str_format(aCmd, sizeof(aCmd), "set_team %d -1 %d", SpectateID, g_Config.m_SvVoteSpectateRejoindelay); } if(aCmd[0]) @@ -773,17 +923,45 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam+Server()->TickSpeed()*3 > Server()->Tick())) return; + if(pPlayer->m_TeamChangeTick > Server()->Tick()) + { + pPlayer->m_LastSetTeam = Server()->Tick(); + int TimeLeft = (pPlayer->m_TeamChangeTick - Server()->Tick())/Server()->TickSpeed(); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Time to wait before changing team: %02d:%02d", TimeLeft/60, TimeLeft%60); + SendBroadcast(aBuf, ClientID); + return; + } + // Switch team on given client and kill/respawn him if(m_pController->CanJoinTeam(pMsg->m_Team, ClientID)) { - if(m_pController->CanChangeTeam(pPlayer, pMsg->m_Team)) + if(m_pController->CanChangeTeam(pPlayer, pMsg->m_Team) && !m_pController->IsZCatch()) //zCatch) { pPlayer->m_LastSetTeam = Server()->Tick(); if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS) m_VoteUpdate = true; pPlayer->SetTeam(pMsg->m_Team); (void)m_pController->CheckTeamBalance(); + pPlayer->m_TeamChangeTick = Server()->Tick(); + } + /* begin zCatch*/ + else if(m_pController->IsZCatch()) + { + if(pPlayer->m_CatchedBy >= 0) + { + char buf[256]; + str_format(buf, sizeof(buf), "You will join automatically when \"%s\" dies.", Server()->ClientName(pPlayer->m_CatchedBy)); + SendChatTarget(ClientID, buf); + return; + } + else if(pPlayer->m_CatchedBy == ZCATCH_NOT_CATCHED) + { + pPlayer->m_LastSetTeam = Server()->Tick(); + pPlayer->SetTeam(pMsg->m_Team); + } } + /* end zCatch*/ else SendBroadcast("Teams must be balanced, please join other team", ClientID); } @@ -945,14 +1123,75 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } else if (MsgID == NETMSGTYPE_CL_KILL && !m_World.m_Paused) { - if(pPlayer->m_LastKill && pPlayer->m_LastKill+Server()->TickSpeed()*3 > Server()->Tick()) - return; - - pPlayer->m_LastKill = Server()->Tick(); - pPlayer->KillCharacter(WEAPON_SELF); + /* begin zCatch*/ + if(pPlayer->m_LastKill && pPlayer->m_LastKill + Server()->TickSpeed()*15 > Server()->Tick()) + { + if((pPlayer->GetTeam() == TEAM_SPECTATORS) || (pPlayer->m_LastKillTry && pPlayer->m_LastKillTry+Server()->TickSpeed()*2 > Server()->Tick())) + return; + SendChatTarget(ClientID, "Only one kill in 15sec is allowed."); + pPlayer->m_LastKillTry = Server()->Tick(); + } + else + { + pPlayer->m_LastKill = Server()->Tick(); + pPlayer->KillCharacter(WEAPON_SELF); + pPlayer->m_Deaths++; + } + /* end zCatch*/ } } +void CGameContext::AddMute(const char* IP, int Secs) +{ + int Pos = Muted(IP); + if(Pos > -1) + m_aMutes[Pos].m_Expires = Server()->TickSpeed() * Secs + Server()->Tick(); // overwrite mute + else + for(int i = 0; i < MAX_MUTES; i++) // find free slot + if(!m_aMutes[i].m_IP[0]) + { + str_copy(m_aMutes[i].m_IP, IP, sizeof(m_aMutes[i].m_IP)); + m_aMutes[i].m_Expires = Server()->TickSpeed() * Secs + Server()->Tick(); + break; + } +} + +void CGameContext::AddMute(int ClientID, int Secs) +{ + char aAddrStr[NETADDR_MAXSTRSIZE] = {0}; + Server()->GetClientAddr(ClientID, aAddrStr, sizeof(aAddrStr)); + AddMute(aAddrStr, Secs); + + char aBuf[128]; + if(Secs > 0) + str_format(aBuf, sizeof(aBuf), "\"%s\" has been muted for %d seconds.", Server()->ClientName(ClientID), Secs); + else + str_format(aBuf, sizeof(aBuf), "\"%s\" has been unmuted.", Server()->ClientName(ClientID)); + SendChatTarget(-1, aBuf); +} + +int CGameContext::Muted(const char* IP) +{ + CleanMutes(); + int Pos = -1; + if(!IP[0]) + return -1; + for(int i = 0; i < MAX_MUTES; i++) + if(!str_comp_num(IP, m_aMutes[i].m_IP, sizeof(m_aMutes[i].m_IP))) + { + Pos = i; + break; + } + return Pos; +} + +void CGameContext::CleanMutes() +{ + for(int i = 0; i < MAX_MUTES; i++) + if(m_aMutes[i].m_Expires < Server()->Tick()) + m_aMutes[i].m_IP[0] = 0; +} + void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -1024,6 +1263,9 @@ void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData) CGameContext *pSelf = (CGameContext *)pUserData; int ClientID = clamp(pResult->GetInteger(0), 0, (int)MAX_CLIENTS-1); int Team = clamp(pResult->GetInteger(1), -1, 1); + int Delay = 0; + if(pResult->NumArguments() > 2) + Delay = pResult->GetInteger(2); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "moved client %d to team %d", ClientID, Team); @@ -1032,6 +1274,7 @@ void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData) if(!pSelf->m_apPlayers[ClientID]) return; + pSelf->m_apPlayers[ClientID]->m_TeamChangeTick = pSelf->Server()->Tick()+pSelf->Server()->TickSpeed()*Delay*60; pSelf->m_apPlayers[ClientID]->SetTeam(Team); (void)pSelf->m_pController->CheckTeamBalance(); } @@ -1248,7 +1491,9 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData) return; } - str_format(aBuf, sizeof(aBuf), "set_team %d -1", SpectateID); + str_format(aBuf, sizeof(aBuf), "admin moved '%s' to spectator (%s)", pSelf->Server()->ClientName(SpectateID), pReason); + pSelf->SendChatTarget(-1, aBuf); + str_format(aBuf, sizeof(aBuf), "set_team %d -1 %d", SpectateID, g_Config.m_SvVoteSpectateRejoindelay); pSelf->Console()->ExecuteLine(aBuf); } } @@ -1274,6 +1519,8 @@ void CGameContext::ConVote(IConsole::IResult *pResult, void *pUserData) else if(str_comp_nocase(pResult->GetString(0), "no") == 0) pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_NO; char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "admin forced vote %s", pResult->GetString(0)); + pSelf->SendChatTarget(-1, aBuf); str_format(aBuf, sizeof(aBuf), "forcing vote %s", pResult->GetString(0)); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } @@ -1292,28 +1539,81 @@ void CGameContext::ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *p } } -void CGameContext::OnConsoleInit() +void CGameContext::ConMute(IConsole::IResult *pResult, void *pUserData) { - m_pServer = Kernel()->RequestInterface<IServer>(); - m_pConsole = Kernel()->RequestInterface<IConsole>(); + CGameContext *pSelf = (CGameContext *)pUserData; + int CID = pResult->GetInteger(0); + if(CID < 0 || CID >= MAX_CLIENTS || !pSelf->m_apPlayers[CID]) + return; + + pSelf->AddMute(CID, pResult->GetInteger(1)); +} - Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, ""); - Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, ""); - Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, ""); +void CGameContext::ConMutes(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + char aBuf[128]; + int Sec, Count = 0; + pSelf->CleanMutes(); + for(int i = 0; i < MAX_MUTES; i++) + { + if(pSelf->m_aMutes[i].m_IP[0] == 0) + continue; + + Sec = (pSelf->m_aMutes[i].m_Expires - pSelf->Server()->Tick())/pSelf->Server()->TickSpeed(); + str_format(aBuf, sizeof(aBuf), "#%d: %s for %d minutes and %d sec", i, pSelf->m_aMutes[i].m_IP, Sec/60, Sec%60); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf); + Count++; + } + str_format(aBuf, sizeof(aBuf), "%d mute(s)", Count); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf); +} - Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, ""); - Console()->Register("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, ""); - Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, ""); - Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, ""); - Console()->Register("set_team", "ii", CFGFLAG_SERVER, ConSetTeam, this, ""); - Console()->Register("set_team_all", "i", CFGFLAG_SERVER, ConSetTeamAll, this, ""); +void CGameContext::ConUnmute(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + int MuteID = pResult->GetInteger(0); + char aBuf[128]; + + if(MuteID < 0 || MuteID >= MAX_MUTES) + return; + + if(pSelf->Muted(pSelf->m_aMutes[MuteID].m_IP) > -1) + { + str_format(aBuf, sizeof(aBuf), "unmuted %s", pSelf->m_aMutes[MuteID].m_IP); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf); + pSelf->AddMute(pSelf->m_aMutes[MuteID].m_IP, 0); + } + else + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", "mute not found"); +} - Console()->Register("add_vote", "sr", CFGFLAG_SERVER, ConAddVote, this, ""); - Console()->Register("remove_vote", "s", CFGFLAG_SERVER, ConRemoveVote, this, ""); - Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConForceVote, this, ""); - Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, ""); - Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, ""); +void CGameContext::OnConsoleInit() +{ + m_pServer = Kernel()->RequestInterface<IServer>(); + m_pConsole = Kernel()->RequestInterface<IConsole>(); + Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, "Tune variable to value"); + Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "Reset tuning"); + Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "Dump tuning"); + + Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, "Change map"); + Console()->Register("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, "Restart in x seconds"); + Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, "Broadcast message"); + Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, "Say in chat"); + Console()->Register("set_team", "ii?i", CFGFLAG_SERVER, ConSetTeam, this, "Set team of player to team"); + Console()->Register("set_team_all", "i", CFGFLAG_SERVER, ConSetTeamAll, this, "Set team of all players to team"); + + Console()->Register("add_vote", "sr", CFGFLAG_SERVER, ConAddVote, this, "Add a voting option"); + Console()->Register("remove_vote", "s", CFGFLAG_SERVER, ConRemoveVote, this, "remove a voting option"); + Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConForceVote, this, "Force a voting option"); + Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, "Clears the voting options"); + Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, "Force a vote to yes/no"); + + Console()->Register("mute", "ii", CFGFLAG_SERVER, ConMute, this, "Mutes a player for x sec"); + Console()->Register("unmute", "i", CFGFLAG_SERVER, ConUnmute, this, "Removes a mute by its index"); + Console()->Register("mutes", "", CFGFLAG_SERVER, ConMutes, this, "Show all mutes"); + Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this); } @@ -1344,6 +1644,8 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) m_pController = new CGameControllerCTF(this); else if(str_comp(g_Config.m_SvGametype, "tdm") == 0) m_pController = new CGameControllerTDM(this); + else if(str_comp_nocase(g_Config.m_SvGametype, "zcatch") == 0) + m_pController = new CGameController_zCatch(this); else m_pController = new CGameControllerDM(this); diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 6288850d..267c388a 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -15,6 +15,9 @@ #include "gameworld.h" #include "player.h" +#define MAX_MUTES 25 +#define ZCATCH_VERSION "0.4.2 BETA" + /* Tick Game Context (CGameContext::tick) @@ -60,6 +63,10 @@ class CGameContext : public IGameServer static void ConClearVotes(IConsole::IResult *pResult, void *pUserData); static void ConVote(IConsole::IResult *pResult, void *pUserData); static void ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + + static void ConMute(IConsole::IResult *pResult, void *pUserData); + static void ConUnmute(IConsole::IResult *pResult, void *pUserData); + static void ConMutes(IConsole::IResult *pResult, void *pUserData); CGameContext(int Resetting); void Construct(int Resetting); @@ -128,6 +135,23 @@ public: CHAT_RED=0, CHAT_BLUE=1 }; + + enum + { + ZCATCH_NOT_CATCHED = -1, + }; + + struct CMutes + { + char m_IP[NETADDR_MAXSTRSIZE]; + int m_Expires; + }; + CMutes m_aMutes[MAX_MUTES]; + // helper functions + void AddMute(const char* IP, int Secs); + void AddMute(int ClientID, int Secs); + int Muted(const char* IP); + void CleanMutes(); // network void SendChatTarget(int To, const char *pText); diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 6685bba3..eb9dc9b5 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -63,10 +63,25 @@ void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type) for(int i = 0; i < m_aNumSpawnPoints[Type]; i++) { // check if the position is occupado - if(GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, 0, 1, CGameWorld::ENTTYPE_CHARACTER)) - continue; + CCharacter *aEnts[MAX_CLIENTS]; + int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); + vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) }; // start, left, up, right, down + int Result = -1; + for(int Index = 0; Index < 5 && Result == -1; ++Index) + { + Result = Index; + for(int c = 0; c < Num; ++c) + if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) || + distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius) + { + Result = -1; + break; + } + } + if(Result == -1) + continue; // try next spawn point - vec2 P = m_aaSpawnPoints[Type][i]; + vec2 P = m_aaSpawnPoints[Type][i]+Positions[Result]; float S = EvaluateSpawnPos(pEval, P); if(!pEval->m_Got || pEval->m_Score > S) { @@ -77,47 +92,6 @@ void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type) } } -void IGameController::FindFreeSpawn(CSpawnEval *pEval, int Type) -{ - // pick the spawn point that is least occupied and has free space for spawning around it - for(int i = 0; i < m_aNumSpawnPoints[Type]; i++) - { - - CCharacter *aEnts[MAX_CLIENTS]; - int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); - float Score = 0.0f; - for(int c = 0; c < Num; ++c) - Score += 96.0f - distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]); - - if(!pEval->m_Got || pEval->m_Score > Score) - { - // start, left, up, right, down - vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) }; - - // check for free space - int Result = -1; - for(int Index = 0; Index < 5 && Result == -1; ++Index) - { - Result = Index; - for(int c = 0; c < Num; ++c) - if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) || - distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius) - { - Result = -1; - break; - } - } - - if(Result == -1) - continue; // try next spawn point - - pEval->m_Got = true; - pEval->m_Score = Score; - pEval->m_Pos = m_aaSpawnPoints[Type][i]+Positions[Result]; - } - } -} - bool IGameController::CanSpawn(int Team, vec2 *pOutPos) { CSpawnEval Eval; @@ -146,28 +120,6 @@ bool IGameController::CanSpawn(int Team, vec2 *pOutPos) EvaluateSpawnType(&Eval, 2); } - // handle crappy maps - if(!Eval.m_Got) - { - if(IsTeamplay()) - { - // first try own team spawn, then normal spawn and then enemy - FindFreeSpawn(&Eval, 1+(Team&1)); - if(!Eval.m_Got) - { - FindFreeSpawn(&Eval, 0); - if(!Eval.m_Got) - FindFreeSpawn(&Eval, 1+((Team+1)&1)); - } - } - else - { - FindFreeSpawn(&Eval, 0); - FindFreeSpawn(&Eval, 1); - FindFreeSpawn(&Eval, 2); - } - } - *pOutPos = Eval.m_Pos; return Eval.m_Got; } @@ -184,30 +136,35 @@ bool IGameController::OnEntity(int Index, vec2 Pos) m_aaSpawnPoints[1][m_aNumSpawnPoints[1]++] = Pos; else if(Index == ENTITY_SPAWN_BLUE) m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos; - else if(Index == ENTITY_ARMOR_1) - Type = POWERUP_ARMOR; - else if(Index == ENTITY_HEALTH_1) - Type = POWERUP_HEALTH; - else if(Index == ENTITY_WEAPON_SHOTGUN) - { - Type = POWERUP_WEAPON; - SubType = WEAPON_SHOTGUN; - } - else if(Index == ENTITY_WEAPON_GRENADE) - { - Type = POWERUP_WEAPON; - SubType = WEAPON_GRENADE; - } - else if(Index == ENTITY_WEAPON_RIFLE) - { - Type = POWERUP_WEAPON; - SubType = WEAPON_RIFLE; - } - else if(Index == ENTITY_POWERUP_NINJA && g_Config.m_SvPowerups) - { - Type = POWERUP_NINJA; - SubType = WEAPON_NINJA; - } + /* zCatch */ + else if(!GameServer()->m_pController->IsZCatch() || g_Config.m_SvMode == 0) + { + if(Index == ENTITY_ARMOR_1) + Type = POWERUP_ARMOR; + else if(Index == ENTITY_HEALTH_1) + Type = POWERUP_HEALTH; + else if(Index == ENTITY_WEAPON_SHOTGUN) + { + Type = POWERUP_WEAPON; + SubType = WEAPON_SHOTGUN; + } + else if(Index == ENTITY_WEAPON_GRENADE) + { + Type = POWERUP_WEAPON; + SubType = WEAPON_GRENADE; + } + else if(Index == ENTITY_WEAPON_RIFLE) + { + Type = POWERUP_WEAPON; + SubType = WEAPON_RIFLE; + } + else if(Index == ENTITY_POWERUP_NINJA && g_Config.m_SvPowerups) + { + Type = POWERUP_NINJA; + SubType = WEAPON_NINJA; + } + } + /* end zCatch*/ if(Type != -1) { @@ -219,6 +176,11 @@ bool IGameController::OnEntity(int Index, vec2 Pos) return false; } +bool IGameController::IsZCatch() +{ + return false; +} + void IGameController::EndRound() { if(m_Warmup) // game can't end when we are running warmup @@ -265,6 +227,7 @@ void IGameController::StartRound() m_aTeamscore[TEAM_RED] = 0; m_aTeamscore[TEAM_BLUE] = 0; m_ForceBalanced = false; + Server()->DemoRecorder_HandleAutoStart(); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS); GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); @@ -568,6 +531,8 @@ void IGameController::Tick() } } } + + DoWincheck(); } @@ -706,51 +671,50 @@ bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam) return true; } -void IGameController::DoPlayerScoreWincheck() +void IGameController::DoWincheck() { - if(m_GameOverTick == -1 && !m_Warmup) + if(m_GameOverTick == -1 && !m_Warmup && !GameServer()->m_World.m_ResetRequested) { - // gather some stats - int Topscore = 0; - int TopscoreCount = 0; - for(int i = 0; i < MAX_CLIENTS; i++) + if(IsTeamplay()) { - if(GameServer()->m_apPlayers[i]) + // check score win condition + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) { - if(GameServer()->m_apPlayers[i]->m_Score > Topscore) - { - Topscore = GameServer()->m_apPlayers[i]->m_Score; - TopscoreCount = 1; - } - else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) - TopscoreCount++; + if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) + EndRound(); + else + m_SuddenDeath = 1; } } - - // check score win condition - if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || - (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + else { - if(TopscoreCount == 1) - EndRound(); - else - m_SuddenDeath = 1; - } - } -} + // gather some stats + int Topscore = 0; + int TopscoreCount = 0; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i]) + { + if(GameServer()->m_apPlayers[i]->m_Score > Topscore) + { + Topscore = GameServer()->m_apPlayers[i]->m_Score; + TopscoreCount = 1; + } + else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) + TopscoreCount++; + } + } -void IGameController::DoTeamScoreWincheck() -{ - if(m_GameOverTick == -1 && !m_Warmup) - { - // check score win condition - if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || - (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) - { - if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) - EndRound(); - else - m_SuddenDeath = 1; + // check score win condition + if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + { + if(TopscoreCount == 1) + EndRound(); + else + m_SuddenDeath = 1; + } } } } diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h index 6ccfe977..914f7985 100644 --- a/src/game/server/gamecontroller.h +++ b/src/game/server/gamecontroller.h @@ -39,7 +39,6 @@ protected: float EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos); void EvaluateSpawnType(CSpawnEval *pEval, int Type); - void FindFreeSpawn(CSpawnEval *pEval, int Type); bool EvaluateSpawn(class CPlayer *pP, vec2 *pPos); void CycleMap(); @@ -69,13 +68,12 @@ public: IGameController(class CGameContext *pGameServer); virtual ~IGameController(); - void DoTeamScoreWincheck(); - void DoPlayerScoreWincheck(); + virtual void DoWincheck(); void DoWarmup(int Seconds); - void StartRound(); - void EndRound(); + virtual void StartRound(); + virtual void EndRound(); void ChangeMap(const char *pToMap); bool IsFriendlyFire(int ClientID1, int ClientID2); @@ -143,6 +141,8 @@ public: int ClampTeam(int Team); virtual void PostReset(); + + virtual bool IsZCatch(); }; #endif diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp index b1d3d2fa..66cc4c2c 100644 --- a/src/game/server/gamemodes/ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -1,6 +1,9 @@ /* (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/shared/config.h> + #include <game/mapitems.h> + #include <game/server/entities/character.h> #include <game/server/entities/flag.h> #include <game/server/player.h> @@ -63,6 +66,30 @@ int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlaye return HadFlag; } +void CGameControllerCTF::DoWincheck() +{ + if(m_GameOverTick == -1 && !m_Warmup) + { + // check score win condition + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + { + if(m_SuddenDeath) + { + if(m_aTeamscore[TEAM_RED]/100 != m_aTeamscore[TEAM_BLUE]/100) + EndRound(); + } + else + { + if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) + EndRound(); + else + m_SuddenDeath = 1; + } + } + } +} + bool CGameControllerCTF::CanBeMovedOnBalance(int ClientID) { CCharacter* Character = GameServer()->m_apPlayers[ClientID]->GetCharacter(); @@ -117,7 +144,8 @@ void CGameControllerCTF::Tick() { IGameController::Tick(); - DoTeamScoreWincheck(); + if(GameServer()->m_World.m_ResetRequested || GameServer()->m_World.m_Paused) + return; for(int fi = 0; fi < 2; fi++) { @@ -220,13 +248,16 @@ void CGameControllerCTF::Tick() for(int c = 0; c < MAX_CLIENTS; c++) { - if(!GameServer()->m_apPlayers[c]) + CPlayer *pPlayer = GameServer()->m_apPlayers[c]; + if(!pPlayer) continue; - if(GameServer()->m_apPlayers[c]->GetTeam() == fi) - GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, GameServer()->m_apPlayers[c]->GetCID()); + if(pPlayer->GetTeam() == TEAM_SPECTATORS && pPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[pPlayer->m_SpectatorID] && GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetTeam() == fi) + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c); + else if(pPlayer->GetTeam() == fi) + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c); else - GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, GameServer()->m_apPlayers[c]->GetCID()); + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, c); } break; } diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h index 44bd9e8e..72747ed7 100644 --- a/src/game/server/gamemodes/ctf.h +++ b/src/game/server/gamemodes/ctf.h @@ -11,6 +11,7 @@ public: class CFlag *m_apFlags[2]; CGameControllerCTF(class CGameContext *pGameServer); + virtual void DoWincheck(); virtual bool CanBeMovedOnBalance(int ClientID); virtual void Snap(int SnappingClient); virtual void Tick(); diff --git a/src/game/server/gamemodes/dm.cpp b/src/game/server/gamemodes/dm.cpp index d2b69b43..bdca4c9a 100644 --- a/src/game/server/gamemodes/dm.cpp +++ b/src/game/server/gamemodes/dm.cpp @@ -11,6 +11,5 @@ CGameControllerDM::CGameControllerDM(class CGameContext *pGameServer) void CGameControllerDM::Tick() { - DoPlayerScoreWincheck(); IGameController::Tick(); } diff --git a/src/game/server/gamemodes/mod.cpp b/src/game/server/gamemodes/mod.cpp index 127be2bc..eb8fd7c8 100644 --- a/src/game/server/gamemodes/mod.cpp +++ b/src/game/server/gamemodes/mod.cpp @@ -15,8 +15,6 @@ CGameControllerMOD::CGameControllerMOD(class CGameContext *pGameServer) void CGameControllerMOD::Tick() { // this is the main part of the gamemode, this function is run every tick - DoPlayerScoreWincheck(); // checks for winners, no teams version - //DoTeamScoreWincheck(); // checks for winners, two teams version IGameController::Tick(); } diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp index 54e645b3..50ecd93e 100644 --- a/src/game/server/gamemodes/tdm.cpp +++ b/src/game/server/gamemodes/tdm.cpp @@ -48,6 +48,5 @@ void CGameControllerTDM::Snap(int SnappingClient) void CGameControllerTDM::Tick() { - DoTeamScoreWincheck(); IGameController::Tick(); } diff --git a/src/game/server/gamemodes/zcatch.cpp b/src/game/server/gamemodes/zcatch.cpp new file mode 100644 index 00000000..eac8a4d3 --- /dev/null +++ b/src/game/server/gamemodes/zcatch.cpp @@ -0,0 +1,230 @@ +/* (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. */ +/* zCatch by erd and Teetime */ + +#include <engine/shared/config.h> +#include <game/server/gamecontext.h> +#include "zcatch.hpp" + +CGameController_zCatch::CGameController_zCatch(class CGameContext *pGameServer) +: IGameController(pGameServer) +{ + m_pGameType = "zCatch"; + m_OldMode = g_Config.m_SvMode; +} + +void CGameController_zCatch::Tick() +{ + DoWincheck(); + IGameController::Tick(); + + if(m_OldMode != g_Config.m_SvMode) + { + Server()->MapReload(); + m_OldMode = g_Config.m_SvMode; + } +} + +bool CGameController_zCatch::IsZCatch() +{ + return true; +} + +void CGameController_zCatch::DoWincheck() +{ + int Players = 0, Players_Spec = 0, Players_SpecExplicit = 0; + + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i]) + { + Players++; + if(GameServer()->m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS) + Players_Spec++; + if(GameServer()->m_apPlayers[i]->m_SpecExplicit == 1) + Players_SpecExplicit++; + } + } + + if(Players == 1) + { + //Do nothing + } + else if((Players - Players_Spec == 1) && (Players != Players_Spec) && (Players - Players_SpecExplicit != 1)) + { + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + GameServer()->m_apPlayers[i]->m_Score += g_Config.m_SvBonus; + } + EndRound(); + } + + IGameController::DoWincheck(); //do also usual wincheck +} + +int CGameController_zCatch::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int WeaponID) +{ + int VictimID = pVictim->GetPlayer()->GetCID(); + char buf[256]; + if(pKiller != pVictim->GetPlayer()) + { + pKiller->m_Kills++; + pVictim->GetPlayer()->m_Deaths++; + + pKiller->m_Score++; + pVictim->GetPlayer()->m_Score--; + + /* Check if the killer is already killed and in spectator (victim may died through wallshot) */ + if(pKiller->GetTeam() != TEAM_SPECTATORS) + { + pVictim->GetPlayer()->m_CatchedBy = pKiller->GetCID(); + pVictim->GetPlayer()->SetTeamDirect(TEAM_SPECTATORS); + + if(pVictim->GetPlayer()->m_PlayerWantToFollowCatcher) + pVictim->GetPlayer()->m_SpectatorID = pKiller->GetCID(); // Let the victim follow his catcher + + str_format(buf, sizeof(buf), "Caught by \"%s\". You will join the game automatically when \"%s\" dies.", Server()->ClientName(pKiller->GetCID()), Server()->ClientName(pKiller->GetCID())); + GameServer()->SendChatTarget(VictimID, buf); + } + } + else + { + //Punish selfkill/death + if(WeaponID == WEAPON_SELF || WeaponID == WEAPON_WORLD) + pVictim->GetPlayer()->m_Score -= 15; + } + + for(int i=0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i]) + { + if(GameServer()->m_apPlayers[i]->m_CatchedBy == VictimID) + { + GameServer()->m_apPlayers[i]->m_CatchedBy = ZCATCH_NOT_CATCHED; + GameServer()->m_apPlayers[i]->SetTeamDirect(GameServer()->m_pController->ClampTeam(1)); + + if(pKiller != pVictim->GetPlayer()) + pKiller->m_Score++; + } + } + } + + // Update color of the killer + OnPlayerInfoChange(pKiller); + + return 0; +} + +void CGameController_zCatch::OnPlayerInfoChange(class CPlayer *pP) +{ + if(g_Config.m_SvColorIndicator) + { + int Num = 161; + for(int i = 0; i < MAX_CLIENTS; i++) + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_CatchedBy == pP->GetCID()) + Num -= 10; + pP->m_TeeInfos.m_ColorBody = Num * 0x010000 + 0xff00; + pP->m_TeeInfos.m_ColorFeet = Num * 0x010000 + 0xff00; + pP->m_TeeInfos.m_UseCustomColor = 1; + } +} + +void CGameController_zCatch::StartRound() +{ + ResetGame(); + + m_RoundStartTick = Server()->Tick(); + m_SuddenDeath = 0; + m_GameOverTick = -1; + GameServer()->m_World.m_Paused = false; + m_aTeamscore[TEAM_RED] = 0; + m_aTeamscore[TEAM_BLUE] = 0; + m_UnbalancedTick = -1; + m_ForceBalanced = false; + for(int i=0; i<MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i]) + { + GameServer()->m_apPlayers[i]->m_CatchedBy = ZCATCH_NOT_CATCHED; + GameServer()->m_apPlayers[i]->m_Kills = 0; + GameServer()->m_apPlayers[i]->m_Deaths = 0; + GameServer()->m_apPlayers[i]->m_TicksSpec = 0; + GameServer()->m_apPlayers[i]->m_TicksIngame = 0; + } + } + char aBufMsg[256]; + str_format(aBufMsg, sizeof(aBufMsg), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS); + GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBufMsg); +} + +void CGameController_zCatch::OnCharacterSpawn(class CCharacter *pChr) +{ + // default health and armor + pChr->IncreaseHealth(10); + if(g_Config.m_SvMode == 2) + pChr->IncreaseArmor(10); + // give default weapons + switch(g_Config.m_SvMode) + { + case 0: + pChr->GiveWeapon(WEAPON_HAMMER, -1); + pChr->GiveWeapon(WEAPON_GUN, 10); + break; + case 1: + pChr->GiveWeapon(WEAPON_RIFLE, -1); + break; + case 2: + pChr->GiveWeapon(WEAPON_HAMMER, -1); + pChr->GiveWeapon(WEAPON_GUN, -1); + pChr->GiveWeapon(WEAPON_GRENADE, -1); + pChr->GiveWeapon(WEAPON_SHOTGUN, -1); + pChr->GiveWeapon(WEAPON_RIFLE, -1); + break; + case 3: + pChr->GiveWeapon(WEAPON_HAMMER, -1); + break; + case 4: + pChr->GiveWeapon(WEAPON_GRENADE, -1); + break; + case 5: + pChr->GiveNinja(); + break; + } + //Update color of spawning tees + OnPlayerInfoChange(pChr->GetPlayer()); +} + +void CGameController_zCatch::EndRound() +{ + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i]) + { + + if(GameServer()->m_apPlayers[i]->m_SpecExplicit == 0) + { + GameServer()->m_apPlayers[i]->SetTeamDirect(GameServer()->m_pController->ClampTeam(1)); + + char abuf[128]; + str_format(abuf, sizeof(abuf), "Kills: %d | Deaths: %d", GameServer()->m_apPlayers[i]->m_Kills, GameServer()->m_apPlayers[i]->m_Deaths); + GameServer()->SendChatTarget(i, abuf); + + if(GameServer()->m_apPlayers[i]->m_TicksSpec != 0 || GameServer()->m_apPlayers[i]->m_TicksIngame != 0) + { + double TimeInSpec = (GameServer()->m_apPlayers[i]->m_TicksSpec*100.0) / (GameServer()->m_apPlayers[i]->m_TicksIngame + GameServer()->m_apPlayers[i]->m_TicksSpec); + str_format(abuf, sizeof(abuf), "Spec: %.2f%% | Ingame: %.2f%%", (double)TimeInSpec, (double)(100.0 - TimeInSpec)); + GameServer()->SendChatTarget(i, abuf); + } + GameServer()->m_apPlayers[i]->m_CatchedBy = ZCATCH_NOT_CATCHED; //Set all players in server as non-catched + } + } + } + + if(m_Warmup) // game can't end when we are running warmup + return; + + GameServer()->m_World.m_Paused = true; + m_GameOverTick = Server()->Tick(); + m_SuddenDeath = 0; +} diff --git a/src/game/server/gamemodes/zcatch.hpp b/src/game/server/gamemodes/zcatch.hpp new file mode 100644 index 00000000..58e43f61 --- /dev/null +++ b/src/game/server/gamemodes/zcatch.hpp @@ -0,0 +1,33 @@ +/* (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. */ +/* zCatch by erd and Teetime */ + +#ifndef GAME_SERVER_GAMEMODES_ZCATCH_H +#define GAME_SERVER_GAMEMODES_ZCATCH_H + +#include <game/server/gamecontroller.h> + +class CGameController_zCatch : public IGameController +{ + private: + int m_OldMode; + + public: + CGameController_zCatch(class CGameContext *pGameServer); + virtual void Tick(); + virtual void DoWincheck(); + virtual bool IsZCatch(); + + enum + { + ZCATCH_NOT_CATCHED = -1, + }; + + virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int WeaponID); + virtual void StartRound(); + virtual void OnCharacterSpawn(class CCharacter *pChr); + virtual void OnPlayerInfoChange(class CPlayer *pP); + virtual void EndRound(); +}; + +#endif diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index 6444cce5..89f4808d 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -183,7 +183,6 @@ CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, v { // Find other players float ClosestLen = distance(Pos0, Pos1) * 100.0f; - vec2 LineDir = normalize(Pos1-Pos0); CCharacter *pClosest = 0; CCharacter *p = (CCharacter *)FindFirst(ENTTYPE_CHARACTER); diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index aeafe3a4..7464e3c7 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -15,17 +15,29 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) m_RespawnTick = Server()->Tick(); m_DieTick = Server()->Tick(); m_ScoreStartTick = Server()->Tick(); - Character = 0; - this->m_ClientID = ClientID; + m_pCharacter = 0; + m_ClientID = ClientID; m_Team = GameServer()->m_pController->ClampTeam(Team); m_SpectatorID = SPEC_FREEVIEW; m_LastActionTick = Server()->Tick(); + m_TeamChangeTick = Server()->Tick(); + + //zCatch + m_CatchedBy = -1; + m_SpecExplicit = 0; + m_Kills = 0; + m_Deaths = 0; + m_PlayerWantToFollowCatcher = g_Config.m_SvFollowCatcher; + m_LastKillTry = Server()->Tick(); + m_TicksSpec = 0; + m_TicksIngame = 0; + m_ChatTicks = 0; } CPlayer::~CPlayer() { - delete Character; - Character = 0; + delete m_pCharacter; + m_pCharacter = 0; } void CPlayer::Tick() @@ -37,6 +49,20 @@ void CPlayer::Tick() return; Server()->SetClientScore(m_ClientID, m_Score); + + /* begin zCatch*/ + + if(m_Team == TEAM_SPECTATORS) + m_TicksSpec++; + else + m_TicksIngame++; + + if(m_ChatTicks > 0) + m_ChatTicks--; + + if(g_Config.m_SvAnticamper && m_pCharacter && !GameServer()->m_World.m_Paused) + Anticamper(); + /* end zCatch*/ // do latency stuff { @@ -59,19 +85,22 @@ void CPlayer::Tick() } } - if(!Character && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick()) + if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW) + m_ViewPos -= vec2(clamp(m_ViewPos.x-m_LatestActivity.m_TargetX, -500.0f, 500.0f), clamp(m_ViewPos.y-m_LatestActivity.m_TargetY, -400.0f, 400.0f)); + + if(!m_pCharacter && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick()) m_Spawning = true; - if(Character) + if(m_pCharacter) { - if(Character->IsAlive()) + if(m_pCharacter->IsAlive()) { - m_ViewPos = Character->m_Pos; + m_ViewPos = m_pCharacter->m_Pos; } else { - delete Character; - Character = 0; + delete m_pCharacter; + m_pCharacter = 0; } } else if(m_Spawning && m_RespawnTick <= Server()->Tick()) @@ -160,23 +189,38 @@ void CPlayer::OnDisconnect(const char *pReason) void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput) { - if(Character) - Character->OnPredictedInput(NewInput); + // skip the input if chat is active + if((m_PlayerFlags&PLAYERFLAG_CHATTING) && (NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING)) + return; + + if(m_pCharacter) + m_pCharacter->OnPredictedInput(NewInput); } void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) { + if(NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING) + { + // skip the input if chat is active + if(m_PlayerFlags&PLAYERFLAG_CHATTING) + return; + + // reset input + if(m_pCharacter) + m_pCharacter->ResetInput(); + + m_PlayerFlags = NewInput->m_PlayerFlags; + return; + } + m_PlayerFlags = NewInput->m_PlayerFlags; - if(Character) - Character->OnDirectInput(NewInput); + if(m_pCharacter) + m_pCharacter->OnDirectInput(NewInput); - if(!Character && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1)) + if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1)) m_Spawning = true; - if(!Character && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW) - m_ViewPos = vec2(NewInput->m_TargetX, NewInput->m_TargetY); - // check for activity if(NewInput->m_Direction || m_LatestActivity.m_TargetX != NewInput->m_TargetX || m_LatestActivity.m_TargetY != NewInput->m_TargetY || NewInput->m_Jump || @@ -190,18 +234,18 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) CCharacter *CPlayer::GetCharacter() { - if(Character && Character->IsAlive()) - return Character; + if(m_pCharacter && m_pCharacter->IsAlive()) + return m_pCharacter; return 0; } void CPlayer::KillCharacter(int Weapon) { - if(Character) + if(m_pCharacter) { - Character->Die(m_ClientID, Weapon); - delete Character; - Character = 0; + m_pCharacter->Die(m_ClientID, Weapon); + delete m_pCharacter; + m_pCharacter = 0; } } @@ -244,6 +288,11 @@ void CPlayer::SetTeam(int Team) } } +void CPlayer::SetTeamDirect(int Team) +{ + m_Team = Team; +} + void CPlayer::TryRespawn() { vec2 SpawnPos; @@ -252,7 +301,43 @@ void CPlayer::TryRespawn() return; m_Spawning = false; - Character = new(m_ClientID) CCharacter(&GameServer()->m_World); - Character->Spawn(this, SpawnPos); + m_pCharacter = new(m_ClientID) CCharacter(&GameServer()->m_World); + m_pCharacter->Spawn(this, SpawnPos); GameServer()->CreatePlayerSpawn(SpawnPos); } + +int CPlayer::Anticamper() +{ + int AnticamperTime = g_Config.m_SvAnticamperTime; + int AnticamperRange = g_Config.m_SvAnticamperRange; + + if(m_CampTick == -1) + { + m_CampPos = m_pCharacter->m_Pos; + m_CampTick = Server()->Tick() + Server()->TickSpeed()*AnticamperTime; + } + + // Check if the player is moving + if((m_CampPos.x - m_pCharacter->m_Pos.x >= (float)AnticamperRange || m_CampPos.x - m_pCharacter->m_Pos.x <= -(float)AnticamperRange) + || (m_CampPos.y - m_pCharacter->m_Pos.y >= (float)AnticamperRange || m_CampPos.y - m_pCharacter->m_Pos.y <= -(float)AnticamperRange)) + { + m_CampTick = -1; + } + + // Send warning to the player + if(m_CampTick <= Server()->Tick() + Server()->TickSpeed() * AnticamperTime/2 && m_CampTick != -1 && !m_SentCampMsg) + { + GameServer()->SendBroadcast("ANTICAMPER: Move or die", m_ClientID); + m_SentCampMsg = true; + } + + // Kill him + if((m_CampTick <= Server()->Tick()) && (m_CampTick > 0)) + { + m_pCharacter->Die(m_ClientID, WEAPON_ANTICAMPER); + m_CampTick = -1; + m_SentCampMsg = false; + return 1; + } + return 0; +} diff --git a/src/game/server/player.h b/src/game/server/player.h index 9d5e462c..99de2952 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -21,6 +21,7 @@ public: void TryRespawn(); void Respawn(); void SetTeam(int Team); + void SetTeamDirect(int Team); //zCatch int GetTeam() const { return m_Team; }; int GetCID() const { return m_ClientID; }; @@ -78,6 +79,7 @@ public: int m_ScoreStartTick; bool m_ForceBalanced; int m_LastActionTick; + int m_TeamChangeTick; struct { int m_TargetX; @@ -94,9 +96,26 @@ public: int m_Min; int m_Max; } m_Latency; - + + //zCatch: + int m_CatchedBy; + int m_SpecExplicit; + int m_Deaths; + int m_Kills; + int m_LastKillTry; + bool m_PlayerWantToFollowCatcher; + + int m_TicksSpec; + int m_TicksIngame; + int m_ChatTicks; + //Anticamper + int Anticamper(); + bool m_SentCampMsg; + int m_CampTick; + vec2 m_CampPos; + private: - CCharacter *Character; + CCharacter *m_pCharacter; CGameContext *m_pGameServer; CGameContext *GameServer() const { return m_pGameServer; } diff --git a/src/game/variables.h b/src/game/variables.h index ee8cb74a..3f0d7bf9 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -45,6 +45,7 @@ MACRO_CONFIG_INT(UiPage, ui_page, 5, 0, 10, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interf MACRO_CONFIG_INT(UiToolboxPage, ui_toolbox_page, 0, 0, 2, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toolbox page") MACRO_CONFIG_STR(UiServerAddress, ui_server_address, 64, "localhost:8303", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface server address") MACRO_CONFIG_INT(UiScale, ui_scale, 100, 50, 150, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface scale") +MACRO_CONFIG_INT(UiMousesens, ui_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity for menus/editor") MACRO_CONFIG_INT(UiColorHue, ui_color_hue, 160, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color hue") MACRO_CONFIG_INT(UiColorSat, ui_color_sat, 70, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color saturation") @@ -73,7 +74,9 @@ MACRO_CONFIG_INT(SvTeambalanceTime, sv_teambalance_time, 1, 0, 1000, CFGFLAG_SER MACRO_CONFIG_INT(SvInactiveKickTime, sv_inactivekick_time, 3, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before taking care of inactive players") MACRO_CONFIG_INT(SvInactiveKick, sv_inactivekick, 1, 0, 2, CFGFLAG_SERVER, "How to deal with inactive players (0=move to spectator, 1=move to free spectator slot/kick, 2=kick)") +MACRO_CONFIG_INT(SvStrictSpectateMode, sv_strict_spectate_mode, 0, 0, 1, CFGFLAG_SERVER, "Restricts information in spectator mode") MACRO_CONFIG_INT(SvVoteSpectate, sv_vote_spectate, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to move players to spectators") +MACRO_CONFIG_INT(SvVoteSpectateRejoindelay, sv_vote_spectate_rejoindelay, 3, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before a player can rejoin after being moved to spectators by vote") MACRO_CONFIG_INT(SvVoteKick, sv_vote_kick, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to kick players") MACRO_CONFIG_INT(SvVoteKickMin, sv_vote_kick_min, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Minimum number of players required to start a kick vote") MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time to ban a player if kicked by vote. 0 makes it just use kick") @@ -85,4 +88,19 @@ MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SE MACRO_CONFIG_INT(DbgFocus, dbg_focus, 0, 0, 1, CFGFLAG_CLIENT, "") MACRO_CONFIG_INT(DbgTuning, dbg_tuning, 0, 0, 1, CFGFLAG_CLIENT, "") + +//zCatch: +MACRO_CONFIG_INT(SvMode, sv_mode, 1, 0, 5, CFGFLAG_SERVER, "0 - Normal; 1 - Instagib; 2 - Rocket area; 3 - Hammerparty; 4 - Grenade; 5 - Ninja") +MACRO_CONFIG_INT(SvAllowJoin, sv_allow_join, 2, 1, 2, CFGFLAG_SERVER, "Allow new Players to join without waiting for the next round") +//1 = Allowed to join; 2 = Will join when person with the most kills die +MACRO_CONFIG_INT(SvColorIndicator, sv_color_indicator, 1, 0, 1, CFGFLAG_SERVER, "Color tees apropriate to the number of currently catched players") +MACRO_CONFIG_INT(SvBonus, sv_bonus, 0, 0, 10000, CFGFLAG_SERVER, "Give the last player extra points") +MACRO_CONFIG_INT(SvFollowCatcher, sv_follow_catcher, 1, 0, 1, CFGFLAG_SERVER, "If a victim should follow his catcher") + +MACRO_CONFIG_INT(SvChatValue, sv_chat_value, 250, 100, 1000, CFGFLAG_SERVER, "A value wich is added on each message and decreased on each tick") +MACRO_CONFIG_INT(SvChatThreshold, sv_chat_threshold, 1000, 250, 10000, CFGFLAG_SERVER, "If this threshold will exceed by too many messages the player will be muted") +MACRO_CONFIG_INT(SvMuteDuration, sv_mute_duration, 60, 0, 3600, CFGFLAG_SERVER, "How long the player will be muted (in seconds)") +MACRO_CONFIG_INT(SvAnticamper, sv_anticamper, 1, 0, 1, CFGFLAG_SERVER, "Toggle to enable/disable Anticamper") +MACRO_CONFIG_INT(SvAnticamperTime, sv_anticamper_time, 10, 5, 120, CFGFLAG_SERVER, "How long to wait till the player dies/freezes") +MACRO_CONFIG_INT(SvAnticamperRange, sv_anticamper_range, 200, 0, 1000, CFGFLAG_SERVER, "Distance how far away the player must move to escape anticamper") #endif diff --git a/src/game/version.h b/src/game/version.h index 8cf1a7c0..76b6d4ab 100644 --- a/src/game/version.h +++ b/src/game/version.h @@ -2,7 +2,7 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #ifndef GAME_VERSION_H #define GAME_VERSION_H -#include "generated/nethash.c" -#define GAME_VERSION "0.6 trunk" +#include "generated/nethash.cpp" +#define GAME_VERSION "0.6.1" #define GAME_NETVERSION "0.6 " GAME_NETVERSION_HASH #endif |