Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/game/client/swarm/c_asw_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ ConVar asw_turret_fog_end( "asw_turret_fog_end", "1200", 0, "Fog end distance fo

ConVar rd_force_spectate_marine( "rd_force_spectate_marine", "-1", FCVAR_DONTRECORD, "spectate this marine resource index if it exists", true, -1, true, ASW_MAX_MARINE_RESOURCES );

static void set_rd_spectate_order( IConVar *pConVar = NULL, const char *pOldValue = NULL, float flOldValue = 0.0f );
ConVar rd_spectate_order( "rd_spectate_order", "", FCVAR_ARCHIVE, "Prioritize this marine profiles for spectate order. Space separated list. Example: \"4 1 7 3 0\".", set_rd_spectate_order );

extern ConVar asw_allow_detach;
extern ConVar asw_stim_cam_time;
extern ConVar asw_rts_controls;
Expand Down Expand Up @@ -1516,6 +1519,12 @@ void C_ASW_Player::OnDataChanged( DataUpdateType_t updateType )
}
}

// send our rd_spectate_order to the server
if ( Q_strlen( rd_spectate_order.GetString() ) > 0 )
{
set_rd_spectate_order();
}

// tell other players that we're fully connected
engine->ServerCmd( "cl_fullyjoined\n" );
}
Expand Down Expand Up @@ -2999,3 +3008,29 @@ CSteamID C_ASW_Player::GetSteamID()
CSteamID invalid;
return invalid;
}

static void set_rd_spectate_order( IConVar *pConVar, const char *pOldValue, float flOldValue )
{
const char *pNewValue = pConVar ? ConVarRef( pConVar ).GetString() : rd_spectate_order.GetString();

int iProfiles[ASW_NUM_MARINE_PROFILES];
const int nProfiles = parseSpectateOrder( pNewValue, iProfiles );

// Failed parsing
if ( nProfiles == -1 )
{
if ( pConVar )
{
ConVarRef( pConVar ).SetValue( pOldValue );
}
return;
}

if ( engine->IsInGame() )
{
const int iBufLen = 30 + ASW_NUM_MARINE_PROFILES * 3;
char szbuf[iBufLen];
Q_snprintf( szbuf, iBufLen, "rd_spectate_order_set \"%s\"\n", pNewValue );
engine->ClientCmd( szbuf );
}
}
153 changes: 153 additions & 0 deletions src/game/server/swarm/asw_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2175,6 +2175,12 @@ HSCRIPT CASW_Player::ScriptGetNPC() const

void CASW_Player::SpectateNextMarine()
{
if ( m_bSpectatingInOrder )
{
SpectateNextMarineInOrder();
return;
}

CASW_Game_Resource* pGameResource = ASWGameResource();
if (!pGameResource)
return;
Expand Down Expand Up @@ -2220,6 +2226,153 @@ void CASW_Player::SpectateNextMarine()
}
}

