source-engine/game/server/stargate/multi_gate.cpp
Deploy Bot 44cb9a4837 StargateTC port — initial integration into nillerusr/source-engine
P1: Source files
- Copy 9 server stargate files + sg_constants.h + sg_weapons.cpp from
  StargateTCSource/sourcecode/ into game/server/stargate/ and
  game/shared/stargate/.
- Add $Folder "Stargate" block to server_hl2mp.vpc and client_hl2mp.vpc.
- Extend $AdditionalIncludeDirectories with stargate paths.

P2/P3: Gamerules + player patches
- hl2mp_gamerules.h: add TEAM_JAFFA=4, TEAM_UNAS=5 to team enum.
- hl2mp_gamerules.cpp: preserve 5 Stargate spawn classnames in
  s_PreserveEnts[]; rename teams to Tau'ri/Goa'uld/Jaffa/Unas;
  rename game description to "StargateTC"/"StargateTC Teamplay".
- hl2mp_player.cpp: EntSelectSpawnPoint now searches Stargate spawn
  classnames first (torri/goauld/marine/unas) with per-team fallback
  chain to combine/rebel/deathmatch.

Build: 0 errors. Server.dll 13.19MB, client.dll 9.85MB.
Runtime test: window title "StargateTC", live game launches,
HEALTH 100 HUD renders, HL2MP MOTD shown, sky/world geometry renders.
Response system loads 217 rules without crashing (was the original
Mapbase+VS2022 heap-corruption point).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 14:08:44 +01:00

214 lines
6.0 KiB
C++

