Add multi view

This commit is contained in:
devdenn 2023-05-24 19:29:38 +02:00
parent 422ff844b8
commit d16fb877dc
8 changed files with 355 additions and 17 deletions

View file

@ -31,7 +31,7 @@ float CCamera::ZoomProgress(float CurrentTime) const
void CCamera::ScaleZoom(float Factor) void CCamera::ScaleZoom(float Factor)
{ {
float CurrentTarget = m_Zooming ? m_ZoomSmoothingTarget : m_Zoom; float CurrentTarget = m_Zooming ? m_ZoomSmoothingTarget : m_Zoom;
ChangeZoom(CurrentTarget * Factor); ChangeZoom(CurrentTarget * Factor, g_Config.m_ClSmoothZoomTime);
} }
float CCamera::MaxZoomLevel() float CCamera::MaxZoomLevel()
@ -44,7 +44,7 @@ float CCamera::MinZoomLevel()
return 0.01f; return 0.01f;
} }
void CCamera::ChangeZoom(float Target) void CCamera::ChangeZoom(float Target, int Smoothness)
{ {
if(Target > MaxZoomLevel() || Target < MinZoomLevel()) if(Target > MaxZoomLevel() || Target < MinZoomLevel())
{ {
@ -64,7 +64,7 @@ void CCamera::ChangeZoom(float Target)
m_ZoomSmoothingTarget = Target; m_ZoomSmoothingTarget = Target;
m_ZoomSmoothing = CCubicBezier::With(Current, Derivative, 0, m_ZoomSmoothingTarget); m_ZoomSmoothing = CCubicBezier::With(Current, Derivative, 0, m_ZoomSmoothingTarget);
m_ZoomSmoothingStart = Now; m_ZoomSmoothingStart = Now;
m_ZoomSmoothingEnd = Now + (float)g_Config.m_ClSmoothZoomTime / 1000; m_ZoomSmoothingEnd = Now + (float)Smoothness / 1000;
m_Zooming = true; m_Zooming = true;
} }
@ -198,6 +198,9 @@ void CCamera::ConZoomPlus(IConsole::IResult *pResult, void *pUserData)
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK) if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK)
{ {
pSelf->ScaleZoom(ZoomStep); pSelf->ScaleZoom(ZoomStep);
if(pSelf->GameClient()->m_MultiViewActivated)
pSelf->GameClient()->m_MultiViewPersonalZoom++;
} }
} }
void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData) void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData)
@ -206,12 +209,19 @@ void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData)
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK) if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK)
{ {
pSelf->ScaleZoom(1 / ZoomStep); pSelf->ScaleZoom(1 / ZoomStep);
if(pSelf->GameClient()->m_MultiViewActivated)
pSelf->GameClient()->m_MultiViewPersonalZoom--;
} }
} }
void CCamera::ConZoom(IConsole::IResult *pResult, void *pUserData) void CCamera::ConZoom(IConsole::IResult *pResult, void *pUserData)
{ {
CCamera *pSelf = (CCamera *)pUserData;
float TargetLevel = pResult->NumArguments() ? pResult->GetFloat(0) : g_Config.m_ClDefaultZoom; float TargetLevel = pResult->NumArguments() ? pResult->GetFloat(0) : g_Config.m_ClDefaultZoom;
((CCamera *)pUserData)->ChangeZoom(std::pow(ZoomStep, TargetLevel - 10)); pSelf->ChangeZoom(std::pow(ZoomStep, TargetLevel - 10), g_Config.m_ClSmoothZoomTime);
if(pSelf->GameClient()->m_MultiViewActivated)
pSelf->GameClient()->m_MultiViewPersonalZoom = 0;
} }
void CCamera::ConSetView(IConsole::IResult *pResult, void *pUserData) void CCamera::ConSetView(IConsole::IResult *pResult, void *pUserData)
{ {
@ -221,3 +231,8 @@ void CCamera::ConSetView(IConsole::IResult *pResult, void *pUserData)
clamp(pResult->GetInteger(0) * 32.0f, 200.0f, pSelf->Collision()->GetWidth() * 32 - 200.0f), clamp(pResult->GetInteger(0) * 32.0f, 200.0f, pSelf->Collision()->GetWidth() * 32 - 200.0f),
clamp(pResult->GetInteger(1) * 32.0f, 200.0f, pSelf->Collision()->GetWidth() * 32 - 200.0f)); clamp(pResult->GetInteger(1) * 32.0f, 200.0f, pSelf->Collision()->GetWidth() * 32 - 200.0f));
} }
void CCamera::SetZoom(float Target, int Smoothness)
{
ChangeZoom(Target, Smoothness);
}