void CASW_Player::SpectateNextMarineInOrder()
{
CASW_Game_Resource* pGameResource = ASWGameResource();
if (!pGameResource)
return;
//Msg("CASW_Player::SpectateNextMarineInOrder\n");

CASW_Marine *pFirst = NULL; // First with best priority or first and only seen
CASW_Marine *pBefore = CASW_Marine::AsMarine(GetSpectatingNPC()); // currently spectating
const int iBeforePrio = pBefore ? GetSpectatingPriority(pBefore) : 0; // when start (or when current died), go with first best
CASW_Marine *pNextBest = NULL; // Next in order after the one we just spectated

// loop through all valid marines
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
{
//Msg("Checking pMR %d\n", i);
CASW_Marine_Resource* pMR = pGameResource->GetMarineResource(i);
CASW_Marine *pMarine = pMR ? pMR->GetMarineEntity() : NULL;
const int iMarinePrio = GetSpectatingPriority(pMarine);

if (!pMarine || !pMarine->IsAlive() || pMarine->GetHealth() <= 0)
{
//Msg(" but he's dead\n");
continue;
}

// there can be multiple unprioritized marines, or multiple players can have same marine character
if (GetSpectatingNPC() == NULL && iMarinePrio == iBeforePrio)
{
//Msg(" We're not spectating anyone and this dude has same priority as current spectator, so we're gonna spec this dude\n");
SetSpectatingNPC(pMarine);
break;
}

// try to find marine with best priority for fallback
if (!pFirst || iMarinePrio < GetSpectatingPriority(pFirst))
{
//Msg(" set this (better) guy as our first\n");
pFirst = pMarine;
}

if (GetSpectatingNPC() == pMarine) // if we're spectating this one, then clear it, so the next one we find will get set
{
//Msg(" we're spectating this dude, so clearing our current spectator\n");
SetSpectatingNPC(NULL);
continue;
}

// Next perfect (iBeforePrio+1) can be dead, so check everyone after iBeforePrio
if (iMarinePrio > iBeforePrio && (!pNextBest || iMarinePrio < GetSpectatingPriority(pNextBest)))
{
//Msg(" remember this guy as next best choice in order\n");
pNextBest = pMarine;
}
}
//Msg("end\n");
// haven't found next marine with same priority, so try to set next best
if (GetSpectatingNPC() == NULL && pNextBest)
{
//Msg(" and spectate next best marine\n");
SetSpectatingNPC(pNextBest);
}
// if we're still not spectating anything but we found at least marine, then that means we were spectating the last one in the list and need to set this
if (GetSpectatingNPC() == NULL && pFirst)
{
//Msg(" but we're still not speccing anyone and we have a first set, so speccing that dude\n");
SetSpectatingNPC(pFirst);
}
}

// Can have some marine profiles; rest will have worst priority
void CASW_Player::SetSpectatingOrder( const int* iProfiles, int nProfiles )
{
if ( nProfiles < 0 )
{
Warning( "set spectating order: bad parsing?\n" );
return;
}

if ( nProfiles == 0 )
{
UnsetSpectatingOrder();
return;
}

nProfiles = MIN( nProfiles, ASW_NUM_MARINE_PROFILES );
m_iWorstPriority = nProfiles;

// Prefill with worst priority in case a profile is not in the list or is invalid
for ( int i = 0; i < ASW_NUM_MARINE_PROFILES; i++ )
{
m_iProfileToSpectatingPriority[i] = m_iWorstPriority;
}

for ( int i = 0; i < nProfiles; i++ )
{
const int iProfile = iProfiles[i];
if ( iProfile >= 0 && iProfile < ASW_NUM_MARINE_PROFILES )
{
m_iProfileToSpectatingPriority[iProfile] = i;
}
}

m_bSpectatingInOrder = true;
}

int CASW_Player::GetSpectatingPriority( CASW_Marine* pMarine ) const
{
const int iProfile = pMarine ? pMarine->GetMarineProfile()->m_ProfileIndex : -1;
return ( iProfile >= 0 && iProfile < ASW_NUM_MARINE_PROFILES )
? m_iProfileToSpectatingPriority[iProfile] : m_iWorstPriority;
}

CON_COMMAND_F( rd_spectate_order_set, "Prioritize this marine profiles for spectate order. Example: \"4 1 7 3 0\". \"\" for unset.", FCVAR_HIDDEN )
{
CASW_Player *pPlayer = ToASW_Player( UTIL_GetCommandClient() );
if ( !pPlayer )
{
Warning( "%s: Not a Player (not connected to a server?)\n", args[0] );
return;
}

if ( args.ArgC() != 2 )
{
Warning( "%s: Expected quoted space separated list. Example: \"4 1 7 3 0\". \"\" for unset.\n", args[0] );
return;
}

int iProfiles[ASW_NUM_MARINE_PROFILES];
const int nProfiles = parseSpectateOrder( args[1], iProfiles );

// Failed parsing
if ( nProfiles == -1 )
{
return;
}

pPlayer->SetSpectatingOrder( iProfiles, nProfiles );

// Start spectating "best" marine if we are spectating
if ( ASWGameRules()->GetGameState() == ASW_GS_INGAME && pPlayer->GetSpectatingNPC() != NULL )
{
pPlayer->SetSpectatingNPC( NULL );
pPlayer->SpectateNextMarine();
}
}

