From 85b2f98d7142b6c0085203fa288cc60a7fbd0efb Mon Sep 17 00:00:00 2001 From: Zwelf Date: Mon, 31 Oct 2016 21:38:05 +0100 Subject: [PATCH] Improved spectator mode closes #1367 - follow flag - click a player/a flag to follow in world-free mode - click to go into the world-free mode while follow-mode --- datasrc/network.py | 12 +- src/game/client/components/hud.cpp | 23 ++- src/game/client/components/spectator.cpp | 169 ++++++++++++++++++----- src/game/client/components/spectator.h | 5 +- src/game/client/gameclient.cpp | 35 +++-- src/game/client/gameclient.h | 2 + src/game/server/entities/pickup.cpp | 2 +- src/game/server/gamecontext.cpp | 4 +- src/game/server/gameworld.cpp | 8 +- src/game/server/gameworld.h | 29 ++-- src/game/server/player.cpp | 137 +++++++++++++++--- src/game/server/player.h | 5 +- 12 files changed, 339 insertions(+), 92 deletions(-) diff --git a/datasrc/network.py b/datasrc/network.py index 1c36202fc..39f49a1c8 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -37,7 +37,11 @@ enum FLAG_ATSTAND, FLAG_TAKEN, - SPEC_FREEVIEW=-1, + SPEC_PLAYER=0, + SPEC_FREEVIEW, + SPEC_FLAGRED, + SPEC_FLAGBLUE, + NUM_SPECMODES, }; ''' @@ -166,7 +170,8 @@ Objects = [ ]), NetObject("SpectatorInfo", [ - NetIntRange("m_SpectatorID", 'SPEC_FREEVIEW', 'MAX_CLIENTS-1'), + NetIntRange("m_SpecMode", 0, 'NUM_SPECMODES-1'), + NetIntRange("m_SpectatorID", -1, 'MAX_CLIENTS-1'), NetIntAny("m_X"), NetIntAny("m_Y"), ]), @@ -356,7 +361,8 @@ Messages = [ ]), NetMessage("Cl_SetSpectatorMode", [ - NetIntRange("m_SpectatorID", 'SPEC_FREEVIEW', 'MAX_CLIENTS-1'), + NetIntRange("m_SpecMode", 0, 'NUM_SPECMODES'), + NetIntRange("m_SpectatorID", -1, 'MAX_CLIENTS-1'), ]), NetMessage("Cl_StartInfo", [ diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 54c616a5e..f492012b4 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -541,8 +541,25 @@ void CHud::RenderSpectatorHud() char aName[64]; str_format(aName, sizeof(aName), "%2d: %s", m_pClient->m_Snap.m_SpecInfo.m_SpectatorID, g_Config.m_ClShowsocial ? m_pClient->m_aClients[m_pClient->m_Snap.m_SpecInfo.m_SpectatorID].m_aName : ""); char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Spectate"), m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW ? - aName : Localize("Free-View")); + switch(m_pClient->m_Snap.m_SpecInfo.m_SpecMode) + { + case SPEC_FREEVIEW: + str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Spectate"), Localize("Free-View")); + break; + case SPEC_PLAYER: + str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Spectate"), aName); + break; + case SPEC_FLAGRED: + case SPEC_FLAGBLUE: + char aFlag[64]; + str_format(aFlag, sizeof(aFlag), "%s flag", Localize(m_pClient->m_Snap.m_SpecInfo.m_SpecMode == SPEC_FLAGRED ? "red" : "blue")); + + if (m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != -1) + str_format(aBuf, sizeof(aBuf), "%s: %s (%s)", Localize("Spectate"), aFlag, aName); + else + str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Spectate"), aFlag); + break; + } TextRender()->Text(0, m_Width-174.0f, m_Height-13.0f, 8.0f, aBuf, -1); } @@ -565,7 +582,7 @@ void CHud::OnRender() RenderHealthAndAmmo(m_pClient->m_Snap.m_pLocalCharacter); else if(m_pClient->m_Snap.m_SpecInfo.m_Active) { - if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW) + if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != -1) RenderHealthAndAmmo(&m_pClient->m_Snap.m_aCharacters[m_pClient->m_Snap.m_SpecInfo.m_SpectatorID].m_Cur); RenderSpectatorHud(); } diff --git a/src/game/client/components/spectator.cpp b/src/game/client/components/spectator.cpp index 06d892356..c7acc825f 100644 --- a/src/game/client/components/spectator.cpp +++ b/src/game/client/components/spectator.cpp @@ -24,37 +24,61 @@ void CSpectator::ConKeySpectator(IConsole::IResult *pResult, void *pUserData) void CSpectator::ConSpectate(IConsole::IResult *pResult, void *pUserData) { - ((CSpectator *)pUserData)->Spectate(pResult->GetInteger(0)); + int SpectatorID = pResult->GetInteger(0); + if (SpectatorID >= 0) + ((CSpectator *)pUserData)->Spectate(SPEC_PLAYER, SpectatorID); + else + ((CSpectator *)pUserData)->Spectate(-SpectatorID, -1); } void CSpectator::ConSpectateNext(IConsole::IResult *pResult, void *pUserData) { CSpectator *pSelf = (CSpectator *)pUserData; + int NewSpecMode = SPEC_PLAYER; int NewSpectatorID; bool GotNewSpectatorID = false; - if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW) + if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpecMode == SPEC_FREEVIEW) { - for(int i = 0; i < MAX_CLIENTS; i++) + if(pSelf->m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_FLAGS) { - if(!pSelf->m_pClient->m_aClients[i].m_Active || pSelf->m_pClient->m_aClients[i].m_Team == TEAM_SPECTATORS) - continue; - - NewSpectatorID = i; + NewSpecMode = SPEC_FLAGRED; + NewSpectatorID = -1; GotNewSpectatorID = true; - break; + } + else + { + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(!pSelf->m_pClient->m_aClients[i].m_Active || pSelf->m_pClient->m_aClients[i].m_Team == TEAM_SPECTATORS) + continue; + + NewSpectatorID = i; + GotNewSpectatorID = true; + break; + } } } else { - for(int i = pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID + 1; i < MAX_CLIENTS; i++) + if(pSelf->m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_FLAGS && pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpecMode == SPEC_FLAGRED) { - if(!pSelf->m_pClient->m_aClients[i].m_Active || pSelf->m_pClient->m_aClients[i].m_Team == TEAM_SPECTATORS) - continue; - - NewSpectatorID = i; + NewSpecMode = SPEC_FLAGBLUE; + NewSpectatorID = -1; GotNewSpectatorID = true; - break; + } + + if(!GotNewSpectatorID) + { + for(int i = pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID + 1; i < MAX_CLIENTS; i++) + { + if(!pSelf->m_pClient->m_aClients[i].m_Active || pSelf->m_pClient->m_aClients[i].m_Team == TEAM_SPECTATORS) + continue; + + NewSpectatorID = i; + GotNewSpectatorID = true; + break; + } } if(!GotNewSpectatorID) @@ -69,18 +93,26 @@ void CSpectator::ConSpectateNext(IConsole::IResult *pResult, void *pUserData) break; } } + + if(!GotNewSpectatorID && pSelf->m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_FLAGS) + { + NewSpecMode = SPEC_FLAGRED; + NewSpectatorID = -1; + GotNewSpectatorID = true; + } } if(GotNewSpectatorID) - pSelf->Spectate(NewSpectatorID); + pSelf->Spectate(NewSpecMode, NewSpectatorID); } void CSpectator::ConSpectatePrevious(IConsole::IResult *pResult, void *pUserData) { CSpectator *pSelf = (CSpectator *)pUserData; + int NewSpecMode = SPEC_PLAYER; int NewSpectatorID; bool GotNewSpectatorID = false; - if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW) + if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpecMode == SPEC_FREEVIEW) { for(int i = MAX_CLIENTS -1; i > -1; i--) { @@ -91,17 +123,33 @@ void CSpectator::ConSpectatePrevious(IConsole::IResult *pResult, void *pUserData GotNewSpectatorID = true; break; } + if(!GotNewSpectatorID && pSelf->m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_FLAGS) + { + NewSpecMode = SPEC_FLAGBLUE; + NewSpectatorID = -1; + GotNewSpectatorID = true; + } } else { - for(int i = pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID - 1; i > -1; i--) + if(pSelf->m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_FLAGS && pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpecMode == SPEC_FLAGBLUE) { - if(!pSelf->m_pClient->m_aClients[i].m_Active || pSelf->m_pClient->m_aClients[i].m_Team == TEAM_SPECTATORS) - continue; - - NewSpectatorID = i; + NewSpecMode = SPEC_FLAGRED; + NewSpectatorID = -1; GotNewSpectatorID = true; - break; + } + + if(!GotNewSpectatorID) + { + for(int i = pSelf->m_pClient->m_Snap.m_SpecInfo.m_SpectatorID - 1; i > -1; i--) + { + if(!pSelf->m_pClient->m_aClients[i].m_Active || pSelf->m_pClient->m_aClients[i].m_Team == TEAM_SPECTATORS) + continue; + + NewSpectatorID = i; + GotNewSpectatorID = true; + break; + } } if(!GotNewSpectatorID) @@ -116,9 +164,16 @@ void CSpectator::ConSpectatePrevious(IConsole::IResult *pResult, void *pUserData break; } } + + if(!GotNewSpectatorID && pSelf->m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_FLAGS) + { + NewSpecMode = SPEC_FLAGBLUE; + NewSpectatorID = -1; + GotNewSpectatorID = true; + } } if(GotNewSpectatorID) - pSelf->Spectate(NewSpectatorID); + pSelf->Spectate(NewSpecMode, NewSpectatorID); } CSpectator::CSpectator() @@ -155,8 +210,8 @@ void CSpectator::OnRender() { if(m_WasActive) { - if(m_SelectedSpectatorID != NO_SELECTION) - Spectate(m_SelectedSpectatorID); + if(m_SelectedSpecMode != NO_SELECTION) + Spectate(m_SelectedSpecMode, m_SelectedSpectatorID); m_WasActive = false; } return; @@ -170,7 +225,8 @@ void CSpectator::OnRender() } m_WasActive = true; - m_SelectedSpectatorID = NO_SELECTION; + m_SelectedSpecMode = NO_SELECTION; + m_SelectedSpectatorID = -1; // draw background float Width = 400*3.0f*Graphics()->ScreenAspect(); @@ -194,7 +250,7 @@ void CSpectator::OnRender() if(m_pClient->m_aClients[m_pClient->m_LocalClientID].m_Team == TEAM_SPECTATORS) { - if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW) + if(m_pClient->m_Snap.m_SpecInfo.m_SpecMode == SPEC_FREEVIEW) { Rect.x = Width/2.0f-280.0f; Rect.y = Height/2.0f-280.0f; @@ -206,14 +262,55 @@ void CSpectator::OnRender() if(m_SelectorMouse.x >= -280.0f && m_SelectorMouse.x <= -10.0f && m_SelectorMouse.y >= -280.0f && m_SelectorMouse.y <= -220.0f) { - m_SelectedSpectatorID = SPEC_FREEVIEW; + m_SelectedSpecMode = SPEC_FREEVIEW; Selected = true; } TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected?1.0f:0.5f); TextRender()->Text(0, Width/2.0f-240.0f, Height/2.0f-265.0f, FontSize, Localize("Free-View"), -1); } - float x = -270.0f, y = StartY; + // + float x = 20.0f, y = -270; + if (m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_FLAGS) + { + for(int Flag = SPEC_FLAGRED; Flag <= SPEC_FLAGBLUE; ++Flag) + { + if(m_pClient->m_Snap.m_SpecInfo.m_SpecMode == Flag) + { + Rect.x = Width/2.0f+x-10.0f; + Rect.y = Height/2.0f+y-10.0f; + Rect.w = 120.0f; + Rect.h = 60.0f; + RenderTools()->DrawRoundRect(&Rect, vec4(1.0f, 1.0f, 1.0f, 0.25f), 20.0f); + } + + Selected = false; + if(m_SelectorMouse.x >= x-10.0f && m_SelectorMouse.x <= x+110.0f && + m_SelectorMouse.y >= y-10.0f && m_SelectorMouse.y <= y+50.0f) + { + m_SelectedSpecMode = Flag; + Selected = true; + } + + Graphics()->BlendNormal(); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); + Graphics()->QuadsBegin(); + + RenderTools()->SelectSprite(Flag == SPEC_FLAGRED ? SPRITE_FLAG_RED : SPRITE_FLAG_BLUE); + + float Size = LineHeight/1.5f + (Selected ? 12.0f : 8.0f); + float FlagWidth = Width/2.0f + x + 40.0f + (Selected ? -3.0f : -2.0f); + float FlagHeight = Height/2.0f + y + (Selected ? -6.0f : -4.0f); + + IGraphics::CQuadItem QuadItem(FlagWidth, FlagHeight, Size/2.0f, Size); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + + x+=140.0f; + } + } + + x = -270.0f, y = StartY; for(int i = 0, Count = 0; i < MAX_CLIENTS; ++i) { if(!m_pClient->m_Snap.m_paPlayerInfos[i] || m_pClient->m_aClients[i].m_Team == TEAM_SPECTATORS || @@ -227,7 +324,7 @@ void CSpectator::OnRender() y = StartY; } - if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == i) + if(m_pClient->m_Snap.m_SpecInfo.m_SpecMode == SPEC_PLAYER && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == i) { Rect.x = Width/2.0f+x-10.0f; Rect.y = Height/2.0f+y-10.0f; @@ -240,6 +337,7 @@ void CSpectator::OnRender() if(m_SelectorMouse.x >= x-10.0f && m_SelectorMouse.x <= x+260.0f && m_SelectorMouse.y >= y-10.0f && m_SelectorMouse.y <= y+50.0f) { + m_SelectedSpecMode = SPEC_PLAYER; m_SelectedSpectatorID = i; Selected = true; } @@ -285,21 +383,24 @@ void CSpectator::OnReset() { m_WasActive = false; m_Active = false; - m_SelectedSpectatorID = NO_SELECTION; + m_SelectedSpecMode = NO_SELECTION; + m_SelectedSpectatorID = -1; } -void CSpectator::Spectate(int SpectatorID) +void CSpectator::Spectate(int SpecMode, int SpectatorID) { if(Client()->State() == IClient::STATE_DEMOPLAYBACK) { - m_pClient->m_DemoSpecID = clamp(SpectatorID, (int)SPEC_FREEVIEW, MAX_CLIENTS-1); + m_pClient->m_DemoSpecMode = clamp(SpecMode, 0, NUM_SPECMODES-1); + m_pClient->m_DemoSpecID = clamp(SpectatorID, -1, MAX_CLIENTS-1); return; } - if(m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SpectatorID) + if(m_pClient->m_Snap.m_SpecInfo.m_SpecMode == SpecMode && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SpectatorID) return; CNetMsg_Cl_SetSpectatorMode Msg; + Msg.m_SpecMode = SpecMode; Msg.m_SpectatorID = SpectatorID; Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } diff --git a/src/game/client/components/spectator.h b/src/game/client/components/spectator.h index 8e775cff9..685889eb8 100644 --- a/src/game/client/components/spectator.h +++ b/src/game/client/components/spectator.h @@ -10,13 +10,14 @@ class CSpectator : public CComponent { enum { - NO_SELECTION=-2, + NO_SELECTION=-1, }; bool m_Active; bool m_WasActive; int m_SelectedSpectatorID; + int m_SelectedSpecMode; vec2 m_SelectorMouse; static void ConKeySpectator(IConsole::IResult *pResult, void *pUserData); @@ -33,7 +34,7 @@ public: virtual void OnRelease(); virtual void OnReset(); - void Spectate(int SpectatorID); + void Spectate(int SpecMode, int SpectatorID); }; #endif diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 6869c6ceb..5cec3228e 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -364,7 +364,8 @@ void CGameClient::OnReset() m_LocalClientID = -1; m_TeamCooldownTick = 0; mem_zero(&m_GameInfo, sizeof(m_GameInfo)); - m_DemoSpecID = SPEC_FREEVIEW; + m_DemoSpecMode = SPEC_FREEVIEW; + m_DemoSpecID = -1; m_Tuning = CTuningParams(); } @@ -393,7 +394,7 @@ void CGameClient::UpdatePositions() if(m_Snap.m_SpecInfo.m_Active) { if(Client()->State() == IClient::STATE_DEMOPLAYBACK && DemoPlayer()->GetDemoType() == IDemoPlayer::DEMOTYPE_SERVER && - m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW) + m_Snap.m_SpecInfo.m_SpectatorID != -1) { m_Snap.m_SpecInfo.m_Position = mix( vec2(m_Snap.m_aCharacters[m_Snap.m_SpecInfo.m_SpectatorID].m_Prev.m_X, m_Snap.m_aCharacters[m_Snap.m_SpecInfo.m_SpectatorID].m_Prev.m_Y), @@ -401,7 +402,7 @@ void CGameClient::UpdatePositions() Client()->IntraGameTick()); m_Snap.m_SpecInfo.m_UsePosition = true; } - else if(m_Snap.m_pSpectatorInfo && (Client()->State() == IClient::STATE_DEMOPLAYBACK || m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW)) + else if(m_Snap.m_pSpectatorInfo && (Client()->State() == IClient::STATE_DEMOPLAYBACK || m_Snap.m_SpecInfo.m_SpecMode != SPEC_FREEVIEW)) { if(m_Snap.m_pPrevSpectatorInfo) m_Snap.m_SpecInfo.m_Position = mix(vec2(m_Snap.m_pPrevSpectatorInfo->m_X, m_Snap.m_pPrevSpectatorInfo->m_Y), @@ -535,8 +536,10 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker) case GAMEMSG_CTF_GRAB: if(m_SuppressEvents) return; - if(m_LocalClientID != -1 && (m_aClients[m_LocalClientID].m_Team != aParaI[0] || - (m_Snap.m_SpecInfo.m_Active && m_Snap.m_SpecInfo.m_SpectatorID != -1 && m_aClients[m_Snap.m_SpecInfo.m_SpectatorID].m_Team != aParaI[0]))) + if(m_LocalClientID != -1 && (m_aClients[m_LocalClientID].m_Team != aParaI[0] || (m_Snap.m_SpecInfo.m_Active && + (m_Snap.m_SpecInfo.m_SpectatorID != -1 && m_aClients[m_Snap.m_SpecInfo.m_SpectatorID].m_Team != aParaI[0]) || + (m_Snap.m_SpecInfo.m_SpecMode == SPEC_FLAGRED && aParaI[0] != TEAM_RED) || + (m_Snap.m_SpecInfo.m_SpecMode == SPEC_FLAGBLUE && aParaI[0] != TEAM_BLUE)))) m_pSounds->Enqueue(CSounds::CHN_GLOBAL, SOUND_CTF_GRAB_PL); else m_pSounds->Enqueue(CSounds::CHN_GLOBAL, SOUND_CTF_GRAB_EN); @@ -979,7 +982,8 @@ void CGameClient::OnNewSnapshot() if(m_aClients[ClientID].m_Team == TEAM_SPECTATORS) { m_Snap.m_SpecInfo.m_Active = true; - m_Snap.m_SpecInfo.m_SpectatorID = SPEC_FREEVIEW; + m_Snap.m_SpecInfo.m_SpecMode = SPEC_FREEVIEW; + m_Snap.m_SpecInfo.m_SpectatorID = -1; } } } @@ -1012,6 +1016,7 @@ void CGameClient::OnNewSnapshot() m_Snap.m_pSpectatorInfo = (const CNetObj_SpectatorInfo *)pData; m_Snap.m_pPrevSpectatorInfo = (const CNetObj_SpectatorInfo *)Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_SPECTATORINFO, Item.m_ID); m_Snap.m_SpecInfo.m_Active = true; + m_Snap.m_SpecInfo.m_SpecMode = m_Snap.m_pSpectatorInfo->m_SpecMode; m_Snap.m_SpecInfo.m_SpectatorID = m_Snap.m_pSpectatorInfo->m_SpectatorID; } else if(Item.m_Type == NETOBJTYPE_GAMEDATA) @@ -1059,10 +1064,24 @@ void CGameClient::OnNewSnapshot() { m_Snap.m_SpecInfo.m_Active = true; if(Client()->State() == IClient::STATE_DEMOPLAYBACK && DemoPlayer()->GetDemoType() == IDemoPlayer::DEMOTYPE_SERVER && - m_DemoSpecID != SPEC_FREEVIEW && m_Snap.m_aCharacters[m_DemoSpecID].m_Active) + m_DemoSpecID != -1 && m_Snap.m_aCharacters[m_DemoSpecID].m_Active) + { + m_Snap.m_SpecInfo.m_SpecMode = SPEC_PLAYER; m_Snap.m_SpecInfo.m_SpectatorID = m_DemoSpecID; + } else - m_Snap.m_SpecInfo.m_SpectatorID = SPEC_FREEVIEW; + { + if (m_DemoSpecMode == SPEC_PLAYER) + { + m_Snap.m_SpecInfo.m_SpecMode = SPEC_FREEVIEW; + m_Snap.m_SpecInfo.m_SpectatorID = -1; + } + else + { + m_Snap.m_SpecInfo.m_SpecMode = m_DemoSpecMode; + m_Snap.m_SpecInfo.m_SpectatorID = m_DemoSpecID; + } + } } // sort player infos by score diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 7c6bd5d65..46e39dc51 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -100,6 +100,7 @@ public: }; int m_ServerMode; + int m_DemoSpecMode; int m_DemoSpecID; vec2 m_LocalCharacterPos; @@ -138,6 +139,7 @@ public: struct CSpectateInfo { bool m_Active; + int m_SpecMode; int m_SpectatorID; bool m_UsePosition; vec2 m_Position; diff --git a/src/game/server/entities/pickup.cpp b/src/game/server/entities/pickup.cpp index f5892a1ef..613a79221 100644 --- a/src/game/server/entities/pickup.cpp +++ b/src/game/server/entities/pickup.cpp @@ -42,7 +42,7 @@ void CPickup::Tick() return; } // Check if a player intersected us - CCharacter *pChr = GameServer()->m_World.ClosestCharacter(m_Pos, 20.0f, 0); + CCharacter *pChr = (CCharacter *)GameServer()->m_World.ClosestEntity(m_Pos, 20.0f, CGameWorld::ENTTYPE_CHARACTER, 0); if(pChr && pChr->IsAlive()) { // player picked us up, is someone was hooking us, let them go diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 2881878c4..3ab710ba0 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -628,7 +628,7 @@ void CGameContext::OnClientEnter(int ClientID) // local info NewClientInfoMsg.m_Local = 1; - Server()->SendPackMsg(&NewClientInfoMsg, MSGFLAG_VITAL|MSGFLAG_NORECORD, ClientID); + Server()->SendPackMsg(&NewClientInfoMsg, MSGFLAG_VITAL|MSGFLAG_NORECORD, ClientID); if(Server()->DemoRecorder_IsRecording()) { @@ -923,7 +923,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) return; pPlayer->m_LastSetSpectatorMode = Server()->Tick(); - if(!pPlayer->SetSpectatorID(pMsg->m_SpectatorID)) + if(!pPlayer->SetSpectatorID(pMsg->m_SpecMode, pMsg->m_SpectatorID)) SendGameMsg(GAMEMSG_SPEC_INVALIDID, ClientID); } else if (MsgID == NETMSGTYPE_CL_EMOTICON && !m_World.m_Paused) diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index 5e43fc838..7b380823d 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -232,14 +232,14 @@ CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, v } -CCharacter *CGameWorld::ClosestCharacter(vec2 Pos, float Radius, CEntity *pNotThis) +CEntity *CGameWorld::ClosestEntity(vec2 Pos, float Radius, int Type, CEntity *pNotThis) { // Find other players float ClosestRange = Radius*2; - CCharacter *pClosest = 0; + CEntity *pClosest = 0; - CCharacter *p = (CCharacter *)GameServer()->m_World.FindFirst(ENTTYPE_CHARACTER); - for(; p; p = (CCharacter *)p->TypeNext()) + CEntity *p = GameServer()->m_World.FindFirst(Type); + for(; p; p = p->TypeNext()) { if(p == pNotThis) continue; diff --git a/src/game/server/gameworld.h b/src/game/server/gameworld.h index 6f95a4ad2..878919c86 100644 --- a/src/game/server/gameworld.h +++ b/src/game/server/gameworld.h @@ -68,6 +68,21 @@ public: */ int FindEntities(vec2 Pos, float Radius, CEntity **ppEnts, int Max, int Type); + /* + Function: closest_CEntity + Finds the closest CEntity of a type to a specific point. + + Arguments: + pos - The center position. + radius - How far off the CEntity is allowed to be + type - Type of the entities to find. + notthis - Entity to ignore + + Returns: + Returns a pointer to the closest CCharacter or NULL if no CCharacter is close enough. + */ + CEntity *ClosestEntity(vec2 Pos, float Radius, int Type, CEntity *pNotThis); + /* Function: interserct_CCharacter Finds the closest CCharacter that intersects the line. @@ -84,20 +99,6 @@ public: */ class CCharacter *IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2 &NewPos, class CEntity *pNotThis = 0); - /* - Function: closest_CCharacter - Finds the closest CCharacter to a specific point. - - Arguments: - pos - The center position. - radius - How far off the CCharacter is allowed to be - notthis - Entity to ignore - - Returns: - Returns a pointer to the closest CCharacter or NULL if no CCharacter is close enough. - */ - class CCharacter *ClosestCharacter(vec2 Pos, float Radius, CEntity *ppNotThis); - /* Function: insert_entity Adds an entity to the world. diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index b24252e3f..87318b8d2 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -2,6 +2,7 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include "entities/character.h" +#include "entities/flag.h" #include "gamecontext.h" #include "gamecontroller.h" #include "player.h" @@ -20,7 +21,10 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, bool Dummy) m_pCharacter = 0; m_ClientID = ClientID; m_Team = GameServer()->m_pController->GetStartTeam(); - m_SpectatorID = SPEC_FREEVIEW; + m_SpecMode = SPEC_FREEVIEW; + m_SpectatorID = -1; + m_pSpecFlag = 0; + m_ActiveSpecSwitch = 0; m_LastActionTick = Server()->Tick(); m_TeamChangeTick = Server()->Tick(); m_InactivityTickCounter = 0; @@ -73,12 +77,20 @@ void CPlayer::Tick() if(!GameServer()->m_pController->IsGamePaused()) { - if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW) + if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpecMode == 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_DeadSpecMode) Respawn(); + if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_pSpecFlag) + { + if(m_pSpecFlag->GetCarrier()) + m_SpectatorID = m_pSpecFlag->GetCarrier()->GetPlayer()->GetCID(); + else + m_SpectatorID = -1; + } + if(m_pCharacter) { if(m_pCharacter->IsAlive()) @@ -113,8 +125,13 @@ void CPlayer::PostTick() } // update view pos for spectators and dead players - if((m_Team == TEAM_SPECTATORS || m_DeadSpecMode) && m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[m_SpectatorID]) - m_ViewPos = GameServer()->m_apPlayers[m_SpectatorID]->m_ViewPos; + if((m_Team == TEAM_SPECTATORS || m_DeadSpecMode) && m_SpecMode != SPEC_FREEVIEW) + { + if(m_pSpecFlag) + m_ViewPos = m_pSpecFlag->GetPos(); + else if (GameServer()->m_apPlayers[m_SpectatorID]) + m_ViewPos = GameServer()->m_apPlayers[m_SpectatorID]->m_ViewPos; + } } void CPlayer::Snap(int SnappingClient) @@ -133,8 +150,9 @@ void CPlayer::Snap(int SnappingClient) pPlayerInfo->m_PlayerFlags |= PLAYERFLAG_READY; if(m_RespawnDisabled && (!GetCharacter() || !GetCharacter()->IsAlive())) pPlayerInfo->m_PlayerFlags |= PLAYERFLAG_DEAD; - if(SnappingClient != -1 && (m_Team == TEAM_SPECTATORS || m_DeadSpecMode) && SnappingClient == m_SpectatorID) + if(SnappingClient != -1 && (m_Team == TEAM_SPECTATORS || m_DeadSpecMode) && (SnappingClient == m_SpectatorID)) pPlayerInfo->m_PlayerFlags |= PLAYERFLAG_WATCHING; + pPlayerInfo->m_Latency = SnappingClient == -1 ? m_Latency.m_Min : GameServer()->m_apPlayers[SnappingClient]->m_aActLatency[m_ClientID]; pPlayerInfo->m_Score = m_Score; @@ -144,9 +162,18 @@ void CPlayer::Snap(int SnappingClient) if(!pSpectatorInfo) return; + pSpectatorInfo->m_SpecMode = m_SpecMode; pSpectatorInfo->m_SpectatorID = m_SpectatorID; - pSpectatorInfo->m_X = m_ViewPos.x; - pSpectatorInfo->m_Y = m_ViewPos.y; + if(m_pSpecFlag) + { + pSpectatorInfo->m_X = m_pSpecFlag->GetPos().x; + pSpectatorInfo->m_Y = m_pSpecFlag->GetPos().y; + } + else + { + pSpectatorInfo->m_X = m_ViewPos.x; + pSpectatorInfo->m_Y = m_ViewPos.y; + } } // demo recording @@ -180,12 +207,15 @@ void CPlayer::OnDisconnect() // update spectator modes for(int i = 0; i < MAX_CLIENTS; ++i) { - if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_SpectatorID == m_ClientID) + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_SpecMode == SPEC_PLAYER && GameServer()->m_apPlayers[i]->m_SpectatorID == m_ClientID) { if(GameServer()->m_apPlayers[i]->m_DeadSpecMode) GameServer()->m_apPlayers[i]->UpdateDeadSpecMode(); else - GameServer()->m_apPlayers[i]->m_SpectatorID = SPEC_FREEVIEW; + { + GameServer()->m_apPlayers[i]->m_SpecMode = SPEC_FREEVIEW; + GameServer()->m_apPlayers[i]->m_SpectatorID = -1; + } } } } @@ -220,7 +250,7 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) m_pCharacter->ResetInput(); m_PlayerFlags = NewInput->m_PlayerFlags; - return; + return; } m_PlayerFlags = NewInput->m_PlayerFlags; @@ -231,6 +261,42 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1)) Respawn(); + if(!m_pCharacter && m_Team == TEAM_SPECTATORS && (NewInput->m_Fire&1)) + { + if(!m_ActiveSpecSwitch) + { + m_ActiveSpecSwitch = true; + if(m_SpecMode == SPEC_FREEVIEW) + { + CCharacter *pChar = (CCharacter *)GameServer()->m_World.ClosestEntity(m_ViewPos, 6.0f*32, CGameWorld::ENTTYPE_CHARACTER, 0); + CFlag *pFlag = (CFlag *)GameServer()->m_World.ClosestEntity(m_ViewPos, 6.0f*32, CGameWorld::ENTTYPE_FLAG, 0); + if(pChar || pFlag) + { + if(!pChar || (pFlag && pChar && distance(m_ViewPos, pFlag->GetPos()) < distance(m_ViewPos, pChar->GetPos()))) + { + m_SpecMode = pFlag->GetTeam() == TEAM_RED ? SPEC_FLAGRED : SPEC_FLAGBLUE; + m_pSpecFlag = pFlag; + m_SpectatorID = -1; + } + else + { + m_SpecMode = SPEC_PLAYER; + m_pSpecFlag = 0; + m_SpectatorID = pChar->GetPlayer()->GetCID(); + } + } + } + else + { + m_SpecMode = SPEC_FREEVIEW; + m_pSpecFlag = 0; + m_SpectatorID = -1; + } + } + } + else if(m_ActiveSpecSwitch) + m_ActiveSpecSwitch = false; + // check for activity if(NewInput->m_Direction || m_LatestActivity.m_TargetX != NewInput->m_TargetX || m_LatestActivity.m_TargetY != NewInput->m_TargetY || NewInput->m_Jump || @@ -277,16 +343,40 @@ void CPlayer::Respawn() m_Spawning = true; } -bool CPlayer::SetSpectatorID(int SpectatorID) +bool CPlayer::SetSpectatorID(int SpecMode, int SpectatorID) { - if(m_SpectatorID == SpectatorID || m_ClientID == SpectatorID) + if((SpecMode == SPEC_PLAYER && (SpectatorID == -1 || m_SpectatorID == SpectatorID || m_ClientID == SpectatorID)) || + (SpecMode != SPEC_PLAYER && SpecMode == m_SpecMode)) return false; if(m_Team == TEAM_SPECTATORS) { // check for freeview or if wanted player is playing - if(SpectatorID == SPEC_FREEVIEW || (GameServer()->m_apPlayers[SpectatorID] && GameServer()->m_apPlayers[SpectatorID]->GetTeam() != TEAM_SPECTATORS)) + if(SpecMode != SPEC_PLAYER || (SpecMode == SPEC_PLAYER && GameServer()->m_apPlayers[SpectatorID] && GameServer()->m_apPlayers[SpectatorID]->GetTeam() != TEAM_SPECTATORS)) { + if(SpecMode == SPEC_FLAGRED || SpecMode == SPEC_FLAGBLUE) + { + CFlag *pFlag = (CFlag*)GameServer()->m_World.FindFirst(CGameWorld::ENTTYPE_FLAG); + while (pFlag) + { + if ((pFlag->GetTeam() == TEAM_RED && SpecMode == SPEC_FLAGRED) || (pFlag->GetTeam() == TEAM_BLUE && SpecMode == SPEC_FLAGBLUE)) + { + m_pSpecFlag = pFlag; + if (pFlag->GetCarrier()) + m_SpectatorID = pFlag->GetCarrier()->GetPlayer()->GetCID(); + else + m_SpectatorID = -1; + break; + } + pFlag = (CFlag*)pFlag->TypeNext(); + } + if (!m_pSpecFlag) + return false; + m_SpecMode = SpecMode; + return true; + } + m_pSpecFlag = 0; + m_SpecMode = SpecMode; m_SpectatorID = SpectatorID; return true; } @@ -294,8 +384,10 @@ bool CPlayer::SetSpectatorID(int SpectatorID) else if(m_DeadSpecMode) { // check if wanted player can be followed - if(GameServer()->m_apPlayers[SpectatorID] && DeadCanFollow(GameServer()->m_apPlayers[SpectatorID])) + if(SpecMode == SPEC_PLAYER && GameServer()->m_apPlayers[SpectatorID] && DeadCanFollow(GameServer()->m_apPlayers[SpectatorID])) { + m_SpecMode = SpecMode; + m_pSpecFlag = 0; m_SpectatorID = SpectatorID; return true; } @@ -313,7 +405,7 @@ bool CPlayer::DeadCanFollow(CPlayer *pPlayer) const void CPlayer::UpdateDeadSpecMode() { // check if actual spectator id is valid - if(m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[m_SpectatorID] && DeadCanFollow(GameServer()->m_apPlayers[m_SpectatorID])) + if(m_SpecMode == SPEC_FREEVIEW && GameServer()->m_apPlayers[m_SpectatorID] && DeadCanFollow(GameServer()->m_apPlayers[m_SpectatorID])) return; // find player to follow @@ -336,23 +428,28 @@ void CPlayer::SetTeam(int Team, bool DoChatMsg) m_Team = Team; m_LastActionTick = Server()->Tick(); - m_SpectatorID = SPEC_FREEVIEW; + m_SpecMode = SPEC_FREEVIEW; + m_SpectatorID = -1; + m_pSpecFlag = 0; m_DeadSpecMode = false; - + // we got to wait 0.5 secs before respawning m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2; - + if(Team == TEAM_SPECTATORS) { // update spectator modes for(int i = 0; i < MAX_CLIENTS; ++i) { - if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_SpectatorID == m_ClientID) + if(GameServer()->m_apPlayers[i] && GameServer()-> m_apPlayers[i]->m_SpecMode == SPEC_PLAYER && GameServer()->m_apPlayers[i]->m_SpectatorID == m_ClientID) { if(GameServer()->m_apPlayers[i]->m_DeadSpecMode) GameServer()->m_apPlayers[i]->UpdateDeadSpecMode(); else - GameServer()->m_apPlayers[i]->m_SpectatorID = SPEC_FREEVIEW; + { + GameServer()->m_apPlayers[i]->m_SpecMode = SPEC_FREEVIEW; + GameServer()->m_apPlayers[i]->m_SpectatorID = -1; + } } } } diff --git a/src/game/server/player.h b/src/game/server/player.h index b102f2258..dd57d450d 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -54,7 +54,7 @@ public: // used for spectator mode int GetSpectatorID() const { return m_SpectatorID; } - bool SetSpectatorID(int SpectatorID); + bool SetSpectatorID(int SpecMode, int SpectatorID); bool m_DeadSpecMode; bool DeadCanFollow(CPlayer *pPlayer) const; void UpdateDeadSpecMode(); @@ -126,7 +126,10 @@ private: bool m_Dummy; // used for spectator mode + int m_SpecMode; int m_SpectatorID; + class CFlag *m_pSpecFlag; + bool m_ActiveSpecSwitch; }; #endif