View file

@ -30,7 +30,7 @@ class CCamera : public CComponent
float m_ZoomSmoothingEnd; float m_ZoomSmoothingEnd;
void ScaleZoom(float Factor); void ScaleZoom(float Factor);
void ChangeZoom(float Target); void ChangeZoom(float Target, int Smoothness);
float ZoomProgress(float CurrentTime) const; float ZoomProgress(float CurrentTime) const;
float MinZoomLevel(); float MinZoomLevel();
@ -52,6 +52,8 @@ public:
virtual void OnConsoleInit() override; virtual void OnConsoleInit() override;
virtual void OnReset() override; virtual void OnReset() override;
void SetZoom(float Target, int Smoothness);
private: private:
static void ConZoomPlus(IConsole::IResult *pResult, void *pUserData); static void ConZoomPlus(IConsole::IResult *pResult, void *pUserData);
static void ConZoomMinus(IConsole::IResult *pResult, void *pUserData); static void ConZoomMinus(IConsole::IResult *pResult, void *pUserData);

View file

@ -106,7 +106,7 @@ void CHud::RenderGameTimer()
} }
else if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_RACETIME) else if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_RACETIME)
{ {
//The Warmup timer is negative in this case to make sure that incompatible clients will not see a warmup timer // The Warmup timer is negative in this case to make sure that incompatible clients will not see a warmup timer
Time = (Client()->GameTick(g_Config.m_ClDummy) + m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer) / Client()->GameTickSpeed(); Time = (Client()->GameTick(g_Config.m_ClDummy) + m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer) / Client()->GameTickSpeed();
} }
else else
@ -1474,7 +1474,7 @@ void CHud::RenderSpectatorHud()
// draw the text // draw the text
char aBuf[128]; char aBuf[128];
str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Spectate"), m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW ? m_pClient->m_aClients[m_pClient->m_Snap.m_SpecInfo.m_SpectatorID].m_aName : Localize("Free-View")); str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Spectate"), GameClient()->m_MultiViewActivated ? Localize("Multi-View") : m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW ? m_pClient->m_aClients[m_pClient->m_Snap.m_SpecInfo.m_SpectatorID].m_aName : Localize("Free-View"));
TextRender()->Text(m_Width - 174.0f, m_Height - 15.0f + (15.f - 8.f) / 2.f, 8.0f, aBuf, -1.0f); TextRender()->Text(m_Width - 174.0f, m_Height - 15.0f + (15.f - 8.f) / 2.f, 8.0f, aBuf, -1.0f);
} }
@ -1527,7 +1527,11 @@ void CHud::OnRender()
{ {
RenderAmmoHealthAndArmor(&m_pClient->m_Snap.m_aCharacters[SpectatorID].m_Cur); RenderAmmoHealthAndArmor(&m_pClient->m_Snap.m_aCharacters[SpectatorID].m_Cur);
} }
if(SpectatorID != SPEC_FREEVIEW && m_pClient->m_Snap.m_aCharacters[SpectatorID].m_HasExtendedData && g_Config.m_ClShowhudDDRace && GameClient()->m_GameInfo.m_HudDDRace) if(SpectatorID != SPEC_FREEVIEW &&
m_pClient->m_Snap.m_aCharacters[SpectatorID].m_HasExtendedData &&
g_Config.m_ClShowhudDDRace &&
(!GameClient()->m_MultiViewActivated || GameClient()->m_MultiViewShowHud) &&
GameClient()->m_GameInfo.m_HudDDRace)
{ {
RenderPlayerState(SpectatorID); RenderPlayerState(SpectatorID);
} }

View file