void CASW_Player::SetSpectatingNPC( CASW_Inhabitable_NPC *pSpectating )
{
unsigned int nOwnIndex = 1u << ( GetClientIndex() + 1 );
Expand Down
9 changes: 9 additions & 0 deletions src/game/server/swarm/asw_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,16 @@ class CASW_Player : public CBaseMultiplayerPlayer, public IASWPlayerAnimStateHel
virtual bool ASWAnim_CanMove();

// spectating
private:
bool m_bSpectatingInOrder = false;
int m_iWorstPriority = ASW_NUM_MARINE_PROFILES; // "unreachable" priority
int m_iProfileToSpectatingPriority[ASW_NUM_MARINE_PROFILES]; // smaller number is higher priority
public:
void SpectateNextMarine();
void SpectateNextMarineInOrder();
void SetSpectatingOrder( const int* iProfiles, int nProfiles );
void UnsetSpectatingOrder() { m_bSpectatingInOrder = false; }
int GetSpectatingPriority( CASW_Marine* pMarine ) const; // returns worst priority if no marine or no profile index
void SetSpectatingNPC( CASW_Inhabitable_NPC *pSpectating );
CASW_Inhabitable_NPC *GetSpectatingNPC() const;
HSCRIPT ScriptGetSpectatingNPC() const;
Expand Down
49 changes: 49 additions & 0 deletions src/game/shared/swarm/asw_player_shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1452,3 +1452,52 @@ DLL_EXPORT ASW_Controls_t GetASWControlsForPlayer( CASW_Player *pPlayer )

return pPlayer->GetASWControls();
}

int parseSpectateOrder( const char *szInput, int *iProfiles, const int nProfilesMax )
{
Assert( nProfilesMax > 0 && nProfilesMax <= ASW_NUM_MARINE_PROFILES );

if ( Q_strlen( szInput ) == 0 )
{
return 0;
}

CSplitString szProfiles( szInput, " " );
const int nProfiles = szProfiles.Count();

if ( nProfiles > nProfilesMax )
{
Warning( "parse specate order: Too many profiles (%d > %d)\n", nProfiles, nProfilesMax );
return -1;
}

for ( int i = 0; i < nProfiles; i++ )
{
const int iProfile = atoi( szProfiles[i] );

if ( iProfile == 0 && szProfiles[i][0] != '0' )
{
Warning( "parse specate order: Not a number (#%d: %s)\n", i + 1, szProfiles[i] );
return -1;
}

if ( iProfile < 0 || iProfile >= nProfilesMax )
{
Warning( "parse specate order: Incorrect profile number (#%d: %s)\n", i + 1, szProfiles[i] );
return -1;
}

for ( int j = 0; j < i; j++ )
{
if ( iProfiles[j] == iProfile )
{
Warning( "parse specate order: Duplicate profile number (#%d: %d)\n", i + 1, iProfile );
return -1;
}
}

iProfiles[i] = iProfile;
}

return nProfiles;
}
7 changes: 7 additions & 0 deletions src/game/shared/swarm/asw_player_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#pragma once
#endif

#include "asw_shareddefs.h"

// Shared header file for players
#if defined( CLIENT_DLL )
#define CASW_Player C_ASW_Player
Expand Down Expand Up @@ -57,4 +59,9 @@ enum CASW_Earned_XP_t
ASW_NUM_XP_TYPES
};

// Parse space separated profile numbers into iProfiles
// @returns Number of parsed profiles
// @returns -1 if parsing failed
int parseSpectateOrder( const char *szInput, int *iProfiles, const int nProfilesMax = ASW_NUM_MARINE_PROFILES );

#endif // ASW_PLAYER_SHARED_H
Loading