mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Merge #6663
6663: New spectate mode (multiview) r=def- a=Vy0x2 <!-- What is the motivation for the changes of this pull request? --> Adding a new spectator mode, that can spectate a team by moving the camera in the middle and zooming in/out Works with team 0 or any team, but you have to work with it a bit to get used to it. I tried to make it as intuitive as possible. Pr is as ready as it can get, please test it. ![screenshot_2023-06-30_19-17-42](https://github.com/ddnet/ddnet/assets/24738662/0446d568-d34b-4d14-8682-dd077f121e91) Youtube video:<a href="http://www.youtube.com/watch?feature=player_embedded&v=7GM6DA3EYAI" target="_blank"> <img src="http://img.youtube.com/vi/7GM6DA3EYAI/maxresdefault.jpg" alt="Watch the video" width="192" height="108"/> </a> <!-- Note that builds and other checks will be run for your change. Don't feel intimidated by failures in some of the checks. If you can't resolve them yourself, experienced devs can also resolve them before merging your pull request. --> ## Checklist - [x] Tested the change ingame - [x] Provided screenshots if it is a visual change - [x] Tested in combination with possibly related configuration options - [ ] Written a unit test (especially base/) or added coverage to integration test - [x] Considered possible null pointers and out of bounds array indexing - [x] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: devdenn <denispaul43@gmail.com> Co-authored-by: Vy0x2 <denispaul43@gmail.com>
This commit is contained in:
commit
6dcb2d1828
|
@ -31,7 +31,7 @@ float CCamera::ZoomProgress(float CurrentTime) const
|
|||
void CCamera::ScaleZoom(float Factor)
|
||||
{
|
||||
float CurrentTarget = m_Zooming ? m_ZoomSmoothingTarget : m_Zoom;
|
||||
ChangeZoom(CurrentTarget * Factor);
|
||||
ChangeZoom(CurrentTarget * Factor, g_Config.m_ClSmoothZoomTime);
|
||||
}
|
||||
|
||||
float CCamera::MaxZoomLevel()
|
||||
|
@ -44,7 +44,7 @@ float CCamera::MinZoomLevel()
|
|||
return 0.01f;
|
||||
}
|
||||
|
||||
void CCamera::ChangeZoom(float Target)
|
||||
void CCamera::ChangeZoom(float Target, int Smoothness)
|
||||
{
|
||||
if(Target > MaxZoomLevel() || Target < MinZoomLevel())
|
||||
{
|
||||
|
@ -64,7 +64,7 @@ void CCamera::ChangeZoom(float Target)
|
|||
m_ZoomSmoothingTarget = Target;
|
||||
m_ZoomSmoothing = CCubicBezier::With(Current, Derivative, 0, m_ZoomSmoothingTarget);
|
||||
m_ZoomSmoothingStart = Now;
|
||||
m_ZoomSmoothingEnd = Now + (float)g_Config.m_ClSmoothZoomTime / 1000;
|
||||
m_ZoomSmoothingEnd = Now + (float)Smoothness / 1000;
|
||||
|
||||
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)
|
||||
{
|
||||
pSelf->ScaleZoom(ZoomStep);
|
||||
|
||||
if(pSelf->GameClient()->m_MultiViewActivated)
|
||||
pSelf->GameClient()->m_MultiViewPersonalZoom++;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
pSelf->ScaleZoom(1 / ZoomStep);
|
||||
|
||||
if(pSelf->GameClient()->m_MultiViewActivated)
|
||||
pSelf->GameClient()->m_MultiViewPersonalZoom--;
|
||||
}
|
||||
}
|
||||
void CCamera::ConZoom(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CCamera *pSelf = (CCamera *)pUserData;
|
||||
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)
|
||||
{
|
||||
|
@ -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(1) * 32.0f, 200.0f, pSelf->Collision()->GetWidth() * 32 - 200.0f));
|
||||
}
|
||||
|
||||
void CCamera::SetZoom(float Target, int Smoothness)
|
||||
{
|
||||
ChangeZoom(Target, Smoothness);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ class CCamera : public CComponent
|
|||
float m_ZoomSmoothingEnd;
|
||||
|
||||
void ScaleZoom(float Factor);
|
||||
void ChangeZoom(float Target);
|
||||
void ChangeZoom(float Target, int Smoothness);
|
||||
float ZoomProgress(float CurrentTime) const;
|
||||
|
||||
float MinZoomLevel();
|
||||
|
@ -52,6 +52,8 @@ public:
|
|||
virtual void OnConsoleInit() override;
|
||||
virtual void OnReset() override;
|
||||
|
||||
void SetZoom(float Target, int Smoothness);
|
||||
|
||||
private:
|
||||
static void ConZoomPlus(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConZoomMinus(IConsole::IResult *pResult, void *pUserData);
|
||||
|
|
|
@ -106,7 +106,7 @@ void CHud::RenderGameTimer()
|
|||
}
|
||||
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();
|
||||
}
|
||||
else
|
||||
|
@ -1474,7 +1474,7 @@ void CHud::RenderSpectatorHud()
|
|||
|
||||
// draw the text
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1527,7 +1527,11 @@ void CHud::OnRender()
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -149,6 +149,17 @@ void CSpectator::ConSpectateClosest(IConsole::IResult *pResult, void *pUserData)
|
|||
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()
|
||||
{
|
||||
OnReset();
|
||||
|
@ -162,6 +173,7 @@ void CSpectator::OnConsoleInit()
|
|||
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_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)
|
||||
|
@ -186,7 +198,12 @@ void CSpectator::OnRender()
|
|||
if(m_WasActive)
|
||||
{
|
||||
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;
|
||||
}
|
||||
return;
|
||||
|
@ -213,6 +230,7 @@ void CSpectator::OnRender()
|
|||
float TeeSizeMod = 1.0f;
|
||||
float RoundRadius = 30.0f;
|
||||
bool Selected = false;
|
||||
bool MultiViewSelected = false;
|
||||
int TotalPlayers = 0;
|
||||
int PerLine = 8;
|
||||
float BoxMove = -10.0f;
|
||||
|
@ -253,34 +271,48 @@ void CSpectator::OnRender()
|
|||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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_SelectedSpectatorID = SPEC_FREEVIEW;
|
||||
Selected = true;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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_SelectedSpectatorID = SPEC_FOLLOW;
|
||||
Selected = true;
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -10,6 +10,7 @@ class CSpectator : public CComponent
|
|||
{
|
||||
enum
|
||||
{
|
||||
MULTI_VIEW = -4,
|
||||
NO_SELECTION = -3,
|
||||
};
|
||||
|
||||
|
@ -30,6 +31,7 @@ class CSpectator : public CComponent
|
|||
static void ConSpectateNext(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConSpectatePrevious(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConSpectateClosest(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConMultiView(IConsole::IResult *pResult, void *pUserData);
|
||||
|
||||
public:
|
||||
CSpectator();
|
||||
|
|
|
@ -604,7 +604,11 @@ void CGameClient::UpdatePositions()
|
|||
// spectator position
|
||||
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(
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
UpdatePositions();
|
||||
|
||||
|
@ -843,6 +864,18 @@ void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker, int Conn, bool Dumm
|
|||
pChar->ResetPrediction();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1668,7 +1701,17 @@ void CGameClient::OnNewSnapshot()
|
|||
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;
|
||||
|
@ -3351,3 +3394,214 @@ void CGameClient::SnapCollectEntities()
|
|||
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 = (0.001309f - 0.000328 * Ratio) * (MaxPos.x - MinPos.x) + (0.741413f - 0.032959 * Ratio);
|
||||
|
||||
// calculate the according zoom with linear function
|
||||
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; });
|
||||
}
|
||||
|
|
|
@ -698,6 +698,11 @@ public:
|
|||
|
||||
const std::vector<CSnapEntities> &SnapEntities() { return m_vSnapEntities; }
|
||||
|
||||
int m_MultiViewPersonalZoom;
|
||||
bool m_MultiViewShowHud;
|
||||
bool m_MultiViewActivated;
|
||||
bool m_aMultiViewId[MAX_CLIENTS];
|
||||
|
||||
private:
|
||||
std::vector<CSnapEntities> m_vSnapEntities;
|
||||
void SnapCollectEntities();
|
||||
|
@ -726,6 +731,28 @@ private:
|
|||
float m_LastZoom;
|
||||
float m_LastScreenAspect;
|
||||
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);
|
||||
|
|
|
@ -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(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, 1300, 0, 5000, CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Set the smoothness of the multi view zoom (in ms, higher = slower)")
|
||||
|
||||
MACRO_CONFIG_INT(EdAutosaveInterval, ed_autosave_interval, 10, 0, 240, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Interval in minutes at which a copy of the current editor map is automatically saved to the 'auto' folder (0 for off)")
|
||||
MACRO_CONFIG_INT(EdAutosaveMax, ed_autosave_max, 10, 0, 1000, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Maximum number of autosaves that are kept per map name (0 = no limit)")
|
||||
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)")
|
||||
|
|
Loading…
Reference in a new issue