@ -149,6 +149,17 @@ void CSpectator::ConSpectateClosest(IConsole::IResult *pResult, void *pUserData)
pSelf->Spectate(NewSpectatorID); pSelf->Spectate(NewSpectatorID);
} }
void CSpectator::ConMultiView(IConsole::IResult *pResult, void *pUserData)
{
CSpectator *pSelf = (CSpectator *)pUserData;
int Input = pResult->GetInteger(0);
if(Input == -1)
std::fill(std::begin(pSelf->GameClient()->m_aMultiViewId), std::end(pSelf->GameClient()->m_aMultiViewId), false); // remove everyone from multiview
else if(Input < MAX_CLIENTS && Input >= 0)
pSelf->GameClient()->m_aMultiViewId[Input] = !pSelf->GameClient()->m_aMultiViewId[Input]; // activate or deactivate one player from multiview
}
CSpectator::CSpectator() CSpectator::CSpectator()
{ {
OnReset(); OnReset();
@ -162,6 +173,7 @@ void CSpectator::OnConsoleInit()
Console()->Register("spectate_next", "", CFGFLAG_CLIENT, ConSpectateNext, this, "Spectate the next player"); Console()->Register("spectate_next", "", CFGFLAG_CLIENT, ConSpectateNext, this, "Spectate the next player");
Console()->Register("spectate_previous", "", CFGFLAG_CLIENT, ConSpectatePrevious, this, "Spectate the previous player"); Console()->Register("spectate_previous", "", CFGFLAG_CLIENT, ConSpectatePrevious, this, "Spectate the previous player");
Console()->Register("spectate_closest", "", CFGFLAG_CLIENT, ConSpectateClosest, this, "Spectate the closest player"); Console()->Register("spectate_closest", "", CFGFLAG_CLIENT, ConSpectateClosest, this, "Spectate the closest player");
Console()->Register("spectate_multiview", "i[id]", CFGFLAG_CLIENT, ConMultiView, this, "Add/remove Client-IDs to spectate them exclusivly (-1 to reset)");
} }
bool CSpectator::OnCursorMove(float x, float y, IInput::ECursorType CursorType) bool CSpectator::OnCursorMove(float x, float y, IInput::ECursorType CursorType)
@ -186,7 +198,12 @@ void CSpectator::OnRender()
if(m_WasActive) if(m_WasActive)
{ {
if(m_SelectedSpectatorID != NO_SELECTION) if(m_SelectedSpectatorID != NO_SELECTION)
Spectate(m_SelectedSpectatorID); {
if(m_SelectedSpectatorID != MULTI_VIEW)
Spectate(m_SelectedSpectatorID);
GameClient()->m_MultiViewActivated = m_SelectedSpectatorID == MULTI_VIEW;
}
m_WasActive = false; m_WasActive = false;
} }
return; return;
@ -213,6 +230,7 @@ void CSpectator::OnRender()
float TeeSizeMod = 1.0f; float TeeSizeMod = 1.0f;
float RoundRadius = 30.0f; float RoundRadius = 30.0f;
bool Selected = false; bool Selected = false;
bool MultiViewSelected = false;
int TotalPlayers = 0; int TotalPlayers = 0;
int PerLine = 8; int PerLine = 8;
float BoxMove = -10.0f; float BoxMove = -10.0f;
@ -253,34 +271,48 @@ void CSpectator::OnRender()
if((Client()->State() == IClient::STATE_DEMOPLAYBACK && m_pClient->m_DemoSpecID == SPEC_FREEVIEW) || if((Client()->State() == IClient::STATE_DEMOPLAYBACK && m_pClient->m_DemoSpecID == SPEC_FREEVIEW) ||
(Client()->State() != IClient::STATE_DEMOPLAYBACK && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW)) (Client()->State() != IClient::STATE_DEMOPLAYBACK && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID == SPEC_FREEVIEW))
{ {
Graphics()->DrawRect(Width / 2.0f - (ObjWidth - 20.0f), Height / 2.0f - 280.0f, 270.0f, 60.0f, ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, 20.0f); Graphics()->DrawRect(Width / 2.0f - (ObjWidth - 20.0f), Height / 2.0f - 280.0f, ((ObjWidth * 2.0f) / 3.0f) - 40.0f, 60.0f, ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, 20.0f);
}
if(GameClient()->m_MultiViewActivated)
{
Graphics()->DrawRect(Width / 2.0f - (ObjWidth - 20.0f) + (ObjWidth * 2.0f / 3.0f), Height / 2.0f - 280.0f, ((ObjWidth * 2.0f) / 3.0f) - 40.0f, 60.0f, ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, 20.0f);
} }
if(Client()->State() == IClient::STATE_DEMOPLAYBACK && m_pClient->m_Snap.m_LocalClientID >= 0 && m_pClient->m_DemoSpecID == SPEC_FOLLOW) if(Client()->State() == IClient::STATE_DEMOPLAYBACK && m_pClient->m_Snap.m_LocalClientID >= 0 && m_pClient->m_DemoSpecID == SPEC_FOLLOW)
{ {
Graphics()->DrawRect(Width / 2.0f - (ObjWidth - 310.0f), Height / 2.0f - 280.0f, 270.0f, 60.0f, ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, 20.0f); Graphics()->DrawRect(Width / 2.0f - (ObjWidth - 20.0f) + (ObjWidth * 2.0f * 2.0f / 3.0f), Height / 2.0f - 280.0f, ((ObjWidth * 2.0f) / 3.0f) - 40.0f, 60.0f, ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_ALL, 20.0f);
} }
if(m_SelectorMouse.x >= -(ObjWidth - 20.0f) && m_SelectorMouse.x <= -(ObjWidth - 290 + 10.0f) && if(m_SelectorMouse.x >= -(ObjWidth - 20.0f) && m_SelectorMouse.x <= -(ObjWidth - 20.0f) + ((ObjWidth * 2.0f) / 3.0f) - 40.0f &&
m_SelectorMouse.y >= -280.0f && m_SelectorMouse.y <= -220.0f) m_SelectorMouse.y >= -280.0f && m_SelectorMouse.y <= -220.0f)
{ {
m_SelectedSpectatorID = SPEC_FREEVIEW; m_SelectedSpectatorID = SPEC_FREEVIEW;
Selected = true; Selected = true;
} }
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected ? 1.0f : 0.5f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected ? 1.0f : 0.5f);
TextRender()->Text(Width / 2.0f - (ObjWidth - 60.0f), Height / 2.0f - 280.f + (60.f - BigFontSize) / 2.f, BigFontSize, Localize("Free-View"), -1.0f); TextRender()->Text(Width / 2.0f - (ObjWidth - 40.0f), Height / 2.0f - 280.f + (60.f - BigFontSize) / 2.f, BigFontSize, Localize("Free-View"), -1.0f);
if(m_SelectorMouse.x >= -(ObjWidth - 20.0f) + (ObjWidth * 2.0f / 3.0f) && m_SelectorMouse.x <= -(ObjWidth - 20.0f) + (ObjWidth * 2.0f / 3.0f) + ((ObjWidth * 2.0f) / 3.0f) - 40.0f &&
m_SelectorMouse.y >= -280.0f && m_SelectorMouse.y <= -220.0f)
{
m_SelectedSpectatorID = MULTI_VIEW;
MultiViewSelected = true;
}
TextRender()->TextColor(1.0f, 1.0f, 1.0f, MultiViewSelected ? 1.0f : 0.5f);
TextRender()->Text(Width / 2.0f - (ObjWidth - 40.0f) + (ObjWidth * 2.0f / 3.0f), Height / 2.0f - 280.f + (60.f - BigFontSize) / 2.f, BigFontSize, Localize("Multi-View"), -1.0f);
if(Client()->State() == IClient::STATE_DEMOPLAYBACK && m_pClient->m_Snap.m_LocalClientID >= 0) if(Client()->State() == IClient::STATE_DEMOPLAYBACK && m_pClient->m_Snap.m_LocalClientID >= 0)
{ {
Selected = false; Selected = false;
if(m_SelectorMouse.x > -(ObjWidth - 290.0f) && m_SelectorMouse.x <= -(ObjWidth - 590.0f) && if(m_SelectorMouse.x >= -(ObjWidth - 20.0f) + (ObjWidth * 2.0f * 2.0f / 3.0f) && m_SelectorMouse.x <= -(ObjWidth - 20.0f) + (ObjWidth * 2.0f * 2.0f / 3.0f) + ((ObjWidth * 2.0f) / 3.0f) - 40.0f &&
m_SelectorMouse.y >= -280.0f && m_SelectorMouse.y <= -220.0f) m_SelectorMouse.y >= -280.0f && m_SelectorMouse.y <= -220.0f)
{ {
m_SelectedSpectatorID = SPEC_FOLLOW; m_SelectedSpectatorID = SPEC_FOLLOW;
Selected = true; Selected = true;
} }
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected ? 1.0f : 0.5f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected ? 1.0f : 0.5f);
TextRender()->Text(Width / 2.0f - (ObjWidth - 350.0f), Height / 2.0f - 280.0f + (60.f - BigFontSize) / 2.f, BigFontSize, Localize("Follow"), -1.0f); TextRender()->Text(Width / 2.0f - (ObjWidth - 40.0f) + (ObjWidth * 2.0f * 2.0f / 3.0f), Height / 2.0f - 280.0f + (60.f - BigFontSize) / 2.f, BigFontSize, Localize("Follow"), -1.0f);
} }
float x = -(ObjWidth - 35.0f), y = StartY; float x = -(ObjWidth - 35.0f), y = StartY;