//============= Copyright (c) StargateTC Source Team ==========================
//
// Source-side port of the original GoldSrc multi_gate_1..multi_gate_49 family.
//
// Ghidra shows every multi_gate_N as a 0x248-byte CMultigateN object with the
// same ManagerUse/ManagerThink loop as CMultiManager plus a small baked target
// table. The first gate contains chevronsnd, dial1change1, dhdglyph1_on, and
// ch1relayon at zero delay; later gates follow the same naming convention.
//
//=============================================================================
#include "cbase.h"
#include "eventqueue.h"
#include "utlvector.h"
namespace
{
constexpr int kMaxGateTargets = 16;
struct GateTarget
{
string_t targetName;
float delay;
};
}
class CStargateMultiGate : public CBaseEntity
{
public:
DECLARE_CLASS( CStargateMultiGate, CBaseEntity );
DECLARE_DATADESC();
explicit CStargateMultiGate( int gateIndex = 0 )
: m_iGateIndex( gateIndex ),
m_iCurrentTarget( 0 ),
m_flStartTime( 0.0f )
{
}
bool KeyValue( const char *szKeyName, const char *szValue ) override;
void Spawn() override;
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) override;
void ManagerThink();
protected:
void AddTarget( const char *pszTarget, float flDelay );
void AddDefaultTargets();
private:
CUtlVector<GateTarget> m_Targets;
int m_iGateIndex;
int m_iCurrentTarget;
float m_flStartTime;
EHANDLE m_hActivator;
};
BEGIN_DATADESC( CStargateMultiGate )
DEFINE_FIELD( m_iGateIndex, FIELD_INTEGER ),
DEFINE_FIELD( m_iCurrentTarget, FIELD_INTEGER ),
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
DEFINE_THINKFUNC( ManagerThink ),
END_DATADESC()
bool CStargateMultiGate::KeyValue( const char *szKeyName, const char *szValue )
{
if ( BaseClass::KeyValue( szKeyName, szValue ) )
return true;
if ( m_Targets.Count() < kMaxGateTargets && szValue && *szValue )
{
char *pEnd = NULL;
const float flDelay = static_cast<float>( strtod( szValue, &pEnd ) );
if ( pEnd != szValue )
{
AddTarget( szKeyName, flDelay );
return true;
}
}
return BaseClass::KeyValue( szKeyName, szValue );
}
void CStargateMultiGate::Spawn()
{
BaseClass::Spawn();
SetSolid( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
SetUse( &CStargateMultiGate::Use );
if ( m_Targets.Count() == 0 )
AddDefaultTargets();
}
void CStargateMultiGate::Use( CBaseEntity *pActivator, CBaseEntity *, USE_TYPE, float )
{
if ( m_Targets.Count() == 0 )
return;
m_hActivator = pActivator;
m_iCurrentTarget = 0;
m_flStartTime = gpGlobals->curtime;
SetThink( &CStargateMultiGate::ManagerThink );
SetNextThink( gpGlobals->curtime );
}
void CStargateMultiGate::ManagerThink()
{
const float flElapsed = gpGlobals->curtime - m_flStartTime;
while ( m_iCurrentTarget < m_Targets.Count() && m_Targets[m_iCurrentTarget].delay <= flElapsed )
{
const char *pszTarget = STRING( m_Targets[m_iCurrentTarget].targetName );
variant_t emptyVariant;
g_EventQueue.AddEvent( pszTarget, "Use", emptyVariant, 0.0f, m_hActivator.Get(), this );
++m_iCurrentTarget;
}
if ( m_iCurrentTarget < m_Targets.Count() )
{
SetNextThink( m_flStartTime + m_Targets[m_iCurrentTarget].delay );
return;
}
SetThink( NULL );
}
void CStargateMultiGate::AddTarget( const char *pszTarget, float flDelay )
{
if ( !pszTarget || !*pszTarget || m_Targets.Count() >= kMaxGateTargets )
return;
GateTarget &target = m_Targets[m_Targets.AddToTail()];
target.targetName = AllocPooledString( pszTarget );
target.delay = flDelay;
}
void CStargateMultiGate::AddDefaultTargets()
{
if ( m_iGateIndex <= 0 )
return;
char targetName[64];
AddTarget( "chevronsnd", 0.0f );
Q_snprintf( targetName, sizeof( targetName ), "dial%dchange1", m_iGateIndex );
AddTarget( targetName, 0.0f );
Q_snprintf( targetName, sizeof( targetName ), "dhdglyph%d_on", m_iGateIndex );
AddTarget( targetName, 0.0f );
Q_snprintf( targetName, sizeof( targetName ), "ch%drelayon", m_iGateIndex );
AddTarget( targetName, 0.0f );
}
#define LINK_MULTIGATE_ENTITY( index ) \
class CStargateMultiGate##index : public CStargateMultiGate \
{ \
public: \
DECLARE_CLASS( CStargateMultiGate##index, CStargateMultiGate ); \
CStargateMultiGate##index() : BaseClass( index ) {} \
}; \
LINK_ENTITY_TO_CLASS( multi_gate_##index, CStargateMultiGate##index )
LINK_MULTIGATE_ENTITY( 1 );
LINK_MULTIGATE_ENTITY( 2 );
LINK_MULTIGATE_ENTITY( 3 );
LINK_MULTIGATE_ENTITY( 4 );
LINK_MULTIGATE_ENTITY( 5 );
LINK_MULTIGATE_ENTITY( 6 );
LINK_MULTIGATE_ENTITY( 7 );
LINK_MULTIGATE_ENTITY( 8 );
LINK_MULTIGATE_ENTITY( 9 );
LINK_MULTIGATE_ENTITY( 10 );
LINK_MULTIGATE_ENTITY( 11 );
LINK_MULTIGATE_ENTITY( 12 );
LINK_MULTIGATE_ENTITY( 13 );
LINK_MULTIGATE_ENTITY( 14 );
LINK_MULTIGATE_ENTITY( 15 );
LINK_MULTIGATE_ENTITY( 16 );
LINK_MULTIGATE_ENTITY( 17 );
LINK_MULTIGATE_ENTITY( 18 );
LINK_MULTIGATE_ENTITY( 19 );
LINK_MULTIGATE_ENTITY( 20 );
LINK_MULTIGATE_ENTITY( 21 );
LINK_MULTIGATE_ENTITY( 22 );
LINK_MULTIGATE_ENTITY( 23 );
LINK_MULTIGATE_ENTITY( 24 );
LINK_MULTIGATE_ENTITY( 25 );
LINK_MULTIGATE_ENTITY( 26 );
LINK_MULTIGATE_ENTITY( 27 );
LINK_MULTIGATE_ENTITY( 28 );
LINK_MULTIGATE_ENTITY( 29 );
LINK_MULTIGATE_ENTITY( 30 );
LINK_MULTIGATE_ENTITY( 31 );
LINK_MULTIGATE_ENTITY( 32 );
LINK_MULTIGATE_ENTITY( 33 );
LINK_MULTIGATE_ENTITY( 34 );
LINK_MULTIGATE_ENTITY( 35 );
LINK_MULTIGATE_ENTITY( 36 );
LINK_MULTIGATE_ENTITY( 37 );
LINK_MULTIGATE_ENTITY( 38 );
LINK_MULTIGATE_ENTITY( 39 );
LINK_MULTIGATE_ENTITY( 40 );
LINK_MULTIGATE_ENTITY( 41 );
LINK_MULTIGATE_ENTITY( 42 );
LINK_MULTIGATE_ENTITY( 43 );
LINK_MULTIGATE_ENTITY( 44 );
LINK_MULTIGATE_ENTITY( 45 );
LINK_MULTIGATE_ENTITY( 46 );
LINK_MULTIGATE_ENTITY( 47 );
LINK_MULTIGATE_ENTITY( 48 );
LINK_MULTIGATE_ENTITY( 49 );