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
32 changes: 32 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 marines in 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,9 @@ void C_ASW_Player::OnDataChanged( DataUpdateType_t updateType )
}
}

// send our rd_spectate_order to the server
set_rd_spectate_order();

// tell other players that we're fully connected
engine->ServerCmd( "cl_fullyjoined\n" );
}
Expand Down Expand Up @@ -2999,3 +3005,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_set_spectate_order \"%s\"\n", pNewValue );
engine->ClientCmd( szbuf );
}
}
140 changes: 139 additions & 1 deletion src/game/server/swarm/asw_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include "missionchooser/iasw_mission_chooser_source.h"
#include "rd_vgui_vscript_shared.h"
#include "rd_crafting_defs.h"
#include <algorithm>

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
Expand Down Expand Up @@ -2173,8 +2174,145 @@ HSCRIPT CASW_Player::ScriptGetNPC() const
return ToHScript( GetNPC() );
}

CON_COMMAND_F( rd_set_spectate_order, "Prioritize this marines in 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;
}

// Empty string
if ( nProfiles == 0 )
{
pPlayer->UnsetSpectatingOrder();
return;
}

pPlayer->SetSpectatingOrder( iProfiles, nProfiles );
}

// Can have some (not all) marine profiles; rest will have worst priority
void CASW_Player::SetSpectatingOrder( const int* iProfiles, int 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_iSpectatingPrioMapping[i] = INT_MAX;
}

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

m_bSpectatingInOrder = true;
}

bool CASW_Player::CompareSpectatingPriority( CASW_Marine* pM1, CASW_Marine* pM2 ) const
{
// Assign worst priority if no marine or no profile index
const int iProfile1 = pM1 ? pM1->GetMarineProfile()->m_ProfileIndex : INT_MAX;
const int iProfile2 = pM2 ? pM2->GetMarineProfile()->m_ProfileIndex : INT_MAX;

const int iPriority1 = ( iProfile1 >= 0 && iProfile1 < ASW_NUM_MARINE_PROFILES ) ?
m_iSpectatingPrioMapping[iProfile1] : INT_MAX;
const int iPriority2 = ( iProfile2 >= 0 && iProfile2 < ASW_NUM_MARINE_PROFILES ) ?
m_iSpectatingPrioMapping[iProfile2] : INT_MAX;

return iPriority1 < iPriority2;
}

void CASW_Player::SpectateNextMarineInOrder()
{
CASW_Game_Resource* pGameResource = ASWGameResource();
if ( !pGameResource )
return;

int nMarinesSorted = 0;
CASW_Marine* pMarinesSorted[ASW_MAX_MARINE_RESOURCES] = { NULL };

// Gather all valid marines
for ( int i = 0; i < pGameResource->GetMaxMarineResources(); i++ )
{
CASW_Marine_Resource* pMR = pGameResource->GetMarineResource( i );
CASW_Marine *pMarine = pMR ? pMR->GetMarineEntity() : NULL;
if ( pMarine && pMarine->IsAlive() && pMarine->GetHealth() > 0 )
{
pMarinesSorted[nMarinesSorted] = pMarine;
nMarinesSorted++;
}
}

std::sort(
pMarinesSorted,
pMarinesSorted + nMarinesSorted,
[this](CASW_Marine* a, CASW_Marine* b) {
return CompareSpectatingPriority(a, b);
}
);

//Msg("CASW_Player::SpectateNextMarineInOrder\n");

//Msg(" set first guy as our first\n");
CASW_Marine *pFirst = pMarinesSorted[0];

// loop through all valid marines
for ( int i = 0; i < nMarinesSorted; i++ )
{
//Msg("Checking pMR %d\n", i);
CASW_Marine *pMarine = pMarinesSorted[i]; // Alive marine

if (GetSpectatingNPC() == NULL) // if we're not spectating anything yet, then spectate the first one we find
{
//Msg(" We're not spectating anyone, so we're gonna spec this dude\n");
SetSpectatingNPC(pMarine);
break;
}
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);
}
}
//Msg("end\n");
// 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);
}
}

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

CASW_Game_Resource* pGameResource = ASWGameResource();
if (!pGameResource)
return;
Expand Down Expand Up @@ -2209,7 +2347,7 @@ void CASW_Player::SpectateNextMarine()
{
//Msg(" we're spectating this dude, so clearing our current spectator\n");
SetSpectatingNPC(NULL);
}
}
}
//Msg("end\n");
// 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
Expand Down
8 changes: 8 additions & 0 deletions src/game/server/swarm/asw_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ class CASW_Player : public CBaseMultiplayerPlayer, public IASWPlayerAnimStateHel
virtual bool ASWAnim_CanMove();

// spectating
private:
bool m_bSpectatingInOrder = false;
int m_iSpectatingPrioMapping[ASW_NUM_MARINE_PROFILES]; // profile -> priority; smaller number is higher priority
public:
void UnsetSpectatingOrder() { m_bSpectatingInOrder = false; }
void SetSpectatingOrder( const int* iProfiles, int nProfiles );
bool CompareSpectatingPriority( CASW_Marine* pM1, CASW_Marine* pM2 ) const;
void SpectateNextMarineInOrder();
void SpectateNextMarine();
void SetSpectatingNPC( CASW_Inhabitable_NPC *pSpectating );
CASW_Inhabitable_NPC *GetSpectatingNPC() 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