View file

@ -10,6 +10,7 @@ class CSpectator : public CComponent
{ {
enum enum
{ {
MULTI_VIEW = -4,
NO_SELECTION = -3, NO_SELECTION = -3,
}; };
@ -30,6 +31,7 @@ class CSpectator : public CComponent
static void ConSpectateNext(IConsole::IResult *pResult, void *pUserData); static void ConSpectateNext(IConsole::IResult *pResult, void *pUserData);
static void ConSpectatePrevious(IConsole::IResult *pResult, void *pUserData); static void ConSpectatePrevious(IConsole::IResult *pResult, void *pUserData);
static void ConSpectateClosest(IConsole::IResult *pResult, void *pUserData); static void ConSpectateClosest(IConsole::IResult *pResult, void *pUserData);
static void ConMultiView(IConsole::IResult *pResult, void *pUserData);
public: public:
CSpectator(); CSpectator();

View file

@ -604,7 +604,11 @@ void CGameClient::UpdatePositions()
// spectator position // spectator position
if(m_Snap.m_SpecInfo.m_Active) if(m_Snap.m_SpecInfo.m_Active)
{ {
if(Client()->State() == IClient::STATE_DEMOPLAYBACK && m_DemoSpecID != SPEC_FOLLOW && m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW) if(m_MultiViewActivated)
{
HandleMultiView();
}
else if(Client()->State() == IClient::STATE_DEMOPLAYBACK && m_DemoSpecID != SPEC_FOLLOW && m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW)
{ {
m_Snap.m_SpecInfo.m_Position = mix( 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), 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),
@ -623,11 +627,28 @@ void CGameClient::UpdatePositions()
} }
} }
if(!m_MultiViewActivated && m_MultiView.m_IsInit)
ResetMultiView();
UpdateRenderedCharacters(); UpdateRenderedCharacters();
} }
void CGameClient::OnRender() void CGameClient::OnRender()
{ {
// check if multi view got activated
if(!m_MultiView.m_IsInit && m_MultiViewActivated)
{
int TeamId = 0;
if(m_Snap.m_SpecInfo.m_SpectatorID >= 0)
TeamId = m_Teams.Team(m_Snap.m_SpecInfo.m_SpectatorID);
if(!InitMultiViewFromFreeview(TeamId))
{
dbg_msg("MultiView", "No players found to spectate");
m_MultiViewActivated = false;
}
}
// update the local character and spectate position // update the local character and spectate position
UpdatePositions(); UpdatePositions();
@ -843,6 +864,18 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker, int Conn, bool Dumm
pChar->ResetPrediction(); pChar->ResetPrediction();
m_GameWorld.ReleaseHooked(pMsg->m_Victim); m_GameWorld.ReleaseHooked(pMsg->m_Victim);
} }
// if we are spectating a static id set (team 0) and somebody killed, we remove him from the list
if(IsMultiViewIdSet() && m_aMultiViewId[pMsg->m_Victim] && !m_aClients[pMsg->m_Victim].m_Spec)
{
// is multi view even activated and we are not spectating a solo guy
if(m_MultiViewActivated && !m_MultiView.m_Solo && m_MultiView.m_Team == 0)
m_aMultiViewId[pMsg->m_Victim] = false;
// if everyone of a team killed, we have no ids to spectate anymore, so we disable multi view
if(!IsMultiViewIdSet())
m_MultiViewActivated = false;
}
} }
} }
@ -1666,7 +1699,17 @@ void CGameClient::OnNewSnapshot()
m_aDDRaceMsgSent[i] = true; m_aDDRaceMsgSent[i] = true;
} }
if(m_aShowOthers[g_Config.m_ClDummy] == SHOW_OTHERS_NOT_SET || m_aShowOthers[g_Config.m_ClDummy] != g_Config.m_ClShowOthers) if(m_MultiViewActivated)
{
// dont show other teams while spectating in multi view
CNetMsg_Cl_ShowOthers Msg;
Msg.m_Show = SHOW_OTHERS_ONLY_TEAM;
Client()->SendPackMsgActive(&Msg, MSGFLAG_VITAL);
// update state
m_aShowOthers[g_Config.m_ClDummy] = SHOW_OTHERS_ONLY_TEAM;
}
else if(m_aShowOthers[g_Config.m_ClDummy] == SHOW_OTHERS_NOT_SET || m_aShowOthers[g_Config.m_ClDummy] != g_Config.m_ClShowOthers)
{ {
{ {
CNetMsg_Cl_ShowOthers Msg; CNetMsg_Cl_ShowOthers Msg;
@ -3350,3 +3393,214 @@ void CGameClient::SnapCollectEntities()
m_vSnapEntities.push_back({Ent.m_Item, Ent.m_pData, pDataEx}); m_vSnapEntities.push_back({Ent.m_Item, Ent.m_pData, pDataEx});
} }
} }
void CGameClient::HandleMultiView()
{
bool IsTeamZero = IsMultiViewIdSet();
bool Init = false;
int AmountPlayers = 0;
vec2 Minpos, Maxpos;
float TmpVel = 0.0f;
for(int i = 0; i < MAX_CLIENTS; i++)
{
// look at players who are vanished
if(m_MultiView.m_aVanish[i])
{
// not in freeze anymore and the delay is over
if(m_MultiView.m_aLastFreeze[i] + 6.0f <= Client()->LocalTime() && m_aClients[i].m_FreezeEnd == 0)
{
m_MultiView.m_aVanish[i] = false;
m_MultiView.m_aLastFreeze[i] = 0.0f;
}
}
// we look at team 0 and the player is not in the spec list
if(IsTeamZero && !m_aMultiViewId[i])
continue;
// player is vanished
if(m_MultiView.m_aVanish[i])
continue;
// the player is not in the team we are spectating
if(m_Teams.Team(i) != m_MultiView.m_Team)
continue;
vec2 PlayerPos;
if(m_Snap.m_aCharacters[i].m_Active)
PlayerPos = vec2(m_aClients[i].m_RenderPos.x, m_aClients[i].m_RenderPos.y);
else if(m_aClients[i].m_Spec) // tee is in spec
PlayerPos = m_aClients[i].m_SpecChar;
else
continue;
// player is far away and frozen
if(distance(m_MultiView.m_OldPos, PlayerPos) > 1100 && m_aClients[i].m_FreezeEnd != 0)
{
// check if the player is frozen for more than 3 seconds, if so vanish him
if(m_MultiView.m_aLastFreeze[i] == 0.0f)
m_MultiView.m_aLastFreeze[i] = Client()->LocalTime();
else if(m_MultiView.m_aLastFreeze[i] + 3.0f <= Client()->LocalTime())
m_MultiView.m_aVanish[i] = true;
}
else if(m_MultiView.m_aLastFreeze[i] != 0)
m_MultiView.m_aLastFreeze[i] = 0;
// set the minimum and maximum position
if(!Init)
{
Minpos = PlayerPos;
Maxpos = PlayerPos;
Init = true;
}
else
{
Minpos.x = std::min(Minpos.x, PlayerPos.x);
Maxpos.x = std::max(Maxpos.x, PlayerPos.x);
Minpos.y = std::min(Minpos.y, PlayerPos.y);
Maxpos.y = std::max(Maxpos.y, PlayerPos.y);
}
// sum up the velocity of all players we are spectating
const CNetObj_Character &CurrentCharacter = m_Snap.m_aCharacters[i].m_Cur;
TmpVel += (length(vec2(CurrentCharacter.m_VelX / 256.0f, CurrentCharacter.m_VelY / 256.0f)) * 50) / 32.0f;
AmountPlayers++;
}
// if we have found no players, we disable multi view
if(AmountPlayers == 0)
{
m_MultiViewActivated = false;
return;
}
vec2 TargetPos = vec2((Minpos.x + Maxpos.x) / 2.0f, (Minpos.y + Maxpos.y) / 2.0f);
// dont hide the position hud if its only one player
m_MultiViewShowHud = AmountPlayers == 1;
// get the average velocity
float AvgVel = clamp(TmpVel / AmountPlayers ? TmpVel / (float)AmountPlayers : 0.0f, 0.0f, 1000.0f);
if(m_MultiView.m_OldPersonalZoom == m_MultiViewPersonalZoom)
m_Camera.SetZoom(CalculateMultiViewZoom(Minpos, Maxpos, AvgVel), g_Config.m_ClMultiViewZoomSmoothness);
else
m_Camera.SetZoom(CalculateMultiViewZoom(Minpos, Maxpos, AvgVel), 50);
m_Snap.m_SpecInfo.m_Position = m_MultiView.m_OldPos + ((TargetPos - m_MultiView.m_OldPos) * CalculateMultiViewMultiplier(TargetPos));
m_MultiView.m_OldPos = m_Snap.m_SpecInfo.m_Position;
m_Snap.m_SpecInfo.m_UsePosition = true;
}
bool CGameClient::InitMultiViewFromFreeview(int Team)
{
float Width, Height;
CleanMultiViewIds();
m_MultiView.m_IsInit = true;
// get the current view coordinates
RenderTools()->CalcScreenParams(Graphics()->ScreenAspect(), m_Camera.m_Zoom, &Width, &Height);
vec2 AxisX = vec2(m_Camera.m_Center.x - (Width / 2), m_Camera.m_Center.x + (Width / 2));
vec2 AxisY = vec2(m_Camera.m_Center.y - (Height / 2), m_Camera.m_Center.y + (Height / 2));
m_MultiView.m_Team = Team;
if(Team > 0)
return true; // spectating a team, not necessary to search the players in view
else
{
int Count = 0;
for(int i = 0; i < MAX_CLIENTS; i++)
{
vec2 PlayerPos;
// get the position of the player
if(m_Snap.m_aCharacters[i].m_Active)
PlayerPos = vec2(m_Snap.m_aCharacters[i].m_Cur.m_X, m_Snap.m_aCharacters[i].m_Cur.m_Y);
else if(m_aClients[i].m_Spec)
PlayerPos = m_aClients[i].m_SpecChar;
else
continue;
// player isnt in the correct team
if(m_Teams.Team(i) != Team)
continue;
if(PlayerPos.x != 0 && PlayerPos.y != 0)
{
// is the player in view
if(PlayerPos.x > AxisX.x && PlayerPos.x < AxisX.y && PlayerPos.y > AxisY.x && PlayerPos.y < AxisY.y)
{
m_aMultiViewId[i] = true;
Count++;
}
}
}
// we are spectating only one player
m_MultiView.m_Solo = Count == 1;
// found players to spectate
return Count > 0;
}
}
float CGameClient::CalculateMultiViewMultiplier(vec2 CameraPos)
{
float MaxCameraDist = 200.0f;
float MinCameraDist = 20.0f;
float MaxVel = 0.1f;
float MinVel = 0.007f;
float CurrentCameraDistance = distance(m_MultiView.m_OldPos, CameraPos);
return clamp(MapValue(MaxCameraDist, MinCameraDist, MaxVel, MinVel, CurrentCameraDistance), MinVel, 1.0f);
}
float CGameClient::CalculateMultiViewZoom(vec2 MinPos, vec2 MaxPos, float Vel)
{
float Ratio = Graphics()->ScreenAspect();
float ZoomX = 0.0f, ZoomY;
// only calc two axis if the aspect ratio is not 1:1
if(Ratio != 1.0f)
ZoomX = ZoomX = (0.001309f - 0.000328 * Ratio) * (MaxPos.x - MinPos.x) + (0.741413f - 0.032959 * Ratio);
// calculate the according zoom with linear function
ZoomY = ZoomY = 0.001309f * (MaxPos.y - MinPos.y) + 0.741413f;
// choose the highest zoom
float Zoom = std::max(ZoomX, ZoomY);
// zoom out to maximum 10 percent of the current zoom for 70 velocity
float Diff = clamp(MapValue(70.0f, 15.0f, Zoom * 0.10f, 0.0f, Vel), 0.0f, Zoom * 0.10f);
// zoom should stay inbetween 1.1 and 20.0
Zoom = clamp(Zoom + Diff, 1.1f, 20.0f);
// add the user preference
Zoom -= (Zoom * 0.075f) * m_MultiViewPersonalZoom;
m_MultiView.m_OldPersonalZoom = m_MultiViewPersonalZoom;
return Zoom;
}
float CGameClient::MapValue(float MaxValue, float MinValue, float MaxRange, float MinRange, float Value)
{
return (MaxRange - MinRange) / (MaxValue - MinValue) * (Value - MinValue) + MinRange;
}
void CGameClient::ResetMultiView()
{
m_MultiView.m_Solo = false;
m_MultiView.m_IsInit = false;
m_MultiViewActivated = false;
m_MultiViewPersonalZoom = 0;
}
void CGameClient::CleanMultiViewIds()
{
std::fill(std::begin(m_aMultiViewId), std::end(m_aMultiViewId), false);
std::fill(std::begin(m_MultiView.m_aLastFreeze), std::end(m_MultiView.m_aLastFreeze), 0.0f);
std::fill(std::begin(m_MultiView.m_aVanish), std::end(m_MultiView.m_aVanish), false);
}
bool CGameClient::IsMultiViewIdSet()
{
return std::any_of(std::begin(m_aMultiViewId), std::end(m_aMultiViewId), [](bool IsSet) { return IsSet; });
}

