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
This commit is contained in:
Zwelf 2016-10-31 21:38:05 +01:00 committed by oy
parent 16965e4bd6
commit 85b2f98d71
12 changed files with 339 additions and 92 deletions

View file

@ -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", [

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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.

View file

@ -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;
}
}
}
}

View file

@ -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