diff options
| author | Teetime <TeetimeTW@yahoo.de> | 2012-02-17 19:31:43 +0100 |
|---|---|---|
| committer | Teetime <TeetimeTW@yahoo.de> | 2012-02-17 19:31:43 +0100 |
| commit | fc99167e687ba688ce9306bbec7e801450c917be (patch) | |
| tree | b440f2568a98b4b6a8db17925a299142adead7fa /src/game | |
| parent | 206e9adb9140c3c8963661f534703de33f8abe05 (diff) | |
| parent | 44a47d4253a829abcf50dac5586fd9a351f0c66b (diff) | |
| download | zcatch-fc99167e687ba688ce9306bbec7e801450c917be.tar.gz zcatch-fc99167e687ba688ce9306bbec7e801450c917be.zip | |
Merge branch 'master' into zCatch-Exp
Conflicts: src/engine/server.h src/engine/server/server.cpp src/engine/server/server.h src/engine/shared/network.h src/engine/shared/network_server.cpp src/game/server/player.h
Diffstat (limited to 'src/game')
65 files changed, 1625 insertions, 515 deletions
diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index aba38bf6..340b9da1 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -41,6 +41,11 @@ void CChat::OnReset() m_PlaceholderOffset = 0; m_PlaceholderLength = 0; m_pHistoryEntry = 0x0; + m_PendingChatCounter = 0; + m_LastChatSend = 0; + + for(int i = 0; i < CHAT_NUM; ++i) + m_aLastSoundPlayed[i] = 0; } void CChat::OnRelease() @@ -107,9 +112,13 @@ bool CChat::OnInput(IInput::CEvent Event) { if(m_Input.GetString()[0]) { - Say(m_Mode == MODE_ALL ? 0 : 1, m_Input.GetString()); - char *pEntry = m_History.Allocate(m_Input.GetLength()+1); - mem_copy(pEntry, m_Input.GetString(), m_Input.GetLength()+1); + if(m_LastChatSend+time_freq() < time_get()) + Say(m_Mode == MODE_ALL ? 0 : 1, m_Input.GetString()); + else + ++m_PendingChatCounter; + CHistoryEntry *pEntry = m_History.Allocate(sizeof(CHistoryEntry)+m_Input.GetLength()); + pEntry->m_Team = m_Mode == MODE_ALL ? 0 : 1; + mem_copy(pEntry->m_aText, m_Input.GetString(), m_Input.GetLength()+1); } m_pHistoryEntry = 0x0; m_Mode = MODE_NONE; @@ -199,26 +208,26 @@ bool CChat::OnInput(IInput::CEvent Event) } if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_UP) { - if (m_pHistoryEntry) + if(m_pHistoryEntry) { - char *pTest = m_History.Prev(m_pHistoryEntry); + CHistoryEntry *pTest = m_History.Prev(m_pHistoryEntry); - if (pTest) + if(pTest) m_pHistoryEntry = pTest; } else m_pHistoryEntry = m_History.Last(); - if (m_pHistoryEntry) - m_Input.Set(m_pHistoryEntry); + if(m_pHistoryEntry) + m_Input.Set(m_pHistoryEntry->m_aText); } else if (Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_DOWN) { - if (m_pHistoryEntry) + if(m_pHistoryEntry) m_pHistoryEntry = m_History.Next(m_pHistoryEntry); if (m_pHistoryEntry) - m_Input.Set(m_pHistoryEntry); + m_Input.Set(m_pHistoryEntry->m_aText); else m_Input.Clear(); } @@ -257,7 +266,8 @@ void CChat::OnMessage(int MsgType, void *pRawMsg) void CChat::AddLine(int ClientID, int Team, const char *pLine) { if(ClientID != -1 && (m_pClient->m_aClients[ClientID].m_aName[0] == '\0' || // unknown client - m_pClient->m_aClients[ClientID].m_ChatIgnore)) + m_pClient->m_aClients[ClientID].m_ChatIgnore || + (m_pClient->m_Snap.m_LocalClientID != ClientID && g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[ClientID].m_Friend))) return; bool Highlighted = false; @@ -283,7 +293,7 @@ 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; - + // 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) @@ -322,16 +332,50 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine) } // play sound + int64 Now = time_get(); if(ClientID == -1) - m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_SERVER, 0, vec2(0,0)); + { + if(Now-m_aLastSoundPlayed[CHAT_SERVER] >= time_freq()*3/10) + { + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_SERVER, 0); + m_aLastSoundPlayed[CHAT_SERVER] = Now; + } + } else if(Highlighted) - m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_HIGHLIGHT, 0, vec2(0.0f, 0.0f)); + { + if(Now-m_aLastSoundPlayed[CHAT_HIGHLIGHT] >= time_freq()*3/10) + { + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0); + m_aLastSoundPlayed[CHAT_HIGHLIGHT] = Now; + } + } else - m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0, vec2(0,0)); + { + if(Now-m_aLastSoundPlayed[CHAT_CLIENT] >= time_freq()*3/10) + { + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_HIGHLIGHT, 0); + m_aLastSoundPlayed[CHAT_CLIENT] = Now; + } + } } void CChat::OnRender() { + // send pending chat messages + if(m_PendingChatCounter > 0 && m_LastChatSend+time_freq() < time_get()) + { + CHistoryEntry *pEntry = m_History.Last(); + for(int i = m_PendingChatCounter-1; pEntry; --i, pEntry = m_History.Prev(pEntry)) + { + if(i == 0) + { + Say(pEntry->m_Team, pEntry->m_aText); + break; + } + } + --m_PendingChatCounter; + } + float Width = 300.0f*Graphics()->ScreenAspect(); Graphics()->MapScreen(0.0f, 0.0f, Width, 300.0f); float x = 5.0f; @@ -457,6 +501,8 @@ void CChat::OnRender() void CChat::Say(int Team, const char *pLine) { + m_LastChatSend = time_get(); + // send chat message CNetMsg_Cl_Say Msg; Msg.m_Team = Team; diff --git a/src/game/client/components/chat.h b/src/game/client/components/chat.h index 60e18387..09519516 100644 --- a/src/game/client/components/chat.h +++ b/src/game/client/components/chat.h @@ -36,6 +36,11 @@ class CChat : public CComponent MODE_NONE=0, MODE_ALL, MODE_TEAM, + + CHAT_SERVER=0, + CHAT_HIGHLIGHT, + CHAT_CLIENT, + CHAT_NUM, }; int m_Mode; @@ -47,8 +52,17 @@ class CChat : public CComponent char m_aCompletionBuffer[256]; int m_PlaceholderOffset; int m_PlaceholderLength; - char *m_pHistoryEntry; - TStaticRingBuffer<char, 64*1024, CRingBufferBase::FLAG_RECYCLE> m_History; + + struct CHistoryEntry + { + int m_Team; + char m_aText[1]; + }; + CHistoryEntry *m_pHistoryEntry; + TStaticRingBuffer<CHistoryEntry, 64*1024, CRingBufferBase::FLAG_RECYCLE> m_History; + int m_PendingChatCounter; + int64 m_LastChatSend; + int64 m_aLastSoundPlayed[CHAT_NUM]; static void ConSay(IConsole::IResult *pResult, void *pUserData); static void ConSayTeam(IConsole::IResult *pResult, void *pUserData); diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index f2e9e65d..d16d56cd 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -612,16 +612,10 @@ void CGameConsole::Dump(int Type) IOHANDLE io = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE); if(io) { - #if defined(CONF_FAMILY_WINDOWS) - static const char Newline[] = "\r\n"; - #else - static const char Newline[] = "\n"; - #endif - for(CInstance::CBacklogEntry *pEntry = pConsole->m_Backlog.First(); pEntry; pEntry = pConsole->m_Backlog.Next(pEntry)) { io_write(io, pEntry->m_aText, str_length(pEntry->m_aText)); - io_write(io, Newline, sizeof(Newline)-1); + io_write_newline(io); } io_close(io); } diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp index 81c4d5ae..c0790799 100644 --- a/src/game/client/components/controls.cpp +++ b/src/game/client/components/controls.cpp @@ -199,7 +199,7 @@ int CControls::SnapInput(int *pData) void CControls::OnRender() { // update target pos - if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED || m_pClient->m_Snap.m_SpecInfo.m_Active)) + if(m_pClient->m_Snap.m_pGameInfoObj && !m_pClient->m_Snap.m_SpecInfo.m_Active) m_TargetPos = m_pClient->m_LocalCharacterPos + m_MousePos; else if(m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_UsePosition) m_TargetPos = m_pClient->m_Snap.m_SpecInfo.m_Position + m_MousePos; @@ -209,7 +209,8 @@ void CControls::OnRender() bool CControls::OnMouseMove(float x, float y) { - if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + if((m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) || + (m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_pChat->IsActive())) return false; m_MousePos += vec2(x, y); // TODO: ugly diff --git a/src/game/client/components/countryflags.cpp b/src/game/client/components/countryflags.cpp index ef350cd7..b63b126f 100644 --- a/src/game/client/components/countryflags.cpp +++ b/src/game/client/components/countryflags.cpp @@ -6,6 +6,8 @@ #include <engine/console.h> #include <engine/graphics.h> #include <engine/storage.h> +#include <engine/textrender.h> +#include <engine/shared/config.h> #include <engine/shared/linereader.h> #include "countryflags.h" @@ -56,31 +58,57 @@ void CCountryFlags::LoadCountryflagsIndexfile() // load the graphic file char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "countryflags/%s.png", aOrigin); CImageInfo Info; - if(!Graphics()->LoadPNG(&Info, aBuf, IStorage::TYPE_ALL)) + if(g_Config.m_ClLoadCountryFlags) { - char aMsg[128]; - str_format(aMsg, sizeof(aMsg), "failed to load '%s'", aBuf); - Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aMsg); - continue; + str_format(aBuf, sizeof(aBuf), "countryflags/%s.png", aOrigin); + if(!Graphics()->LoadPNG(&Info, aBuf, IStorage::TYPE_ALL)) + { + char aMsg[128]; + str_format(aMsg, sizeof(aMsg), "failed to load '%s'", aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aMsg); + continue; + } } // add entry CCountryFlag CountryFlag; CountryFlag.m_CountryCode = 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); - Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf); - m_aCountryFlags.add(CountryFlag); + if(g_Config.m_ClLoadCountryFlags) + { + CountryFlag.m_Texture = Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); + mem_free(Info.m_pData); + } + else + CountryFlag.m_Texture = -1; + if(g_Config.m_Debug) + { + str_format(aBuf, sizeof(aBuf), "loaded country flag '%s'", aOrigin); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "countryflags", aBuf); + } + m_aCountryFlags.add_unsorted(CountryFlag); } io_close(File); + m_aCountryFlags.sort_range(); - mem_zero(m_CodeIndexLUT, sizeof(m_CodeIndexLUT)); + // find index of default item + int DefaultIndex = 0, Index = 0; + for(sorted_array<CCountryFlag>::range r = m_aCountryFlags.all(); !r.empty(); r.pop_front(), ++Index) + if(r.front().m_CountryCode == -1) + { + DefaultIndex = Index; + break; + } + + // init LUT + if(DefaultIndex != 0) + for(int i = 0; i < CODE_RANGE; ++i) + m_CodeIndexLUT[i] = DefaultIndex; + else + mem_zero(m_CodeIndexLUT, sizeof(m_CodeIndexLUT)); for(int i = 0; i < m_aCountryFlags.size(); ++i) - m_CodeIndexLUT[max(0, (m_aCountryFlags[i].m_CountryCode-CODE_LB)%CODE_RANGE)] = i+1; + m_CodeIndexLUT[max(0, (m_aCountryFlags[i].m_CountryCode-CODE_LB)%CODE_RANGE)] = i; } void CCountryFlags::OnInit() @@ -94,6 +122,7 @@ void CCountryFlags::OnInit() CCountryFlag DummyEntry; DummyEntry.m_CountryCode = -1; DummyEntry.m_Texture = -1; + mem_zero(DummyEntry.m_aCountryCodeString, sizeof(DummyEntry.m_aCountryCodeString)); m_aCountryFlags.add(DummyEntry); } } @@ -105,10 +134,31 @@ int CCountryFlags::Num() const const CCountryFlags::CCountryFlag *CCountryFlags::GetByCountryCode(int CountryCode) const { - return GetByIndex(m_CodeIndexLUT[max(0, (CountryCode-CODE_LB)%CODE_RANGE)]-1); + return GetByIndex(m_CodeIndexLUT[max(0, (CountryCode-CODE_LB)%CODE_RANGE)]); } const CCountryFlags::CCountryFlag *CCountryFlags::GetByIndex(int Index) const { return &m_aCountryFlags[max(0, Index%m_aCountryFlags.size())]; } + +void CCountryFlags::Render(int CountryCode, const vec4 *pColor, float x, float y, float w, float h) +{ + const CCountryFlag *pFlag = GetByCountryCode(CountryCode); + if(pFlag->m_Texture != -1) + { + Graphics()->TextureSet(pFlag->m_Texture); + Graphics()->QuadsBegin(); + Graphics()->SetColor(pColor->r, pColor->g, pColor->b, pColor->a); + IGraphics::CQuadItem QuadItem(x, y, w, h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + else + { + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, x, y, 10.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = w; + TextRender()->TextEx(&Cursor, pFlag->m_aCountryCodeString, -1); + } +} diff --git a/src/game/client/components/countryflags.h b/src/game/client/components/countryflags.h index ad24a762..df934821 100644 --- a/src/game/client/components/countryflags.h +++ b/src/game/client/components/countryflags.h @@ -23,7 +23,7 @@ public: int Num() const; const CCountryFlag *GetByCountryCode(int CountryCode) const; const CCountryFlag *GetByIndex(int Index) const; - //int Find(int CountryCode) const; + void Render(int CountryCode, const vec4 *pColor, float x, float y, float w, float h); private: enum diff --git a/src/game/client/components/damageind.cpp b/src/game/client/components/damageind.cpp index cba47666..0f521753 100644 --- a/src/game/client/components/damageind.cpp +++ b/src/game/client/components/damageind.cpp @@ -1,5 +1,6 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ +#include <engine/demo.h> #include <engine/graphics.h> #include <game/generated/protocol.h> #include <game/generated/client_data.h> @@ -38,7 +39,7 @@ void CDamageInd::Create(vec2 Pos, vec2 Dir) if (i) { i->m_Pos = Pos; - i->m_Life = 0.75f; + i->m_StartTime = Client()->LocalTime(); i->m_Dir = Dir*-1; i->m_StartAngle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; } @@ -48,21 +49,39 @@ void CDamageInd::OnRender() { Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); + static float s_LastLocalTime = Client()->LocalTime(); for(int i = 0; i < m_NumItems;) { - vec2 Pos = mix(m_aItems[i].m_Pos+m_aItems[i].m_Dir*75.0f, m_aItems[i].m_Pos, clamp((m_aItems[i].m_Life-0.60f)/0.15f, 0.0f, 1.0f)); + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + { + const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); + if(pInfo->m_Paused) + m_aItems[i].m_StartTime += Client()->LocalTime()-s_LastLocalTime; + else + m_aItems[i].m_StartTime += (Client()->LocalTime()-s_LastLocalTime)*(1.0f-pInfo->m_Speed); + } + else + { + if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + m_aItems[i].m_StartTime += Client()->LocalTime()-s_LastLocalTime; + } - m_aItems[i].m_Life -= Client()->FrameTime(); - if(m_aItems[i].m_Life < 0.0f) + float Life = 0.75f - (Client()->LocalTime() - m_aItems[i].m_StartTime); + if(Life < 0.0f) DestroyI(&m_aItems[i]); else { - Graphics()->SetColor(1.0f,1.0f,1.0f, m_aItems[i].m_Life/0.1f); - Graphics()->QuadsSetRotation(m_aItems[i].m_StartAngle + m_aItems[i].m_Life * 2.0f); + vec2 Pos = mix(m_aItems[i].m_Pos+m_aItems[i].m_Dir*75.0f, m_aItems[i].m_Pos, clamp((Life-0.60f)/0.15f, 0.0f, 1.0f)); + Graphics()->SetColor(1.0f,1.0f,1.0f, Life/0.1f); + Graphics()->QuadsSetRotation(m_aItems[i].m_StartAngle + Life * 2.0f); RenderTools()->SelectSprite(SPRITE_STAR1); RenderTools()->DrawSprite(Pos.x, Pos.y, 48.0f); i++; } } + s_LastLocalTime = Client()->LocalTime(); Graphics()->QuadsEnd(); } + + + diff --git a/src/game/client/components/damageind.h b/src/game/client/components/damageind.h index 2f89e422..4a5975a8 100644 --- a/src/game/client/components/damageind.h +++ b/src/game/client/components/damageind.h @@ -12,7 +12,7 @@ class CDamageInd : public CComponent { vec2 m_Pos; vec2 m_Dir; - float m_Life; + float m_StartTime; float m_StartAngle; }; diff --git a/src/game/client/components/effects.cpp b/src/game/client/components/effects.cpp index 8554b7ba..933e7fe6 100644 --- a/src/game/client/components/effects.cpp +++ b/src/game/client/components/effects.cpp @@ -44,7 +44,7 @@ void CEffects::AirJump(vec2 Pos) p.m_Pos = Pos + vec2(6.0f, 16.0f); m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); - m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, Pos); + m_pClient->m_pSounds->PlayAt(CSounds::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, Pos); } void CEffects::DamageIndicator(vec2 Pos, vec2 Dir) @@ -147,7 +147,7 @@ void CEffects::PlayerSpawn(vec2 Pos) m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); } - m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_PLAYER_SPAWN, 1.0f, Pos); + m_pClient->m_pSounds->PlayAt(CSounds::CHN_WORLD, SOUND_PLAYER_SPAWN, 1.0f, Pos); } void CEffects::PlayerDeath(vec2 Pos, int ClientID) @@ -242,7 +242,7 @@ void CEffects::HammerHit(vec2 Pos) p.m_EndSize = 0; p.m_Rot = frandom()*pi*2; m_pClient->m_pParticles->Add(CParticles::GROUP_EXPLOSIONS, &p); - m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_HAMMER_HIT, 1.0f, Pos); + m_pClient->m_pSounds->PlayAt(CSounds::CHN_WORLD, SOUND_HAMMER_HIT, 1.0f, Pos); } void CEffects::OnRender() diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp index b2f48b80..9e2a80cf 100644 --- a/src/game/client/components/emoticon.cpp +++ b/src/game/client/components/emoticon.cpp @@ -111,14 +111,14 @@ void CEmoticon::OnRender() m_WasActive = true; - if (length(m_SelectorMouse) > 140) - m_SelectorMouse = normalize(m_SelectorMouse) * 140; + if (length(m_SelectorMouse) > 170.0f) + m_SelectorMouse = normalize(m_SelectorMouse) * 170.0f; float SelectedAngle = GetAngle(m_SelectorMouse) + 2*pi/24; if (SelectedAngle < 0) SelectedAngle += 2*pi; - if (length(m_SelectorMouse) > 100) + if (length(m_SelectorMouse) > 110.0f) m_SelectedEmote = (int)(SelectedAngle / (2*pi) * NUM_EMOTICONS); CUIRect Screen = *UI()->Screen(); @@ -130,7 +130,7 @@ void CEmoticon::OnRender() Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.3f); - DrawCircle(Screen.w/2, Screen.h/2, 160, 64); + DrawCircle(Screen.w/2, Screen.h/2, 190.0f, 64); Graphics()->QuadsEnd(); Graphics()->TextureSet(g_pData->m_aImages[IMAGE_EMOTICONS].m_Id); @@ -144,10 +144,10 @@ void CEmoticon::OnRender() bool Selected = m_SelectedEmote == i; - float Size = Selected ? 80 : 32; + float Size = Selected ? 80.0f : 50.0f; - float NudgeX = 120 * cosf(Angle); - float NudgeY = 120 * sinf(Angle); + float NudgeX = 150.0f * cosf(Angle); + float NudgeY = 150.0f * sinf(Angle); RenderTools()->SelectSprite(SPRITE_OOP + i); IGraphics::CQuadItem QuadItem(Screen.w/2 + NudgeX, Screen.h/2 + NudgeY, Size, Size); Graphics()->QuadsDraw(&QuadItem, 1); diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 11343912..6881728c 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -59,6 +59,18 @@ void CHud::RenderGameTimer() } } +void CHud::RenderPauseNotification() +{ + if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED && + !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)) + { + const char *pText = Localize("Game paused"); + float FontSize = 20.0f; + float w = TextRender()->TextWidth(0, FontSize,pText, -1); + TextRender()->Text(0, 150.0f*Graphics()->ScreenAspect()+-w/2.0f, 50.0f, FontSize, pText, -1); + } +} + void CHud::RenderSuddenDeath() { if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_SUDDENDEATH) @@ -78,6 +90,7 @@ void CHud::RenderScoreHud() { int GameFlags = m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags; float Whole = 300*Graphics()->ScreenAspect(); + float StartY = 229.0f; if(GameFlags&GAMEFLAG_TEAMS && m_pClient->m_Snap.m_pGameDataObj) { @@ -100,15 +113,15 @@ void CHud::RenderScoreHud() Graphics()->SetColor(1.0f, 0.0f, 0.0f, 0.25f); else Graphics()->SetColor(0.0f, 0.0f, 1.0f, 0.25f); - RenderTools()->DrawRoundRectExt(Whole-ScoreWidthMax-ImageSize-2*Split, 245.0f+t*20, ScoreWidthMax+ImageSize+2*Split, 18.0f, 5.0f, CUI::CORNER_L); + RenderTools()->DrawRoundRectExt(Whole-ScoreWidthMax-ImageSize-2*Split, StartY+t*20, ScoreWidthMax+ImageSize+2*Split, 18.0f, 5.0f, CUI::CORNER_L); Graphics()->QuadsEnd(); // draw score - TextRender()->Text(0, Whole-ScoreWidthMax+(ScoreWidthMax-aScoreTeamWidth[t])/2-Split, 245.0f+t*20, 14.0f, aScoreTeam[t], -1); + TextRender()->Text(0, Whole-ScoreWidthMax+(ScoreWidthMax-aScoreTeamWidth[t])/2-Split, StartY+t*20, 14.0f, aScoreTeam[t], -1); if(GameFlags&GAMEFLAG_FLAGS) { - int BlinkTimer = (m_pClient->m_FlagDropTick[t] != 0 && + 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))) { @@ -117,7 +130,7 @@ void CHud::RenderScoreHud() Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); RenderTools()->SelectSprite(t==0?SPRITE_FLAG_RED:SPRITE_FLAG_BLUE); - IGraphics::CQuadItem QuadItem(Whole-ScoreWidthMax-ImageSize, 246.0f+t*20, ImageSize/2, ImageSize); + IGraphics::CQuadItem QuadItem(Whole-ScoreWidthMax-ImageSize, StartY+1.0f+t*20, ImageSize/2, ImageSize); Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } @@ -126,16 +139,17 @@ void CHud::RenderScoreHud() // draw name of the flag holder int ID = FlagCarrier[t]%MAX_CLIENTS; const char *pName = m_pClient->m_aClients[ID].m_aName; - float w = TextRender()->TextWidth(0, 10.0f, pName, -1); - TextRender()->Text(0, Whole-ScoreWidthMax-ImageSize-3*Split-w, 247.0f+t*20, 10.0f, pName, -1); + float w = TextRender()->TextWidth(0, 8.0f, pName, -1); + TextRender()->Text(0, min(Whole-w-1.0f, Whole-ScoreWidthMax-ImageSize-2*Split), StartY+(t+1)*20.0f-3.0f, 8.0f, pName, -1); // draw tee of the flag holder CTeeRenderInfo Info = m_pClient->m_aClients[ID].m_RenderInfo; Info.m_Size = 18.0f; RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1,0), - vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, 246.0f+Info.m_Size/2+t*20)); + vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, StartY+1.0f+Info.m_Size/2+t*20)); } } + StartY += 8.0f; } } else @@ -191,25 +205,33 @@ void CHud::RenderScoreHud() Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.25f); else Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.25f); - RenderTools()->DrawRoundRectExt(Whole-ScoreWidthMax-ImageSize-2*Split-PosSize, 245.0f+t*20, ScoreWidthMax+ImageSize+2*Split+PosSize, 18.0f, 5.0f, CUI::CORNER_L); + RenderTools()->DrawRoundRectExt(Whole-ScoreWidthMax-ImageSize-2*Split-PosSize, StartY+t*20, ScoreWidthMax+ImageSize+2*Split+PosSize, 18.0f, 5.0f, CUI::CORNER_L); Graphics()->QuadsEnd(); // draw score - TextRender()->Text(0, Whole-ScoreWidthMax+(ScoreWidthMax-aScoreWidth[t])/2-Split, 245.0f+t*20, 14.0f, aScore[t], -1); + TextRender()->Text(0, Whole-ScoreWidthMax+(ScoreWidthMax-aScoreWidth[t])/2-Split, StartY+t*20, 14.0f, aScore[t], -1); - // draw tee if(apPlayerInfo[t]) { - CTeeRenderInfo Info = m_pClient->m_aClients[apPlayerInfo[t]->m_ClientID].m_RenderInfo; + // draw name + int ID = apPlayerInfo[t]->m_ClientID; + const char *pName = m_pClient->m_aClients[ID].m_aName; + float w = TextRender()->TextWidth(0, 8.0f, pName, -1); + TextRender()->Text(0, min(Whole-w-1.0f, Whole-ScoreWidthMax-ImageSize-2*Split-PosSize), StartY+(t+1)*20.0f-3.0f, 8.0f, pName, -1); + + // draw tee + CTeeRenderInfo Info = m_pClient->m_aClients[ID].m_RenderInfo; Info.m_Size = 18.0f; RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1,0), - vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, 246.0f+Info.m_Size/2+t*20)); + vec2(Whole-ScoreWidthMax-Info.m_Size/2-Split, StartY+1.0f+Info.m_Size/2+t*20)); } // draw position char aBuf[32]; str_format(aBuf, sizeof(aBuf), "%d.", aPos[t]); - TextRender()->Text(0, Whole-ScoreWidthMax-ImageSize-Split-PosSize, 247.0f+t*20, 10.0f, aBuf, -1); + TextRender()->Text(0, Whole-ScoreWidthMax-ImageSize-Split-PosSize, StartY+2.0f+t*20, 10.0f, aBuf, -1); + + StartY += 8.0f; } } } @@ -248,7 +270,7 @@ void CHud::RenderFps() if(g_Config.m_ClShowfps) { // calculate avg. fps - float FPS = 1.0f / Client()->FrameTime(); + float FPS = 1.0f / Client()->RenderFrameTime(); m_AverageFPS = (m_AverageFPS*(1.0f-(1.0f/m_AverageFPS))) + (FPS*(1.0f/m_AverageFPS)); char Buf[512]; str_format(Buf, sizeof(Buf), "%d", (int)m_AverageFPS); @@ -440,6 +462,7 @@ void CHud::OnRender() } RenderGameTimer(); + RenderPauseNotification(); RenderSuddenDeath(); RenderScoreHud(); RenderWarmupTimer(); diff --git a/src/game/client/components/hud.h b/src/game/client/components/hud.h index f0250c7b..34720854 100644 --- a/src/game/client/components/hud.h +++ b/src/game/client/components/hud.h @@ -17,6 +17,7 @@ class CHud : public CComponent void RenderVoting(); void RenderHealthAndAmmo(const CNetObj_Character *pCharacter); void RenderGameTimer(); + void RenderPauseNotification(); void RenderSuddenDeath(); void RenderScoreHud(); void RenderSpectatorHud(); diff --git a/src/game/client/components/items.cpp b/src/game/client/components/items.cpp index 827b28a7..e1032a51 100644 --- a/src/game/client/components/items.cpp +++ b/src/game/client/components/items.cpp @@ -17,12 +17,11 @@ void CItems::OnReset() { - ExtraProjectilesNum = 0; + m_NumExtraProjectiles = 0; } void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID) { - // get positions float Curvature = 0; float Speed = 0; @@ -42,7 +41,10 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID) Speed = m_pClient->m_Tuning.m_GunSpeed; } - float Ct = (Client()->PrevGameTick()-pCurrent->m_StartTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + static float s_LastGameTickTime = Client()->GameTickTime(); + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_LastGameTickTime = Client()->GameTickTime(); + float Ct = (Client()->PrevGameTick()-pCurrent->m_StartTick)/(float)SERVER_TICK_SPEED + s_LastGameTickTime; if(Ct < 0) return; // projectile havn't been shot yet @@ -64,28 +66,27 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID) if(pCurrent->m_Type == WEAPON_GRENADE) { m_pClient->m_pEffects->SmokeTrail(Pos, Vel*-1); - m_pClient->m_pFlow->Add(Pos, Vel*1000*Client()->FrameTime(), 10.0f); + static float s_Time = 0.0f; + static float s_LastLocalTime = Client()->LocalTime(); if(Client()->State() == IClient::STATE_DEMOPLAYBACK) { const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); - static float Time = 0; - static float LastLocalTime = Client()->LocalTime(); - if(!pInfo->m_Paused) - Time += (Client()->LocalTime()-LastLocalTime)*pInfo->m_Speed; - - Graphics()->QuadsSetRotation(Time*pi*2*2 + ItemID); - - LastLocalTime = Client()->LocalTime(); + s_Time += (Client()->LocalTime()-s_LastLocalTime)*pInfo->m_Speed; } else - Graphics()->QuadsSetRotation(Client()->LocalTime()*pi*2*2 + ItemID); + { + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_Time += Client()->LocalTime()-s_LastLocalTime; + } + + Graphics()->QuadsSetRotation(s_Time*pi*2*2 + ItemID); + s_LastLocalTime = Client()->LocalTime(); } else { m_pClient->m_pEffects->BulletTrail(Pos); - m_pClient->m_pFlow->Add(Pos, Vel*1000*Client()->FrameTime(), 10.0f); if(length(Vel) > 0.00001f) Graphics()->QuadsSetRotation(GetAngle(Vel)); @@ -133,26 +134,23 @@ void CItems::RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCu Graphics()->QuadsSetRotation(Angle); + static float s_Time = 0.0f; + static float s_LastLocalTime = Client()->LocalTime(); float Offset = Pos.y/32.0f + Pos.x/32.0f; if(Client()->State() == IClient::STATE_DEMOPLAYBACK) { const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); - static float Time = 0; - static float LastLocalTime = Client()->LocalTime(); - if(!pInfo->m_Paused) - Time += (Client()->LocalTime()-LastLocalTime)*pInfo->m_Speed; - - Pos.x += cosf(Time*2.0f+Offset)*2.5f; - Pos.y += sinf(Time*2.0f+Offset)*2.5f; - - LastLocalTime = Client()->LocalTime(); + s_Time += (Client()->LocalTime()-s_LastLocalTime)*pInfo->m_Speed; } else { - Pos.x += cosf(Client()->LocalTime()*2.0f+Offset)*2.5f; - Pos.y += sinf(Client()->LocalTime()*2.0f+Offset)*2.5f; - } + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_Time += Client()->LocalTime()-s_LastLocalTime; + } + Pos.x += cosf(s_Time*2.0f+Offset)*2.5f; + Pos.y += sinf(s_Time*2.0f+Offset)*2.5f; + s_LastLocalTime = Client()->LocalTime(); RenderTools()->DrawSprite(Pos.x, Pos.y, Size); Graphics()->QuadsEnd(); } @@ -310,23 +308,23 @@ void CItems::OnRender() } // render extra projectiles - for(int i = 0; i < ExtraProjectilesNum; i++) + for(int i = 0; i < m_NumExtraProjectiles; i++) { - if(aExtraProjectiles[i].m_StartTick < Client()->GameTick()) + if(m_aExtraProjectiles[i].m_StartTick < Client()->GameTick()) { - aExtraProjectiles[i] = aExtraProjectiles[ExtraProjectilesNum-1]; - ExtraProjectilesNum--; + m_aExtraProjectiles[i] = m_aExtraProjectiles[m_NumExtraProjectiles-1]; + m_NumExtraProjectiles--; } else - RenderProjectile(&aExtraProjectiles[i], 0); + RenderProjectile(&m_aExtraProjectiles[i], 0); } } void CItems::AddExtraProjectile(CNetObj_Projectile *pProj) { - if(ExtraProjectilesNum != MAX_EXTRA_PROJECTILES) + if(m_NumExtraProjectiles != MAX_EXTRA_PROJECTILES) { - aExtraProjectiles[ExtraProjectilesNum] = *pProj; - ExtraProjectilesNum++; + m_aExtraProjectiles[m_NumExtraProjectiles] = *pProj; + m_NumExtraProjectiles++; } } diff --git a/src/game/client/components/items.h b/src/game/client/components/items.h index 604c1b1c..caf6176b 100644 --- a/src/game/client/components/items.h +++ b/src/game/client/components/items.h @@ -11,8 +11,8 @@ class CItems : public CComponent MAX_EXTRA_PROJECTILES=32, }; - CNetObj_Projectile aExtraProjectiles[MAX_EXTRA_PROJECTILES]; - int ExtraProjectilesNum; + CNetObj_Projectile m_aExtraProjectiles[MAX_EXTRA_PROJECTILES]; + int m_NumExtraProjectiles; void RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID); void RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent); diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 096f9cc5..db74b165 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -22,6 +22,9 @@ CMapLayers::CMapLayers(int t) { m_Type = t; m_pLayers = 0; + m_CurrentLocalTick = 0; + m_LastLocalTick = 0; + m_EnvelopeUpdate = false; } void CMapLayers::OnInit() @@ -29,6 +32,17 @@ void CMapLayers::OnInit() m_pLayers = Layers(); } +void CMapLayers::EnvelopeUpdate() +{ + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + { + const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); + m_CurrentLocalTick = pInfo->m_CurrentTick; + m_LastLocalTick = pInfo->m_CurrentTick; + m_EnvelopeUpdate = true; + } +} + void CMapLayers::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup) { @@ -63,24 +77,42 @@ 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; + static float s_Time = 0.0f; + static float s_LastLocalTime = pThis->Client()->LocalTime(); if(pThis->Client()->State() == IClient::STATE_DEMOPLAYBACK) { const IDemoPlayer::CInfo *pInfo = pThis->DemoPlayer()->BaseInfo(); - static int LastLocalTick = pInfo->m_CurrentTick; - - if(!pInfo->m_Paused) - Time += (pInfo->m_CurrentTick-LastLocalTick) / (float)pThis->Client()->GameTickSpeed() * pInfo->m_Speed; + + if(!pInfo->m_Paused || pThis->m_EnvelopeUpdate) + { + if(pThis->m_CurrentLocalTick != pInfo->m_CurrentTick) + { + pThis->m_LastLocalTick = pThis->m_CurrentLocalTick; + pThis->m_CurrentLocalTick = pInfo->m_CurrentTick; + } - pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, Time+TimeOffset, pChannels); + s_Time = mix(pThis->m_LastLocalTick / (float)pThis->Client()->GameTickSpeed(), + pThis->m_CurrentLocalTick / (float)pThis->Client()->GameTickSpeed(), + pThis->Client()->IntraGameTick()); + } - LastLocalTick = pInfo->m_CurrentTick; + pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, s_Time+TimeOffset, pChannels); } else { - 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); + if(pThis->m_pClient->m_Snap.m_pGameInfoObj && !(pThis->m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + { + if(pItem->m_Version < 2 || pItem->m_Synchronized) + { + s_Time = mix((pThis->Client()->PrevGameTick()-pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)pThis->Client()->GameTickSpeed(), + (pThis->Client()->GameTick()-pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)pThis->Client()->GameTickSpeed(), + pThis->Client()->IntraGameTick()); + } + else + s_Time += pThis->Client()->LocalTime()-s_LastLocalTime; + } + pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, s_Time+TimeOffset, pChannels); + s_LastLocalTime = pThis->Client()->LocalTime(); } } @@ -160,16 +192,11 @@ void CMapLayers::OnRender() IOHANDLE File = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE); if(File) { - #if defined(CONF_FAMILY_WINDOWS) - static const char Newline[] = "\r\n"; - #else - static const char Newline[] = "\n"; - #endif for(int y = 0; y < pTMap->m_Height; y++) { for(int x = 0; x < pTMap->m_Width; x++) io_write(File, &(pTiles[y*pTMap->m_Width + x].m_Index), sizeof(pTiles[y*pTMap->m_Width + x].m_Index)); - io_write(File, Newline, sizeof(Newline)-1); + io_write_newline(File); } io_close(File); } diff --git a/src/game/client/components/maplayers.h b/src/game/client/components/maplayers.h index 694633ee..d0efcfc7 100644 --- a/src/game/client/components/maplayers.h +++ b/src/game/client/components/maplayers.h @@ -8,6 +8,9 @@ class CMapLayers : public CComponent { CLayers *m_pLayers; // todo refactor: maybe remove it and access it through client* int m_Type; + int m_CurrentLocalTick; + int m_LastLocalTick; + bool m_EnvelopeUpdate; void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup); static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser); @@ -21,6 +24,8 @@ public: CMapLayers(int Type); virtual void OnInit(); virtual void OnRender(); + + void EnvelopeUpdate(); }; #endif diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 8f330f78..d27307f4 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -35,11 +35,10 @@ vec4 CMenus::ms_GuiColor; vec4 CMenus::ms_ColorTabbarInactiveOutgame; vec4 CMenus::ms_ColorTabbarActiveOutgame; vec4 CMenus::ms_ColorTabbarInactive; -vec4 CMenus::ms_ColorTabbarActive; +vec4 CMenus::ms_ColorTabbarActive = vec4(0,0,0,0.5f); vec4 CMenus::ms_ColorTabbarInactiveIngame; vec4 CMenus::ms_ColorTabbarActiveIngame; - float CMenus::ms_ButtonHeight = 25.0f; float CMenus::ms_ListheaderHeight = 17.0f; float CMenus::ms_FontmodHeight = 0.8f; @@ -95,14 +94,16 @@ int CMenus::DoButton_Icon(int ImageId, int SpriteId, const CUIRect *pRect) return 0; } -int CMenus::DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect) +int CMenus::DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect, bool Active) { Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GUIBUTTONS].m_Id); Graphics()->QuadsBegin(); + if(!Active) + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f); RenderTools()->SelectSprite(Checked?SPRITE_GUIBUTTON_ON:SPRITE_GUIBUTTON_OFF); IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h); Graphics()->QuadsDrawTL(&QuadItem, 1); - if((UI()->HotItem() == pID)) + if(UI()->HotItem() == pID && Active) { RenderTools()->SelectSprite(SPRITE_GUIBUTTON_HOVER); IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h); @@ -110,7 +111,7 @@ int CMenus::DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect) } Graphics()->QuadsEnd(); - return UI()->DoButtonLogic(pID, "", Checked, pRect); + return Active ? UI()->DoButtonLogic(pID, "", Checked, pRect) : 0; } int CMenus::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect) @@ -628,6 +629,8 @@ int CMenus::RenderMenubar(CUIRect r) void CMenus::RenderLoading() { + // TODO: not supported right now due to separate render thread + static int64 LastLoadRender = 0; float Percent = m_LoadCurrent++/(float)m_LoadTotal; @@ -1108,7 +1111,7 @@ int CMenus::Render() 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; @@ -1131,12 +1134,8 @@ int CMenus::Render() float OldWidth = Item.m_Rect.w; Item.m_Rect.w = Item.m_Rect.h*2; Item.m_Rect.x += (OldWidth-Item.m_Rect.w)/ 2.0f; - Graphics()->TextureSet(pEntry->m_Texture); - Graphics()->QuadsBegin(); - Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); - IGraphics::CQuadItem QuadItem(Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h); - Graphics()->QuadsDrawTL(&QuadItem, 1); - Graphics()->QuadsEnd(); + vec4 Color(1.0f, 1.0f, 1.0f, 1.0f); + m_pClient->m_pCountryFlags->Render(pEntry->m_CountryCode, &Color, Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h); UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, 0); } } @@ -1402,8 +1401,8 @@ void CMenus::OnStateChange(int NewState, int OldState) if(NewState == IClient::STATE_OFFLINE) { - if(OldState >= IClient::STATE_ONLINE) - m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); + if(OldState >= IClient::STATE_ONLINE && NewState < IClient::STATE_QUITING) + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f); m_Popup = POPUP_NONE; if(Client()->ErrorString() && Client()->ErrorString()[0] != 0) { diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 585fb91f..0125278c 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -40,7 +40,7 @@ class CMenus : public CComponent int DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect); int DoButton_Sprite(const void *pID, int ImageID, int SpriteID, int Checked, const CUIRect *pRect, int Corners); - int DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect); + int DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect, bool Active); int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect); int DoButton_MenuTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Corners); diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 8501c67d..a9c434b3 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -516,7 +516,7 @@ 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; @@ -563,16 +563,12 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) 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(); + vec4 Color(1.0f, 1.0f, 1.0f, g_Config.m_BrFilterCountry?1.0f: 0.5f); + m_pClient->m_pCountryFlags->Render(g_Config.m_BrFilterCountryIndex, &Color, Rect.x, Rect.y, Rect.w, Rect.h); if(g_Config.m_BrFilterCountry && UI()->DoButtonLogic(&g_Config.m_BrFilterCountryIndex, "", 0, &Rect)) m_Popup = POPUP_COUNTRY; @@ -766,12 +762,8 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) TextRender()->TextEx(&Cursor, pClan, -1); // flag - 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); - Graphics()->QuadsDrawTL(&QuadItem, 1); - Graphics()->QuadsEnd(); + vec4 Color(1.0f, 1.0f, 1.0f, 0.5f); + m_pClient->m_pCountryFlags->Render(pSelectedServer->m_aClients[i].m_Country, &Color, Flag.x, Flag.y, Flag.w, Flag.h); } } } diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index f3a75f0c..40a9e5b5 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -17,6 +17,7 @@ #include <game/generated/client_data.h> +#include "maplayers.h" #include "menus.h" int CMenus::DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect) @@ -85,15 +86,28 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) void *id = &s_SeekBarID; char aBuffer[128]; + // draw seek bar RenderTools()->DrawUIRect(&SeekBar, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 5.0f); + // draw filled bar float Amount = CurrentTick/(float)TotalTicks; - CUIRect FilledBar = SeekBar; FilledBar.w = 10.0f + (FilledBar.w-10.0f)*Amount; - RenderTools()->DrawUIRect(&FilledBar, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); + // draw markers + for(int i = 0; i < pInfo->m_NumTimelineMarkers; i++) + { + float Ratio = (pInfo->m_aTimelineMarkers[i]-pInfo->m_FirstTick) / (float)TotalTicks; + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + IGraphics::CQuadItem QuadItem(SeekBar.x + (SeekBar.w-10.0f)*Ratio, SeekBar.y, UI()->PixelSize(), SeekBar.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + + // draw time str_format(aBuffer, sizeof(aBuffer), "%d:%02d / %d:%02d", CurrentTick/SERVER_TICK_SPEED/60, (CurrentTick/SERVER_TICK_SPEED)%60, TotalTicks/SERVER_TICK_SPEED/60, (TotalTicks/SERVER_TICK_SPEED)%60); @@ -117,6 +131,8 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) m_pClient->m_SuppressEvents = true; DemoPlayer()->SetPos(Amount); m_pClient->m_SuppressEvents = false; + m_pClient->m_pMapLayersBackGround->EnvelopeUpdate(); + m_pClient->m_pMapLayersForeGround->EnvelopeUpdate(); } } } diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 1afef922..a9cf35e9 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -149,7 +149,11 @@ void CMenus::RenderPlayers(CUIRect MainView) static int s_aPlayerIDs[MAX_CLIENTS][2] = {{0}}; for(int i = 0, Count = 0; i < MAX_CLIENTS; ++i) { - if(!m_pClient->m_Snap.m_paPlayerInfos[i] || i == m_pClient->m_Snap.m_LocalClientID) + if(!m_pClient->m_Snap.m_paInfoByTeam[i]) + continue; + + int Index = m_pClient->m_Snap.m_paInfoByTeam[i]->m_ClientID; + if(Index == m_pClient->m_Snap.m_LocalClientID) continue; Options.HSplitTop(28.0f, &ButtonBar, &Options); @@ -159,7 +163,7 @@ void CMenus::RenderPlayers(CUIRect MainView) // player info Player.VSplitLeft(28.0f, &Button, &Player); - CTeeRenderInfo Info = m_pClient->m_aClients[i].m_RenderInfo; + CTeeRenderInfo Info = m_pClient->m_aClients[Index].m_RenderInfo; Info.m_Size = Button.h; RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1.0f, 0.0f), vec2(Button.x+Button.h/2, Button.y+Button.h/2)); @@ -168,31 +172,34 @@ void CMenus::RenderPlayers(CUIRect MainView) CTextCursor Cursor; TextRender()->SetCursor(&Cursor, Player.x, Player.y, 14.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = Player.w; - TextRender()->TextEx(&Cursor, m_pClient->m_aClients[i].m_aName, -1); + TextRender()->TextEx(&Cursor, m_pClient->m_aClients[Index].m_aName, -1); TextRender()->SetCursor(&Cursor, Button.x,Button.y, 14.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = Button.w; - TextRender()->TextEx(&Cursor, m_pClient->m_aClients[i].m_aClan, -1); + TextRender()->TextEx(&Cursor, m_pClient->m_aClients[Index].m_aClan, -1); // ignore button ButtonBar.HMargin(2.0f, &ButtonBar); ButtonBar.VSplitLeft(Width, &Button, &ButtonBar); Button.VSplitLeft((Width-Button.h)/4.0f, 0, &Button); Button.VSplitLeft(Button.h, &Button, 0); - if(DoButton_Toggle(&s_aPlayerIDs[i][0], m_pClient->m_aClients[i].m_ChatIgnore, &Button)) - m_pClient->m_aClients[i].m_ChatIgnore ^= 1; + if(&g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[Index].m_Friend) + DoButton_Toggle(&s_aPlayerIDs[Index][0], 1, &Button, false); + else + if(DoButton_Toggle(&s_aPlayerIDs[Index][0], m_pClient->m_aClients[Index].m_ChatIgnore, &Button, true)) + m_pClient->m_aClients[Index].m_ChatIgnore ^= 1; // friend button ButtonBar.VSplitLeft(20.0f, &Button, &ButtonBar); ButtonBar.VSplitLeft(Width, &Button, &ButtonBar); Button.VSplitLeft((Width-Button.h)/4.0f, 0, &Button); Button.VSplitLeft(Button.h, &Button, 0); - if(DoButton_Toggle(&s_aPlayerIDs[i][1], m_pClient->m_aClients[i].m_Friend, &Button)) + if(DoButton_Toggle(&s_aPlayerIDs[Index][1], m_pClient->m_aClients[Index].m_Friend, &Button, true)) { - if(m_pClient->m_aClients[i].m_Friend) - m_pClient->Friends()->RemoveFriend(m_pClient->m_aClients[i].m_aName, m_pClient->m_aClients[i].m_aClan); + if(m_pClient->m_aClients[Index].m_Friend) + m_pClient->Friends()->RemoveFriend(m_pClient->m_aClients[Index].m_aName, m_pClient->m_aClients[Index].m_aClan); else - m_pClient->Friends()->AddFriend(m_pClient->m_aClients[i].m_aName, m_pClient->m_aClients[i].m_aClan); + m_pClient->Friends()->AddFriend(m_pClient->m_aClients[Index].m_aName, m_pClient->m_aClients[Index].m_aClan); } } @@ -385,13 +392,15 @@ void CMenus::RenderServerControlKick(CUIRect MainView, bool FilterSpectators) static int aPlayerIDs[MAX_CLIENTS]; for(int i = 0; i < MAX_CLIENTS; i++) { - if(!m_pClient->m_Snap.m_paPlayerInfos[i] || i == m_pClient->m_Snap.m_LocalClientID) + if(!m_pClient->m_Snap.m_paInfoByTeam[i]) continue; - if(FilterSpectators && m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + + int Index = m_pClient->m_Snap.m_paInfoByTeam[i]->m_ClientID; + if(Index == m_pClient->m_Snap.m_LocalClientID || (FilterSpectators && m_pClient->m_Snap.m_paInfoByTeam[i]->m_Team == TEAM_SPECTATORS)) continue; - if(m_CallvoteSelectedPlayer == i) + if(m_CallvoteSelectedPlayer == Index) Selected = NumOptions; - aPlayerIDs[NumOptions++] = i; + aPlayerIDs[NumOptions++] = Index; } static int s_VoteList = 0; diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 51fdbd29..553195b1 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -96,6 +96,12 @@ void CMenus::RenderSettingsGeneral(CUIRect MainView) if(DoButton_CheckBox(&g_Config.m_ClShowhud, Localize("Show ingame HUD"), g_Config.m_ClShowhud, &Button)) g_Config.m_ClShowhud ^= 1; + // chat messages + Left.HSplitTop(5.0f, 0, &Left); + Left.HSplitTop(20.0f, &Button, &Left); + if(DoButton_CheckBox(&g_Config.m_ClShowChatFriends, Localize("Show only chat messages from friends"), g_Config.m_ClShowChatFriends, &Button)) + g_Config.m_ClShowChatFriends ^= 1; + // name plates Right.HSplitTop(20.0f, &Button, &Right); if(DoButton_CheckBox(&g_Config.m_ClNameplates, Localize("Show name plates"), g_Config.m_ClNameplates, &Button)) @@ -221,13 +227,10 @@ void CMenus::RenderSettingsPlayer(CUIRect MainView) 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); + vec4 Color(1.0f, 1.0f, 1.0f, 1.0f); + m_pClient->m_pCountryFlags->Render(pEntry->m_CountryCode, &Color, Item.m_Rect.x, Item.m_Rect.y, Item.m_Rect.w, Item.m_Rect.h); + if(pEntry->m_Texture != -1) + UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, 0); } } @@ -446,13 +449,14 @@ static CKeyInfo gs_aKeys[] = { "Remote console", "toggle_remote_console", 0 }, { "Screenshot", "screenshot", 0 }, { "Scoreboard", "+scoreboard", 0 }, + { "Respawn", "kill", 0 }, }; /* This is for scripts/update_localization.py to work, don't remove! Localize("Move left");Localize("Move right");Localize("Jump");Localize("Fire");Localize("Hook");Localize("Hammer"); Localize("Pistol");Localize("Shotgun");Localize("Grenade");Localize("Rifle");Localize("Next weapon");Localize("Prev. weapon"); Localize("Vote yes");Localize("Vote no");Localize("Chat");Localize("Team chat");Localize("Show chat");Localize("Emoticon"); - Localize("Spectator mode");Localize("Spectate next");Localize("Spectate previous");Localize("Console");Localize("Remote console");Localize("Screenshot");Localize("Scoreboard"); + Localize("Spectator mode");Localize("Spectate next");Localize("Spectate previous");Localize("Console");Localize("Remote console");Localize("Screenshot");Localize("Scoreboard");Localize("Respawn"); */ const int g_KeyCount = sizeof(gs_aKeys) / sizeof(CKeyInfo); @@ -504,10 +508,11 @@ void CMenus::RenderSettingsControls(CUIRect MainView) } CUIRect MovementSettings, WeaponSettings, VotingSettings, ChatSettings, MiscSettings, ResetButton; - MainView.VSplitLeft(MainView.w/2-5.0f, &MovementSettings, &VotingSettings); + MainView.VSplitMid(&MovementSettings, &VotingSettings); // movement settings { + MovementSettings.VMargin(5.0f, &MovementSettings); MovementSettings.HSplitTop(MainView.h/3+60.0f, &MovementSettings, &WeaponSettings); RenderTools()->DrawUIRect(&MovementSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); MovementSettings.Margin(10.0f, &MovementSettings); @@ -543,21 +548,22 @@ void CMenus::RenderSettingsControls(CUIRect MainView) WeaponSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &WeaponSettings); UiDoGetButtons(5, 12, WeaponSettings); } - + // defaults { ResetButton.HSplitTop(10.0f, 0, &ResetButton); RenderTools()->DrawUIRect(&ResetButton, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); ResetButton.HMargin(10.0f, &ResetButton); ResetButton.VMargin(30.0f, &ResetButton); + ResetButton.HSplitTop(20.0f, &ResetButton, 0); static int s_DefaultButton = 0; if(DoButton_Menu((void*)&s_DefaultButton, Localize("Reset to defaults"), 0, &ResetButton)) m_pClient->m_pBinds->SetDefaults(); } - + // voting settings { - VotingSettings.VSplitLeft(10.0f, 0, &VotingSettings); + VotingSettings.VMargin(5.0f, &VotingSettings); VotingSettings.HSplitTop(MainView.h/3-75.0f, &VotingSettings, &ChatSettings); RenderTools()->DrawUIRect(&VotingSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); VotingSettings.Margin(10.0f, &VotingSettings); @@ -590,7 +596,7 @@ void CMenus::RenderSettingsControls(CUIRect MainView) TextRender()->Text(0, MiscSettings.x, MiscSettings.y, 14.0f*UI()->Scale(), Localize("Miscellaneous"), -1); MiscSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &MiscSettings); - UiDoGetButtons(17, 25, MiscSettings); + UiDoGetButtons(17, 26, MiscSettings); } } @@ -761,7 +767,7 @@ void CMenus::RenderSettingsSound(CUIRect MainView) if(g_Config.m_SndEnable) { if(g_Config.m_SndMusic) - m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f); } else m_pClient->m_pSounds->Stop(SOUND_MENU); @@ -776,7 +782,7 @@ void CMenus::RenderSettingsSound(CUIRect MainView) { g_Config.m_SndMusic ^= 1; if(g_Config.m_SndMusic) - m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f, vec2(0, 0)); + m_pClient->m_pSounds->Play(CSounds::CHN_MUSIC, SOUND_MENU, 1.0f); else m_pClient->m_pSounds->Stop(SOUND_MENU); } @@ -917,11 +923,8 @@ void CMenus::RenderLanguageSelection(CUIRect MainView) 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(); + vec4 Color(1.0f, 1.0f, 1.0f, 1.0f); + m_pClient->m_pCountryFlags->Render(r.front().m_CountryCode, &Color, Rect.x, Rect.y, Rect.w, Rect.h); Item.m_Rect.HSplitTop(2.0f, 0, &Item.m_Rect); UI()->DoLabelScaled(&Item.m_Rect, r.front().m_Name, 16.0f, -1); } diff --git a/src/game/client/components/particles.cpp b/src/game/client/components/particles.cpp index c4583cb1..66a62f1c 100644 --- a/src/game/client/components/particles.cpp +++ b/src/game/client/components/particles.cpp @@ -43,6 +43,11 @@ void CParticles::Add(int Group, CParticle *pPart) if(pInfo->m_Paused) return; } + else + { + if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + return; + } if (m_FirstFree == -1) return; @@ -142,7 +147,10 @@ void CParticles::OnRender() Update((float)((t-LastTime)/(double)time_freq())*pInfo->m_Speed); } else - Update((float)((t-LastTime)/(double)time_freq())); + { + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + Update((float)((t-LastTime)/(double)time_freq())); + } LastTime = t; } diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 6811c2ad..2eddd8a9 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -95,30 +95,7 @@ void CPlayers::RenderHook( Player = *pPlayerChar; CNetObj_PlayerInfo pInfo = *pPlayerInfo; - CTeeRenderInfo RenderInfo = m_pClient->m_aClients[pInfo.m_ClientID].m_RenderInfo; - - // check for teamplay modes - bool IsTeamplay = false; - if(m_pClient->m_Snap.m_pGameInfoObj) - IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) != 0; - - // check for ninja - if (Player.m_Weapon == WEAPON_NINJA) - { - // change the skin for the player to the ninja - int Skin = m_pClient->m_pSkins->Find("x_ninja"); - if(Skin != -1) - { - if(IsTeamplay) - RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_ColorTexture; - else - { - RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_OrgTexture; - RenderInfo.m_ColorBody = vec4(1,1,1,1); - RenderInfo.m_ColorFeet = vec4(1,1,1,1); - } - } - } + CTeeRenderInfo RenderInfo = m_aRenderInfo[pInfo.m_ClientID]; float IntraTick = Client()->IntraGameTick(); @@ -218,31 +195,9 @@ void CPlayers::RenderPlayer( Player = *pPlayerChar; CNetObj_PlayerInfo pInfo = *pPlayerInfo; - CTeeRenderInfo RenderInfo = m_pClient->m_aClients[pInfo.m_ClientID].m_RenderInfo; + CTeeRenderInfo RenderInfo = m_aRenderInfo[pInfo.m_ClientID]; - // check for teamplay modes - bool IsTeamplay = false; bool NewTick = m_pClient->m_NewTick; - if(m_pClient->m_Snap.m_pGameInfoObj) - IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) != 0; - - // check for ninja - if (Player.m_Weapon == WEAPON_NINJA) - { - // change the skin for the player to the ninja - int Skin = m_pClient->m_pSkins->Find("x_ninja"); - if(Skin != -1) - { - if(IsTeamplay) - RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_ColorTexture; - else - { - RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_OrgTexture; - RenderInfo.m_ColorBody = vec4(1,1,1,1); - RenderInfo.m_ColorFeet = vec4(1,1,1,1); - } - } - } // set size RenderInfo.m_Size = 64.0f; @@ -330,14 +285,17 @@ void CPlayers::RenderPlayer( else if(!WantOtherDir) State.Add(&g_pData->m_aAnimations[ANIM_WALK], WalkTime, 1.0f); + static float s_LastGameTickTime = Client()->GameTickTime(); + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_LastGameTickTime = Client()->GameTickTime(); if (Player.m_Weapon == WEAPON_HAMMER) { - float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + s_LastGameTickTime; State.Add(&g_pData->m_aAnimations[ANIM_HAMMER_SWING], clamp(ct*5.0f,0.0f,1.0f), 1.0f); } if (Player.m_Weapon == WEAPON_NINJA) { - float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + s_LastGameTickTime; State.Add(&g_pData->m_aAnimations[ANIM_NINJA_SWING], clamp(ct*2.0f,0.0f,1.0f), 1.0f); } @@ -347,7 +305,7 @@ void CPlayers::RenderPlayer( static int64 SkidSoundTime = 0; if(time_get()-SkidSoundTime > time_freq()/10) { - m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, Position); + m_pClient->m_pSounds->PlayAt(CSounds::CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, Position); SkidSoundTime = time_get(); } @@ -409,15 +367,22 @@ void CPlayers::RenderPlayer( if ((Client()->GameTick()-Player.m_AttackTick) <= (SERVER_TICK_SPEED / 6) && g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles) { int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles; + static int s_LastIteX = IteX; if(Client()->State() == IClient::STATE_DEMOPLAYBACK) { - static int s_LastIteX = IteX; const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); if(pInfo->m_Paused) IteX = s_LastIteX; else s_LastIteX = IteX; } + else + { + if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + IteX = s_LastIteX; + else + s_LastIteX = IteX; + } if(g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX]) { vec2 Dir = vec2(pPlayerChar->m_X,pPlayerChar->m_Y) - vec2(pPrevChar->m_X, pPrevChar->m_Y); @@ -438,7 +403,11 @@ void CPlayers::RenderPlayer( { // TODO: should be an animation Recoil = 0; - float a = (Client()->GameTick()-Player.m_AttackTick+IntraTick)/5.0f; + static float s_LastIntraTick = IntraTick; + if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)) + s_LastIntraTick = IntraTick; + + float a = (Client()->GameTick()-Player.m_AttackTick+s_LastIntraTick)/5.0f; if(a < 1) Recoil = sinf(a*pi); p = Position + Dir * g_pData->m_Weapons.m_aId[iw].m_Offsetx - Dir*Recoil*10.0f; @@ -460,6 +429,22 @@ void CPlayers::RenderPlayer( } int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles; + static int s_LastIteX = IteX; + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + { + const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); + if(pInfo->m_Paused) + IteX = s_LastIteX; + else + s_LastIteX = IteX; + } + else + { + if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) + IteX = s_LastIteX; + else + s_LastIteX = IteX; + } if (Alpha > 0.0f && g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX]) { float OffsetY = -g_pData->m_Weapons.m_aId[iw].m_Muzzleoffsety; @@ -546,6 +531,31 @@ void CPlayers::RenderPlayer( void CPlayers::OnRender() { + // update RenderInfo for ninja + bool IsTeamplay = false; + if(m_pClient->m_Snap.m_pGameInfoObj) + IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) != 0; + for(int i = 0; i < MAX_CLIENTS; ++i) + { + m_aRenderInfo[i] = m_pClient->m_aClients[i].m_RenderInfo; + if(m_pClient->m_Snap.m_aCharacters[i].m_Cur.m_Weapon == WEAPON_NINJA) + { + // change the skin for the player to the ninja + int Skin = m_pClient->m_pSkins->Find("x_ninja"); + if(Skin != -1) + { + if(IsTeamplay) + m_aRenderInfo[i].m_Texture = m_pClient->m_pSkins->Get(Skin)->m_ColorTexture; + else + { + m_aRenderInfo[i].m_Texture = m_pClient->m_pSkins->Get(Skin)->m_OrgTexture; + m_aRenderInfo[i].m_ColorBody = vec4(1,1,1,1); + m_aRenderInfo[i].m_ColorFeet = vec4(1,1,1,1); + } + } + } + } + // render other players in two passes, first pass we render the other, second pass we render our self for(int p = 0; p < 4; p++) { diff --git a/src/game/client/components/players.h b/src/game/client/components/players.h index cedad0ff..9f23a0e6 100644 --- a/src/game/client/components/players.h +++ b/src/game/client/components/players.h @@ -6,6 +6,7 @@ class CPlayers : public CComponent { + CTeeRenderInfo m_aRenderInfo[MAX_CLIENTS]; void RenderHand(class CTeeRenderInfo *pInfo, vec2 CenterPos, vec2 Dir, float AngleOffset, vec2 PostRotOffset); void RenderPlayer( const CNetObj_Character *pPrevChar, diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp index ae11c7ea..935f7bad 100644 --- a/src/game/client/components/scoreboard.cpp +++ b/src/game/client/components/scoreboard.cpp @@ -273,12 +273,9 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aClan, -1); // country flag - Graphics()->TextureSet(m_pClient->m_pCountryFlags->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); - Graphics()->QuadsDrawTL(&QuadItem, 1); - Graphics()->QuadsEnd(); + vec4 Color(1.0f, 1.0f, 1.0f, 0.5f); + m_pClient->m_pCountryFlags->Render(m_pClient->m_aClients[pInfo->m_ClientID].m_Country, &Color, + CountryOffset, y+(Spacing+TeeSizeMod*5.0f)/2.0f, CountryLength, LineHeight-Spacing-TeeSizeMod*5.0f); // ping str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Latency, 0, 1000)); diff --git a/src/game/client/components/skins.cpp b/src/game/client/components/skins.cpp index dd38e9ea..babf49bb 100644 --- a/src/game/client/components/skins.cpp +++ b/src/game/client/components/skins.cpp @@ -7,6 +7,7 @@ #include <engine/graphics.h> #include <engine/storage.h> +#include <engine/shared/config.h> #include "skins.h" @@ -103,8 +104,11 @@ int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser) // set skin data str_copy(Skin.m_aName, pName, min((int)sizeof(Skin.m_aName),l-3)); - str_format(aBuf, sizeof(aBuf), "load skin %s", Skin.m_aName); - pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); + if(g_Config.m_Debug) + { + str_format(aBuf, sizeof(aBuf), "load skin %s", Skin.m_aName); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); + } pSelf->m_aSkins.add(Skin); return 0; diff --git a/src/game/client/components/sounds.cpp b/src/game/client/components/sounds.cpp index c4ade00e..be63415c 100644 --- a/src/game/client/components/sounds.cpp +++ b/src/game/client/components/sounds.cpp @@ -34,6 +34,30 @@ static int LoadSoundsThread(void *pUser) return 0; } + +int CSounds::GetSampleId(int SetId) +{ + if(!g_Config.m_SndEnable || !Sound()->IsSoundEnabled() || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) + return -1; + + CDataSoundset *pSet = &g_pData->m_aSounds[SetId]; + if(!pSet->m_NumSounds) + return -1; + + if(pSet->m_NumSounds == 1) + return pSet->m_aSounds[0].m_Id; + + // return random one + int Id; + do + { + Id = rand() % pSet->m_NumSounds; + } + while(Id == pSet->m_Last); + pSet->m_Last = Id; + return pSet->m_aSounds[Id].m_Id; +} + void CSounds::OnInit() { // setup sound channels @@ -98,7 +122,7 @@ void CSounds::OnRender() int64 Now = time_get(); if(m_QueueWaitTime <= Now) { - Play(m_aQueue[0].m_Channel, m_aQueue[0].m_SetId, 1.0f, vec2(0,0)); + Play(m_aQueue[0].m_Channel, m_aQueue[0].m_SetId, 1.0f); m_QueueWaitTime = Now+time_freq()*3/10; // wait 300ms before playing the next one if(--m_QueuePos > 0) mem_move(m_aQueue, m_aQueue+1, m_QueuePos*sizeof(QueueEntry)); @@ -132,47 +156,48 @@ void CSounds::PlayAndRecord(int Chn, int SetId, float Vol, vec2 Pos) Msg.m_SoundID = SetId; Client()->SendPackMsg(&Msg, MSGFLAG_NOSEND|MSGFLAG_RECORD); - Play(Chn, SetId, Vol, Pos); + Play(Chn, SetId, Vol); } -void CSounds::Play(int Chn, int SetId, float Vol, vec2 Pos) +void CSounds::Play(int Chn, int SetId, float Vol) { - if(!g_Config.m_SndEnable || !Sound()->IsSoundEnabled() || (Chn == CHN_MUSIC && !g_Config.m_SndMusic) || m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) + if(Chn == CHN_MUSIC && !g_Config.m_SndMusic) return; - CDataSoundset *pSet = &g_pData->m_aSounds[SetId]; - - if(!pSet->m_NumSounds) + int SampleId = GetSampleId(SetId); + if(SampleId == -1) return; - + int Flags = 0; if(Chn == CHN_MUSIC) Flags = ISound::FLAG_LOOP; - if(pSet->m_NumSounds == 1) - { - Sound()->PlayAt(Chn, pSet->m_aSounds[0].m_Id, Flags, Pos.x, Pos.y); + Sound()->Play(Chn, SampleId, Flags); +} + +void CSounds::PlayAt(int Chn, int SetId, float Vol, vec2 Pos) +{ + if(Chn == CHN_MUSIC && !g_Config.m_SndMusic) + return; + + int SampleId = GetSampleId(SetId); + if(SampleId == -1) return; - } - // play a random one - int Id; - do - { - Id = rand() % pSet->m_NumSounds; - } - while(Id == pSet->m_Last); - Sound()->PlayAt(Chn, pSet->m_aSounds[Id].m_Id, Flags, Pos.x, Pos.y); - pSet->m_Last = Id; + int Flags = 0; + if(Chn == CHN_MUSIC) + Flags = ISound::FLAG_LOOP; + + Sound()->PlayAt(Chn, SampleId, Flags, Pos.x, Pos.y); } void CSounds::Stop(int SetId) { if(m_WaitForSoundJob || SetId < 0 || SetId >= g_pData->m_NumSounds) return; - + 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 ab9cc8e6..9d647398 100644 --- a/src/game/client/components/sounds.h +++ b/src/game/client/components/sounds.h @@ -19,6 +19,8 @@ class CSounds : public CComponent int64 m_QueueWaitTime; class CJob m_SoundJob; bool m_WaitForSoundJob; + + int GetSampleId(int SetId); public: // sound channels @@ -37,7 +39,8 @@ public: void ClearQueue(); void Enqueue(int Channel, int SetId); - void Play(int Channel, int SetId, float Vol, vec2 Pos); + void Play(int Channel, int SetId, float Vol); + void PlayAt(int Channel, int SetId, float Vol, vec2 Pos); void PlayAndRecord(int Channel, int SetId, float Vol, vec2 Pos); void Stop(int SetId); }; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 7b6b1192..86fb5ad0 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -95,7 +95,6 @@ void CGameClient::OnConsoleInit() { m_pEngine = Kernel()->RequestInterface<IEngine>(); m_pClient = Kernel()->RequestInterface<IClient>(); - m_pGraphics = Kernel()->RequestInterface<IGraphics>(); m_pTextRender = Kernel()->RequestInterface<ITextRender>(); m_pSound = Kernel()->RequestInterface<ISound>(); m_pInput = Kernel()->RequestInterface<IInput>(); @@ -126,6 +125,8 @@ void CGameClient::OnConsoleInit() m_pVoting = &::gs_Voting; m_pScoreboard = &::gs_Scoreboard; m_pItems = &::gs_Items; + m_pMapLayersBackGround = &::gs_MapLayersBackGround; + m_pMapLayersForeGround = &::gs_MapLayersForeGround; // make a list of all the systems, make sure to add them in the corrent render order m_All.Add(m_pSkins); @@ -192,12 +193,10 @@ void CGameClient::OnConsoleInit() Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, 0, 0, "Force a voting option"); Console()->Register("clear_votes", "", CFGFLAG_SERVER, 0, 0, "Clears the voting options"); Console()->Register("vote", "r", CFGFLAG_SERVER, 0, 0, "Force a vote to yes/no"); + Console()->Register("swap_teams", "", CFGFLAG_SERVER, 0, 0, "Swap the current teams"); + Console()->Register("shuffle_teams", "", CFGFLAG_SERVER, 0, 0, "Shuffle the current teams"); - // propagate pointers - m_UI.SetGraphics(Graphics(), TextRender()); - m_RenderTools.m_pGraphics = Graphics(); - m_RenderTools.m_pUI = UI(); for(int i = 0; i < m_All.m_Num; i++) m_All.m_paComponents[i]->m_pClient = this; @@ -221,6 +220,13 @@ void CGameClient::OnConsoleInit() void CGameClient::OnInit() { + m_pGraphics = Kernel()->RequestInterface<IGraphics>(); + + // propagate pointers + m_UI.SetGraphics(Graphics(), TextRender()); + m_RenderTools.m_pGraphics = Graphics(); + m_RenderTools.m_pUI = UI(); + int64 Start = time_get(); // set the language @@ -443,7 +449,7 @@ void CGameClient::OnRender() m_NewPredictedTick = false; // check if client info has to be resent - if(m_LastSendInfo && Client()->State() == IClient::STATE_ONLINE && !m_pMenus->IsActive() && m_LastSendInfo+time_freq()*5 < time_get()) + if(m_LastSendInfo && Client()->State() == IClient::STATE_ONLINE && m_Snap.m_LocalClientID >= 0 && !m_pMenus->IsActive() && m_LastSendInfo+time_freq()*5 < time_get()) { // resend if client info differs if(str_comp(g_Config.m_PlayerName, m_aClients[m_Snap.m_LocalClientID].m_aName) || @@ -545,7 +551,7 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker) pMsg->m_SoundID == SOUND_CTF_GRAB_PL) g_GameClient.m_pSounds->Enqueue(CSounds::CHN_GLOBAL, pMsg->m_SoundID); else - g_GameClient.m_pSounds->Play(CSounds::CHN_GLOBAL, pMsg->m_SoundID, 1.0f, vec2(0,0)); + g_GameClient.m_pSounds->Play(CSounds::CHN_GLOBAL, pMsg->m_SoundID, 1.0f); } } @@ -620,7 +626,7 @@ void CGameClient::ProcessEvents() else if(Item.m_Type == NETEVENTTYPE_SOUNDWORLD) { 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)); + g_GameClient.m_pSounds->PlayAt(CSounds::CHN_WORLD, ev->m_SoundID, 1.0f, vec2(ev->m_X, ev->m_Y)); } } } @@ -862,6 +868,17 @@ void CGameClient::OnNewSnapshot() } } } + // sort player infos by team + int Teams[3] = { TEAM_RED, TEAM_BLUE, TEAM_SPECTATORS }; + int Index = 0; + for(int Team = 0; Team < 3; ++Team) + { + for(int i = 0; i < MAX_CLIENTS && Index < MAX_CLIENTS; ++i) + { + if(m_Snap.m_paPlayerInfos[i] && m_Snap.m_paPlayerInfos[i]->m_Team == Teams[Team]) + m_Snap.m_paInfoByTeam[Index++] = m_Snap.m_paPlayerInfos[i]; + } + } CTuningParams StandardTuning; CServerInfo CurrentServerInfo; @@ -876,6 +893,15 @@ void CGameClient::OnNewSnapshot() m_ServerMode = SERVERMODE_PUREMOD; } + // add tuning to demo + if(DemoRecorder()->IsRecording() && mem_comp(&StandardTuning, &m_Tuning, sizeof(CTuningParams)) != 0) + { + CMsgPacker Msg(NETMSGTYPE_SV_TUNEPARAMS); + int *pParams = (int *)&m_Tuning; + for(unsigned i = 0; i < sizeof(m_Tuning)/sizeof(int); i++) + Msg.AddInt(pParams[i]); + Client()->SendMsg(&Msg, MSGFLAG_RECORD|MSGFLAG_NOSEND); + } } void CGameClient::OnPredict() diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 4783f8b4..e8c7e7bd 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -125,6 +125,7 @@ public: const CNetObj_PlayerInfo *m_paPlayerInfos[MAX_CLIENTS]; const CNetObj_PlayerInfo *m_paInfoByScore[MAX_CLIENTS]; + const CNetObj_PlayerInfo *m_paInfoByTeam[MAX_CLIENTS]; int m_LocalClientID; int m_NumPlayers; @@ -241,6 +242,8 @@ public: class CVoting *m_pVoting; class CScoreboard *m_pScoreboard; class CItems *m_pItems; + class CMapLayers *m_pMapLayersBackGround; + class CMapLayers *m_pMapLayersForeGround; }; diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp index 93909d0c..278ed51a 100644 --- a/src/game/client/render.cpp +++ b/src/game/client/render.cpp @@ -75,7 +75,7 @@ void CRenderTools::SelectSprite(CDataSprite *pSpr, int Flags, int sx, int sy) void CRenderTools::SelectSprite(int Id, int Flags, int sx, int sy) { - if(Id < 0 || Id > g_pData->m_NumSprites) + if(Id < 0 || Id >= g_pData->m_NumSprites) return; SelectSprite(&g_pData->m_aSprites[Id], Flags, sx, sy); } diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 00a30c15..c5219575 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -74,6 +74,11 @@ CUIRect *CUI::Screen() return &m_Screen; } +float CUI::PixelSize() +{ + return Screen()->w/Graphics()->ScreenWidth(); +} + void CUI::SetScale(float s) { g_Config.m_UiScale = (int)(s*100.0f); diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 7cd78d6f..daba5d51 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -82,6 +82,7 @@ public: void ConvertMouseMove(float *x, float *y); CUIRect *Screen(); + float PixelSize(); void ClipEnable(const CUIRect *pRect); void ClipDisable(); diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp index 528e459b..3abcf0f4 100644 --- a/src/game/editor/auto_map.cpp +++ b/src/game/editor/auto_map.cpp @@ -20,15 +20,15 @@ void CAutoMapper::Load(const char* 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()) { @@ -40,11 +40,11 @@ void CAutoMapper::Load(const char* pTileName) { // 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 @@ -54,15 +54,15 @@ void CAutoMapper::Load(const char* pTileName) // 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")) @@ -70,7 +70,7 @@ void CAutoMapper::Load(const char* pTileName) 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]; @@ -85,9 +85,9 @@ void CAutoMapper::Load(const char* pTileName) 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)) @@ -106,12 +106,12 @@ void CAutoMapper::Load(const char* pTileName) } } } - + io_close(RulesFile); - + str_format(aBuf, sizeof(aBuf),"loaded %s", aPath); m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor", aBuf); - + m_FileLoaded = true; } @@ -127,14 +127,14 @@ 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) { @@ -144,7 +144,7 @@ void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID) break; } } - + // auto map ! int MaxIndex = pLayer->m_Width*pLayer->m_Height; for(int y = 0; y < pLayer->m_Height; y++) @@ -159,7 +159,7 @@ void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID) 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) @@ -170,7 +170,7 @@ void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID) { 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 @@ -184,15 +184,15 @@ void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID) { 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)) + (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 index ee570378..c5537d4f 100644 --- a/src/game/editor/auto_map.h +++ b/src/game/editor/auto_map.h @@ -11,7 +11,7 @@ class CAutoMapper int m_Y; int m_Value; bool m_IndexValue; - + enum { EMPTY=0, @@ -33,16 +33,16 @@ class CAutoMapper 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; diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 500e600b..fa1024e0 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -164,7 +164,7 @@ void CEditorImage::AnalyseTileFlags() int tw = m_Width/16; // tilesizes int th = m_Height/16; if ( tw == th ) - { + { unsigned char *pPixelData = (unsigned char *)m_pData; int TileID = 0; @@ -214,7 +214,6 @@ void CEditor::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pU // 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, float *Offset, bool Hidden, int Corners) { int Inside = UI()->MouseInside(pRect); @@ -228,6 +227,7 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str if(UI()->LastActiveItem() == pID) { + m_EditBoxActive = 2; int Len = str_length(pStr); if(Len == 0) s_AtIndex = 0; @@ -381,8 +381,8 @@ float CEditor::UiDoScrollbarV(const void *pID, const CUIRect *pRect, float Curre Handle.y += (pRect->h-Handle.h)*Current; // logic - float Ret = Current; - int Inside = UI()->MouseInside(&Handle); + float Ret = Current; + int Inside = UI()->MouseInside(&Handle); if(UI()->ActiveItem() == pID) { @@ -423,7 +423,7 @@ float CEditor::UiDoScrollbarV(const void *pID, const CUIRect *pRect, float Curre Slider.Margin(5.0f, &Slider); RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*ButtonColorMul(pID), CUI::CORNER_ALL, 2.5f); - return Ret; + return Ret; } vec4 CEditor::GetButtonColor(const void *pID, int Checked) @@ -490,7 +490,7 @@ int CEditor::DoButton_File(const void *pID, const char *pText, int Checked, cons int CEditor::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { CUIRect r = *pRect; - RenderTools()->DrawUIRect(&r, vec4(0.5f, 0.5f, 0.5f, 1.0f), CUI::CORNER_T, 3.0f); + RenderTools()->DrawUIRect(&r, vec4(0.5f, 0.5f, 0.5f, 1.0f), CUI::CORNER_T, 3.0f); r = *pRect; r.VMargin(5.0f, &r); @@ -515,18 +515,18 @@ int CEditor::DoButton_MenuItem(const void *pID, const char *pText, int Checked, int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_T, 5.0f); - CUIRect NewRect = *pRect; - NewRect.y += NewRect.h/2.0f-7.0f; - UI()->DoLabel(&NewRect, pText, 10, 0, -1); + CUIRect NewRect = *pRect; + NewRect.y += NewRect.h/2.0f-7.0f; + UI()->DoLabel(&NewRect, pText, 10, 0, -1); 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, float FontSize) { RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), Corners, 3.0f); - CUIRect NewRect = *pRect; - NewRect.HMargin(NewRect.h/2.0f-FontSize/2.0f-1.0f, &NewRect); - UI()->DoLabel(&NewRect, pText, FontSize, 0, -1); + CUIRect NewRect = *pRect; + 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); } @@ -550,7 +550,7 @@ void CEditor::RenderGrid(CLayerGroup *pGroup) return; float aGroupPoints[4]; - pGroup->Mapping(aGroupPoints); + pGroup->Mapping(aGroupPoints); float w = UI()->Screen()->w; float h = UI()->Screen()->h; @@ -562,7 +562,7 @@ void CEditor::RenderGrid(CLayerGroup *pGroup) int XGridOffset = XOffset % m_GridFactor; int YGridOffset = YOffset % m_GridFactor; - Graphics()->TextureSet(-1); + Graphics()->TextureSet(-1); Graphics()->LinesBegin(); for(int i = 0; i < (int)w; i++) @@ -601,9 +601,9 @@ void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brig int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, int Current, int Min, int Max, int Step, float Scale, const char *pToolTip) { - // logic - static float s_Value; - int Inside = UI()->MouseInside(pRect); + // logic + static float s_Value; + int Inside = UI()->MouseInside(pRect); if(UI()->ActiveItem() == pID) { @@ -652,9 +652,9 @@ int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, in char aBuf[128]; str_format(aBuf, sizeof(aBuf),"%s %d", pLabel, Current); RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, 0), CUI::CORNER_ALL, 5.0f); - pRect->y += pRect->h/2.0f-7.0f; - UI()->DoLabel(pRect, aBuf, 10, 0, -1); - + pRect->y += pRect->h/2.0f-7.0f; + UI()->DoLabel(pRect, aBuf, 10, 0, -1); + return Current; } @@ -713,7 +713,7 @@ void CEditor::CallbackAppendMap(const char *pFileName, int StorageType, void *pU pEditor->m_aFileName[0] = 0; else pEditor->SortImages(); - + pEditor->m_Dialog = DIALOG_NONE; } void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUser) @@ -734,7 +734,7 @@ void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUse pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder; pEditor->m_Map.m_Modified = false; } - + pEditor->m_Dialog = DIALOG_NONE; } @@ -742,32 +742,38 @@ void CEditor::DoToolbar(CUIRect ToolBar) { CUIRect TB_Top, TB_Bottom; CUIRect Button; - + ToolBar.HSplitTop(ToolBar.h/2.0f, &TB_Top, &TB_Bottom); - - TB_Top.HSplitBottom(2.5f, &TB_Top, 0); - TB_Bottom.HSplitTop(2.5f, 0, &TB_Bottom); + + TB_Top.HSplitBottom(2.5f, &TB_Top, 0); + TB_Bottom.HSplitTop(2.5f, 0, &TB_Bottom); // ctrl+o to open - if(Input()->KeyDown('o') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))) + if(Input()->KeyDown('o') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)) && m_Dialog == DIALOG_NONE) { if(HasUnsavedData()) { - m_PopupEventType = POPEVENT_LOAD; - m_PopupEventActivated = true; + if(!m_PopupEventWasActivated) + { + m_PopupEventType = POPEVENT_LOAD; + m_PopupEventActivated = true; + } } else InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", CallbackOpenMap, this); } // ctrl+s to save - if(Input()->KeyDown('s') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))) + if(Input()->KeyDown('s') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)) && m_Dialog == DIALOG_NONE) { - if(m_aFileName[0] && m_ValidSaveFilename) + if(m_aFileName[0] && m_ValidSaveFilename) { - str_copy(m_aFileSaveName, m_aFileName, sizeof(m_aFileSaveName)); - m_PopupEventType = POPEVENT_SAVE; - m_PopupEventActivated = true; + if(!m_PopupEventWasActivated) + { + str_copy(m_aFileSaveName, m_aFileName, sizeof(m_aFileSaveName)); + m_PopupEventType = POPEVENT_SAVE; + m_PopupEventActivated = true; + } } else InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveMap, this); @@ -814,6 +820,7 @@ void CEditor::DoToolbar(CUIRect ToolBar) (Input()->KeyDown('i') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))) { m_ShowTileInfo = !m_ShowTileInfo; + m_ShowEnvelopePreview = 0; } TB_Top.VSplitLeft(15.0f, 0, &TB_Top); @@ -821,12 +828,12 @@ void CEditor::DoToolbar(CUIRect ToolBar) // zoom group TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_ZoomOutButton = 0; - if(DoButton_Ex(&s_ZoomOutButton, "ZO", 0, &Button, 0, "[NumPad-] Zoom out", CUI::CORNER_L) || Input()->KeyDown(KEY_KP_MINUS)) + if(DoButton_Ex(&s_ZoomOutButton, "ZO", 0, &Button, 0, "[NumPad-] Zoom out", CUI::CORNER_L)) m_ZoomLevel += 50; TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_ZoomNormalButton = 0; - if(DoButton_Ex(&s_ZoomNormalButton, "1:1", 0, &Button, 0, "[NumPad*] Zoom to normal and remove editor offset", 0) || Input()->KeyDown(KEY_KP_MULTIPLY)) + if(DoButton_Ex(&s_ZoomNormalButton, "1:1", 0, &Button, 0, "[NumPad*] Zoom to normal and remove editor offset", 0)) { m_EditorOffsetX = 0; m_EditorOffsetY = 0; @@ -835,7 +842,7 @@ void CEditor::DoToolbar(CUIRect ToolBar) TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); static int s_ZoomInButton = 0; - if(DoButton_Ex(&s_ZoomInButton, "ZI", 0, &Button, 0, "[NumPad+] Zoom in", CUI::CORNER_R) || Input()->KeyDown(KEY_KP_PLUS)) + if(DoButton_Ex(&s_ZoomInButton, "ZI", 0, &Button, 0, "[NumPad+] Zoom in", CUI::CORNER_R)) m_ZoomLevel -= 50; TB_Top.VSplitLeft(10.0f, 0, &TB_Top); @@ -859,8 +866,6 @@ void CEditor::DoToolbar(CUIRect ToolBar) m_AnimateSpeed -= 0.5f; } - m_WorldZoom = m_ZoomLevel/100.0f; - TB_Top.VSplitLeft(10.0f, &Button, &TB_Top); @@ -947,17 +952,17 @@ void CEditor::DoToolbar(CUIRect ToolBar) } } } - + // tile manipulation { TB_Bottom.VSplitLeft(40.0f, &Button, &TB_Bottom); static int s_BorderBut = 0; CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); - + if(DoButton_Editor(&s_BorderBut, "Border", pT?0:-1, &Button, 0, "Adds border tiles")) { if(pT) - DoMapBorder(); + DoMapBorder(); } } @@ -966,7 +971,7 @@ void CEditor::DoToolbar(CUIRect ToolBar) // refocus button TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom); static int s_RefocusButton = 0; - if(DoButton_Editor(&s_RefocusButton, "Refocus", m_WorldOffsetX&&m_WorldOffsetY?0:-1, &Button, 0, "[HOME] Restore map focus") || Input()->KeyDown(KEY_HOME)) + if(DoButton_Editor(&s_RefocusButton, "Refocus", m_WorldOffsetX&&m_WorldOffsetY?0:-1, &Button, 0, "[HOME] Restore map focus") || (m_EditBoxActive == 0 && Input()->KeyDown(KEY_HOME))) { m_WorldOffsetX = 0; m_WorldOffsetY = 0; @@ -1046,6 +1051,12 @@ void CEditor::DoQuad(CQuad *q, int Index) if(dx*dx+dy*dy < 50) UI()->SetHotItem(pID); + bool IgnoreGrid; + if(Input()->KeyPressed(KEY_LALT) || Input()->KeyPressed(KEY_RALT)) + IgnoreGrid = true; + else + IgnoreGrid = false; + // draw selection background if(m_SelectedQuad == Index) { @@ -1056,58 +1067,82 @@ void CEditor::DoQuad(CQuad *q, int Index) if(UI()->ActiveItem() == pID) { - // check if we only should move pivot - if(s_Operation == OP_MOVE_PIVOT) + if(m_MouseDeltaWx*m_MouseDeltaWx+m_MouseDeltaWy*m_MouseDeltaWy > 0.5f) { - q->m_aPoints[4].x += f2fx(wx-s_LastWx); - q->m_aPoints[4].y += f2fx(wy-s_LastWy); - } - else if(s_Operation == OP_MOVE_ALL) - { - // move all points including pivot - if(m_GridActive) + // check if we only should move pivot + if(s_Operation == OP_MOVE_PIVOT) { - int LineDistance = GetLineDistance(); + if(m_GridActive && !IgnoreGrid) + { + 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); + 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); + + q->m_aPoints[4].x = f2fx(x); + q->m_aPoints[4].y = f2fx(y); + } 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; + q->m_aPoints[4].x += f2fx(wx-s_LastWx); + q->m_aPoints[4].y += f2fx(wy-s_LastWy); } } - else + else if(s_Operation == OP_MOVE_ALL) { - for(int v = 0; v < 5; v++) + // move all points including pivot + if(m_GridActive && !IgnoreGrid) { - 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) - { - for(int v = 0; v < 4; v++) + else if(s_Operation == OP_ROTATE) { - q->m_aPoints[v] = s_RotatePoints[v]; - Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle); + for(int v = 0; v < 4; v++) + { + q->m_aPoints[v] = s_RotatePoints[v]; + Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle); + } } } @@ -1143,7 +1178,7 @@ void CEditor::DoQuad(CQuad *q, int Index) ms_pUiGotContext = pID; Graphics()->SetColor(1,1,1,1); - m_pTooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate."; + m_pTooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate. Hold alt to ignore grid."; if(UI()->MouseButton(0)) { @@ -1163,6 +1198,8 @@ void CEditor::DoQuad(CQuad *q, int Index) s_Operation = OP_MOVE_ALL; UI()->SetActiveItem(pID); + if(m_SelectedQuad != Index) + m_SelectedPoints = 0; m_SelectedQuad = Index; s_LastWx = wx; s_LastWy = wy; @@ -1170,6 +1207,8 @@ void CEditor::DoQuad(CQuad *q, int Index) if(UI()->MouseButton(1)) { + if(m_SelectedQuad != Index) + m_SelectedPoints = 0; m_SelectedQuad = Index; s_Operation = OP_CONTEXT_MENU; UI()->SetActiveItem(pID); @@ -1216,6 +1255,12 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) static bool s_Moved; static int s_Operation = OP_NONE; + bool IgnoreGrid; + if(Input()->KeyPressed(KEY_LALT) || Input()->KeyPressed(KEY_RALT)) + IgnoreGrid = true; + else + IgnoreGrid = false; + if(UI()->ActiveItem() == pID) { float dx = m_MouseDeltaWx; @@ -1230,7 +1275,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) { if(s_Operation == OP_MOVEPOINT) { - if(m_GridActive) + if(m_GridActive && !IgnoreGrid) { for(int m = 0; m < 4; m++) if(m_SelectedPoints&(1<<m)) @@ -1267,12 +1312,12 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) for(int m = 0; m < 4; m++) if(m_SelectedPoints&(1<<m)) { - // 0,2;1,3 - line x + // 0,2;1,3 - line x // 0,1;2,3 - line y pQuad->m_aTexcoords[m].x += f2fx(dx*0.001f); pQuad->m_aTexcoords[(m+2)%4].x += f2fx(dx*0.001f); - + pQuad->m_aTexcoords[m].y += f2fx(dy*0.001f); pQuad->m_aTexcoords[m^1].y += f2fx(dy*0.001f); } @@ -1311,7 +1356,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) ms_pUiGotContext = pID; Graphics()->SetColor(1,1,1,1); - m_pTooltip = "Left mouse button to move. Hold shift to move the texture."; + m_pTooltip = "Left mouse button to move. Hold shift to move the texture. Hold alt to ignore grid."; if(UI()->MouseButton(0)) { @@ -1358,6 +1403,218 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V) Graphics()->QuadsDraw(&QuadItem, 1); } +void CEditor::DoQuadEnvelopes(CQuad *pQuad, int Index, int TexID) +{ + CEnvelope *pEnvelope = 0x0; + if(pQuad->m_PosEnv >= 0 && pQuad->m_PosEnv < m_Map.m_lEnvelopes.size()) + pEnvelope = m_Map.m_lEnvelopes[pQuad->m_PosEnv]; + if (!pEnvelope) + return; + + //QuadParams + CPoint *pPoints = pQuad->m_aPoints; + + //Draw Lines + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); + Graphics()->SetColor(80.0f/255, 150.0f/255, 230.f/255, 0.5f); + for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++) + { + float OffsetX = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]); + float OffsetY = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]); + vec2 Pos0 = vec2(fx2f(pPoints[4].x)+OffsetX, fx2f(pPoints[4].y)+OffsetY); + + OffsetX = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[0]); + OffsetY = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[1]); + vec2 Pos1 = vec2(fx2f(pPoints[4].x)+OffsetX, fx2f(pPoints[4].y)+OffsetY); + + IGraphics::CLineItem Line = IGraphics::CLineItem(Pos0.x, Pos0.y, Pos1.x, Pos1.y); + Graphics()->LinesDraw(&Line, 1); + } + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + Graphics()->LinesEnd(); + + //Draw Quads + for(int i = 0; i < pEnvelope->m_lPoints.size(); i++) + { + Graphics()->TextureSet(TexID); + Graphics()->QuadsBegin(); + + //Calc Env Position + float OffsetX = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]); + float OffsetY = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]); + float Rot = fx2f(pEnvelope->m_lPoints[i].m_aValues[2])/360.0f*pi*2; + + //Set Colours + float Alpha = (m_SelectedQuadEnvelope == pQuad->m_PosEnv && m_SelectedEnvelopePoint == i) ? 0.65f : 0.35f; + IGraphics::CColorVertex aArray[4] = { + IGraphics::CColorVertex(0, pQuad->m_aColors[0].r, pQuad->m_aColors[0].g, pQuad->m_aColors[0].b, Alpha), + IGraphics::CColorVertex(1, pQuad->m_aColors[1].r, pQuad->m_aColors[1].g, pQuad->m_aColors[1].b, Alpha), + IGraphics::CColorVertex(2, pQuad->m_aColors[2].r, pQuad->m_aColors[2].g, pQuad->m_aColors[2].b, Alpha), + IGraphics::CColorVertex(3, pQuad->m_aColors[3].r, pQuad->m_aColors[3].g, pQuad->m_aColors[3].b, Alpha)}; + Graphics()->SetColorVertex(aArray, 4); + + //Rotation + if(Rot != 0) + { + static CPoint aRotated[4]; + aRotated[0] = pQuad->m_aPoints[0]; + aRotated[1] = pQuad->m_aPoints[1]; + aRotated[2] = pQuad->m_aPoints[2]; + aRotated[3] = pQuad->m_aPoints[3]; + pPoints = aRotated; + + Rotate(&pQuad->m_aPoints[4], &aRotated[0], Rot); + Rotate(&pQuad->m_aPoints[4], &aRotated[1], Rot); + Rotate(&pQuad->m_aPoints[4], &aRotated[2], Rot); + Rotate(&pQuad->m_aPoints[4], &aRotated[3], Rot); + } + + //Set Texture Coords + Graphics()->QuadsSetSubsetFree( + fx2f(pQuad->m_aTexcoords[0].x), fx2f(pQuad->m_aTexcoords[0].y), + fx2f(pQuad->m_aTexcoords[1].x), fx2f(pQuad->m_aTexcoords[1].y), + fx2f(pQuad->m_aTexcoords[2].x), fx2f(pQuad->m_aTexcoords[2].y), + fx2f(pQuad->m_aTexcoords[3].x), fx2f(pQuad->m_aTexcoords[3].y) + ); + + //Set Quad Coords & Draw + IGraphics::CFreeformItem Freeform( + fx2f(pPoints[0].x)+OffsetX, fx2f(pPoints[0].y)+OffsetY, + fx2f(pPoints[1].x)+OffsetX, fx2f(pPoints[1].y)+OffsetY, + fx2f(pPoints[2].x)+OffsetX, fx2f(pPoints[2].y)+OffsetY, + fx2f(pPoints[3].x)+OffsetX, fx2f(pPoints[3].y)+OffsetY); + Graphics()->QuadsDrawFreeform(&Freeform, 1); + + Graphics()->QuadsEnd(); + + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + DoQuadEnvPoint(pQuad, Index, i); + Graphics()->QuadsEnd(); + } +} + +void CEditor::DoQuadEnvPoint(CQuad *pQuad, int QIndex, int PIndex) +{ + enum + { + OP_NONE=0, + OP_MOVE, + OP_ROTATE, + }; + + // some basic values + static float s_LastWx; + static float s_LastWy; + static int s_Operation = OP_NONE; + float wx = UI()->MouseWorldX(); + float wy = UI()->MouseWorldY(); + CEnvelope *pEnvelope = m_Map.m_lEnvelopes[pQuad->m_PosEnv]; + void *pID = &pEnvelope->m_lPoints[PIndex]; + static int s_ActQIndex = -1; + + // get pivot + float CenterX = fx2f(pQuad->m_aPoints[4].x)+fx2f(pEnvelope->m_lPoints[PIndex].m_aValues[0]); + float CenterY = fx2f(pQuad->m_aPoints[4].y)+fx2f(pEnvelope->m_lPoints[PIndex].m_aValues[1]); + + float dx = (CenterX - wx)/m_WorldZoom; + float dy = (CenterY - wy)/m_WorldZoom; + if(dx*dx+dy*dy < 50.0f && UI()->ActiveItem() == 0) + { + UI()->SetHotItem(pID); + s_ActQIndex = QIndex; + } + + bool IgnoreGrid; + if(Input()->KeyPressed(KEY_LALT) || Input()->KeyPressed(KEY_RALT)) + IgnoreGrid = true; + else + IgnoreGrid = false; + + if(UI()->ActiveItem() == pID && s_ActQIndex == QIndex) + { + if(s_Operation == OP_MOVE) + { + if(m_GridActive && !IgnoreGrid) + { + 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); + + pEnvelope->m_lPoints[PIndex].m_aValues[0] = f2fx(x); + pEnvelope->m_lPoints[PIndex].m_aValues[1] = f2fx(y); + } + else + { + pEnvelope->m_lPoints[PIndex].m_aValues[0] += f2fx(wx-s_LastWx); + pEnvelope->m_lPoints[PIndex].m_aValues[1] += f2fx(wy-s_LastWy); + } + } + else if(s_Operation == OP_ROTATE) + pEnvelope->m_lPoints[PIndex].m_aValues[2] += 10*m_MouseDeltaX; + + s_LastWx = wx; + s_LastWy = wy; + + if(!UI()->MouseButton(0)) + { + m_LockMouse = false; + s_Operation = OP_NONE; + UI()->SetActiveItem(0); + } + + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + } + else if(UI()->HotItem() == pID && s_ActQIndex == QIndex) + { + ms_pUiGotContext = pID; + + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + m_pTooltip = "Left mouse button to move. Hold ctrl to rotate. Hold alt to ignore grid."; + + if(UI()->MouseButton(0)) + { + if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)) + { + m_LockMouse = true; + s_Operation = OP_ROTATE; + } + else + s_Operation = OP_MOVE; + + m_SelectedEnvelopePoint = PIndex; + m_SelectedQuadEnvelope = pQuad->m_PosEnv; + + UI()->SetActiveItem(pID); + if(m_SelectedQuad != QIndex) + m_SelectedPoints = 0; + m_SelectedQuad = QIndex; + s_LastWx = wx; + s_LastWy = wy; + } + else + { + m_SelectedEnvelopePoint = -1; + m_SelectedQuadEnvelope = -1; + } + } + else + Graphics()->SetColor(0.0f, 1.0f, 0.0f, 1.0f); + + IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f*m_WorldZoom, 5.0f*m_WorldZoom); + Graphics()->QuadsDraw(&QuadItem, 1); +} + void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) { // render all good stuff @@ -1384,6 +1641,12 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) pT->ShowInfo(); } } + else + { + // fix aspect ratio of the image in the picker + float Max = min(View.w, View.h); + View.w = View.h = Max; + } static void *s_pEditorID = (void *)&s_pEditorID; int Inside = UI()->MouseInside(&View); @@ -1563,8 +1826,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) { if(!UI()->MouseButton(0)) { - for(int k = 0; k < NumEditLayers; k++) - pEditLayers[k]->FillSelection(m_Brush.IsEmpty(), m_Brush.m_lLayers[0], r); + for(int k = 0; k < NumEditLayers; k++) + pEditLayers[k]->FillSelection(m_Brush.IsEmpty(), m_Brush.m_lLayers[0], r); } else { @@ -1596,10 +1859,10 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) } } - + CLayerTiles *pLayer = (CLayerTiles*)GetSelectedLayerType(0, LAYERTYPE_TILES); if((Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) && pLayer) - s_Operation = OP_BRUSH_PAINT; + s_Operation = OP_BRUSH_PAINT; } if(!m_Brush.IsEmpty()) @@ -1656,6 +1919,9 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) { CLayerQuads *pLayer = (CLayerQuads *)pEditLayers[k]; + if(!m_ShowEnvelopePreview) + m_ShowEnvelopePreview = 2; + Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); for(int i = 0; i < pLayer->m_lQuads.size(); i++) @@ -1705,11 +1971,11 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) } } - if(GetSelectedGroup() && GetSelectedGroup()->m_UseClipping) + if(!m_ShowPicker && GetSelectedGroup() && GetSelectedGroup()->m_UseClipping) { CLayerGroup *g = m_Map.m_pGameGroup; g->MapScreen(); - + Graphics()->TextureSet(-1); Graphics()->LinesBegin(); @@ -1813,6 +2079,24 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) Graphics()->LinesEnd(); } + if (!m_ShowPicker && m_ShowTileInfo && m_ShowEnvelopePreview != 0 && GetSelectedLayer(0) && GetSelectedLayer(0)->m_Type == LAYERTYPE_QUADS) + { + GetSelectedGroup()->MapScreen(); + + CLayerQuads *pLayer = (CLayerQuads*)GetSelectedLayer(0); + int TexID = -1; + if(pLayer->m_Image >= 0 && pLayer->m_Image < m_Map.m_lImages.size()) + TexID = m_Map.m_lImages[pLayer->m_Image]->m_TexID; + + for(int i = 0; i < pLayer->m_lQuads.size(); i++) + { + if((m_ShowEnvelopePreview == 1 && pLayer->m_lQuads[i].m_PosEnv == m_SelectedEnvelope) || m_ShowEnvelopePreview == 2) + DoQuadEnvelopes(&pLayer->m_lQuads[i], i, TexID); + } + + m_ShowEnvelopePreview = 0; + } + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); //UI()->ClipDisable(); } @@ -1908,7 +2192,7 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int * if(pProps[i].m_Value < 0) str_copy(aBuf, "None", sizeof(aBuf)); else - str_format(aBuf, sizeof(aBuf),"%s", m_Map.m_lImages[pProps[i].m_Value]->m_aName); + str_format(aBuf, sizeof(aBuf),"%s", m_Map.m_lImages[pProps[i].m_Value]->m_aName); if(DoButton_Editor(&pIDs[i], aBuf, 0, &Shifter, 0, 0)) PopupSelectImageInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY()); @@ -2052,7 +2336,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) static int s_GroupPopupId = 0; if(Result == 2) - UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 220, PopupGroup); + UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 220, PopupGroup); if(m_Map.m_lGroups[g]->m_lLayers.size() && Input()->MouseDoubleClick()) m_Map.m_lGroups[g]->m_Collapse ^= 1; @@ -2147,8 +2431,8 @@ void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser) pEditor->SortImages(); for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i) { - if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, pImg->m_aName)) - pEditor->m_SelectedImage = i; + if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, pImg->m_aName)) + pEditor->m_SelectedImage = i; } pEditor->m_Dialog = DIALOG_NONE; } @@ -2165,8 +2449,8 @@ void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser) ExtractName(pFileName, aBuf, sizeof(aBuf)); for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i) { - if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, aBuf)) - return; + if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, aBuf)) + return; } CEditorImage *pImg = new CEditorImage(pEditor); @@ -2292,7 +2576,7 @@ void CEditor::SortImages() gs_pSortedIndex = 0; } } - + void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) { @@ -2390,15 +2674,28 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) r.w = r.h; else r.h = r.w; + float Max = (float)(max(m_Map.m_lImages[i]->m_Width, m_Map.m_lImages[i]->m_Height)); + r.w *= m_Map.m_lImages[i]->m_Width/Max; + r.h *= m_Map.m_lImages[i]->m_Height/Max; Graphics()->TextureSet(m_Map.m_lImages[i]->m_TexID); Graphics()->BlendNormal(); + Graphics()->WrapClamp(); Graphics()->QuadsBegin(); IGraphics::CQuadItem QuadItem(r.x, r.y, r.w, r.h); Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); - + Graphics()->WrapNormal(); } } + + // separator + ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); + ImageCur += 5.0f; + IGraphics::CLineItem LineItem(Slot.x, Slot.y+Slot.h/2, Slot.x+Slot.w, Slot.y+Slot.h/2); + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); + Graphics()->LinesDraw(&LineItem, 1); + Graphics()->LinesEnd(); } if(ImageCur + 27.0f > ImageStopAt) @@ -2409,7 +2706,6 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) // new image static int s_NewImageButton = 0; - ToolBox.HSplitTop(10.0f, &Slot, &ToolBox); ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); if(DoButton_Editor(&s_NewImageButton, "Add", 0, &Slot, 0, "Load a new image to use in the map")) InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", "", AddImage, this); @@ -2485,11 +2781,13 @@ void CEditor::RenderFileDialog() RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.75f), CUI::CORNER_ALL, 5.0f); View.Margin(10.0f, &View); - CUIRect Title, FileBox, FileBoxLabel, ButtonBar, Scroll; + CUIRect Title, FileBox, FileBoxLabel, ButtonBar, Scroll, PathBox; View.HSplitTop(18.0f, &Title, &View); View.HSplitTop(5.0f, 0, &View); // some spacing View.HSplitBottom(14.0f, &View, &ButtonBar); View.HSplitBottom(10.0f, &View, 0); // some spacing + View.HSplitBottom(14.0f, &View, &PathBox); + View.HSplitBottom(5.0f, &View, 0); // some spacing View.HSplitBottom(14.0f, &View, &FileBox); FileBox.VSplitLeft(55.0f, &FileBoxLabel, &FileBox); View.HSplitBottom(10.0f, &View, 0); // some spacing @@ -2500,6 +2798,15 @@ void CEditor::RenderFileDialog() Title.VMargin(10.0f, &Title); UI()->DoLabel(&Title, m_pFileDialogTitle, 12.0f, -1, -1); + // pathbox + char aPath[128], aBuf[128]; + if(m_FilesSelectedIndex != -1) + Storage()->GetCompletePath(m_FileList[m_FilesSelectedIndex].m_StorageType, m_pFileDialogPath, aPath, sizeof(aPath)); + else + aPath[0] = 0; + str_format(aBuf, sizeof(aBuf), "Current path: %s", aPath); + UI()->DoLabel(&PathBox, aBuf, 10.0f, -1, -1); + // filebox if(m_FileDialogStorageType == IStorage::TYPE_SAVE) { @@ -2596,6 +2903,7 @@ void CEditor::RenderFileDialog() static int s_OkButton = 0; static int s_CancelButton = 0; static int s_NewFolderButton = 0; + static int s_MapInfoButton = 0; CUIRect Button; ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button); @@ -2671,6 +2979,22 @@ void CEditor::RenderFileDialog() UI()->SetActiveItem(0); } } + + if(m_FileDialogStorageType == IStorage::TYPE_SAVE) + { + ButtonBar.VSplitLeft(40.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(70.0f, &Button, &ButtonBar); + if(DoButton_Editor(&s_MapInfoButton, "Map details", 0, &Button, 0, 0)) + { + str_copy(m_Map.m_MapInfo.m_aAuthorTmp, m_Map.m_MapInfo.m_aAuthor, sizeof(m_Map.m_MapInfo.m_aAuthorTmp)); + str_copy(m_Map.m_MapInfo.m_aVersionTmp, m_Map.m_MapInfo.m_aVersion, sizeof(m_Map.m_MapInfo.m_aVersionTmp)); + str_copy(m_Map.m_MapInfo.m_aCreditsTmp, m_Map.m_MapInfo.m_aCredits, sizeof(m_Map.m_MapInfo.m_aCreditsTmp)); + str_copy(m_Map.m_MapInfo.m_aLicenseTmp, m_Map.m_MapInfo.m_aLicense, sizeof(m_Map.m_MapInfo.m_aLicenseTmp)); + static int s_MapInfoPopupID = 0; + UiInvokePopupMenu(&s_MapInfoPopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupMapInfo); + UI()->SetActiveItem(0); + } + } } void CEditor::FilelistPopulate(int StorageType) @@ -2711,7 +3035,7 @@ void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle str_copy(m_aFileDialogFileName, pDefaultName, sizeof(m_aFileDialogFileName)); if(pBasePath) str_copy(m_aFileDialogCurrentFolder, pBasePath, sizeof(m_aFileDialogCurrentFolder)); - + FilelistPopulate(m_FileDialogStorageType); m_Dialog = DIALOG_FILE; @@ -2731,10 +3055,10 @@ void CEditor::RenderModebar(CUIRect View) const char *pButName = m_Mode == MODE_LAYERS ? "Layers" : "Images"; if(DoButton_Tab(&s_Button, pButName, 0, &Button, 0, "Switch between images and layers managment.")) { - if(m_Mode == MODE_LAYERS) - m_Mode = MODE_IMAGES; - else - m_Mode = MODE_LAYERS; + if(m_Mode == MODE_LAYERS) + m_Mode = MODE_IMAGES; + else + m_Mode = MODE_LAYERS; } } @@ -2908,6 +3232,17 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) if(DoButton_Editor(&s_aChannelButtons[i], s_paNames[pEnvelope->m_Channels-3][i], s_ActiveChannels&Bit, &Button, 0, paDescriptions[pEnvelope->m_Channels-3][i])) s_ActiveChannels ^= Bit; } + + // sync checkbox + ToolBar.VSplitLeft(15.0f, &Button, &ToolBar); + ToolBar.VSplitLeft(12.0f, &Button, &ToolBar); + static int s_SyncButton; + if(DoButton_Editor(&s_SyncButton, pEnvelope->m_Synchronized?"X":"", 0, &Button, 0, "Enable envelope synchronization between clients")) + pEnvelope->m_Synchronized = !pEnvelope->m_Synchronized; + + ToolBar.VSplitLeft(4.0f, &Button, &ToolBar); + ToolBar.VSplitLeft(80.0f, &Button, &ToolBar); + UI()->DoLabel(&Button, "Synchronized", 10.0f, -1, -1); } float EndTime = pEnvelope->EndTime(); @@ -2947,6 +3282,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) m_Map.m_Modified = true; } + m_ShowEnvelopePreview = 1; m_pTooltip = "Press right mouse button to create a new point"; } } @@ -3085,10 +3421,13 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) { if(!UI()->MouseButton(0)) { + m_SelectedQuadEnvelope = -1; + m_SelectedEnvelopePoint = -1; + UI()->SetActiveItem(0); } else - { + { if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) { if(i != 0) @@ -3110,6 +3449,10 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) else pEnvelope->m_lPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY*ValueScale); } + + m_SelectedQuadEnvelope = m_SelectedEnvelope; + m_ShowEnvelopePreview = 1; + m_SelectedEnvelopePoint = i; m_Map.m_Modified = true; } @@ -3132,6 +3475,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) m_Map.m_Modified = true; } + m_ShowEnvelopePreview = 1; ColorMod = 100.0f; Graphics()->SetColor(1,0.75f,0.75f,1); m_pTooltip = "Left mouse to drag. Hold ctrl to be more precise. Hold shift to alter time point aswell. Right click to delete."; @@ -3143,7 +3487,10 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) CurrentValue = pEnvelope->m_lPoints[i].m_aValues[c]; } - Graphics()->SetColor(aColors[c].r*ColorMod, aColors[c].g*ColorMod, aColors[c].b*ColorMod, 1.0f); + if (m_SelectedQuadEnvelope == m_SelectedEnvelope && m_SelectedEnvelopePoint == i) + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + else + Graphics()->SetColor(aColors[c].r*ColorMod, aColors[c].g*ColorMod, aColors[c].b*ColorMod, 1.0f); IGraphics::CQuadItem QuadItem(Final.x, Final.y, Final.w, Final.h); Graphics()->QuadsDrawTL(&QuadItem, 1); } @@ -3266,10 +3613,15 @@ void CEditor::RenderMenubar(CUIRect MenuBar) (void)0; */ + CUIRect Info; MenuBar.VSplitLeft(40.0f, 0, &MenuBar); + MenuBar.VSplitLeft(MenuBar.w*0.75f, &MenuBar, &Info); char aBuf[128]; str_format(aBuf, sizeof(aBuf), "File: %s", m_aFileName); UI()->DoLabel(&MenuBar, aBuf, 10.0f, -1, -1); + + str_format(aBuf, sizeof(aBuf), "Z: %i, A: %.1f, G: %i", m_ZoomLevel, m_AnimateSpeed, m_GridFactor); + UI()->DoLabel(&Info, aBuf, 10.0f, 1, -1); } void CEditor::Render() @@ -3285,6 +3637,9 @@ void CEditor::Render() // reset tip m_pTooltip = 0; + if(m_EditBoxActive) + --m_EditBoxActive; + // render checker RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f); @@ -3314,7 +3669,17 @@ void CEditor::Render() if(m_Mode == MODE_LAYERS) DoMapEditor(View, ToolBar); - // do the scrolling + // do zooming + if(Input()->KeyDown(KEY_KP_MINUS)) + m_ZoomLevel += 50; + if(Input()->KeyDown(KEY_KP_PLUS)) + m_ZoomLevel -= 50; + if(Input()->KeyDown(KEY_KP_MULTIPLY)) + { + m_EditorOffsetX = 0; + m_EditorOffsetY = 0; + m_ZoomLevel = 100; + } if(m_Dialog == DIALOG_NONE && UI()->MouseInside(&View)) { if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) @@ -3322,9 +3687,9 @@ void CEditor::Render() if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) m_ZoomLevel += 20; - - m_ZoomLevel = clamp(m_ZoomLevel, 50, 2000); } + m_ZoomLevel = clamp(m_ZoomLevel, 50, 2000); + m_WorldZoom = m_ZoomLevel/100.0f; if(m_GuiActive) { @@ -3382,6 +3747,7 @@ void CEditor::Render() static int s_PopupID = 0; UiInvokePopupMenu(&s_PopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupEvent); m_PopupEventActivated = false; + m_PopupEventWasActivated = true; } @@ -3443,12 +3809,12 @@ void CEditor::Reset(bool CreateDefault) m_SelectedPoints = 0; m_SelectedEnvelope = 0; m_SelectedImage = 0; - + m_WorldOffsetX = 0; m_WorldOffsetY = 0; m_EditorOffsetX = 0.0f; m_EditorOffsetY = 0.0f; - + m_WorldZoom = 1.0f; m_ZoomLevel = 200; @@ -3458,6 +3824,8 @@ void CEditor::Reset(bool CreateDefault) m_MouseDeltaWy = 0; m_Map.m_Modified = false; + + m_ShowEnvelopePreview = 0; } int CEditor::GetLineDistance() @@ -3490,19 +3858,27 @@ void CEditorMap::DeleteEnvelope(int Index) for(int j = 0; j < m_lGroups[i]->m_lLayers.size(); ++j) if(m_lGroups[i]->m_lLayers[j]->m_Type == LAYERTYPE_QUADS) { - CLayerQuads *Layer = static_cast<CLayerQuads *>(m_lGroups[i]->m_lLayers[j]); - for(int k = 0; k < Layer->m_lQuads.size(); ++k) + CLayerQuads *pLayer = static_cast<CLayerQuads *>(m_lGroups[i]->m_lLayers[j]); + for(int k = 0; k < pLayer->m_lQuads.size(); ++k) { - if(Layer->m_lQuads[k].m_PosEnv == Index) - Layer->m_lQuads[k].m_PosEnv = -1; - else if(Layer->m_lQuads[k].m_PosEnv > Index) - Layer->m_lQuads[k].m_PosEnv--; - if(Layer->m_lQuads[k].m_ColorEnv == Index) - Layer->m_lQuads[k].m_ColorEnv = -1; - else if(Layer->m_lQuads[k].m_ColorEnv > Index) - Layer->m_lQuads[k].m_ColorEnv--; + if(pLayer->m_lQuads[k].m_PosEnv == Index) + pLayer->m_lQuads[k].m_PosEnv = -1; + else if(pLayer->m_lQuads[k].m_PosEnv > Index) + pLayer->m_lQuads[k].m_PosEnv--; + if(pLayer->m_lQuads[k].m_ColorEnv == Index) + pLayer->m_lQuads[k].m_ColorEnv = -1; + else if(pLayer->m_lQuads[k].m_ColorEnv > Index) + pLayer->m_lQuads[k].m_ColorEnv--; } } + else if(m_lGroups[i]->m_lLayers[j]->m_Type == LAYERTYPE_TILES) + { + CLayerTiles *pLayer = static_cast<CLayerTiles *>(m_lGroups[i]->m_lLayers[j]); + if(pLayer->m_ColorEnv == Index) + pLayer->m_ColorEnv = -1; + if(pLayer->m_ColorEnv > Index) + pLayer->m_ColorEnv--; + } m_lEnvelopes.remove_index(Index); } @@ -3529,6 +3905,8 @@ void CEditorMap::Clean() m_lEnvelopes.delete_all(); m_lImages.delete_all(); + m_MapInfo.Reset(); + m_pGameLayer = 0x0; m_pGameGroup = 0x0; @@ -3594,19 +3972,19 @@ void CEditor::Init() void CEditor::DoMapBorder() { - CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); - - for(int i = 0; i < pT->m_Width*2; ++i) - pT->m_pTiles[i].m_Index = 1; - - for(int i = 0; i < pT->m_Width*pT->m_Height; ++i) - { - if(i%pT->m_Width < 2 || i%pT->m_Width > pT->m_Width-3) - pT->m_pTiles[i].m_Index = 1; - } - - for(int i = (pT->m_Width*(pT->m_Height-2)); i < pT->m_Width*pT->m_Height; ++i) - pT->m_pTiles[i].m_Index = 1; + CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); + + for(int i = 0; i < pT->m_Width*2; ++i) + pT->m_pTiles[i].m_Index = 1; + + for(int i = 0; i < pT->m_Width*pT->m_Height; ++i) + { + if(i%pT->m_Width < 2 || i%pT->m_Width > pT->m_Width-3) + pT->m_pTiles[i].m_Index = 1; + } + + for(int i = (pT->m_Width*(pT->m_Height-2)); i < pT->m_Width*pT->m_Height; ++i) + pT->m_pTiles[i].m_Index = 1; } void CEditor::UpdateAndRender() diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 1a904953..a81474d9 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -51,6 +51,7 @@ public: array<CEnvPoint> m_lPoints; char m_aName[32]; float m_Bottom, m_Top; + bool m_Synchronized; CEnvelope(int Chan) { @@ -58,6 +59,7 @@ public: m_aName[0] = 0; m_Bottom = 0; m_Top = 0; + m_Synchronized = true; } void Resort() @@ -274,6 +276,34 @@ public: array<CEditorImage*> m_lImages; array<CEnvelope*> m_lEnvelopes; + class CMapInfo + { + public: + char m_aAuthorTmp[32]; + char m_aVersionTmp[16]; + char m_aCreditsTmp[128]; + char m_aLicenseTmp[32]; + + char m_aAuthor[32]; + char m_aVersion[16]; + char m_aCredits[128]; + char m_aLicense[32]; + + void Reset() + { + m_aAuthorTmp[0] = 0; + m_aVersionTmp[0] = 0; + m_aCreditsTmp[0] = 0; + m_aLicenseTmp[0] = 0; + + m_aAuthor[0] = 0; + m_aVersion[0] = 0; + m_aCredits[0] = 0; + m_aLicense[0] = 0; + } + }; + CMapInfo m_MapInfo; + class CLayerGame *m_pGameLayer; CLayerGroup *m_pGameGroup; @@ -476,6 +506,7 @@ public: m_Mode = MODE_LAYERS; m_Dialog = 0; + m_EditBoxActive = 0; m_pTooltip = 0; m_GridActive = false; @@ -486,6 +517,7 @@ public: m_ValidSaveFilename = false; m_PopupEventActivated = false; + m_PopupEventWasActivated = false; m_FileDialogStorageType = 0; m_pFileDialogTitle = 0; @@ -528,6 +560,10 @@ public: m_ShowEnvelopeEditor = 0; + m_ShowEnvelopePreview = 0; + m_SelectedQuadEnvelope = -1; + m_SelectedEnvelopePoint = -1; + ms_CheckerTexture = 0; ms_BackgroundTexture = 0; ms_CursorTexture = 0; @@ -560,6 +596,7 @@ public: int m_Mode; int m_Dialog; + int m_EditBoxActive; const char *m_pTooltip; bool m_GridActive; @@ -579,6 +616,7 @@ public: int m_PopupEventType; int m_PopupEventActivated; + int m_PopupEventWasActivated; enum { @@ -644,6 +682,7 @@ public: float m_AnimateSpeed; int m_ShowEnvelopeEditor; + int m_ShowEnvelopePreview; //Values: 0-Off|1-Selected Envelope|2-All bool m_ShowPicker; int m_SelectedLayer; @@ -651,6 +690,8 @@ public: int m_SelectedQuad; int m_SelectedPoints; int m_SelectedEnvelope; + int m_SelectedEnvelopePoint; + int m_SelectedQuadEnvelope; int m_SelectedImage; static int ms_CheckerTexture; @@ -697,6 +738,7 @@ public: static int PopupQuad(CEditor *pEditor, CUIRect View); static int PopupPoint(CEditor *pEditor, CUIRect View); static int PopupNewFolder(CEditor *pEditor, CUIRect View); + static int PopupMapInfo(CEditor *pEditor, CUIRect View); static int PopupEvent(CEditor *pEditor, CUIRect View); static int PopupSelectImage(CEditor *pEditor, CUIRect View); static int PopupSelectGametileOp(CEditor *pEditor, CUIRect View); @@ -713,13 +755,16 @@ public: void PopupSelectGametileOpInvoke(float x, float y); int PopupSelectGameTileOpResult(); - + void PopupSelectConfigAutoMapInvoke(float x, float y); int PopupSelectConfigAutoMapResult(); vec4 ButtonColorMul(const void *pID); + void DoQuadEnvelopes(CQuad *pQuad, int Index, int TexID = -1); + void DoQuadEnvPoint(CQuad *pQuad, int QIndex, int pIndex); void DoQuadPoint(CQuad *pQuad, int QuadIndex, int v); + void DoMapEditor(CUIRect View, CUIRect Toolbar); void DoToolbar(CUIRect Toolbar); void DoQuad(CQuad *pQuad, int Index); diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 68330f03..463147e1 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -220,6 +220,31 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) df.AddItem(MAPITEMTYPE_VERSION, 0, sizeof(Item), &Item); } + // save map info + { + CMapItemInfo Item; + Item.m_Version = 1; + + if(m_MapInfo.m_aAuthor[0]) + Item.m_Author = df.AddData(str_length(m_MapInfo.m_aAuthor)+1, m_MapInfo.m_aAuthor); + else + Item.m_Author = -1; + if(m_MapInfo.m_aVersion[0]) + Item.m_MapVersion = df.AddData(str_length(m_MapInfo.m_aVersion)+1, m_MapInfo.m_aVersion); + else + Item.m_MapVersion = -1; + if(m_MapInfo.m_aCredits[0]) + Item.m_Credits = df.AddData(str_length(m_MapInfo.m_aCredits)+1, m_MapInfo.m_aCredits); + else + Item.m_Credits = -1; + if(m_MapInfo.m_aLicense[0]) + Item.m_License = df.AddData(str_length(m_MapInfo.m_aLicense)+1, m_MapInfo.m_aLicense); + else + Item.m_License = -1; + + df.AddItem(MAPITEMTYPE_INFO, 0, sizeof(Item), &Item); + } + // save images for(int i = 0; i < m_lImages.size(); i++) { @@ -342,10 +367,11 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) for(int e = 0; e < m_lEnvelopes.size(); e++) { CMapItemEnvelope Item; - Item.m_Version = 1; + Item.m_Version = CMapItemEnvelope::CURRENT_VERSION; Item.m_Channels = m_lEnvelopes[e]->m_Channels; Item.m_StartPoint = PointCount; Item.m_NumPoints = m_lEnvelopes[e]->m_lPoints.size(); + Item.m_Synchronized = m_lEnvelopes[e]->m_Synchronized; StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), m_lEnvelopes[e]->m_aName); df.AddItem(MAPITEMTYPE_ENVELOPE, e, sizeof(Item), &Item); @@ -413,6 +439,22 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag { //editor.reset(false); + // load map info + { + CMapItemInfo *pItem = (CMapItemInfo *)DataFile.FindItem(MAPITEMTYPE_INFO, 0); + if(pItem && pItem->m_Version == 1) + { + if(pItem->m_Author > -1) + str_copy(m_MapInfo.m_aAuthor, (char *)DataFile.GetData(pItem->m_Author), sizeof(m_MapInfo.m_aAuthor)); + if(pItem->m_MapVersion > -1) + str_copy(m_MapInfo.m_aVersion, (char *)DataFile.GetData(pItem->m_MapVersion), sizeof(m_MapInfo.m_aVersion)); + if(pItem->m_Credits > -1) + str_copy(m_MapInfo.m_aCredits, (char *)DataFile.GetData(pItem->m_Credits), sizeof(m_MapInfo.m_aCredits)); + if(pItem->m_License > -1) + str_copy(m_MapInfo.m_aLicense, (char *)DataFile.GetData(pItem->m_License), sizeof(m_MapInfo.m_aLicense)); + } + } + // load images { int Start, Num; @@ -601,6 +643,8 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag if(pItem->m_aName[0] != -1) // compatibility with old maps IntsToStr(pItem->m_aName, sizeof(pItem->m_aName)/sizeof(int), pEnv->m_aName); m_lEnvelopes.add(pEnv); + if(pItem->m_Version >= 2) + pEnv->m_Synchronized = pItem->m_Synchronized; } } } diff --git a/src/game/editor/layer_tiles.cpp b/src/game/editor/layer_tiles.cpp index 5662613c..9a21e5ce 100644 --- a/src/game/editor/layer_tiles.cpp +++ b/src/game/editor/layer_tiles.cpp @@ -159,6 +159,8 @@ void CLayerTiles::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) if(m_Readonly) return; + Snap(&Rect); + int sx = ConvertX(Rect.x); int sy = ConvertY(Rect.y); int w = ConvertX(Rect.w); @@ -166,9 +168,9 @@ void CLayerTiles::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) CLayerTiles *pLt = static_cast<CLayerTiles*>(pBrush); - for(int y = 0; y <= h; y++) + for(int y = 0; y < h; y++) { - for(int x = 0; x <= w; x++) + for(int x = 0; x < w; x++) { int fx = x+sx; int fy = y+sy; @@ -363,7 +365,7 @@ void CLayerTiles::ShowInfo() int CLayerTiles::RenderProperties(CUIRect *pToolBox) { CUIRect Button; - + bool InGameGroup = !find_linear(m_pEditor->m_Map.m_pGameGroup->m_lLayers.all(), this).empty(); if(m_pEditor->m_Map.m_pGameLayer != this) { @@ -373,7 +375,7 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) 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) { @@ -473,7 +475,19 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox) m_Color.a = NewVal&0xff; } if(Prop == PROP_COLOR_ENV) - m_ColorEnv = clamp(NewVal-1, -1, m_pEditor->m_Map.m_lEnvelopes.size()-1); + { + int Index = clamp(NewVal-1, -1, m_pEditor->m_Map.m_lEnvelopes.size()-1); + int Step = (Index-m_ColorEnv)%2; + if(Step != 0) + { + for(; Index >= -1 && Index < m_pEditor->m_Map.m_lEnvelopes.size(); Index += Step) + if(Index == -1 || m_pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 4) + { + m_ColorEnv = Index; + break; + } + } + } if(Prop == PROP_COLOR_ENV_OFFSET) m_ColorEnvOffset = NewVal; diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index f29ae7e2..2382823d 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -458,9 +458,35 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) for(int k = 0; k < 5; ++k) pQuad->m_aPoints[k].y += Offset; } - if(Prop == PROP_POS_ENV) pQuad->m_PosEnv = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + if(Prop == PROP_POS_ENV) + { + int Index = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + int Step = (Index-pQuad->m_PosEnv)%2; + if(Step != 0) + { + for(; Index >= -1 && Index < pEditor->m_Map.m_lEnvelopes.size(); Index += Step) + if(Index == -1 || pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 3) + { + pQuad->m_PosEnv = Index; + break; + } + } + } if(Prop == PROP_POS_ENV_OFFSET) pQuad->m_PosEnvOffset = NewVal; - if(Prop == PROP_COLOR_ENV) pQuad->m_ColorEnv = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + if(Prop == PROP_COLOR_ENV) + { + int Index = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + int Step = (Index-pQuad->m_ColorEnv)%2; + if(Step != 0) + { + for(; Index >= -1 && Index < pEditor->m_Map.m_lEnvelopes.size(); Index += Step) + if(Index == -1 || pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 4) + { + pQuad->m_ColorEnv = Index; + break; + } + } + } if(Prop == PROP_COLOR_ENV_OFFSET) pQuad->m_ColorEnvOffset = NewVal; return 0; @@ -609,6 +635,74 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View) return 0; } +int CEditor::PopupMapInfo(CEditor *pEditor, CUIRect View) +{ + CUIRect Label, ButtonBar, Button; + + // title + View.HSplitTop(10.0f, 0, &View); + View.HSplitTop(30.0f, &Label, &View); + pEditor->UI()->DoLabel(&Label, "Map details", 20.0f, 0); + + View.HSplitBottom(10.0f, &View, 0); + View.HSplitBottom(20.0f, &View, &ButtonBar); + + View.VMargin(40.0f, &View); + + // author box + View.HSplitTop(20.0f, &Label, &View); + pEditor->UI()->DoLabel(&Label, "Author:", 10.0f, -1); + Label.VSplitLeft(40.0f, 0, &Button); + Button.HSplitTop(12.0f, &Button, 0); + static float s_AuthorBox = 0; + pEditor->DoEditBox(&s_AuthorBox, &Button, pEditor->m_Map.m_MapInfo.m_aAuthorTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aAuthorTmp), 10.0f, &s_AuthorBox); + + // version box + View.HSplitTop(20.0f, &Label, &View); + pEditor->UI()->DoLabel(&Label, "Version:", 10.0f, -1); + Label.VSplitLeft(40.0f, 0, &Button); + Button.HSplitTop(12.0f, &Button, 0); + static float s_VersionBox = 0; + pEditor->DoEditBox(&s_VersionBox, &Button, pEditor->m_Map.m_MapInfo.m_aVersionTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aVersionTmp), 10.0f, &s_VersionBox); + + // credits box + View.HSplitTop(20.0f, &Label, &View); + pEditor->UI()->DoLabel(&Label, "Credits:", 10.0f, -1); + Label.VSplitLeft(40.0f, 0, &Button); + Button.HSplitTop(12.0f, &Button, 0); + static float s_CreditsBox = 0; + pEditor->DoEditBox(&s_CreditsBox, &Button, pEditor->m_Map.m_MapInfo.m_aCreditsTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aCreditsTmp), 10.0f, &s_CreditsBox); + + // license box + View.HSplitTop(20.0f, &Label, &View); + pEditor->UI()->DoLabel(&Label, "License:", 10.0f, -1); + Label.VSplitLeft(40.0f, 0, &Button); + Button.HSplitTop(12.0f, &Button, 0); + static float s_LicenseBox = 0; + pEditor->DoEditBox(&s_LicenseBox, &Button, pEditor->m_Map.m_MapInfo.m_aLicenseTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aLicenseTmp), 10.0f, &s_LicenseBox); + + // button bar + ButtonBar.VSplitLeft(30.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(110.0f, &Label, &ButtonBar); + static int s_CreateButton = 0; + if(pEditor->DoButton_Editor(&s_CreateButton, "Save", 0, &Label, 0, 0)) + { + str_copy(pEditor->m_Map.m_MapInfo.m_aAuthor, pEditor->m_Map.m_MapInfo.m_aAuthorTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aAuthor)); + str_copy(pEditor->m_Map.m_MapInfo.m_aVersion, pEditor->m_Map.m_MapInfo.m_aVersionTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aVersion)); + str_copy(pEditor->m_Map.m_MapInfo.m_aCredits, pEditor->m_Map.m_MapInfo.m_aCreditsTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aCredits)); + str_copy(pEditor->m_Map.m_MapInfo.m_aLicense, pEditor->m_Map.m_MapInfo.m_aLicenseTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aLicense)); + return 1; + } + + ButtonBar.VSplitRight(30.0f, &ButtonBar, 0); + ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label); + static int s_AbortButton = 0; + if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, 0)) + return 1; + + return 0; +} + int CEditor::PopupEvent(CEditor *pEditor, CUIRect View) { CUIRect Label, ButtonBar; @@ -658,13 +752,17 @@ int CEditor::PopupEvent(CEditor *pEditor, CUIRect View) } else if(pEditor->m_PopupEventType == POPEVENT_SAVE) pEditor->CallbackSaveMap(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor); + pEditor->m_PopupEventWasActivated = false; return 1; } ButtonBar.VSplitRight(30.0f, &ButtonBar, 0); ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label); static int s_AbortButton = 0; if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, 0)) + { + pEditor->m_PopupEventWasActivated = false; return 1; + } return 0; } @@ -703,13 +801,23 @@ int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View) } if(ShowImage >= 0 && ShowImage < pEditor->m_Map.m_lImages.size()) + { + if(ImageView.h < ImageView.w) + ImageView.w = ImageView.h; + else + ImageView.h = ImageView.w; + float Max = (float)(max(pEditor->m_Map.m_lImages[ShowImage]->m_Width, pEditor->m_Map.m_lImages[ShowImage]->m_Height)); + ImageView.w *= pEditor->m_Map.m_lImages[ShowImage]->m_Width/Max; + ImageView.h *= pEditor->m_Map.m_lImages[ShowImage]->m_Height/Max; pEditor->Graphics()->TextureSet(pEditor->m_Map.m_lImages[ShowImage]->m_TexID); - else - pEditor->Graphics()->TextureSet(-1); - pEditor->Graphics()->QuadsBegin(); - IGraphics::CQuadItem QuadItem(ImageView.x, ImageView.y, ImageView.w, ImageView.h); - pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1); - pEditor->Graphics()->QuadsEnd(); + pEditor->Graphics()->BlendNormal(); + pEditor->Graphics()->WrapClamp(); + pEditor->Graphics()->QuadsBegin(); + IGraphics::CQuadItem QuadItem(ImageView.x, ImageView.y, ImageView.w, ImageView.h); + pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1); + pEditor->Graphics()->QuadsEnd(); + pEditor->Graphics()->WrapNormal(); + } return 0; } @@ -776,7 +884,7 @@ int CEditor::PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View) 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); @@ -802,7 +910,7 @@ 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 af086df2..d43492ac 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -302,7 +302,7 @@ void CCharacterCore::Tick(bool UseInput) } } - if(m_pWorld && m_pWorld->m_Tuning.m_PlayerCollision) + if(m_pWorld) { for(int i = 0; i < MAX_CLIENTS; i++) { @@ -317,7 +317,7 @@ void CCharacterCore::Tick(bool UseInput) // handle player <-> player collision float Distance = distance(m_Pos, pCharCore->m_Pos); vec2 Dir = normalize(m_Pos - pCharCore->m_Pos); - if(Distance < PhysSize*1.25f && Distance > 0.0f) + if(m_pWorld->m_Tuning.m_PlayerCollision && Distance < PhysSize*1.25f && Distance > 0.0f) { float a = (PhysSize*1.45f - Distance); float Velocity = 0.5f; @@ -332,7 +332,7 @@ void CCharacterCore::Tick(bool UseInput) } // handle hook influence - if(m_HookedPlayer == i) + if(m_HookedPlayer == i && m_pWorld->m_Tuning.m_PlayerHooking) { if(Distance > PhysSize*1.50f) // TODO: fix tweakable variable { diff --git a/src/game/layers.cpp b/src/game/layers.cpp index 82c0a61b..6deb0829 100644 --- a/src/game/layers.cpp +++ b/src/game/layers.cpp @@ -29,7 +29,7 @@ void CLayers::Init(class IKernel *pKernel) if(pLayer->m_Type == LAYERTYPE_TILES) { CMapItemLayerTilemap *pTilemap = reinterpret_cast<CMapItemLayerTilemap *>(pLayer); - if(pTilemap->m_Flags&1) + if(pTilemap->m_Flags&TILESLAYERFLAG_GAME) { m_pGameLayer = pTilemap; m_pGameGroup = pGroup; diff --git a/src/game/mapitems.h b/src/game/mapitems.h index fb66e12d..e1dda4ee 100644 --- a/src/game/mapitems.h +++ b/src/game/mapitems.h @@ -7,7 +7,7 @@ enum { LAYERTYPE_INVALID=0, - LAYERTYPE_GAME, // not used + LAYERTYPE_GAME, LAYERTYPE_TILES, LAYERTYPE_QUADS, @@ -90,6 +90,15 @@ public: unsigned char m_Reserved; }; +struct CMapItemInfo +{ + int m_Version; + int m_Author; + int m_MapVersion; + int m_Credits; + int m_License; +} ; + struct CMapItemImage { int m_Version; @@ -178,7 +187,7 @@ struct CEnvPoint bool operator<(const CEnvPoint &Other) { return m_Time < Other.m_Time; } } ; -struct CMapItemEnvelope +struct CMapItemEnvelope_v1 { int m_Version; int m_Channels; @@ -187,4 +196,10 @@ struct CMapItemEnvelope int m_aName[8]; } ; +struct CMapItemEnvelope : public CMapItemEnvelope_v1 +{ + enum { CURRENT_VERSION=2 }; + int m_Synchronized; +}; + #endif diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index aab489bf..f64a16e3 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -538,7 +538,7 @@ void CCharacter::OnPredictedInput(CNetObj_PlayerInput *pNewInput) mem_copy(&m_Input, pNewInput, sizeof(m_Input)); m_NumInputs++; - // or are not allowed to aim in the center + // it is not allowed to aim in the center if(m_Input.m_TargetX == 0 && m_Input.m_TargetY == 0) m_Input.m_TargetY = -1; } @@ -551,6 +551,10 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); mem_copy(&m_LatestInput, pNewInput, sizeof(m_LatestInput)); + // it is not allowed to aim in the center + if(m_LatestInput.m_TargetX == 0 && m_LatestInput.m_TargetY == 0) + m_LatestInput.m_TargetY = -1; + if(m_NumInputs > 2 && m_pPlayer->GetTeam() != TEAM_SPECTATORS) { HandleWeaponSwitch(); @@ -705,6 +709,20 @@ void CCharacter::TickDefered() } } +void CCharacter::TickPaused() +{ + ++m_AttackTick; + ++m_DamageTakenTick; + ++m_Ninja.m_ActivationTick; + ++m_ReckoningTick; + if(m_LastAction != -1) + ++m_LastAction; + if(m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart > -1) + ++m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart; + if(m_EmoteStop > -1) + ++m_EmoteStop; +} + bool CCharacter::IncreaseHealth(int Amount) { if(m_Health >= 10) diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index a3a7e9d2..dae01bd4 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -30,6 +30,7 @@ public: virtual void Destroy(); virtual void Tick(); virtual void TickDefered(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); bool IsGrounded(); diff --git a/src/game/server/entities/flag.cpp b/src/game/server/entities/flag.cpp index d279e4df..558ee154 100644 --- a/src/game/server/entities/flag.cpp +++ b/src/game/server/entities/flag.cpp @@ -23,6 +23,13 @@ void CFlag::Reset() m_GrabTick = 0; } +void CFlag::TickPaused() +{ + ++m_DropTick; + if(m_GrabTick) + ++m_GrabTick; +} + void CFlag::Snap(int SnappingClient) { if(NetworkClipped(SnappingClient)) diff --git a/src/game/server/entities/flag.h b/src/game/server/entities/flag.h index 2f91bc02..be22d32b 100644 --- a/src/game/server/entities/flag.h +++ b/src/game/server/entities/flag.h @@ -21,6 +21,7 @@ public: CFlag(CGameWorld *pGameWorld, int Team); virtual void Reset(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); }; diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index af66fe0c..eb40c4e1 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -21,15 +21,15 @@ CLaser::CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEner bool CLaser::HitCharacter(vec2 From, vec2 To) { vec2 At; - CCharacter *OwnerChar = GameServer()->GetPlayerChar(m_Owner); - CCharacter *Hit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, OwnerChar); - if(!Hit) + CCharacter *pOwnerChar = GameServer()->GetPlayerChar(m_Owner); + CCharacter *pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, pOwnerChar); + if(!pHit) return false; m_From = From; m_Pos = At; m_Energy = -1; - Hit->TakeDamage(vec2(0.f, 0.f), GameServer()->Tuning()->m_LaserDamage, m_Owner, WEAPON_RIFLE); + pHit->TakeDamage(vec2(0.f, 0.f), GameServer()->Tuning()->m_LaserDamage, m_Owner, WEAPON_RIFLE); return true; } @@ -91,6 +91,11 @@ void CLaser::Tick() DoBounce(); } +void CLaser::TickPaused() +{ + ++m_EvalTick; +} + void CLaser::Snap(int SnappingClient) { if(NetworkClipped(SnappingClient)) diff --git a/src/game/server/entities/laser.h b/src/game/server/entities/laser.h index 1d7fa227..8ae6f792 100644 --- a/src/game/server/entities/laser.h +++ b/src/game/server/entities/laser.h @@ -12,6 +12,7 @@ public: virtual void Reset(); virtual void Tick(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); protected: diff --git a/src/game/server/entities/pickup.cpp b/src/game/server/entities/pickup.cpp index ba26d85b..1aff5750 100644 --- a/src/game/server/entities/pickup.cpp +++ b/src/game/server/entities/pickup.cpp @@ -117,6 +117,12 @@ void CPickup::Tick() } } +void CPickup::TickPaused() +{ + if(m_SpawnTick != -1) + ++m_SpawnTick; +} + void CPickup::Snap(int SnappingClient) { if(m_SpawnTick != -1 || NetworkClipped(SnappingClient)) diff --git a/src/game/server/entities/pickup.h b/src/game/server/entities/pickup.h index 77347de2..fe45b5ae 100644 --- a/src/game/server/entities/pickup.h +++ b/src/game/server/entities/pickup.h @@ -14,6 +14,7 @@ public: virtual void Reset(); virtual void Tick(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); private: diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp index 2baa24b1..e89e0e6f 100644 --- a/src/game/server/entities/projectile.cpp +++ b/src/game/server/entities/projectile.cpp @@ -82,6 +82,11 @@ void CProjectile::Tick() } } +void CProjectile::TickPaused() +{ + ++m_StartTick; +} + void CProjectile::FillInfo(CNetObj_Projectile *pProj) { pProj->m_X = (int)m_Pos.x; diff --git a/src/game/server/entities/projectile.h b/src/game/server/entities/projectile.h index 5df04bcd..7b5c5e0e 100644 --- a/src/game/server/entities/projectile.h +++ b/src/game/server/entities/projectile.h @@ -14,6 +14,7 @@ public: virtual void Reset(); virtual void Tick(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); private: diff --git a/src/game/server/entity.h b/src/game/server/entity.h index b9b33eb7..0f838730 100644 --- a/src/game/server/entity.h +++ b/src/game/server/entity.h @@ -106,6 +106,12 @@ public: virtual void TickDefered() {} /* + Function: TickPaused + Called when the game is paused, to freeze the state and position of the entity. + */ + virtual void TickPaused() {} + + /* Function: snap Called when a new snapshot is being generated for a specific client. diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 3f40bfcd..9e8afea8 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -34,6 +34,7 @@ void CGameContext::Construct(int Resetting) m_pVoteOptionFirst = 0; m_pVoteOptionLast = 0; m_NumVoteOptions = 0; + m_LockTeams = 0; if(Resetting==NO_RESET) m_pVoteOptionHeap = new CHeap(); @@ -208,7 +209,15 @@ void CGameContext::CreateSoundGlobal(int Sound, int Target) CNetMsg_Sv_SoundGlobal Msg; Msg.m_SoundID = Sound; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, Target); + if(Target == -2) + Server()->SendPackMsg(&Msg, MSGFLAG_NOSEND, -1); + else + { + int Flag = MSGFLAG_VITAL; + if(Target != -1) + Flag |= MSGFLAG_NORECORD; + Server()->SendPackMsg(&Msg, Flag, Target); + } } @@ -383,6 +392,22 @@ void CGameContext::SendTuningParams(int ClientID) Server()->SendMsg(&Msg, MSGFLAG_VITAL, ClientID); } +void CGameContext::SwapTeams() +{ + if(!m_pController->IsTeamplay()) + return; + + SendChat(-1, CGameContext::CHAT_ALL, "Teams were swapped"); + + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + m_apPlayers[i]->SetTeam(m_apPlayers[i]->GetTeam()^1, false); + } + + (void)m_pController->CheckTeamBalance(); +} + void CGameContext::OnTick() { // check tuning @@ -464,7 +489,9 @@ void CGameContext::OnTick() if(m_VoteEnforce == VOTE_ENFORCE_YES) { + Server()->SetRconCID(IServer::RCON_CID_VOTE); Console()->ExecuteLine(m_aVoteCommand); + Server()->SetRconCID(IServer::RCON_CID_SERV); EndVote(); SendChat(-1, CGameContext::CHAT_ALL, "Vote passed"); @@ -868,7 +895,6 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) char aAddrStr[NETADDR_MAXSTRSIZE] = {0}; Server()->GetClientAddr(KickID, aAddrStr, sizeof(aAddrStr)); str_format(aCmd, sizeof(aCmd), "ban %s %d Banned by vote", aAddrStr, g_Config.m_SvVoteKickBantime); - Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aCmd); } } else if(str_comp_nocase(pMsg->m_Type, "spectate") == 0) @@ -929,6 +955,13 @@ 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(pMsg->m_Team != TEAM_SPECTATORS && m_LockTeams) + { + pPlayer->m_LastSetTeam = Server()->Tick(); + SendBroadcast("Teams are locked", ClientID); + return; + } + if(pPlayer->m_TeamChangeTick > Server()->Tick()) { pPlayer->m_LastSetTeam = Server()->Tick(); @@ -974,7 +1007,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) else { char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", g_Config.m_SvMaxClients-g_Config.m_SvSpectatorSlots); + str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", Server()->MaxClients()-g_Config.m_SvSpectatorSlots); SendBroadcast(aBuf, ClientID); } } @@ -1250,6 +1283,16 @@ void CGameContext::ConTuneDump(IConsole::IResult *pResult, void *pUserData) } } +void CGameContext::ConPause(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + + if(pSelf->m_pController->IsGameOver()) + return; + + pSelf->m_World.m_Paused ^= 1; +} + void CGameContext::ConChangeMap(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -1282,17 +1325,14 @@ 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); + int Delay = pResult->NumArguments()>2 ? pResult->GetInteger(2) : 0; + if(!pSelf->m_apPlayers[ClientID]) + return; char aBuf[256]; str_format(aBuf, sizeof(aBuf), "moved client %d to team %d", ClientID, Team); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); - 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(); @@ -1304,16 +1344,75 @@ void CGameContext::ConSetTeamAll(IConsole::IResult *pResult, void *pUserData) int Team = clamp(pResult->GetInteger(0), -1, 1); char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "moved all clients to team %d", Team); - pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); + str_format(aBuf, sizeof(aBuf), "All players were moved to the %s", pSelf->m_pController->GetTeamName(Team)); + pSelf->SendChat(-1, CGameContext::CHAT_ALL, aBuf); for(int i = 0; i < MAX_CLIENTS; ++i) if(pSelf->m_apPlayers[i]) - pSelf->m_apPlayers[i]->SetTeam(Team); + pSelf->m_apPlayers[i]->SetTeam(Team, false); + + (void)pSelf->m_pController->CheckTeamBalance(); +} + +void CGameContext::ConSwapTeams(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->SwapTeams(); +} + +void CGameContext::ConShuffleTeams(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(!pSelf->m_pController->IsTeamplay()) + return; + + int CounterRed = 0; + int CounterBlue = 0; + int PlayerTeam = 0; + for(int i = 0; i < MAX_CLIENTS; ++i) + if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + ++PlayerTeam; + PlayerTeam = (PlayerTeam+1)/2; + + pSelf->SendChat(-1, CGameContext::CHAT_ALL, "Teams were shuffled"); + + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + { + if(CounterRed == PlayerTeam) + pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false); + else if(CounterBlue == PlayerTeam) + pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false); + else + { + if(rand() % 2) + { + pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false); + ++CounterBlue; + } + else + { + pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false); + ++CounterRed; + } + } + } + } (void)pSelf->m_pController->CheckTeamBalance(); } +void CGameContext::ConLockTeams(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->m_LockTeams ^= 1; + if(pSelf->m_LockTeams) + pSelf->SendChat(-1, CGameContext::CHAT_ALL, "Teams were locked"); + else + pSelf->SendChat(-1, CGameContext::CHAT_ALL, "Teams were unlocked"); +} + void CGameContext::ConAddVote(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -1533,6 +1632,11 @@ void CGameContext::ConClearVotes(IConsole::IResult *pResult, void *pUserData) void CGameContext::ConVote(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; + + // check if there is a vote running + if(!pSelf->m_VoteCloseTime) + return; + if(str_comp_nocase(pResult->GetString(0), "yes") == 0) pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_YES; else if(str_comp_nocase(pResult->GetString(0), "no") == 0) @@ -1625,12 +1729,16 @@ void CGameContext::OnConsoleInit() Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "Reset tuning"); Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "Dump tuning"); + Console()->Register("pause", "", CFGFLAG_SERVER, ConPause, this, "Pause/unpause game"); 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("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, "Restart in x seconds (0 = abort)"); 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("swap_teams", "", CFGFLAG_SERVER, ConSwapTeams, this, "Swap the current teams"); + Console()->Register("shuffle_teams", "", CFGFLAG_SERVER, ConShuffleTeams, this, "Shuffle the current teams"); + Console()->Register("lock_teams", "", CFGFLAG_SERVER, ConLockTeams, this, "Lock/unlock teams"); 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"); @@ -1731,6 +1839,17 @@ void CGameContext::OnShutdown() void CGameContext::OnSnap(int ClientID) { + // add tuning to demo + CTuningParams StandardTuning; + if(ClientID == -1 && Server()->DemoRecorder_IsRecording() && mem_comp(&StandardTuning, &m_Tuning, sizeof(CTuningParams)) != 0) + { + CMsgPacker Msg(NETMSGTYPE_SV_TUNEPARAMS); + int *pParams = (int *)&m_Tuning; + for(unsigned i = 0; i < sizeof(m_Tuning)/sizeof(int); i++) + Msg.AddInt(pParams[i]); + Server()->SendMsg(&Msg, MSGFLAG_RECORD|MSGFLAG_NOSEND, ClientID); + } + m_World.Snap(ClientID); m_pController->Snap(ClientID); m_Events.Snap(ClientID); diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 4e09cac2..fa85151c 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -51,12 +51,16 @@ class CGameContext : public IGameServer static void ConTuneParam(IConsole::IResult *pResult, void *pUserData); static void ConTuneReset(IConsole::IResult *pResult, void *pUserData); static void ConTuneDump(IConsole::IResult *pResult, void *pUserData); + static void ConPause(IConsole::IResult *pResult, void *pUserData); static void ConChangeMap(IConsole::IResult *pResult, void *pUserData); static void ConRestart(IConsole::IResult *pResult, void *pUserData); static void ConBroadcast(IConsole::IResult *pResult, void *pUserData); static void ConSay(IConsole::IResult *pResult, void *pUserData); static void ConSetTeam(IConsole::IResult *pResult, void *pUserData); static void ConSetTeamAll(IConsole::IResult *pResult, void *pUserData); + static void ConSwapTeams(IConsole::IResult *pResult, void *pUserData); + static void ConShuffleTeams(IConsole::IResult *pResult, void *pUserData); + static void ConLockTeams(IConsole::IResult *pResult, void *pUserData); static void ConAddVote(IConsole::IResult *pResult, void *pUserData); static void ConRemoveVote(IConsole::IResult *pResult, void *pUserData); static void ConForceVote(IConsole::IResult *pResult, void *pUserData); @@ -93,6 +97,8 @@ public: // helper functions class CCharacter *GetPlayerChar(int ClientID); + int m_LockTeams; + // voting void StartVote(const char *pDesc, const char *pCommand, const char *pReason); void EndVote(); @@ -166,6 +172,9 @@ public: void CheckPureTuning(); void SendTuningParams(int ClientID); + // + void SwapTeams(); + // engine events virtual void OnInit(); virtual void OnConsoleInit(); diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 5f330110..58c4d9f0 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -255,7 +255,11 @@ void IGameController::CycleMap() return; if(m_RoundCount < g_Config.m_SvRoundsPerMap-1) + { + if(g_Config.m_SvRoundSwap) + GameServer()->SwapTeams(); return; + } // handle maprotation const char *pMapRotation = g_Config.m_SvMaprotation; @@ -436,8 +440,12 @@ void IGameController::Tick() } } + // game is Paused + if(GameServer()->m_World.m_Paused) + ++m_RoundStartTick; + // do team-balancing - if (IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60) + if(IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60) { GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", "Balancing teams"); @@ -496,6 +504,13 @@ void IGameController::Tick() { for(int i = 0; i < MAX_CLIENTS; ++i) { + #ifdef CONF_DEBUG + if(g_Config.m_DbgDummies) + { + if(i >= MAX_CLIENTS-g_Config.m_DbgDummies) + break; + } + #endif if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && !Server()->IsAuthed(i)) { if(Server()->Tick() > GameServer()->m_apPlayers[i]->m_LastActionTick+g_Config.m_SvInactiveKickTime*Server()->TickSpeed()*60) @@ -605,7 +620,7 @@ bool IGameController::CanJoinTeam(int Team, int NotThisID) } } - return (aNumplayers[0] + aNumplayers[1]) < g_Config.m_SvMaxClients-g_Config.m_SvSpectatorSlots; + return (aNumplayers[0] + aNumplayers[1]) < Server()->MaxClients()-g_Config.m_SvSpectatorSlots; } bool IGameController::CheckTeamBalance() diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h index 914f7985..1be24509 100644 --- a/src/game/server/gamecontroller.h +++ b/src/game/server/gamecontroller.h @@ -64,6 +64,7 @@ public: const char *m_pGameType; bool IsTeamplay() const; + bool IsGameOver() const { return m_GameOverTick != -1; } IGameController(class CGameContext *pGameServer); virtual ~IGameController(); diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp index 66cc4c2c..9e45c1fe 100644 --- a/src/game/server/gamemodes/ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -259,6 +259,8 @@ void CGameControllerCTF::Tick() else GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, c); } + // demo record entry + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, -2); break; } } diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index 89f4808d..d803ae67 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -173,6 +173,17 @@ void CGameWorld::Tick() pEnt = m_pNextTraverseEntity; } } + else + { + // update all objects + for(int i = 0; i < NUM_ENTTYPES; i++) + for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; ) + { + m_pNextTraverseEntity = pEnt->m_pNextTypeEntity; + pEnt->TickPaused(); + pEnt = m_pNextTraverseEntity; + } + } RemoveEntities(); } diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 39590c4e..09518a78 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -85,26 +85,37 @@ void CPlayer::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(!GameServer()->m_World.m_Paused) + { + 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(!m_pCharacter && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick()) + m_Spawning = true; - if(m_pCharacter) - { - if(m_pCharacter->IsAlive()) - { - m_ViewPos = m_pCharacter->m_Pos; - } - else + if(m_pCharacter) { - delete m_pCharacter; - m_pCharacter = 0; + if(m_pCharacter->IsAlive()) + { + m_ViewPos = m_pCharacter->m_Pos; + } + else + { + delete m_pCharacter; + m_pCharacter = 0; + } } + else if(m_Spawning && m_RespawnTick <= Server()->Tick()) + TryRespawn(); } - else if(m_Spawning && m_RespawnTick <= Server()->Tick()) - TryRespawn(); + else + { + ++m_RespawnTick; + ++m_DieTick; + ++m_ScoreStartTick; + ++m_LastActionTick; + ++m_TeamChangeTick; + } } void CPlayer::PostTick() @@ -261,7 +272,7 @@ void CPlayer::Respawn() m_Spawning = true; } -void CPlayer::SetTeam(int Team) +void CPlayer::SetTeam(int Team, bool DoChatMsg) { // clamp the team Team = GameServer()->m_pController->ClampTeam(Team); @@ -269,8 +280,11 @@ void CPlayer::SetTeam(int Team) return; char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "'%s' joined the %s", Server()->ClientName(m_ClientID), GameServer()->m_pController->GetTeamName(Team)); - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + if(DoChatMsg) + { + str_format(aBuf, sizeof(aBuf), "'%s' joined the %s", Server()->ClientName(m_ClientID), GameServer()->m_pController->GetTeamName(Team)); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + } KillCharacter(); diff --git a/src/game/server/player.h b/src/game/server/player.h index 99de2952..92e2ccc8 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -20,7 +20,7 @@ public: void TryRespawn(); void Respawn(); - void SetTeam(int Team); + void SetTeam(int Team, bool DoChatMsg=true); void SetTeamDirect(int Team); //zCatch int GetTeam() const { return m_Team; }; int GetCID() const { return m_ClientID; }; diff --git a/src/game/variables.h b/src/game/variables.h index 79df19fb..99377a64 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -14,6 +14,7 @@ MACRO_CONFIG_INT(ClNameplatesSize, cl_nameplates_size, 50, 0, 100, CFGFLAG_CLIEN MACRO_CONFIG_INT(ClAutoswitchWeapons, cl_autoswitch_weapons, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Auto switch weapon on pickup") MACRO_CONFIG_INT(ClShowhud, cl_showhud, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ingame HUD") +MACRO_CONFIG_INT(ClShowChatFriends, cl_show_chat_friends, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show only chat messages from friends") MACRO_CONFIG_INT(ClShowfps, cl_showfps, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ingame FPS counter") MACRO_CONFIG_INT(ClAirjumpindicator, cl_airjumpindicator, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") @@ -41,7 +42,7 @@ MACRO_CONFIG_INT(PlayerColorBody, player_color_body, 65408, 0, 0xFFFFFF, CFGFLAG MACRO_CONFIG_INT(PlayerColorFeet, player_color_feet, 65408, 0, 0xFFFFFF, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player feet color") MACRO_CONFIG_STR(PlayerSkin, player_skin, 24, "default", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player skin") -MACRO_CONFIG_INT(UiPage, ui_page, 5, 0, 10, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface page") +MACRO_CONFIG_INT(UiPage, ui_page, 6, 0, 10, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface page") 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") @@ -60,6 +61,7 @@ MACRO_CONFIG_STR(SvMotd, sv_motd, 900, "", CFGFLAG_SERVER, "Message of the day t MACRO_CONFIG_INT(SvTeamdamage, sv_teamdamage, 0, 0, 1, CFGFLAG_SERVER, "Team damage") MACRO_CONFIG_STR(SvMaprotation, sv_maprotation, 768, "", CFGFLAG_SERVER, "Maps to rotate between") MACRO_CONFIG_INT(SvRoundsPerMap, sv_rounds_per_map, 1, 1, 100, CFGFLAG_SERVER, "Number of rounds on each map before rotating") +MACRO_CONFIG_INT(SvRoundSwap, sv_round_swap, 1, 0, 1, CFGFLAG_SERVER, "Swap teams between rounds") MACRO_CONFIG_INT(SvPowerups, sv_powerups, 1, 0, 1, CFGFLAG_SERVER, "Allow powerups like ninja") MACRO_CONFIG_INT(SvScorelimit, sv_scorelimit, 20, 0, 1000, CFGFLAG_SERVER, "Score limit (0 disables)") MACRO_CONFIG_INT(SvTimelimit, sv_timelimit, 0, 0, 1000, CFGFLAG_SERVER, "Time limit in minutes (0 disables)") diff --git a/src/game/version.h b/src/game/version.h index 76b6d4ab..3d909e36 100644 --- a/src/game/version.h +++ b/src/game/version.h @@ -3,6 +3,7 @@ #ifndef GAME_VERSION_H #define GAME_VERSION_H #include "generated/nethash.cpp" -#define GAME_VERSION "0.6.1" +#define GAME_VERSION "0.6 trunk" #define GAME_NETVERSION "0.6 " GAME_NETVERSION_HASH +static const char GAME_RELEASE_VERSION[8] = {'0', '.', '6', '1', 0}; #endif |