View file

@ -698,6 +698,11 @@ public:
const std::vector<CSnapEntities> &SnapEntities() { return m_vSnapEntities; } const std::vector<CSnapEntities> &SnapEntities() { return m_vSnapEntities; }
int m_MultiViewPersonalZoom;
bool m_MultiViewShowHud;
bool m_MultiViewActivated;
bool m_aMultiViewId[MAX_CLIENTS];
private: private:
std::vector<CSnapEntities> m_vSnapEntities; std::vector<CSnapEntities> m_vSnapEntities;
void SnapCollectEntities(); void SnapCollectEntities();
@ -726,6 +731,28 @@ private:
float m_LastZoom; float m_LastZoom;
float m_LastScreenAspect; float m_LastScreenAspect;
bool m_LastDummyConnected; bool m_LastDummyConnected;
void ResetMultiView();
void HandleMultiView();
bool IsMultiViewIdSet();
void CleanMultiViewIds();
bool InitMultiViewFromFreeview(int Team);
float CalculateMultiViewMultiplier(vec2 CameraPos);
float CalculateMultiViewZoom(vec2 MinPos, vec2 MaxPos, float Vel);
float MapValue(float MaxValue, float MinValue, float MaxRange, float MinRange, float Value);
struct SMultiView
{
bool m_Solo;
bool m_IsInit;
bool m_aVanish[MAX_CLIENTS];
vec2 m_OldPos;
int m_Team;
int m_OldPersonalZoom;
float m_aLastFreeze[MAX_CLIENTS];
};
SMultiView m_MultiView;
}; };
ColorRGBA CalculateNameColor(ColorHSLA TextColorHSL); ColorRGBA CalculateNameColor(ColorHSLA TextColorHSL);

View file

@ -90,6 +90,8 @@ MACRO_CONFIG_INT(ClDyncamFollowFactor, cl_dyncam_follow_factor, 60, 0, 200, CFGF
MACRO_CONFIG_INT(ClDyncamSmoothness, cl_dyncam_smoothness, 0, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Transition amount of the camera movement, 0=instant, 100=slow and smooth") MACRO_CONFIG_INT(ClDyncamSmoothness, cl_dyncam_smoothness, 0, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Transition amount of the camera movement, 0=instant, 100=slow and smooth")
MACRO_CONFIG_INT(ClDyncamStabilizing, cl_dyncam_stabilizing, 0, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Amount of camera slowdown during fast cursor movement. High value can cause delay in camera movement") MACRO_CONFIG_INT(ClDyncamStabilizing, cl_dyncam_stabilizing, 0, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Amount of camera slowdown during fast cursor movement. High value can cause delay in camera movement")
MACRO_CONFIG_INT(ClMultiViewZoomSmoothness, cl_multi_view_zoom_smoothness, 700, 0, 5000, CFGFLAG_CLIENT, "Set the smoothness of the multi view zoom (in ms, higher = slower)")
MACRO_CONFIG_INT(EdSmoothZoomTime, ed_smooth_zoom_time, 250, 0, 5000, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Time of smooth zoom animation in the editor in ms (0 for off)") MACRO_CONFIG_INT(EdSmoothZoomTime, ed_smooth_zoom_time, 250, 0, 5000, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Time of smooth zoom animation in the editor in ms (0 for off)")
MACRO_CONFIG_INT(EdLimitMaxZoomLevel, ed_limit_max_zoom_level, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Specifies, if zooming in the editor should be limited or not (0 = no limit)") MACRO_CONFIG_INT(EdLimitMaxZoomLevel, ed_limit_max_zoom_level, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Specifies, if zooming in the editor should be limited or not (0 = no limit)")
MACRO_CONFIG_INT(EdZoomTarget, ed_zoom_target, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Zoom to the current mouse target") MACRO_CONFIG_INT(EdZoomTarget, ed_zoom_target, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Zoom to the current mouse target")