diff --git a/src/game/client/client_hl2mp-2005.vcxproj b/src/game/client/client_hl2mp-2005.vcxproj index 7fd9da5..a755ace 100644 --- a/src/game/client/client_hl2mp-2005.vcxproj +++ b/src/game/client/client_hl2mp-2005.vcxproj @@ -225,6 +225,7 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client.pdb "c:\program file + @@ -268,6 +269,7 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client.pdb "c:\program file + @@ -896,6 +898,7 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client.pdb "c:\program file + diff --git a/src/game/client/client_hl2mp-2005.vcxproj.filters b/src/game/client/client_hl2mp-2005.vcxproj.filters index 1953786..c360156 100644 --- a/src/game/client/client_hl2mp-2005.vcxproj.filters +++ b/src/game/client/client_hl2mp-2005.vcxproj.filters @@ -996,9 +996,6 @@ Source Files\Temporary Entities - - Source Files\Temporary Entities - Source Files\Temporary Entities @@ -1644,6 +1641,15 @@ Source Files\Contingency\UI + + Source Files\Contingency\Modified Engine Files + + + Source Files\Contingency\Weapons + + + Source Files\Contingency\Weapons + @@ -3503,6 +3509,9 @@ Source Files\Contingency\UI + + Source Files\Contingency\Weapons + diff --git a/src/game/client/contingency/c_contingency_spawnableprop.cpp b/src/game/client/contingency/c_contingency_spawnableprop.cpp index 18136bf..30622d0 100644 --- a/src/game/client/contingency/c_contingency_spawnableprop.cpp +++ b/src/game/client/contingency/c_contingency_spawnableprop.cpp @@ -10,7 +10,6 @@ IMPLEMENT_CLIENTCLASS_DT( C_Contingency_SpawnableProp, DT_Contingency_SpawnableProp, CContingency_SpawnableProp ) RecvPropEHandle( RECVINFO(m_hSpawnerPlayer) ), - RecvPropBool( RECVINFO(m_bIsFrozen) ), END_RECV_TABLE() CUtlVector m_SpawnablePropList = NULL; @@ -30,25 +29,54 @@ const char* C_Contingency_SpawnableProp::GetOwnerDisplay( void ) { C_Contingency_Player *pPlayer = ToContingencyPlayer( GetSpawnerPlayer() ); if ( !pPlayer ) + return "Orphaned Prop"; + + if ( pPlayer == C_Contingency_Player::GetLocalContingencyPlayer() ) + return "Your Prop"; + + char szOwnerDisplay[256]; + Q_snprintf( szOwnerDisplay, sizeof(szOwnerDisplay), "%s's Prop", pPlayer->GetPlayerName() ); + return szOwnerDisplay; +} + +const char* C_Contingency_SpawnableProp::GetHealthCondition( void ) +{ + if ( m_iHealth <= 0 ) + return "DESTROYED"; + + float ratio = ((float)m_iHealth) / ((float)m_iMaxHealth); + if ( (ratio <= 1.00) && (ratio >= 0.00) ) { - if ( m_bIsFrozen ) - return "Orphaned Prop"; - else - return "Orphaned Prop (Unfrozen)"; + if ( ratio >= 0.75 ) + return "Structurally Sound"; + else if ( ratio >= 0.50 ) + return "Damaged"; + else if ( ratio >= 0.25 ) + return "Severely Damaged"; + else if ( ratio < 0.25 ) + return "Critically Damaged"; } - if ( pPlayer == C_Contingency_Player::GetLocalContingencyPlayer() ) + return "Condition Unknown"; +} + +Color C_Contingency_SpawnableProp::GetHealthConditionColor( void ) +{ + if ( m_iHealth <= 0 ) + return Color( 204, 0, 0, 255 ); // dark(er) red + + float ratio = ((float)m_iHealth) / ((float)m_iMaxHealth); + if ( (ratio <= 1.00) && (ratio >= 0.00) ) { - if ( m_bIsFrozen ) - return "Your Prop"; - else - return "Your Prop (Unfrozen)"; + if ( ratio >= 0.75 ) + return Color( 0, 255, 0, 255 ); // green + else if ( ratio >= 0.50 ) + return Color( 255, 204, 0, 255 ); // yellow + else if ( ratio >= 0.25 ) + return Color( 255, 153, 0, 255 ); // orange + else if ( ratio < 0.25 ) + return Color( 255, 0, 0, 255 ); // red } - char szOwnerDisplay[256]; - if ( m_bIsFrozen ) - Q_snprintf( szOwnerDisplay, sizeof(szOwnerDisplay), "%s's Prop", pPlayer->GetPlayerName() ); - else - Q_snprintf( szOwnerDisplay, sizeof(szOwnerDisplay), "%s's Prop (Unfrozen)", pPlayer->GetPlayerName() ); - return szOwnerDisplay; + return Color( 255, 255, 255, 255 ); // white } diff --git a/src/game/client/contingency/c_contingency_spawnableprop.h b/src/game/client/contingency/c_contingency_spawnableprop.h index ec88dfc..3914ccf 100644 --- a/src/game/client/contingency/c_contingency_spawnableprop.h +++ b/src/game/client/contingency/c_contingency_spawnableprop.h @@ -4,28 +4,25 @@ #define C_CONTINGENCY_SPAWNABLEPROP_H #pragma once -#include "cbase.h" -#include "c_physicsprop.h" +#include "c_ai_basenpc.h" -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -class C_Contingency_SpawnableProp : public C_PhysicsProp +class C_Contingency_SpawnableProp : public C_AI_BaseNPC { public: - DECLARE_CLASS( C_Contingency_SpawnableProp, C_PhysicsProp ); + DECLARE_CLASS( C_Contingency_SpawnableProp, C_AI_BaseNPC ); DECLARE_CLIENTCLASS(); C_Contingency_SpawnableProp(); virtual ~C_Contingency_SpawnableProp(); const char* GetOwnerDisplay( void ); + const char* GetHealthCondition( void ); + Color GetHealthConditionColor( void ); CBasePlayer *GetSpawnerPlayer( void ) { return m_hSpawnerPlayer; } private: CHandle m_hSpawnerPlayer; - bool m_bIsFrozen; C_Contingency_SpawnableProp( const C_Contingency_SpawnableProp & ); // not defined, not accessible }; diff --git a/src/game/client/contingency/c_weapon_hopwire.cpp b/src/game/client/contingency/c_weapon_hopwire.cpp new file mode 100644 index 0000000..f31366d --- /dev/null +++ b/src/game/client/contingency/c_weapon_hopwire.cpp @@ -0,0 +1,422 @@ +// Ported directly from Lethal Stigma, with a few adjustments + +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "basegrenade_shared.h" +#include "fx_interpvalue.h" +#include "fx_envelope.h" +#include "materialsystem/imaterialvar.h" +#include "particles_simple.h" +#include "particles_attractor.h" + +// FIXME: Move out +extern void DrawSpriteTangentSpace( const Vector &vecOrigin, float flWidth, float flHeight, color32 color ); + +#define EXPLOSION_DURATION 3.0f + +//----------------------------------------------------------------------------- +// Explosion effect for hopwire +//----------------------------------------------------------------------------- + +class C_HopwireExplosion : public C_EnvelopeFX +{ + typedef C_EnvelopeFX BaseClass; + +public: + C_HopwireExplosion( void ) : + m_hOwner( NULL ) + { + m_FXCoreScale.SetAbsolute( 0.0f ); + m_FXCoreAlpha.SetAbsolute( 0.0f ); + } + + virtual void Update( void ); + virtual int DrawModel( int flags ); + virtual void GetRenderBounds( Vector& mins, Vector& maxs ); + + bool SetupEmitters( void ); + void AddParticles( void ); + void SetOwner( C_BaseEntity *pOwner ); + void StartExplosion( void ); + void StopExplosion( void ); + void StartPreExplosion( void ); + +private: + CInterpolatedValue m_FXCoreScale; + CInterpolatedValue m_FXCoreAlpha; + + CSmartPtr m_pSimpleEmitter; + CSmartPtr m_pAttractorEmitter; + + TimedEvent m_ParticleTimer; + + CHandle m_hOwner; +}; + +//----------------------------------------------------------------------------- +// Purpose: Setup the emitters we'll be using +//----------------------------------------------------------------------------- +bool C_HopwireExplosion::SetupEmitters( void ) +{ + // Setup the basic core emitter + if ( m_pSimpleEmitter.IsValid() == false ) + { + m_pSimpleEmitter = CSimpleEmitter::Create( "hopwirecore" ); + + if ( m_pSimpleEmitter.IsValid() == false ) + return false; + } + + // Setup the attractor emitter + if ( m_pAttractorEmitter.IsValid() == false ) + { + m_pAttractorEmitter = CParticleAttractor::Create( GetRenderOrigin(), "hopwireattractor" ); + + if ( m_pAttractorEmitter.IsValid() == false ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_HopwireExplosion::AddParticles( void ) +{ + // Make sure the emitters are setup properly + if ( SetupEmitters() == false ) + return; + + float tempDelta = gpGlobals->frametime; + while( m_ParticleTimer.NextEvent( tempDelta ) ) + { + // ======================== + // Attracted dust particles + // ======================== + + // Update our attractor point + m_pAttractorEmitter->SetAttractorOrigin( GetRenderOrigin() ); + + Vector offset; + SimpleParticle *sParticle; + + offset = GetRenderOrigin() + RandomVector( -256.0f, 256.0f ); + + sParticle = (SimpleParticle *) m_pAttractorEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_Fleck_Cement[0], offset ); + + if ( sParticle == NULL ) + return; + + sParticle->m_vecVelocity = Vector(0,0,8); + sParticle->m_flDieTime = 0.5f; + sParticle->m_flLifetime = 0.0f; + + sParticle->m_flRoll = Helper_RandomInt( 0, 360 ); + sParticle->m_flRollDelta = 1.0f; + + float alpha = random->RandomFloat( 128.0f, 200.0f ); + + sParticle->m_uchColor[0] = alpha; + sParticle->m_uchColor[1] = alpha; + sParticle->m_uchColor[2] = alpha; + sParticle->m_uchStartAlpha = alpha; + sParticle->m_uchEndAlpha = alpha; + + sParticle->m_uchStartSize = random->RandomInt( 1, 4 ); + sParticle->m_uchEndSize = 0; + + // ======================== + // Core effects + // ======================== + + // Reset our sort origin + m_pSimpleEmitter->SetSortOrigin( GetRenderOrigin() ); + + // Base of the core effect + sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), m_pSimpleEmitter->GetPMaterial( "effects/strider_muzzle" ), GetRenderOrigin() ); + + if ( sParticle == NULL ) + return; + + sParticle->m_vecVelocity = vec3_origin; + sParticle->m_flDieTime = 0.2f; + sParticle->m_flLifetime = 0.0f; + + sParticle->m_flRoll = Helper_RandomInt( 0, 360 ); + sParticle->m_flRollDelta = 4.0f; + + alpha = random->RandomInt( 32, 200 ); + + sParticle->m_uchColor[0] = alpha; + sParticle->m_uchColor[1] = alpha; + sParticle->m_uchColor[2] = alpha; + sParticle->m_uchStartAlpha = 0; + sParticle->m_uchEndAlpha = alpha; + + sParticle->m_uchStartSize = 255; + sParticle->m_uchEndSize = 0; + + // Make sure we encompass the complete particle here! + m_pSimpleEmitter->SetParticleCullRadius( sParticle->m_uchEndSize ); + + // ========================= + // Dust ring effect + // ========================= + + if ( random->RandomInt( 0, 5 ) != 1 ) + return; + + Vector vecDustColor; + vecDustColor.x = 0.35f; + vecDustColor.y = 0.3f; + vecDustColor.z = 0.25f; + + Vector color; + + int numRingSprites = 8; + float yaw; + Vector forward, vRight, vForward; + + vForward = Vector( 0, 1, 0 ); + vRight = Vector( 1, 0, 0 ); + + float yawOfs = random->RandomFloat( 0, 359 ); + + for ( int i = 0; i < numRingSprites; i++ ) + { + yaw = ( (float) i / (float) numRingSprites ) * 360.0f; + yaw += yawOfs; + + forward = ( vRight * sin( DEG2RAD( yaw) ) ) + ( vForward * cos( DEG2RAD( yaw ) ) ); + VectorNormalize( forward ); + + trace_t tr; + + UTIL_TraceLine( GetRenderOrigin(), GetRenderOrigin()+(Vector(0, 0, -1024)), MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); + + offset = ( RandomVector( -4.0f, 4.0f ) + tr.endpos ) + ( forward * 512.0f ); + + sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[random->RandomInt(0,1)], offset ); + + if ( sParticle != NULL ) + { + sParticle->m_flLifetime = 0.0f; + sParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f ); + + sParticle->m_vecVelocity = forward * -random->RandomFloat( 1000, 1500 ); + sParticle->m_vecVelocity[2] += 128.0f; + + #if __EXPLOSION_DEBUG + debugoverlay->AddLineOverlay( m_vecOrigin, m_vecOrigin + sParticle->m_vecVelocity, 255, 0, 0, false, 3 ); + #endif + + sParticle->m_uchColor[0] = vecDustColor.x * 255.0f; + sParticle->m_uchColor[1] = vecDustColor.y * 255.0f; + sParticle->m_uchColor[2] = vecDustColor.z * 255.0f; + + sParticle->m_uchStartSize = random->RandomInt( 32, 128 ); + sParticle->m_uchEndSize = 200; + + sParticle->m_uchStartAlpha = random->RandomFloat( 16, 64 ); + sParticle->m_uchEndAlpha = 0; + + sParticle->m_flRoll = random->RandomInt( 0, 360 ); + sParticle->m_flRollDelta = random->RandomFloat( -16.0f, 16.0f ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOwner - +//----------------------------------------------------------------------------- +void C_HopwireExplosion::SetOwner( C_BaseEntity *pOwner ) +{ + m_hOwner = pOwner; +} + +//----------------------------------------------------------------------------- +// Purpose: Updates the internal values for the effect +//----------------------------------------------------------------------------- +void C_HopwireExplosion::Update( void ) +{ + if ( m_hOwner ) + { + SetRenderOrigin( m_hOwner->GetRenderOrigin() ); + } + + BaseClass::Update(); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates and renders all effects +//----------------------------------------------------------------------------- +int C_HopwireExplosion::DrawModel( int flags ) +{ + AddParticles(); + + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->Flush(); + UpdateRefractTexture(); + + IMaterial *pMat = materials->FindMaterial( "effects/strider_pinch_dudv", TEXTURE_GROUP_CLIENT_EFFECTS ); + + float refract = m_FXCoreAlpha.Interp( gpGlobals->curtime ); + float scale = m_FXCoreScale.Interp( gpGlobals->curtime ); + + IMaterialVar *pVar = pMat->FindVar( "$refractamount", NULL ); + pVar->SetFloatValue( refract ); + + pRenderContext->Bind( pMat, (IClientRenderable*)this ); + + float sin1 = sinf( gpGlobals->curtime * 10 ); + float sin2 = sinf( gpGlobals->curtime ); + + float scaleY = ( sin1 * sin2 ) * 32.0f; + float scaleX = (sin2 * sin2) * 32.0f; + + // FIXME: The ball needs to sort properly at all times + static color32 white = {255,255,255,255}; + DrawSpriteTangentSpace( GetRenderOrigin() + ( CurrentViewForward() * 128.0f ), scale+scaleX, scale+scaleY, white ); + + return 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the bounds relative to the origin (render bounds) +//----------------------------------------------------------------------------- +void C_HopwireExplosion::GetRenderBounds( Vector& mins, Vector& maxs ) +{ + float scale = m_FXCoreScale.Interp( gpGlobals->curtime ); + + mins.Init( -scale, -scale, -scale ); + maxs.Init( scale, scale, scale ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_HopwireExplosion::StartExplosion( void ) +{ + m_FXCoreScale.Init( 300.0f, 500.0f, 2.0f, INTERP_SPLINE ); + m_FXCoreAlpha.Init( 0.0f, 0.1f, 1.5f, INTERP_SPLINE ); + + // Particle timer + m_ParticleTimer.Init( 60 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_HopwireExplosion::StopExplosion( void ) +{ + m_FXCoreAlpha.InitFromCurrent( 0.0f, 1.0f, INTERP_SPLINE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_HopwireExplosion::StartPreExplosion( void ) +{ +} + +//----------------------------------------------------------------------------- +// Hopwire client class +//----------------------------------------------------------------------------- + +class C_GrenadeHopwire : public C_BaseGrenade +{ + DECLARE_CLASS( C_GrenadeHopwire, C_BaseGrenade ); + DECLARE_CLIENTCLASS(); + +public: + C_GrenadeHopwire( void ); + + virtual int DrawModel( int flags ); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual void ReceiveMessage( int classID, bf_read &msg ); + +private: + + C_HopwireExplosion m_ExplosionEffect; // Explosion effect information and drawing +}; + +IMPLEMENT_CLIENTCLASS_DT( C_GrenadeHopwire, DT_GrenadeHopwire, CGrenadeHopwire ) +END_RECV_TABLE() + +#define HOPWIRE_START_EXPLOSION 0 +#define HOPWIRE_STOP_EXPLOSION 1 +#define HOPWIRE_START_PRE_EXPLOSION 2 + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +C_GrenadeHopwire::C_GrenadeHopwire( void ) +{ + m_ExplosionEffect.SetActive( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Receive messages from the server +// Input : classID - class to receive the message +// &msg - message in question +//----------------------------------------------------------------------------- +void C_GrenadeHopwire::ReceiveMessage( int classID, bf_read &msg ) +{ + if ( classID != GetClientClass()->m_ClassID ) + { + // Message is for subclass + BaseClass::ReceiveMessage( classID, msg ); + return; + } + + int messageType = msg.ReadByte(); + switch( messageType ) + { + case HOPWIRE_START_EXPLOSION: + { + m_ExplosionEffect.SetActive(); + m_ExplosionEffect.SetOwner( this ); + m_ExplosionEffect.StartExplosion(); + } + break; + case HOPWIRE_STOP_EXPLOSION: + { + m_ExplosionEffect.StopExplosion(); + } + break; + default: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : updateType - +//----------------------------------------------------------------------------- +void C_GrenadeHopwire::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + m_ExplosionEffect.Update(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flags - +//----------------------------------------------------------------------------- +int C_GrenadeHopwire::DrawModel( int flags ) +{ + if ( m_ExplosionEffect.IsActive() ) + return 1; + + return BaseClass::DrawModel( flags ); +} diff --git a/src/game/client/contingency/hud_contingency_phasedisplay.cpp b/src/game/client/contingency/hud_contingency_phasedisplay.cpp index c5c773c..13855f0 100644 --- a/src/game/client/contingency/hud_contingency_phasedisplay.cpp +++ b/src/game/client/contingency/hud_contingency_phasedisplay.cpp @@ -140,15 +140,28 @@ void CHudContingencyPhaseDisplay::OnThink() m_pWarmupLabel->SetFgColor( Color(255, 255, 255, 255) ); if ( ContingencyRules()->GetCurrentPhase() == PHASE_INTERIM ) + { Q_snprintf( text, sizeof(text), "%s:\n%i seconds remaining before wave %i", ContingencyRules()->GetCurrentPhaseName(), ContingencyRules()->GetInterimPhaseTimeLeft(), ContingencyRules()->GetWaveNumber() + 1 ); + } else - Q_snprintf( text, sizeof(text), "%s:\nWave %i (%i enemies remaining)", - ContingencyRules()->GetCurrentPhaseName(), - ContingencyRules()->GetWaveNumber(), - ContingencyRules()->GetNumEnemiesRemaining() ); + { + if ( ContingencyRules()->IsChallengeWave() ) + { + Q_snprintf( text, sizeof(text), "%s:\nChallenge Wave %i", + ContingencyRules()->GetCurrentPhaseName(), + ContingencyRules()->GetWaveNumber() ); + } + else + { + Q_snprintf( text, sizeof(text), "%s:\nWave %i (%i enemies remaining)", + ContingencyRules()->GetCurrentPhaseName(), + ContingencyRules()->GetWaveNumber(), + ContingencyRules()->GetNumEnemiesRemaining() ); + } + } m_pWarmupLabel->SetText( text ); m_pWarmupLabel->SetVisible( true ); diff --git a/src/game/client/contingency/hud_contingency_radar.cpp b/src/game/client/contingency/hud_contingency_radar.cpp index 2ef3d88..85a47b4 100644 --- a/src/game/client/contingency/hud_contingency_radar.cpp +++ b/src/game/client/contingency/hud_contingency_radar.cpp @@ -209,6 +209,18 @@ void CHudRadarDisplay::Paint() // Update any NPCs on radar for ( i = 0; i < m_NPCList.Count(); i++ ) { + // Added prop spawning system + // Update any spawnable props on radar + pTargetSpawnableProp = dynamic_cast( m_NPCList[i] ); + if ( pTargetSpawnableProp ) + { + if ( pTargetSpawnableProp->GetSpawnerPlayer() != pLocalPlayer ) + continue; // only show the local player's spawnable props on the radar + + DrawRadarDot( pTargetSpawnableProp, COLOR_BLUE ); + continue; + } + pTargetCitizen = dynamic_cast( m_NPCList[i] ); if ( pTargetCitizen ) { @@ -239,21 +251,6 @@ void CHudRadarDisplay::Paint() continue; } } - - // Added prop spawning system - // Update any spawnable props on radar - for ( i = 0; i < m_SpawnablePropList.Count(); i++ ) - { - pTargetSpawnableProp = dynamic_cast( m_SpawnablePropList[i] ); - if ( pTargetSpawnableProp ) - { - if ( pTargetSpawnableProp->GetSpawnerPlayer() != pLocalPlayer ) - continue; // only show the local player's spawnable props on the radar - - DrawRadarDot( pTargetSpawnableProp, COLOR_BLUE ); - continue; - } - } } void CHudRadarDisplay::Toggle( void ) diff --git a/src/game/client/contingency/hud_contingency_target_id.cpp b/src/game/client/contingency/hud_contingency_target_id.cpp index c45e1ca..f6002d8 100644 --- a/src/game/client/contingency/hud_contingency_target_id.cpp +++ b/src/game/client/contingency/hud_contingency_target_id.cpp @@ -202,6 +202,14 @@ void CTargetID::Paint() vgui::surface()->DrawSetTextPos( (ScreenWidth() - wide) / 2, YRES(260) ); vgui::surface()->DrawSetTextColor( GetColorForTargetTeam(pTargetSpawnableProp->GetTeamNumber()) ); vgui::surface()->DrawPrintText( wszSpawnablePropOwnerName, wcslen(wszSpawnablePropOwnerName) ); + + wchar_t wszHealthConditionText[256]; + g_pVGuiLocalize->ConvertANSIToUnicode( pTargetSpawnableProp->GetHealthCondition(), wszHealthConditionText, sizeof(wszHealthConditionText) ); + vgui::surface()->GetTextSize( m_hFont, wszHealthConditionText, wide, tall ); + vgui::surface()->DrawSetTextFont( m_hFont ); + vgui::surface()->DrawSetTextPos( (ScreenWidth() - wide) / 2, YRES(280) ); + vgui::surface()->DrawSetTextColor( pTargetSpawnableProp->GetHealthConditionColor() ); + vgui::surface()->DrawPrintText( wszHealthConditionText, wcslen(wszHealthConditionText) ); } } } diff --git a/src/game/client/contingency/hud_contingency_wavedisplay.cpp b/src/game/client/contingency/hud_contingency_wavedisplay.cpp index b0f9025..0256851 100644 --- a/src/game/client/contingency/hud_contingency_wavedisplay.cpp +++ b/src/game/client/contingency/hud_contingency_wavedisplay.cpp @@ -123,20 +123,25 @@ void CHudContingencyWaveDisplay::OnThink() { // Determine what (if any) image we should show according to the current wave type imagePath = ""; - switch ( ContingencyRules()->GetWaveType() ) + if ( ContingencyRules()->IsChallengeWave() ) + imagePath = "HUDicons/icon_wave_challenge"; + else { - case WAVE_HEADCRABS: - imagePath = "HUDicons/icon_wave_headcrab"; - break; - case WAVE_ANTLIONS: - imagePath = "HUDicons/icon_wave_antlion"; - break; - case WAVE_ZOMBIES: - imagePath = "HUDicons/icon_wave_zombie"; - break; - case WAVE_COMBINE: - imagePath = "HUDicons/icon_wave_combine"; - break; + switch ( ContingencyRules()->GetWaveType() ) + { + /*case WAVE_HEADCRABS: + imagePath = "HUDicons/icon_wave_headcrab"; + break;*/ + case WAVE_ANTLIONS: + imagePath = "HUDicons/icon_wave_antlion"; + break; + case WAVE_ZOMBIES: + imagePath = "HUDicons/icon_wave_zombie"; + break; + case WAVE_COMBINE: + imagePath = "HUDicons/icon_wave_combine"; + break; + } } if ( Q_strcmp(imagePath, "") != 0 ) diff --git a/src/game/client/contingency/propspawningmenu.cpp b/src/game/client/contingency/propspawningmenu.cpp index 58b1c55..99fc6f0 100644 --- a/src/game/client/contingency/propspawningmenu.cpp +++ b/src/game/client/contingency/propspawningmenu.cpp @@ -141,6 +141,9 @@ void CPropSpawningMenu::Reset() currentSpawnablePropSelected[1] = kSpawnablePropTypes[currentSpawnablePropIndex][1]; currentSpawnablePropSelected[2] = kSpawnablePropTypes[currentSpawnablePropIndex][2]; currentSpawnablePropSelected[3] = kSpawnablePropTypes[currentSpawnablePropIndex][3]; + currentSpawnablePropSelected[4] = kSpawnablePropTypes[currentSpawnablePropIndex][4]; + currentSpawnablePropSelected[5] = kSpawnablePropTypes[currentSpawnablePropIndex][5]; + currentSpawnablePropSelected[6] = kSpawnablePropTypes[currentSpawnablePropIndex][6]; } void CPropSpawningMenu::OnThink() @@ -169,7 +172,7 @@ void CPropSpawningMenu::OnThink() else if ( Q_strcmp(labelName, "PropCost") == 0 ) { char szPropCost[128]; - Q_snprintf( szPropCost, sizeof(szPropCost), "COST: %s credits", currentSpawnablePropSelected[1] ); + Q_snprintf( szPropCost, sizeof(szPropCost), "COST: %s credits\nHP: %s", currentSpawnablePropSelected[1], currentSpawnablePropSelected[6] ); pLabel->SetText( szPropCost ); } } @@ -208,6 +211,9 @@ void CPropSpawningMenu::OnCommand( const char *command ) currentSpawnablePropSelected[1] = kSpawnablePropTypes[currentSpawnablePropIndex][1]; currentSpawnablePropSelected[2] = kSpawnablePropTypes[currentSpawnablePropIndex][2]; currentSpawnablePropSelected[3] = kSpawnablePropTypes[currentSpawnablePropIndex][3]; + currentSpawnablePropSelected[4] = kSpawnablePropTypes[currentSpawnablePropIndex][4]; + currentSpawnablePropSelected[5] = kSpawnablePropTypes[currentSpawnablePropIndex][5]; + currentSpawnablePropSelected[6] = kSpawnablePropTypes[currentSpawnablePropIndex][6]; } else if ( Q_stricmp(command, "previousprop") == 0 ) { @@ -220,6 +226,9 @@ void CPropSpawningMenu::OnCommand( const char *command ) currentSpawnablePropSelected[1] = kSpawnablePropTypes[currentSpawnablePropIndex][1]; currentSpawnablePropSelected[2] = kSpawnablePropTypes[currentSpawnablePropIndex][2]; currentSpawnablePropSelected[3] = kSpawnablePropTypes[currentSpawnablePropIndex][3]; + currentSpawnablePropSelected[4] = kSpawnablePropTypes[currentSpawnablePropIndex][4]; + currentSpawnablePropSelected[5] = kSpawnablePropTypes[currentSpawnablePropIndex][5]; + currentSpawnablePropSelected[6] = kSpawnablePropTypes[currentSpawnablePropIndex][6]; } else if ( Q_stricmp(command, "selectprop") == 0 ) { diff --git a/src/game/client/contingency/spawnableprop_deletionmenu.cpp b/src/game/client/contingency/spawnableprop_deletionmenu.cpp index 0855b0e..a872495 100644 --- a/src/game/client/contingency/spawnableprop_deletionmenu.cpp +++ b/src/game/client/contingency/spawnableprop_deletionmenu.cpp @@ -39,7 +39,6 @@ CSpawnableProp_DeletionMenu::CSpawnableProp_DeletionMenu( IViewPort *pViewPort ) m_pTitle = new Label( this, "TitleLabel", "" ); m_pText = new Label( this, "TextLabel", "" ); m_pDeleteButton = new Button( this, "DeleteButton", "" ); - m_pToggleFrozenButton = new Button( this, "ToggleFrozenButton", ""); m_pCancelButton = new Button( this, "CancelButton", "" ); LoadControlSettings( "Resource/UI/SpawnableProp_DeletionMenu.res" ); @@ -102,9 +101,6 @@ void CSpawnableProp_DeletionMenu::OnCommand( const char *command ) if ( Q_stricmp(command, "removespawnablepropinfocus") == 0 ) engine->ServerCmd( "removespawnablepropinfocus\n" ); - if ( Q_stricmp(command, "togglefrozenspawnablepropinfocus") == 0 ) - engine->ServerCmd( "togglefrozenspawnablepropinfocus\n" ); - Close(); gViewPortInterface->ShowBackGround( false ); diff --git a/src/game/client/contingency/spawnableprop_deletionmenu.h b/src/game/client/contingency/spawnableprop_deletionmenu.h index 03ecfca..cbad283 100644 --- a/src/game/client/contingency/spawnableprop_deletionmenu.h +++ b/src/game/client/contingency/spawnableprop_deletionmenu.h @@ -46,7 +46,6 @@ class CSpawnableProp_DeletionMenu : public Frame, public IViewPortPanel Label *m_pTitle; Label *m_pText; Button *m_pDeleteButton; - Button *m_pToggleFrozenButton; Button *m_pCancelButton; }; diff --git a/src/game/server/ai_senses.cpp b/src/game/server/ai_senses.cpp index 6bf1d77..d754c8b 100644 --- a/src/game/server/ai_senses.cpp +++ b/src/game/server/ai_senses.cpp @@ -453,13 +453,12 @@ int CAI_Senses::LookForHighPriorityEntities( int iDistance ) for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { - CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( !pPlayer ) + continue; - if ( pPlayer ) - { - if ( Look(pPlayer) ) - nSeen++; - } + if ( Look(pPlayer) ) + nSeen++; } ///// diff --git a/src/game/server/baseentity.h b/src/game/server/baseentity.h index c9a3b4e..75bad02 100644 --- a/src/game/server/baseentity.h +++ b/src/game/server/baseentity.h @@ -116,6 +116,22 @@ enum Class_T CLASS_HACKED_ROLLERMINE, CLASS_COMBINE_HUNTER, +///// + + // Contingency - James + // Added spawnable prop system + + CLASS_CONTINGENCY_SPAWNABLE_PROP, + + // This class should be neutral towards everything, + // but every other class's disposition towards it + // should be the same as CLASS_PLAYER, just with a lower priority + + // The result should be a class that is handled like CLASS_PLAYER, + // but is considered less frequently due to the lower priority level + +///// + NUM_AI_CLASSES }; diff --git a/src/game/server/contingency/contingency_boss_spawnpoint.cpp b/src/game/server/contingency/contingency_boss_spawnpoint.cpp index a13a706..9eeb818 100644 --- a/src/game/server/contingency/contingency_boss_spawnpoint.cpp +++ b/src/game/server/contingency/contingency_boss_spawnpoint.cpp @@ -102,12 +102,12 @@ void CContingencyBossSpawner::MakeNPC( void ) int currentWaveType = ContingencyRules()->GetWaveType(); const char *NPCClassName = STRING(m_BossNPCType); string_t equipmentName = NULL_STRING; - if ( currentWaveType == WAVE_HEADCRABS ) + /*if ( currentWaveType == WAVE_HEADCRABS ) { // There are no headcrab wave bosses yet! :( return; } - else if ( currentWaveType == WAVE_ANTLIONS ) + else */if ( currentWaveType == WAVE_ANTLIONS ) { // Only spawn bosses associated with an antlion wave if ( Q_strcmp(NPCClassName, "npc_antlionguard") != 0 ) @@ -152,7 +152,12 @@ void CContingencyBossSpawner::MakeNPC( void ) pent->AddSpawnFlags( SF_NPC_NO_WEAPON_DROP ); pent->AddSpawnFlags( SF_NPC_ALWAYSTHINK ); - pent->AddSpawnFlags( SF_NPC_LONG_RANGE ); + + // As long as we aren't wielding a weapon, we're safe to look long ranges + // NOTE: NPCs who have a weapon and this spawn flag enabled fire at insanely long distances, + // hence why we're preventing that from happening here! + if ( equipmentName == NULL_STRING ) + pent->AddSpawnFlags( SF_NPC_LONG_RANGE ); // Apply any defined squads and hint groups the mapper may have defined // as well as weapons (if applicable) diff --git a/src/game/server/contingency/contingency_configuration.cpp b/src/game/server/contingency/contingency_configuration.cpp index b93f605..9a73fb8 100644 --- a/src/game/server/contingency/contingency_configuration.cpp +++ b/src/game/server/contingency/contingency_configuration.cpp @@ -7,7 +7,7 @@ // Spawnflags // Added wave system -#define SF_CONFIGURATION_SUPPORTSHEADCRABS 1 +//#define SF_CONFIGURATION_SUPPORTSHEADCRABS 1 #define SF_CONFIGURATION_SUPPORTSANTLIONS 16 #define SF_CONFIGURATION_SUPPORTSZOMBIES 32 #define SF_CONFIGURATION_SUPPORTSCOMBINE 64 @@ -40,7 +40,7 @@ class CContingencyConfiguration : public CBaseEntity // Added wave system int m_iMaxLivingNPCs; - float m_flHeadcrabWaveMultiplierOffset; + //float m_flHeadcrabWaveMultiplierOffset; float m_flAntlionWaveMultiplierOffset; float m_flZombieWaveMultiplierOffset; float m_flCombineWaveMultiplierOffset; @@ -71,7 +71,7 @@ BEGIN_DATADESC( CContingencyConfiguration ) // Added wave system DEFINE_KEYFIELD( m_iMaxLivingNPCs, FIELD_INTEGER, "MaxLivingNPCs" ), - DEFINE_KEYFIELD( m_flHeadcrabWaveMultiplierOffset, FIELD_FLOAT, "HeadcrabWaveMultiplierOffset" ), + //DEFINE_KEYFIELD( m_flHeadcrabWaveMultiplierOffset, FIELD_FLOAT, "HeadcrabWaveMultiplierOffset" ), DEFINE_KEYFIELD( m_flAntlionWaveMultiplierOffset, FIELD_FLOAT, "AntlionWaveMultiplierOffset" ), DEFINE_KEYFIELD( m_flZombieWaveMultiplierOffset, FIELD_FLOAT, "ZombieWaveMultiplierOffset" ), DEFINE_KEYFIELD( m_flCombineWaveMultiplierOffset, FIELD_FLOAT, "CombineWaveMultiplierOffset" ), @@ -109,14 +109,14 @@ void CContingencyConfiguration::Spawn( void ) // Check to see what types of waves our map supports by its spawnflags // and update our gamerules accordingly ContingencyRules()->SetMapMaxLivingNPCs( m_iMaxLivingNPCs ); - ContingencyRules()->DoesMapSupportHeadcrabs( HasSpawnFlags(SF_CONFIGURATION_SUPPORTSHEADCRABS) ); + //ContingencyRules()->DoesMapSupportHeadcrabs( HasSpawnFlags(SF_CONFIGURATION_SUPPORTSHEADCRABS) ); ContingencyRules()->DoesMapSupportAntlions( HasSpawnFlags(SF_CONFIGURATION_SUPPORTSANTLIONS) ); ContingencyRules()->DoesMapSupportZombies( HasSpawnFlags(SF_CONFIGURATION_SUPPORTSZOMBIES) ); ContingencyRules()->DoesMapSupportCombine( HasSpawnFlags(SF_CONFIGURATION_SUPPORTSCOMBINE) ); // Added wave system // Send our wave multiplier offsets to our gamerules - ContingencyRules()->SetMapHeadcrabWaveMultiplierOffset( m_flHeadcrabWaveMultiplierOffset ); + //ContingencyRules()->SetMapHeadcrabWaveMultiplierOffset( m_flHeadcrabWaveMultiplierOffset ); ContingencyRules()->SetMapAntlionWaveMultiplierOffset( m_flAntlionWaveMultiplierOffset ); ContingencyRules()->SetMapZombieWaveMultiplierOffset( m_flZombieWaveMultiplierOffset ); ContingencyRules()->SetMapCombineWaveMultiplierOffset( m_flCombineWaveMultiplierOffset ); diff --git a/src/game/server/contingency/contingency_spawnableprop.cpp b/src/game/server/contingency/contingency_spawnableprop.cpp index 039bd2e..1160ff0 100644 --- a/src/game/server/contingency/contingency_spawnableprop.cpp +++ b/src/game/server/contingency/contingency_spawnableprop.cpp @@ -6,6 +6,9 @@ #include "contingency_system_propspawning.h" +#include "props_shared.h" +#include "npc_citizen17.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -13,7 +16,6 @@ LINK_ENTITY_TO_CLASS( contingency_spawnableprop, CContingency_SpawnableProp ); IMPLEMENT_SERVERCLASS_ST( CContingency_SpawnableProp, DT_Contingency_SpawnableProp ) SendPropEHandle( SENDINFO(m_hSpawnerPlayer) ), - SendPropBool( SENDINFO(m_bIsFrozen) ), END_SEND_TABLE() BEGIN_DATADESC( CContingency_SpawnableProp ) @@ -21,7 +23,6 @@ END_DATADESC() CContingency_SpawnableProp::CContingency_SpawnableProp( void ) { - m_bIsFrozen = false; m_hSpawnerPlayer = NULL; m_iSpawnablePropIndex = 0; pTarget = NULL; @@ -59,74 +60,52 @@ CContingency_SpawnableProp::~CContingency_SpawnableProp( void ) } } +Class_T CContingency_SpawnableProp::Classify( void ) +{ + return CLASS_CONTINGENCY_SPAWNABLE_PROP; + + // See Class_T enumerator in baseentity.h for + // a description of CLASS_CONTINGENCY_SPAWNABLE_PROP +} + void CContingency_SpawnableProp::Spawn( void ) { - BaseClass::Spawn(); + Precache(); + // Model should be set by what is spawning us + SetSolid( SOLID_VPHYSICS ); + AddSolidFlags( FSOLID_COLLIDE_WITH_OWNER ); SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS ); + SetMoveType( MOVETYPE_NONE ); + SetBlocksLOS( true ); + SetAIWalkable( true ); + SetGravity( 0.0 ); AddEffects( EF_NOSHADOW ); // save some FPS (?) - AddSolidFlags( FSOLID_COLLIDE_WITH_OWNER ); + SetBloodColor( BLOOD_COLOR_MECH ); // change to DONT_BLEED? - // Fade spawned prop in to the world - SetRenderColorA( 0 ); - m_nRenderFX = kRenderFxSolidFast; - m_flSpeed = gpGlobals->curtime + 3.0f; + m_flFieldOfView = 0.0f; - SetFrozenState( true ); // default to frozen + AddFlag( FL_NPC ); - pTarget = static_cast( CreateEntityByName("npc_contingency_target") ); - if ( pTarget ) + if ( VPhysicsGetObject() ) { - pTarget->SetAbsOrigin( this->GetAbsOrigin() ); - pTarget->SetAbsAngles( this->GetAbsAngles() ); - pTarget->AddSpawnFlags( SF_BULLSEYE_NONSOLID | SF_BULLSEYE_ENEMYDAMAGEONLY | SF_BULLSEYE_PERFECTACC ); - pTarget->Spawn(); - DispatchSpawn( pTarget ); - pTarget->Activate(); - pTarget->SetParent( this ); - pTarget->SetHealth( this->GetHealth() ); - pTarget->SetPainPartner( this ); + VPhysicsGetObject()->EnableCollisions( false ); + VPhysicsGetObject()->EnableMotion( false ); + VPhysicsGetObject()->EnableDrag( false ); + VPhysicsGetObject()->EnableGravity( false ); } + + m_takedamage = DAMAGE_YES; - SetNextThink( gpGlobals->curtime ); -} - -void CContingency_SpawnableProp::SetFrozenState( bool shouldFreeze ) -{ - if ( shouldFreeze ) - { - // Freeze us! - - SetMoveType( MOVETYPE_NONE ); - SetBlocksLOS( true ); - SetAIWalkable( true ); - - if ( VPhysicsGetObject() ) - { - VPhysicsGetObject()->EnableCollisions( false ); - VPhysicsGetObject()->EnableMotion( false ); - VPhysicsGetObject()->EnableDrag( false ); - VPhysicsGetObject()->EnableGravity( false ); - } - } - else - { - // Unfreeze us! - - SetMoveType( MOVETYPE_VPHYSICS ); - SetBlocksLOS( false ); - SetAIWalkable( false ); + CapabilitiesAdd( bits_CAP_SKIP_NAV_GROUND_CHECK ); - if ( VPhysicsGetObject() ) - { - VPhysicsGetObject()->EnableCollisions( true ); - VPhysicsGetObject()->EnableMotion( true ); - VPhysicsGetObject()->EnableDrag( true ); - VPhysicsGetObject()->EnableGravity( true ); - } - } + // Our initial "health" depends on what type of spawnable prop we are + // and therefore should be set by whatever is spawning us - m_bIsFrozen = shouldFreeze; + // Fade spawned prop in to the world + SetRenderColorA( 0 ); + m_nRenderFX = kRenderFxSolidFast; + m_flSpeed = gpGlobals->curtime + 3.0f; } void CContingency_SpawnableProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) @@ -156,35 +135,81 @@ void CContingency_SpawnableProp::Use( CBaseEntity *pActivator, CBaseEntity *pCal int CContingency_SpawnableProp::OnTakeDamage( const CTakeDamageInfo &info ) { - // Don't let players damage each other's spawnable props - // Players can still damage their own props though - CBasePlayer *pPlayerAttacker = dynamic_cast( info.GetAttacker() ); + // Don't let players damage each other's spawned props + // Players can still damage their own spawned props though + CBasePlayer *pPlayerAttacker = ToBasePlayer( info.GetAttacker() ); if ( pPlayerAttacker && (pPlayerAttacker != GetSpawnerPlayer()) ) return 0; + if ( info.GetAttacker() && info.GetAttacker()->IsNPC() ) + { + // Don't let support NPCs do damage to us + CNPC_Citizen *pCitizenAttacker = dynamic_cast( info.GetAttacker() ); + if ( pCitizenAttacker ) + return 0; + + // Don't let deployed turrets do damage to us + CNPC_FloorTurret *pTurretAttacker = dynamic_cast( info.GetAttacker() ); + if ( pTurretAttacker ) + return 0; + } + return BaseClass::OnTakeDamage( info ); } -void CContingency_SpawnableProp::Think( void ) +void CContingency_SpawnableProp::Event_Killed( const CTakeDamageInfo &info ) { - BaseClass::Think(); + BaseClass::Event_Killed( info ); - // This is an absolutely horrible hack that prevents spawnable props from sleeping - // when unfrozen so that we can still perform tracelines that require them to be awake - // TODO: For the love of all that is good, come up with a better solution! - if ( !m_bIsFrozen && VPhysicsGetObject() ) - VPhysicsGetObject()->Wake(); + SetMoveType( MOVETYPE_NONE ); + AddSolidFlags( FSOLID_NOT_SOLID ); - SetNextThink( gpGlobals->curtime ); -} + if ( !IsDissolving() ) // don't break if we're dissolving + { +// +// This next block of code is taken directly from CAI_BaseNPC::Break, +// which is a private function of CAI_BaseNPC + + m_takedamage = DAMAGE_NO; + + Vector velocity; + AngularImpulse angVelocity; + IPhysicsObject *pPhysics = VPhysicsGetObject(); + Vector origin; + QAngle angles; + AddSolidFlags( FSOLID_NOT_SOLID ); + if ( pPhysics ) + { + pPhysics->GetVelocity( &velocity, &angVelocity ); + pPhysics->GetPosition( &origin, &angles ); + pPhysics->RecheckCollisionFilter(); + } + else + { + velocity = GetAbsVelocity(); + QAngleToAngularImpulse( GetLocalAngularVelocity(), angVelocity ); + origin = GetAbsOrigin(); + angles = GetAbsAngles(); + } -bool CContingency_SpawnableProp::OnAttemptPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ) -{ - if ( m_bIsFrozen ) - return false; // frozen props cannot be picked up + breakablepropparams_t params( GetAbsOrigin(), GetAbsAngles(), velocity, angVelocity ); + params.impactEnergyScale = m_impactEnergyScale; + params.defCollisionGroup = GetCollisionGroup(); + if ( params.defCollisionGroup == COLLISION_GROUP_NONE ) + { + // don't automatically make anything COLLISION_GROUP_NONE or it will + // collide with debris being ejected by breaking + params.defCollisionGroup = COLLISION_GROUP_INTERACTIVE; + } - if ( pPhysGunUser != GetSpawnerPlayer() ) - return false; // props that do not belong to us cannot be picked up + // no damage/damage force? set a burst of 100 for some movement + params.defBurstScale = 100;//pDamageInfo ? 0 : 100; + PropBreakableCreateAll( GetModelIndex(), pPhysics, params, this, -1, false ); + +// END of code block from CAI_BaseNPC::Break +// + } - return true; + SetNextThink( gpGlobals->curtime + 0.1f ); + SetThink( &CBaseEntity::SUB_Remove ); } diff --git a/src/game/server/contingency/contingency_spawnableprop.h b/src/game/server/contingency/contingency_spawnableprop.h index 4bc8816..5bc4ed1 100644 --- a/src/game/server/contingency/contingency_spawnableprop.h +++ b/src/game/server/contingency/contingency_spawnableprop.h @@ -4,13 +4,12 @@ #define CONTINGENCY_SPAWNABLEPROP_H #pragma once -#include "props.h" #include "npc_contingency_target.h" -class CContingency_SpawnableProp : public CPhysicsProp +class CContingency_SpawnableProp : public CAI_BaseNPC { public: - DECLARE_CLASS( CContingency_SpawnableProp, CPhysicsProp ); + DECLARE_CLASS( CContingency_SpawnableProp, CAI_BaseNPC ); DECLARE_SERVERCLASS(); DECLARE_DATADESC(); @@ -23,21 +22,18 @@ class CContingency_SpawnableProp : public CPhysicsProp int GetSpawnablePropIndex( void ) { return m_iSpawnablePropIndex; } void SetSpawnablePropIndex( int iNewSpawnablePropIndex ) { m_iSpawnablePropIndex = iNewSpawnablePropIndex; } + Class_T Classify( void ); void Spawn( void ); - void SetFrozenState( bool shouldFreeze ); - bool IsFrozen( void ) { return m_bIsFrozen; } - int ObjectCaps() { return BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION | FCAP_IMPULSE_USE; } + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); int OnTakeDamage( const CTakeDamageInfo &info ); - void Think( void ); - bool OnAttemptPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); + void Event_Killed( const CTakeDamageInfo &info ); private: CNetworkHandle( CBasePlayer, m_hSpawnerPlayer ); - CNetworkVar( bool, m_bIsFrozen ); int m_iSpawnablePropIndex; CNPC_Contingency_Target *pTarget; }; diff --git a/src/game/server/contingency/contingency_support_spawnpoint.cpp b/src/game/server/contingency/contingency_support_spawnpoint.cpp index 21f1299..05f0d7d 100644 --- a/src/game/server/contingency/contingency_support_spawnpoint.cpp +++ b/src/game/server/contingency/contingency_support_spawnpoint.cpp @@ -119,7 +119,12 @@ void CContingencySupportWaveSpawner::MakeNPC( void ) pent->AddSpawnFlags( SF_NPC_NO_WEAPON_DROP ); pent->AddSpawnFlags( SF_NPC_ALWAYSTHINK ); - pent->AddSpawnFlags( SF_NPC_LONG_RANGE ); + + // As long as we aren't wielding a weapon, we're safe to look long ranges + // NOTE: NPCs who have a weapon and this spawn flag enabled fire at insanely long distances, + // hence why we're preventing that from happening here! + if ( equipmentName == NULL_STRING ) + pent->AddSpawnFlags( SF_NPC_LONG_RANGE ); // Apply any defined squads and hint groups the mapper may have defined // as well as weapons (if applicable) diff --git a/src/game/server/contingency/contingency_wave_spawnpoint.cpp b/src/game/server/contingency/contingency_wave_spawnpoint.cpp index 40edca7..3582008 100644 --- a/src/game/server/contingency/contingency_wave_spawnpoint.cpp +++ b/src/game/server/contingency/contingency_wave_spawnpoint.cpp @@ -84,8 +84,10 @@ void CContingencyWaveSpawner::MakeNPC( void ) // Spawn a random type of NPC type associated with the current wave int currentWaveType = ContingencyRules()->GetWaveType(); const char *NPCClassName = ""; + if ( ContingencyRules()->IsChallengeWave() ) + NPCClassName = ContingencyRules()->GetPreferredNPCType(); string_t equipmentName = NULL_STRING; - if ( currentWaveType == WAVE_HEADCRABS ) + /*if ( currentWaveType == WAVE_HEADCRABS ) { if ( !HasSpawnFlags(SF_WAVESPAWNER_HEADCRAB) && !HasSpawnFlags(SF_WAVESPAWNER_HEADCRAB_FAST) && @@ -93,21 +95,33 @@ void CContingencyWaveSpawner::MakeNPC( void ) return; // this spawner cannot spawn any headcrabs // Choose a random type of headcrab to spawn - NPCClassName = kWaveHeadcrabsNPCTypes[random->RandomInt(0, NUM_HEADCRAB_NPCS - 1)]; + if ( !ContingencyRules()->IsChallengeWave() ) + NPCClassName = kWaveHeadcrabsNPCTypes[random->RandomInt(0, NUM_HEADCRAB_NPCS - 1)]; while ( ((Q_strcmp(NPCClassName, "npc_headcrab") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_HEADCRAB)) || ((Q_strcmp(NPCClassName, "npc_headcrab_fast") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_HEADCRAB_FAST)) || ((Q_strcmp(NPCClassName, "npc_headcrab_black") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_HEADCRAB_BLACK)) ) - NPCClassName = kWaveHeadcrabsNPCTypes[random->RandomInt(0, NUM_HEADCRAB_NPCS - 1)]; + { + if ( ContingencyRules()->IsChallengeWave() ) + return; // if we can't spawn our challenge wave NPC, that's okay, another wave spawnpoint will + else + NPCClassName = kWaveHeadcrabsNPCTypes[random->RandomInt(0, NUM_HEADCRAB_NPCS - 1)]; + } } - else if ( currentWaveType == WAVE_ANTLIONS ) + else */if ( currentWaveType == WAVE_ANTLIONS ) { if ( !HasSpawnFlags(SF_WAVESPAWNER_ANTLION) ) return; // this spawner cannot spawn any antlions // Choose a random type of antlion to spawn - NPCClassName = kWaveAntlionsNPCTypes[random->RandomInt(0, NUM_ANTLION_NPCS - 1)]; - while ( ((Q_strcmp(NPCClassName, "npc_antlion") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_ANTLION)) ) + if ( !ContingencyRules()->IsChallengeWave() ) NPCClassName = kWaveAntlionsNPCTypes[random->RandomInt(0, NUM_ANTLION_NPCS - 1)]; + while ( ((Q_strcmp(NPCClassName, "npc_antlion") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_ANTLION)) ) + { + if ( ContingencyRules()->IsChallengeWave() ) + return; // if we can't spawn our challenge wave NPC, that's okay, another wave spawnpoint will + else + NPCClassName = kWaveAntlionsNPCTypes[random->RandomInt(0, NUM_ANTLION_NPCS - 1)]; + } } else if ( currentWaveType == WAVE_ZOMBIES ) { @@ -118,12 +132,18 @@ void CContingencyWaveSpawner::MakeNPC( void ) return; // this spawner cannot spawn any zombies // Choose a random type of zombie to spawn - NPCClassName = kWaveZombiesNPCTypes[random->RandomInt(0, NUM_ZOMBIE_NPCS - 1)]; + if ( !ContingencyRules()->IsChallengeWave() ) + NPCClassName = kWaveZombiesNPCTypes[random->RandomInt(0, NUM_ZOMBIE_NPCS - 1)]; while ( ((Q_strcmp(NPCClassName, "npc_zombie") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_ZOMBIE)) || ((Q_strcmp(NPCClassName, "npc_zombie_torso") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_ZOMBIE_TORSO)) || ((Q_strcmp(NPCClassName, "npc_fastzombie") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_ZOMBIE_FAST)) || ((Q_strcmp(NPCClassName, "npc_poisonzombie") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_ZOMBIE_POISON)) ) - NPCClassName = kWaveZombiesNPCTypes[random->RandomInt(0, NUM_ZOMBIE_NPCS - 1)]; + { + if ( ContingencyRules()->IsChallengeWave() ) + return; // if we can't spawn our challenge wave NPC, that's okay, another wave spawnpoint will + else + NPCClassName = kWaveZombiesNPCTypes[random->RandomInt(0, NUM_ZOMBIE_NPCS - 1)]; + } } else if ( currentWaveType == WAVE_COMBINE ) { @@ -135,13 +155,19 @@ void CContingencyWaveSpawner::MakeNPC( void ) return; // this spawner cannot spawn any zombies // Choose a random type of zombie to spawn - NPCClassName = kWaveCombineNPCTypes[random->RandomInt(0, NUM_COMBINE_NPCS - 1)]; + if ( !ContingencyRules()->IsChallengeWave() ) + NPCClassName = kWaveCombineNPCTypes[random->RandomInt(0, NUM_COMBINE_NPCS - 1)]; while ( ((Q_strcmp(NPCClassName, "npc_combine_s") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_COMBINE)) || ((Q_strcmp(NPCClassName, "npc_metropolice") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_COMBINE_METRO)) || ((Q_strcmp(NPCClassName, "npc_cscanner") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_COMBINE_SCANNER)) || ((Q_strcmp(NPCClassName, "npc_manhack") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_COMBINE_MANHACK)) || ((Q_strcmp(NPCClassName, "npc_stalker") == 0) && !HasSpawnFlags(SF_WAVESPAWNER_COMBINE_STALKER)) ) - NPCClassName = kWaveCombineNPCTypes[random->RandomInt(0, NUM_COMBINE_NPCS - 1)]; + { + if ( ContingencyRules()->IsChallengeWave() ) + return; // if we can't spawn our challenge wave NPC, that's okay, another wave spawnpoint will + else + NPCClassName = kWaveCombineNPCTypes[random->RandomInt(0, NUM_COMBINE_NPCS - 1)]; + } // Certain combine types should be given equipment (weapons) if ( Q_strcmp(NPCClassName, "npc_combine_s") == 0 ) @@ -178,7 +204,12 @@ void CContingencyWaveSpawner::MakeNPC( void ) pent->AddSpawnFlags( SF_NPC_NO_WEAPON_DROP ); pent->AddSpawnFlags( SF_NPC_ALWAYSTHINK ); - pent->AddSpawnFlags( SF_NPC_LONG_RANGE ); + + // As long as we aren't wielding a weapon, we're safe to look long ranges + // NOTE: NPCs who have a weapon and this spawn flag enabled fire at insanely long distances, + // hence why we're preventing that from happening here! + if ( equipmentName == NULL_STRING ) + pent->AddSpawnFlags( SF_NPC_LONG_RANGE ); // Apply any defined squads and hint groups the mapper may have defined // as well as weapons (if applicable) diff --git a/src/game/server/contingency/contingency_wave_spawnpoint.h b/src/game/server/contingency/contingency_wave_spawnpoint.h index 4a15a72..9bbf59e 100644 --- a/src/game/server/contingency/contingency_wave_spawnpoint.h +++ b/src/game/server/contingency/contingency_wave_spawnpoint.h @@ -13,9 +13,9 @@ /* SPAWNFLAGS */ // kWaveHeadcrabsNPCTypes -#define SF_WAVESPAWNER_HEADCRAB 1024 -#define SF_WAVESPAWNER_HEADCRAB_FAST 2048 -#define SF_WAVESPAWNER_HEADCRAB_BLACK 4096 +//#define SF_WAVESPAWNER_HEADCRAB 1024 +//#define SF_WAVESPAWNER_HEADCRAB_FAST 2048 +//#define SF_WAVESPAWNER_HEADCRAB_BLACK 4096 // kWaveAntlionsNPCTypes #define SF_WAVESPAWNER_ANTLION 8192 // kWaveZombiesNPCTypes diff --git a/src/game/server/contingency/grenade_hopwire.cpp b/src/game/server/contingency/grenade_hopwire.cpp new file mode 100644 index 0000000..0ff71aa --- /dev/null +++ b/src/game/server/contingency/grenade_hopwire.cpp @@ -0,0 +1,634 @@ +// Ported directly from Lethal Stigma, with a few adjustments + +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Gravity well device +// +//=====================================================================================// + +#include "cbase.h" +#include "grenade_hopwire.h" +#include "rope.h" +#include "rope_shared.h" +#include "beam_shared.h" +#include "physics.h" +#include "physics_saverestore.h" +#include "explode.h" +#include "physics_prop_ragdoll.h" +#include "movevars_shared.h" + +#include "ai_basenpc.h" +#include "npc_citizen17.h" +// Added spawnable prop system +#include "contingency_spawnableprop.h" +#include "npc_contingency_turret.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// Lethal Stigma +// ADDED: Hopwire +ConVar hopwire_vortex( "hopwire_vortex", "1" ); + +ConVar hopwire_trap( "hopwire_trap", "1" ); +ConVar hopwire_strider_kill_dist_h( "hopwire_strider_kill_dist_h", "300" ); +ConVar hopwire_strider_kill_dist_v( "hopwire_strider_kill_dist_v", "256" ); +ConVar hopwire_strider_hits( "hopwire_strider_hits", "1" ); +ConVar hopwire_hopheight( "hopwire_hopheight", "400" ); + +ConVar g_debug_hopwire( "g_debug_hopwire", "0" ); + +#define DENSE_BALL_MODEL "models/props_junk/metal_paintcan001b.mdl" + +#define MAX_HOP_HEIGHT (hopwire_hopheight.GetFloat()) // Maximum amount the grenade will "hop" upwards when detonated + +class CGravityVortexController : public CBaseEntity +{ + DECLARE_CLASS( CGravityVortexController, CBaseEntity ); + DECLARE_DATADESC(); + +public: + + CGravityVortexController( void ) : m_flEndTime( 0.0f ), m_flRadius( 256 ), m_flStrength( 256 ), m_flMass( 0.0f ) {} + float GetConsumedMass( void ) const; + + // Lethal Stigma + // ADDED: Hopwire + // Death messages support for multiplayer! + //static CGravityVortexController *Create( const Vector &origin, float radius, float strength, float duration ); + static CGravityVortexController *Create( const Vector &origin, float radius, float strength, float duration, CBaseEntity *pOwnerOfHopwireGrenade ); + +private: + + void ConsumeEntity( CBaseEntity *pEnt ); + void PullPlayersInRange( void ); + bool KillNPCInRange( CAI_BaseNPC *pVictim, IPhysicsObject **pPhysObj ); + void CreateDenseBall( void ); + void PullThink( void ); + void StartPull( const Vector &origin, float radius, float strength, float duration ); + + float m_flMass; // Mass consumed by the vortex + float m_flEndTime; // Time when the vortex will stop functioning + float m_flRadius; // Area of effect for the vortex + float m_flStrength; // Pulling strength of the vortex +}; + +//----------------------------------------------------------------------------- +// Purpose: Returns the amount of mass consumed by the vortex +//----------------------------------------------------------------------------- +float CGravityVortexController::GetConsumedMass( void ) const +{ + return m_flMass; +} + +//----------------------------------------------------------------------------- +// Purpose: Adds the entity's mass to the aggregate mass consumed +//----------------------------------------------------------------------------- +void CGravityVortexController::ConsumeEntity( CBaseEntity *pEnt ) +{ + // Get our base physics object + IPhysicsObject *pPhysObject = pEnt->VPhysicsGetObject(); + if ( pPhysObject == NULL ) + return; + + // Ragdolls need to report the sum of all their parts + CRagdollProp *pRagdoll = dynamic_cast< CRagdollProp* >( pEnt ); + if ( pRagdoll != NULL ) + { + // Find the aggregate mass of the whole ragdoll + ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll(); + for ( int j = 0; j < pRagdollPhys->listCount; ++j ) + { + m_flMass += pRagdollPhys->list[j].pObject->GetMass(); + } + } + else + { + // Otherwise we just take the normal mass + m_flMass += pPhysObject->GetMass(); + } + + // Destroy the entity + UTIL_Remove( pEnt ); +} + +//----------------------------------------------------------------------------- +// Purpose: Causes players within the radius to be sucked in +//----------------------------------------------------------------------------- +void CGravityVortexController::PullPlayersInRange( void ) +{ + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + + Vector vecForce = GetAbsOrigin() - pPlayer->WorldSpaceCenter(); + float dist = VectorNormalize( vecForce ); + + // FIXME: Need a more deterministic method here + if ( dist < 128.0f ) + { + // Lethal Stigma + // ADDED: Hopwire + // Kill the player (with falling death sound and effects) + //CTakeDamageInfo deathInfo( this, this, GetAbsOrigin(), GetAbsOrigin(), 200, DMG_FALL ); + CTakeDamageInfo deathInfo( GetOwnerEntity(), GetOwnerEntity(), this, 200, DMG_FALL ); + pPlayer->TakeDamage( deathInfo ); + + if ( pPlayer->IsAlive() == false ) + { + color32 black = { 0, 0, 0, 255 }; + UTIL_ScreenFade( pPlayer, black, 0.1f, 0.0f, (FFADE_OUT|FFADE_STAYOUT) ); + return; + } + } + + // Must be within the radius + if ( dist > m_flRadius ) + return; + + float mass = pPlayer->VPhysicsGetObject()->GetMass(); + float playerForce = m_flStrength * 0.05f; + + // Find the pull force + // NOTE: We might want to make this non-linear to give more of a "grace distance" + vecForce *= ( 1.0f - ( dist / m_flRadius ) ) * playerForce * mass; + vecForce[2] *= 0.025f; + + pPlayer->SetBaseVelocity( vecForce ); + pPlayer->AddFlag( FL_BASEVELOCITY ); + + // Make sure the player moves + if ( vecForce.z > 0 && ( pPlayer->GetFlags() & FL_ONGROUND) ) + { + pPlayer->SetGroundEntity( NULL ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Attempts to kill an NPC if it's within range and other criteria +// Input : *pVictim - NPC to assess +// **pPhysObj - pointer to the ragdoll created if the NPC is killed +// Output : bool - whether or not the NPC was killed and the returned pointer is valid +//----------------------------------------------------------------------------- +bool CGravityVortexController::KillNPCInRange( CAI_BaseNPC *pVictim, IPhysicsObject **pPhysObj ) +{ + if ( pVictim && pVictim->CanBecomeRagdoll() ) + { + // This NPC is valid and can become a ragdoll, so do so! + + CTakeDamageInfo info( this, this, 1.0f, DMG_GENERIC ); + CBaseEntity *pRagdoll = CreateServerRagdoll( pVictim, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + if ( pRagdoll ) + { + pRagdoll->SetCollisionBounds( pVictim->CollisionProp()->OBBMins(), pVictim->CollisionProp()->OBBMaxs() ); + + CTakeDamageInfo ragdollInfo( GetOwnerEntity(), GetOwnerEntity(), 10000.0f, DMG_GENERIC | DMG_REMOVENORAGDOLL ); + pVictim->TakeDamage( ragdollInfo ); + + // Return a pointer to the new ragdoll + *pPhysObj = pRagdoll->VPhysicsGetObject(); + return true; + } + } + + *pPhysObj = NULL; + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Creates a dense ball with a mass equal to the aggregate mass consumed by the vortex +//----------------------------------------------------------------------------- +void CGravityVortexController::CreateDenseBall( void ) +{ + CBaseEntity *pBall = CreateEntityByName( "prop_physics" ); + + pBall->SetModel( DENSE_BALL_MODEL ); + pBall->SetAbsOrigin( GetAbsOrigin() ); + pBall->Spawn(); + + IPhysicsObject *pObj = pBall->VPhysicsGetObject(); + if ( pObj != NULL ) + { + pObj->SetMass( GetConsumedMass() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Pulls physical objects towards the vortex center, killing them if they come too near +//----------------------------------------------------------------------------- +void CGravityVortexController::PullThink( void ) +{ + Vector mins, maxs; + mins = GetAbsOrigin() - Vector( m_flRadius, m_flRadius, m_flRadius ); + maxs = GetAbsOrigin() + Vector( m_flRadius, m_flRadius, m_flRadius ); + + // Sort through nearby entities, pulling all the ones we can into our vortex! + CBaseEntity *pEnts[128]; + int numEnts = UTIL_EntitiesInBox( pEnts, ARRAYSIZE(pEnts), mins, maxs, 0 ); + for ( int i = 0; i < numEnts; i++ ) + { + if ( pEnts[i]->IsPlayer() ) + continue; // ignore players + + CNPC_Citizen *pCitizen = dynamic_cast( pEnts[i] ); + if ( pCitizen ) + continue; // ignore support NPCs + + // Added spawnable prop system + CContingency_SpawnableProp *pSpawnableProp = dynamic_cast( pEnts[i] ); + if ( pSpawnableProp ) + continue; // ignore spawned props + + CNPC_FloorTurret *pTurret = dynamic_cast( pEnts[i] ); + if ( pTurret ) + continue; // ignore deployed turrets + + IPhysicsObject *pPhysObject = NULL; + + CAI_BaseNPC *pNPC = dynamic_cast( pEnts[i] ); + if ( pNPC ) + KillNPCInRange( pNPC, &pPhysObject ); + + if ( !pPhysObject ) + { + pPhysObject = pEnts[i]->VPhysicsGetObject(); + if ( !pPhysObject ) + continue; + } + + float mass; + + CRagdollProp *pRagdoll = dynamic_cast( pEnts[i] ); + if ( pRagdoll ) + { + ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll(); + mass = 0.0f; + + // Find the aggregate mass of the whole ragdoll + for ( int j = 0; j < pRagdollPhys->listCount; ++j ) + mass += pRagdollPhys->list[j].pObject->GetMass(); + } + else + mass = pPhysObject->GetMass(); + + Vector vecForce = GetAbsOrigin() - pEnts[i]->WorldSpaceCenter(); + Vector vecForce2D = vecForce; + vecForce2D[2] = 0.0f; + float dist2D = VectorNormalize( vecForce2D ); + float dist = VectorNormalize( vecForce ); + + if ( dist < 48.0f ) + { + ConsumeEntity( pEnts[i] ); + continue; + } + + // Must be within the radius + if ( dist > m_flRadius ) + continue; + + // Find the pull force + vecForce *= ( 1.0f - ( dist2D / m_flRadius ) ) * m_flStrength * mass; + + if ( pEnts[i]->VPhysicsGetObject() ) + { + // Lethal Stigma + // ADDED: Hopwire + // Pull the object in + //pEnts[i]->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, GetAbsOrigin(), m_flStrength, DMG_BLAST ) ); + pEnts[i]->VPhysicsTakeDamage( CTakeDamageInfo( GetOwnerEntity(), GetOwnerEntity(), vecForce, GetAbsOrigin(), m_flStrength, DMG_BLAST ) ); + } + + // Lethal Stigma + // ADDED: Hopwire + // Dissolve ragdolls! + if ( pRagdoll ) + pRagdoll->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); + } + + // Keep going if need-be + if ( m_flEndTime > gpGlobals->curtime ) + { + SetThink( &CGravityVortexController::PullThink ); + SetNextThink( gpGlobals->curtime + 0.1f ); + } + else + { + //Msg( "Consumed %.2f kilograms\n", m_flMass ); + //CreateDenseBall(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Starts the vortex working +//----------------------------------------------------------------------------- +void CGravityVortexController::StartPull( const Vector &origin, float radius, float strength, float duration ) +{ + SetAbsOrigin( origin ); + m_flEndTime = gpGlobals->curtime + duration; + m_flRadius = radius; + m_flStrength= strength; + + SetThink( &CGravityVortexController::PullThink ); + SetNextThink( gpGlobals->curtime + 0.1f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Creation utility +//----------------------------------------------------------------------------- + +// Lethal Stigma +// ADDED: Hopwire +// Death messages support for multiplayer! +//CGravityVortexController *CGravityVortexController::Create( const Vector &origin, float radius, float strength, float duration, CBaseEntity *pEntity ) +CGravityVortexController *CGravityVortexController::Create( const Vector &origin, float radius, float strength, float duration, CBaseEntity *pOwnerOfHopwireGrenade ) +{ + // Create an instance of the vortex + CGravityVortexController *pVortex = (CGravityVortexController *) CreateEntityByName( "vortex_controller" ); + if ( pVortex == NULL ) + return NULL; + + // Start the vortex working + pVortex->StartPull( origin, radius, strength, duration ); + + // Lethal Stigma + // ADDED: Hopwire + // Death messages support for multiplayer! + pVortex->SetOwnerEntity( pOwnerOfHopwireGrenade ); + + return pVortex; +} + +BEGIN_DATADESC( CGravityVortexController ) + DEFINE_FIELD( m_flMass, FIELD_FLOAT ), + DEFINE_FIELD( m_flEndTime, FIELD_TIME ), + DEFINE_FIELD( m_flRadius, FIELD_FLOAT ), + DEFINE_FIELD( m_flStrength, FIELD_FLOAT ), + + DEFINE_THINKFUNC( PullThink ), +END_DATADESC() + +LINK_ENTITY_TO_CLASS( vortex_controller, CGravityVortexController ); + +#define GRENADE_MODEL_CLOSED "models/roller.mdl" +#define GRENADE_MODEL_OPEN "models/roller_spikes.mdl" + +BEGIN_DATADESC( CGrenadeHopwire ) + DEFINE_FIELD( m_hVortexController, FIELD_EHANDLE ), + + DEFINE_THINKFUNC( EndThink ), + DEFINE_THINKFUNC( CombatThink ), +END_DATADESC() + +LINK_ENTITY_TO_CLASS( npc_grenade_hopwire, CGrenadeHopwire ); + +IMPLEMENT_SERVERCLASS_ST( CGrenadeHopwire, DT_GrenadeHopwire ) +END_SEND_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGrenadeHopwire::Spawn( void ) +{ + Precache(); + + SetModel( GRENADE_MODEL_CLOSED ); + + // Lethal Stigma + // ADDED: Hopwire + //SetCollisionGroup( COLLISION_GROUP_PROJECTILE ); + SetCollisionGroup( COLLISION_GROUP_WEAPON ); + + CreateVPhysics(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CGrenadeHopwire::CreateVPhysics() +{ + // Create the object in the physics system + VPhysicsInitNormal( SOLID_BBOX, 0, false ); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGrenadeHopwire::Precache( void ) +{ + // FIXME: Replace + //PrecacheSound("NPC_Strider.Shoot"); + //PrecacheSound("d3_citadel.weapon_zapper_beam_loop2"); + + // Lethal Stigma + // ADDED: Hopwire + // Exploding sound! + PrecacheScriptSound( "Weapon_Hopwire.Explode" ); + + PrecacheModel( GRENADE_MODEL_OPEN ); + PrecacheModel( GRENADE_MODEL_CLOSED ); + + PrecacheModel( DENSE_BALL_MODEL ); + + BaseClass::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : timer - +//----------------------------------------------------------------------------- +void CGrenadeHopwire::SetTimer( float timer ) +{ + SetThink( &CBaseGrenade::PreDetonate ); + SetNextThink( gpGlobals->curtime + timer ); +} + +#define MAX_STRIDER_KILL_DISTANCE_HORZ (hopwire_strider_kill_dist_h.GetFloat()) // Distance a Strider will be killed if within +#define MAX_STRIDER_KILL_DISTANCE_VERT (hopwire_strider_kill_dist_v.GetFloat()) // Distance a Strider will be killed if within + +#define MAX_STRIDER_STUN_DISTANCE_HORZ (MAX_STRIDER_KILL_DISTANCE_HORZ*2) // Distance a Strider will be stunned if within +#define MAX_STRIDER_STUN_DISTANCE_VERT (MAX_STRIDER_KILL_DISTANCE_VERT*2) // Distance a Strider will be stunned if within + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGrenadeHopwire::KillStriders( void ) +{ + CBaseEntity *pEnts[128]; + Vector mins, maxs; + + ClearBounds( mins, maxs ); + AddPointToBounds( -Vector( MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ ), mins, maxs ); + AddPointToBounds( Vector( MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ, MAX_STRIDER_STUN_DISTANCE_HORZ ), mins, maxs ); + AddPointToBounds( -Vector( MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT ), mins, maxs ); + AddPointToBounds( Vector( MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT, MAX_STRIDER_STUN_DISTANCE_VERT ), mins, maxs ); + + // FIXME: It's probably much faster to simply iterate over the striders in the map, rather than any entity in the radius - jdw + + // Find any striders in range of us + int numTargets = UTIL_EntitiesInBox( pEnts, ARRAYSIZE( pEnts ), GetAbsOrigin()+mins, GetAbsOrigin()+maxs, FL_NPC ); + float targetDistHorz, targetDistVert; + + for ( int i = 0; i < numTargets; i++ ) + { + // Only affect striders + if ( FClassnameIs( pEnts[i], "npc_strider" ) == false ) + continue; + + // We categorize our spatial relation to the strider in horizontal and vertical terms, so that we can specify both parameters separately + targetDistHorz = UTIL_DistApprox2D( pEnts[i]->GetAbsOrigin(), GetAbsOrigin() ); + targetDistVert = fabs( pEnts[i]->GetAbsOrigin()[2] - GetAbsOrigin()[2] ); + + if ( targetDistHorz < MAX_STRIDER_KILL_DISTANCE_HORZ && targetDistHorz < MAX_STRIDER_KILL_DISTANCE_VERT ) + { + // Kill the strider + float fracDamage = ( pEnts[i]->GetMaxHealth() / hopwire_strider_hits.GetFloat() ) + 1.0f; + CTakeDamageInfo killInfo( this, this, fracDamage, DMG_GENERIC ); + Vector killDir = pEnts[i]->GetAbsOrigin() - GetAbsOrigin(); + VectorNormalize( killDir ); + + killInfo.SetDamageForce( killDir * -1000.0f ); + killInfo.SetDamagePosition( GetAbsOrigin() ); + + pEnts[i]->TakeDamage( killInfo ); + } + else if ( targetDistHorz < MAX_STRIDER_STUN_DISTANCE_HORZ && targetDistHorz < MAX_STRIDER_STUN_DISTANCE_VERT ) + { + // Stun the strider + CTakeDamageInfo killInfo( this, this, 200.0f, DMG_GENERIC ); + pEnts[i]->TakeDamage( killInfo ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGrenadeHopwire::EndThink( void ) +{ + if ( hopwire_vortex.GetBool() ) + { + EntityMessageBegin( this, true ); + WRITE_BYTE( 1 ); + MessageEnd(); + } + + SetThink( &CBaseEntity::SUB_Remove ); + SetNextThink( gpGlobals->curtime + 1.0f ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGrenadeHopwire::CombatThink( void ) +{ + // Stop the grenade from moving + AddEFlags( EF_NODRAW ); + AddFlag( FSOLID_NOT_SOLID ); + VPhysicsDestroyObject(); + SetAbsVelocity( vec3_origin ); + SetMoveType( MOVETYPE_NONE ); + + // Do special behaviors if there are any striders in the area + KillStriders(); + + // FIXME: Replace + //EmitSound("NPC_Strider.Shoot"); + //EmitSound("d3_citadel.weapon_zapper_beam_loop2"); + + // Quick screen flash + CBasePlayer *pPlayer = ToBasePlayer( GetThrower() ); + color32 white = { 255,255,255,255 }; + UTIL_ScreenFade( pPlayer, white, 0.2f, 0.0f, FFADE_IN ); + + // Create the vortex controller to pull entities towards us + if ( hopwire_vortex.GetBool() ) + { + // Lethal Stigma + // ADDED: Hopwire + // Dramatically lowered radius of vortex to prevent massive slaughter + // Death messages support for multiplayer! + //m_hVortexController = CGravityVortexController::Create( GetAbsOrigin(), 512, 150, 3.0f ); + m_hVortexController = CGravityVortexController::Create( GetAbsOrigin(), 200, 150, 3.0f, pPlayer ); + + // Start our client-side effect + EntityMessageBegin( this, true ); + WRITE_BYTE( 0 ); + MessageEnd(); + + // Begin to stop in two seconds + SetThink( &CGrenadeHopwire::EndThink ); + SetNextThink( gpGlobals->curtime + 2.0f ); + } + else + { + // Remove us immediately + SetThink( &CBaseEntity::SUB_Remove ); + SetNextThink( gpGlobals->curtime + 0.1f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGrenadeHopwire::SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity ) +{ + IPhysicsObject *pPhysicsObject = VPhysicsGetObject(); + + if ( pPhysicsObject != NULL ) + { + pPhysicsObject->AddVelocity( &velocity, &angVelocity ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Hop off the ground to start deployment +//----------------------------------------------------------------------------- +void CGrenadeHopwire::Detonate( void ) +{ + // Lethal Stigma + // ADDED: Hopwire + // Exploding sound! + EmitSound( "Weapon_Hopwire.Explode" ); + + SetModel( GRENADE_MODEL_OPEN ); + + AngularImpulse hopAngle = RandomAngularImpulse( -300, 300 ); + + //Find out how tall the ceiling is and always try to hop halfway + trace_t tr; + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, MAX_HOP_HEIGHT*2 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); + + // Jump half the height to the found ceiling + float hopHeight = min( MAX_HOP_HEIGHT, (MAX_HOP_HEIGHT*tr.fraction) ); + + //Add upwards velocity for the "hop" + Vector hopVel( 0.0f, 0.0f, hopHeight ); + SetVelocity( hopVel, hopAngle ); + + // Get the time until the apex of the hop + float apexTime = sqrt( hopHeight / sv_gravity.GetFloat() ); + + // Explode at the apex + SetThink( &CGrenadeHopwire::CombatThink ); + SetNextThink( gpGlobals->curtime + apexTime); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseGrenade *HopWire_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, float timer ) +{ + CGrenadeHopwire *pGrenade = (CGrenadeHopwire *) CBaseEntity::Create( "npc_grenade_hopwire", position, angles, pOwner ); + + // Only set ourselves to detonate on a timer if we're not a trap hopwire + //if ( hopwire_trap.GetBool() == false ) + //{ + pGrenade->SetTimer( timer ); + //} + + pGrenade->SetVelocity( velocity, angVelocity ); + pGrenade->SetThrower( ToBaseCombatCharacter( pOwner ) ); + + return pGrenade; +} diff --git a/src/game/server/contingency/grenade_hopwire.h b/src/game/server/contingency/grenade_hopwire.h new file mode 100644 index 0000000..2c813ce --- /dev/null +++ b/src/game/server/contingency/grenade_hopwire.h @@ -0,0 +1,48 @@ +// Ported directly from Lethal Stigma, with a few adjustments + +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef GRENADE_HOPWIRE_H +#define GRENADE_HOPWIRE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basegrenade_shared.h" +#include "Sprite.h" + +extern ConVar hopwire_trap; + +class CGravityVortexController; + +class CGrenadeHopwire : public CBaseGrenade +{ + DECLARE_CLASS( CGrenadeHopwire, CBaseGrenade ); + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + +public: + void Spawn( void ); + void Precache( void ); + bool CreateVPhysics( void ); + void SetTimer( float timer ); + void SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity ); + void Detonate( void ); + + void EndThink( void ); // Last think before going away + void CombatThink( void ); // Makes the main explosion go off + +protected: + + void KillStriders( void ); + + CHandle m_hVortexController; +}; + +extern CBaseGrenade *HopWire_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, float timer ); + +#endif // GRENADE_HOPWIRE_H diff --git a/src/game/server/hl2/npc_BaseZombie.cpp b/src/game/server/hl2/npc_BaseZombie.cpp index 292ce45..229a7a5 100644 --- a/src/game/server/hl2/npc_BaseZombie.cpp +++ b/src/game/server/hl2/npc_BaseZombie.cpp @@ -47,6 +47,15 @@ #include "weapon_physcannon.h" #include "ammodef.h" #include "vehicle_base.h" + +///// + + // Contingency - James + // Consider any detatched headcrabs of wave NPCs, well, wave NPCs + +#include "contingency_gamerules.h" + +///// // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -761,14 +770,7 @@ bool CNPC_BaseZombie::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDa //----------------------------------------------------------------------------- HeadcrabRelease_t CNPC_BaseZombie::ShouldReleaseHeadcrab( const CTakeDamageInfo &info, float flDamageThreshold ) { - -///// - - // Contingency - James - // Disable headcrabs on zombies - // http://developer.valvesoftware.com/wiki/HL2_snippets#Remove_Head_Crab - - /*if ( m_iHealth <= 0 ) + if ( m_iHealth <= 0 ) { if ( info.GetDamageType() & DMG_REMOVENORAGDOLL ) return RELEASE_NO; @@ -806,12 +808,7 @@ HeadcrabRelease_t CNPC_BaseZombie::ShouldReleaseHeadcrab( const CTakeDamageInfo } } - return RELEASE_NO;*/ - - return ((m_iHealth <= 0) && m_fIsTorso && IsChopped(info)) ? RELEASE_RAGDOLL_SLICED_OFF : RELEASE_NO; - -///// - + return RELEASE_NO; } //----------------------------------------------------------------------------- @@ -2384,13 +2381,7 @@ bool CNPC_BaseZombie::HeadcrabFits( CBaseAnimating *pCrab ) //----------------------------------------------------------------------------- void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &vecVelocity, bool fRemoveHead, bool fRagdollBody, bool fRagdollCrab ) { - -///// - - // Contingency - James - // Disable headcrabs on zombies - - /*CAI_BaseNPC *pCrab; + CAI_BaseNPC *pCrab; Vector vecSpot = vecOrigin; // Until the headcrab is a bodygroup, we have to approximate the @@ -2514,6 +2505,28 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve CopyRenderColorTo( pCrab ); pCrab->Activate(); + +///// + + // Contingency - James + // Consider any released headcrabs of wave NPCs, well, wave NPCs + + if ( ContingencyRules()->GetCurrentWaveNPCList()->Find(this) != -1 ) + { + // This zombie is a wave NPC! + // That means any of its released headcrabs should also be considered wave NPCs... + + if ( ContingencyRules()->GetCurrentWaveNPCList() && (ContingencyRules()->GetCurrentWaveNPCList()->Find(pCrab) == -1) ) + { + ContingencyRules()->GetCurrentWaveNPCList()->AddToTail( pCrab ); + ContingencyRules()->IncrementNumEnemiesRemaining(); + // notice how ContingencyRules()->IncrementNumEnemiesSpawned() is NOT used here + // so as not to throw off our original wave count + } + } + +///// + } if( fRemoveHead ) @@ -2524,10 +2537,7 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve if( fRagdollBody ) { BecomeRagdollOnClient( vec3_origin ); - }*/ - -///// - + } } diff --git a/src/game/server/hl2/npc_PoisonZombie.cpp b/src/game/server/hl2/npc_PoisonZombie.cpp index 16cf12d..2dac03c 100644 --- a/src/game/server/hl2/npc_PoisonZombie.cpp +++ b/src/game/server/hl2/npc_PoisonZombie.cpp @@ -24,6 +24,15 @@ #include "engine/IEngineSound.h" #include "npc_BaseZombie.h" +///// + + // Contingency - James + // Consider any detatched headcrabs of wave NPCs, well, wave NPCs + +#include "contingency_gamerules.h" + +///// + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -285,17 +294,7 @@ void CNPC_PoisonZombie::Spawn( void ) { Precache(); -///// - - // Contingency - James - // Disable headcrabs on zombies - - //m_fIsTorso = m_fIsHeadless = false; - - m_fIsTorso = false; - m_fIsHeadless = true; - -///// + m_fIsTorso = m_fIsHeadless = false; #ifdef HL2_EPISODIC SetBloodColor( BLOOD_COLOR_ZOMBIE ); @@ -319,12 +318,7 @@ void CNPC_PoisonZombie::Spawn( void ) m_pSlowBreathSound = ENVELOPE_CONTROLLER.SoundCreate( filter2, entindex(), CHAN_ITEM, "NPC_PoisonZombie.Moan1", ATTN_NORM ); ENVELOPE_CONTROLLER.Play( m_pSlowBreathSound, BREATH_VOL_MAX, 100 ); -///// - - // Contingency - James - // Disable headcrabs on zombies - - /*int nCrabs = m_nCrabCount; + int nCrabs = m_nCrabCount; if ( !nCrabs ) { nCrabs = MAX_CRABS; @@ -361,12 +355,7 @@ void CNPC_PoisonZombie::Spawn( void ) for ( int i = 0; i < MAX_CRABS; i++ ) { EnableCrab( i, ( nBitMask & ( 1 << i ) ) != 0 ); - }*/ - - m_nCrabCount = 0; - -///// - + } } @@ -727,6 +716,27 @@ void CNPC_PoisonZombie::HandleAnimEvent( animevent_t *pEvent ) pCrab->ThrowAt( vecEnemyEyePos ); } +///// + + // Contingency - James + // Consider any released headcrabs of wave NPCs, well, wave NPCs + + if ( ContingencyRules()->GetCurrentWaveNPCList()->Find(this) != -1 ) + { + // This zombie is a wave NPC! + // That means any of its released headcrabs should also be considered wave NPCs... + + if ( ContingencyRules()->GetCurrentWaveNPCList() && (ContingencyRules()->GetCurrentWaveNPCList()->Find(pCrab) == -1) ) + { + ContingencyRules()->GetCurrentWaveNPCList()->AddToTail( pCrab ); + ContingencyRules()->IncrementNumEnemiesRemaining(); + // notice how ContingencyRules()->IncrementNumEnemiesSpawned() is NOT used here + // so as not to throw off our original wave count + } + } + +///// + if (m_nCrabCount == 0) { CapabilitiesRemove( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 ); @@ -833,6 +843,28 @@ void CNPC_PoisonZombie::EvacuateNest( bool bExplosion, float flDamage, CBaseEnti pCrab->Eject( vecAngles, flVelocityScale, pAttacker ); EnableCrab( i, false ); + +///// + + // Contingency - James + // Consider any released headcrabs of wave NPCs, well, wave NPCs + + if ( ContingencyRules()->GetCurrentWaveNPCList()->Find(this) != -1 ) + { + // This zombie is a wave NPC! + // That means any of its released headcrabs should also be considered wave NPCs... + + if ( ContingencyRules()->GetCurrentWaveNPCList() && (ContingencyRules()->GetCurrentWaveNPCList()->Find(pCrab) == -1) ) + { + ContingencyRules()->GetCurrentWaveNPCList()->AddToTail( pCrab ); + ContingencyRules()->IncrementNumEnemiesRemaining(); + // notice how ContingencyRules()->IncrementNumEnemiesSpawned() is NOT used here + // so as not to throw off our original wave count + } + } + +///// + } } } diff --git a/src/game/server/hl2/npc_fastzombie.cpp b/src/game/server/hl2/npc_fastzombie.cpp index bd8b649..9fa2487 100644 --- a/src/game/server/hl2/npc_fastzombie.cpp +++ b/src/game/server/hl2/npc_fastzombie.cpp @@ -652,17 +652,7 @@ void CFastZombie::Spawn( void ) m_fJustJumped = false; -///// - - // Contingency - James - // Disable headcrabs on zombies - - //m_fIsTorso = m_fIsHeadless = false; - - m_fIsTorso = false; - m_fIsHeadless = true; - -///// + m_fIsTorso = m_fIsHeadless = false; if( FClassnameIs( this, "npc_fastzombie" ) ) { diff --git a/src/game/server/hl2/npc_zombie.cpp b/src/game/server/hl2/npc_zombie.cpp index ff4b0c2..3736bf8 100644 --- a/src/game/server/hl2/npc_zombie.cpp +++ b/src/game/server/hl2/npc_zombie.cpp @@ -273,17 +273,7 @@ void CZombie::Spawn( void ) m_fIsTorso = true; } -///// - - // Contingency - James - // Disable headcrabs on zombies - // http://developer.valvesoftware.com/wiki/HL2_snippets#Remove_Head_Crab - - //m_fIsHeadless = false; - - m_fIsHeadless = true; - -///// + m_fIsHeadless = false; #ifdef HL2_EPISODIC SetBloodColor( BLOOD_COLOR_ZOMBIE ); diff --git a/src/game/server/hl2mp/grenade_tripmine.cpp b/src/game/server/hl2mp/grenade_tripmine.cpp index 13f5cc9..0fecd8f 100644 --- a/src/game/server/hl2mp/grenade_tripmine.cpp +++ b/src/game/server/hl2mp/grenade_tripmine.cpp @@ -13,6 +13,20 @@ #include "engine/IEngineSound.h" #include "explode.h" +///// + + // Contingency - James + // Prevent S.L.A.M. tripmines from being set off by certain entities + +#include "npc_citizen17.h" + +// Added spawnable prop system +#include "contingency_spawnableprop.h" + +#include "npc_contingency_turret.h" + +///// + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -244,8 +258,13 @@ void CTripmineGrenade::BeamBreakThink( void ) bAttachMoved = true; } +///// + + // Contingency - James + // Prevent S.L.A.M. tripmines from being set off by certain entities + // Also blow up if the attached entity goes away, ie: a crate - if (pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001 || ( m_bAttached && m_hAttachEntity.Get() == NULL) || bAttachMoved ) + /*if (pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001 || ( m_bAttached && m_hAttachEntity.Get() == NULL) || bAttachMoved ) { m_iHealth = 0; if (m_pConstraint) @@ -253,8 +272,43 @@ void CTripmineGrenade::BeamBreakThink( void ) Event_Killed( CTakeDamageInfo( (CBaseEntity*)m_hOwner, this, 100, GIB_NORMAL ) ); return; + }*/ + + bool shouldBlowUp = true; + + CBasePlayer *pPlayer = ToBasePlayer( pEntity ); + if ( pPlayer ) + shouldBlowUp = false; + + CNPC_Citizen *pCitizen = dynamic_cast( pEntity ); + if ( pCitizen ) + shouldBlowUp = false; + + // Added spawnable prop system + CContingency_SpawnableProp *pSpawnableProp = dynamic_cast( pEntity ); + if ( pSpawnableProp ) + shouldBlowUp = false; + + CNPC_FloorTurret *pTurret = dynamic_cast( pEntity ); + if ( pTurret ) + shouldBlowUp = false; + + if ( shouldBlowUp ) + { + // Also blow up if the attached entity goes away, ie: a crate + if (pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001 || ( m_bAttached && m_hAttachEntity.Get() == NULL) || bAttachMoved ) + { + m_iHealth = 0; + if (m_pConstraint) + m_pConstraint->Deactivate(); + + Event_Killed( CTakeDamageInfo( (CBaseEntity*)m_hOwner, this, 100, GIB_NORMAL ) ); + return; + } } +///// + SetNextThink( gpGlobals->curtime + 0.05f ); } diff --git a/src/game/server/server_hl2mp-2005.vcxproj b/src/game/server/server_hl2mp-2005.vcxproj index acf74db..e2fdb17 100644 --- a/src/game/server/server_hl2mp-2005.vcxproj +++ b/src/game/server/server_hl2mp-2005.vcxproj @@ -225,6 +225,7 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server.pdb "c:\program file + @@ -345,6 +346,7 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server.pdb "c:\program file + @@ -941,6 +943,7 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server.pdb "c:\program file + @@ -1044,6 +1047,7 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server.pdb "c:\program file + diff --git a/src/game/server/server_hl2mp-2005.vcxproj.filters b/src/game/server/server_hl2mp-2005.vcxproj.filters index 7ee533b..2afd745 100644 --- a/src/game/server/server_hl2mp-2005.vcxproj.filters +++ b/src/game/server/server_hl2mp-2005.vcxproj.filters @@ -1788,6 +1788,12 @@ Source Files\Contingency\Modified Engine Files + + Source Files\Contingency\Entities + + + Source Files\Contingency\Weapons + @@ -3476,9 +3482,6 @@ Source Files\Contingency\Modified Engine Files - - Source Files - Source Files\Contingency\Modified Engine Files @@ -3545,6 +3548,15 @@ Source Files\Contingency\Entities\NPCs + + Source Files\Contingency\Modified Engine Files + + + Source Files\Contingency\Entities + + + Source Files\Contingency\Weapons + diff --git a/src/game/shared/contingency/contingency_concommands.cpp b/src/game/shared/contingency/contingency_concommands.cpp index a4057ad..a86d8f6 100644 --- a/src/game/shared/contingency/contingency_concommands.cpp +++ b/src/game/shared/contingency/contingency_concommands.cpp @@ -16,6 +16,46 @@ #ifdef CLIENT_DLL #else +void CC_Teleport( void ) +{ + CContingency_Player *pPlayer = ToContingencyPlayer( UTIL_GetCommandClient() ); + if ( !pPlayer ) + return; + + // Determine whether or not we are actually stuck inside something + // We can only teleport back to spawn if we are + Ray_t ray; + ray.Init( pPlayer->GetAbsOrigin(), pPlayer->GetAbsOrigin(), pPlayer->GetPlayerMins(), pPlayer->GetPlayerMaxs() ); + trace_t trace; + UTIL_TraceRay( ray, MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + if ( (trace.contents & MASK_PLAYERSOLID) && trace.m_pEnt ) + { + // We are in fact stuck, so we are allowed to teleport! + + // Find all spawnpoints on the map + CUtlVector *spawnpointsList = new CUtlVector(); + CBaseEntity *pEntity = gEntList.FindEntityByClassname( NULL, "info_player_deathmatch" ); + while ( pEntity != NULL ) + { + spawnpointsList->AddToTail( pEntity ); + pEntity = gEntList.FindEntityByClassname( pEntity, "info_player_deathmatch" ); + } + + // Choose a random spawnpoint to use + pEntity = spawnpointsList->Element( random->RandomInt(0, spawnpointsList->Count() - 1) ); + + delete spawnpointsList; // we don't need this anymore + + // Actually teleport the player + pPlayer->SetAbsVelocity( Vector(0, 0, 0) ); // make sure we don't kill the player from fall damage or something when teleporting them + pPlayer->SetAbsOrigin( pEntity->GetAbsOrigin() ); + pPlayer->SetAbsAngles( pEntity->GetAbsAngles() ); + } + else + ClientPrint( pPlayer, HUD_PRINTTALK, "You must be stuck to teleport back to spawn!" ); +} +static ConCommand teleport( "teleport", CC_Teleport, "Teleport to spawn (only when stuck)" ); + // Added drop system void CC_DropCurrentWeapon( void ) { @@ -120,37 +160,6 @@ void CC_RemoveSpawnablePropInFocus( const CCommand &args ) } static ConCommand removespawnablepropinfocus( "removespawnablepropinfocus", CC_RemoveSpawnablePropInFocus, "Removes the spawnable prop currently in focus" ); -// Added spawnable prop system -void CC_ToggleFrozenPropInFocus( const CCommand &args ) -{ - CContingency_Player *pPlayer = ToContingencyPlayer( UTIL_GetCommandClient() ); - if ( !pPlayer ) - return; - - if ( ContingencyRules()->GetCurrentPhase() != PHASE_INTERIM ) - { - ClientPrint( pPlayer, HUD_PRINTTALK, "Props can only be operated during interim phases." ); - return; // we're only allowed to operate props during interim phases - } - - CContingency_SpawnableProp *pProp = pPlayer->GetSpawnablePropInFocus(); - if ( !pProp ) - return; // this concommand has been used improperly - - if ( pPlayer != pProp->GetSpawnerPlayer() ) - { - ClientPrint( pPlayer, HUD_PRINTTALK, "This prop is not yours to operate." ); - return; // only our spawner can operate us - } - - if ( pProp->IsDissolving() ) - return; // props that are already dissolving should be ignored - - // Actually toggle frozen state of spawnable prop in focus - pProp->SetFrozenState( !pProp->IsFrozen() ); -} -static ConCommand togglefrozenspawnablepropinfocus( "togglefrozenspawnablepropinfocus", CC_ToggleFrozenPropInFocus, "Freezes/unfreezes the spawnable prop currently in focus" ); - // Added spawnable prop system void CC_ForgetSpawnablePropInFocus( const CCommand &args ) { @@ -187,14 +196,14 @@ void CC_CheatSetWaveType( const CCommand &args ) if ( (args.ArgC() < 1) || (Q_strcmp(args.Arg(1), "") == 0) || (atoi(args.Arg(1)) < WAVE_NONE) || (atoi(args.Arg(1)) >= NUM_WAVES) ) { - Msg( "Description: sets the next wave type according to the integer specified (-1 for no preference, 0 for headcrab, 1 for antlion, 2 for zombie, 3 for combine)\n" ); - Msg( "Usage: contingency_cheat_setwavetype \n" ); + Msg( "Description: sets the next wave type according to the integer specified (-1 for no preference, 0 for antlion, 1 for zombie, 2 for combine)\n" ); + Msg( "Usage: contingency_cheat_setwavetype \n" ); return; } ContingencyRules()->SetPreferredWaveType( atoi(args.Arg(1)) ); } -static ConCommand contingency_cheat_setwavetype( "contingency_cheat_setwavetype", CC_CheatSetWaveType, "CHEAT: Sets the next wave type according to the integer specified (-1 for no preference, 0 for headcrab, 1 for antlion, 2 for zombie, 3 for combine)", FCVAR_CHEAT ); +static ConCommand contingency_cheat_setwavetype( "contingency_cheat_setwavetype", CC_CheatSetWaveType, "CHEAT: Sets the next wave type according to the integer specified (-1 for no preference, 0 for antlion, 1 for zombie, 2 for combine)", FCVAR_CHEAT ); void CC_CheatSetCredits( const CCommand &args ) { diff --git a/src/game/shared/contingency/contingency_gamerules.cpp b/src/game/shared/contingency/contingency_gamerules.cpp index 7d781d7..0ba4567 100644 --- a/src/game/shared/contingency/contingency_gamerules.cpp +++ b/src/game/shared/contingency/contingency_gamerules.cpp @@ -43,6 +43,9 @@ #include "npc_citizen17.h" #include "npc_contingency_turret.h" + + // Added wave system + #include "contingency_wave_spawnpoint.h" #endif REGISTER_GAMERULES_CLASS( CContingencyRules ); @@ -108,10 +111,12 @@ IMPLEMENT_NETWORKCLASS_ALIASED( ContingencyRulesProxy, DT_ContingencyRulesProxy ConVar contingency_wave_support( "contingency_wave_support", "1", FCVAR_NOTIFY, "Toggles the spawning support NPCs during interim phases when server isn't full" ); // Added wave system - ConVar contingency_wave_multiplier_headcrabs( "contingency_wave_multiplier_headcrabs", "3", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by during headcrab waves" ); - ConVar contingency_wave_multiplier_antlions( "contingency_wave_multiplier_antlions", "4", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by during antlion waves" ); - ConVar contingency_wave_multiplier_zombies( "contingency_wave_multiplier_zombies", "5", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by during zombie waves" ); - ConVar contingency_wave_multiplier_combine( "contingency_wave_multiplier_combine", "2", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by during combine waves" ); + ConVar contingency_wave_multiplier_arbitrary( "contingency_wave_multiplier_arbitrary", "5", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by based on nothing (i.e. a magic number)" ); + ConVar contingency_wave_multiplier_players( "contingency_wave_multiplier_players", "0.125", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by based on the number of players" ); + //ConVar contingency_wave_multiplier_headcrabs( "contingency_wave_multiplier_headcrabs", "3", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by during headcrab waves" ); + ConVar contingency_wave_multiplier_antlions( "contingency_wave_multiplier_antlions", "1.25", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by during antlion waves" ); + ConVar contingency_wave_multiplier_zombies( "contingency_wave_multiplier_zombies", "1", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by during zombie waves" ); + ConVar contingency_wave_multiplier_combine( "contingency_wave_multiplier_combine", "0.75", FCVAR_NOTIFY, "Defines the amount to scale the amount of NPCs spawned by during combine waves" ); #else ConVar contingency_client_heartbeatsounds( "contingency_client_heartbeatsounds", "1", FCVAR_ARCHIVE, "Toggles heartbeat sounds when health is low" ); @@ -125,6 +130,9 @@ IMPLEMENT_NETWORKCLASS_ALIASED( ContingencyRulesProxy, DT_ContingencyRulesProxy ConVar contingency_client_updateloadout( "contingency_client_updateloadout", "0", FCVAR_USERINFO | FCVAR_SERVER_CAN_EXECUTE, "Requests a manual update to the preferred loadout" ); #endif +// Added wave system +ConVar contingency_wave_challenge_frequency( "contingency_wave_challenge_frequency", "5", FCVAR_NOTIFY | FCVAR_REPLICATED, "Defines the frequency of challenge waves (i.e. defines x, where every x wave is a challenge wave)" ); + #ifdef CLIENT_DLL void RecvProxy_ContingencyRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) { @@ -160,7 +168,7 @@ CContingencyRules::CContingencyRules() m_iRestartDelay = 0; // Added phase system - m_iInterimPhaseLength = 30; + m_iInterimPhaseLength = 60; // Added radar display // This information is updated by a contingency_configuration entity (if one exists) when it spawns @@ -168,11 +176,11 @@ CContingencyRules::CContingencyRules() // Added spawnable prop system // This information is updated by a contingency_configuration entity (if one exists) when it spawns - m_iMapMaxPropsPerPlayer = 100; + m_iMapMaxPropsPerPlayer = 50; // Added credits system // This information is updated by a contingency_configuration entity (if one exists) when it spawns - m_iMapStartingCredits = 0; + m_iMapStartingCredits = 3; // Added wave system // This information is updated by a contingency_configuration entity (if one exists) when it spawns @@ -227,6 +235,7 @@ void CContingencyRules::ResetPhaseVariables( void ) SetWaveNumber( 0 ); // wave number is set to 1 after the first interim phase (i.e. this is intentional) SetWaveType( WAVE_NONE ); SetPreferredWaveType( WAVE_NONE ); + SetPreferredNPCType( "" ); SetNumEnemiesRemaining( 0 ); SetCalculatedNumEnemies( 0 ); SetNumEnemiesSpawned( 0 ); @@ -265,8 +274,8 @@ void CContingencyRules::PrecacheStuff( void ) int i; // Added wave system - for ( i = 0; i < NUM_HEADCRAB_NPCS; i++ ) - UTIL_PrecacheOther( kWaveHeadcrabsNPCTypes[i] ); + /*for ( i = 0; i < NUM_HEADCRAB_NPCS; i++ ) + UTIL_PrecacheOther( kWaveHeadcrabsNPCTypes[i] );*/ for ( i = 0; i < NUM_ANTLION_NPCS; i++ ) UTIL_PrecacheOther( kWaveAntlionsNPCTypes[i] ); for ( i = 0; i < NUM_ZOMBIE_NPCS; i++ ) @@ -539,19 +548,16 @@ void CContingencyRules::ClientDisconnected( edict_t *pClient ) #ifndef CLIENT_DLL void CContingencyRules::PerformWaveCalculations( void ) { - // See what wave types our current map supports - // via our custom contingency_configuration entity - // (if a map doesn't have one, then we're assuming - // it supports all wave types) - - if ( !DoesMapSupportHeadcrabs() && !DoesMapSupportAntlions() && !DoesMapSupportZombies() && !DoesMapSupportCombine() ) + // See what wave types our current map supports via our custom contingency_configuration entity + // (if a map doesn't have one, then we're assuming it supports all wave types) + if ( /*!DoesMapSupportHeadcrabs() && */!DoesMapSupportAntlions() && !DoesMapSupportZombies() && !DoesMapSupportCombine() ) { // The current map does not appear to support any type of wave // This isn't allowed, so just pretend we support all of them Warning("The current map is set not to allow any waves. This is not allowed, and to prevent the game from breaking, all waves have been enabled. Please check your contingency_configuration entity flags!\n"); - DoesMapSupportHeadcrabs( true ); + //DoesMapSupportHeadcrabs( true ); DoesMapSupportAntlions( true ); DoesMapSupportZombies( true ); DoesMapSupportCombine( true ); @@ -562,7 +568,7 @@ void CContingencyRules::PerformWaveCalculations( void ) if ( waveSelected == WAVE_NONE ) { waveSelected = random->RandomInt( WAVE_NONE + 1, NUM_WAVES - 1 ); - while ( ((waveSelected == WAVE_HEADCRABS) && !DoesMapSupportHeadcrabs()) || + while ( /*((waveSelected == WAVE_HEADCRABS) && !DoesMapSupportHeadcrabs()) ||*/ ((waveSelected == WAVE_ANTLIONS) && !DoesMapSupportAntlions()) || ((waveSelected == WAVE_ZOMBIES) && !DoesMapSupportZombies()) || ((waveSelected == WAVE_COMBINE) && !DoesMapSupportCombine()) ) @@ -573,49 +579,152 @@ void CContingencyRules::PerformWaveCalculations( void ) SetWaveType( waveSelected ); - // Now we need to calculate exactly how many NPCs to spawn, - // which depends on several variables, including how many - // waves players have successfully defeated as well as - // the type of NPCs we're going to be spawning, the ladder - // of which was just determined above - - // For this task, I've chosen to use some simple multiplers for each wave type, - // each of which is tied to its own ConVar for easy editing - // (maps can now manipulate these ConVars using addition/subtraction-based offsets too) - - // So yeah, we're also going to want to factor in how many players are - // on the server so as not to overwhelm (or underwhelm) folks - // so quickly that we turn them off from the game entirely... - - // ...so instead of just using wave multipliers, I've opted for - // multiplying the current wave number by the total number of players - // on the server as a base... - - // ...which seems kind of random, and it is, so I would imagine - // this is going to be changed eventually, but we'll see! - - int numEnemiesToSpawnThisWave = GetWaveNumber() * GetTotalNumPlayers(); - + // Start by factoring in the current wave number + int numEnemiesToSpawnThisWave = contingency_wave_multiplier_arbitrary.GetFloat() * GetWaveNumber(); + + // Next, we consider the wave type that's been selected switch ( waveSelected ) { - case WAVE_HEADCRABS: + /*case WAVE_HEADCRABS: numEnemiesToSpawnThisWave = numEnemiesToSpawnThisWave * (contingency_wave_multiplier_headcrabs.GetFloat() + GetMapHeadcrabWaveMultiplierOffset()); - break; + break;*/ case WAVE_ANTLIONS: - numEnemiesToSpawnThisWave = numEnemiesToSpawnThisWave * - (contingency_wave_multiplier_antlions.GetFloat() + GetMapAntlionWaveMultiplierOffset()); + numEnemiesToSpawnThisWave *= contingency_wave_multiplier_antlions.GetFloat() + GetMapAntlionWaveMultiplierOffset(); break; case WAVE_ZOMBIES: - numEnemiesToSpawnThisWave = numEnemiesToSpawnThisWave * - (contingency_wave_multiplier_zombies.GetFloat() + GetMapZombieWaveMultiplierOffset()); + numEnemiesToSpawnThisWave *= contingency_wave_multiplier_zombies.GetFloat() + GetMapZombieWaveMultiplierOffset(); break; case WAVE_COMBINE: - numEnemiesToSpawnThisWave = numEnemiesToSpawnThisWave * - (contingency_wave_multiplier_combine.GetFloat() + GetMapCombineWaveMultiplierOffset()); + numEnemiesToSpawnThisWave *= contingency_wave_multiplier_combine.GetFloat() + GetMapCombineWaveMultiplierOffset(); break; } + // Lastly, we consider how many players there are + numEnemiesToSpawnThisWave *= 1 - ((GetMaxNumPlayers() - GetTotalNumPlayers()) * contingency_wave_multiplier_players.GetFloat()); + + // Handle challenge waves + if ( IsChallengeWave() ) + { + // Firstly, we need to figure out exactly what NPCs we can and cannot spawn on this map + // to prevent us from picking an NPC type for our challenge wave that we + // cannot actually spawn, which would throw the whole game out of whack + + // We do this by looping through all of the wave spawnpoints on this map, + // seeing what NPC types each one supports, where NPC types that are not supported + // by any wave spawnpoints should be ignored with regards to our challenge wave "drawing" + + /*bool bConsiderHeadcrab = false; + bool bConsiderHeadcrabFast = false; + bool bConsiderHeadcrabBlack = false;*/ + bool bConsiderAntlion = false; + bool bConsiderZombie = false; + bool bConsiderZombieTorso = false; + bool bConsiderZombieFast = false; + bool bConsiderZombiePoison = false; + bool bConsiderCombine = false; + bool bConsiderCombineMetro = false; + bool bConsiderCombineScanner = false; + bool bConsiderCombineManhack = false; + bool bConsiderCombineStalker = false; + CBaseEntity *pEntity = gEntList.FindEntityByClassname( NULL, "contingency_wave_spawnpoint" ); + while ( pEntity != NULL ) + { + /*if ( waveSelected == WAVE_HEADCRABS ) + { + if ( !bConsiderHeadcrab && pEntity->HasSpawnFlags(SF_WAVESPAWNER_HEADCRAB) ) + bConsiderHeadcrab = true; + + if ( !bConsiderHeadcrabFast && pEntity->HasSpawnFlags(SF_WAVESPAWNER_HEADCRAB_FAST) ) + bConsiderHeadcrabFast = true; + + if ( !bConsiderHeadcrabBlack && pEntity->HasSpawnFlags(SF_WAVESPAWNER_HEADCRAB_BLACK) ) + bConsiderHeadcrabBlack = true; + } + else */if ( waveSelected == WAVE_ANTLIONS ) + { + if ( !bConsiderAntlion && pEntity->HasSpawnFlags(SF_WAVESPAWNER_ANTLION) ) + bConsiderAntlion = true; + } + else if ( waveSelected == WAVE_ZOMBIES ) + { + if ( !bConsiderZombie && pEntity->HasSpawnFlags(SF_WAVESPAWNER_ZOMBIE) ) + bConsiderZombie = true; + + if ( !bConsiderZombieTorso && pEntity->HasSpawnFlags(SF_WAVESPAWNER_ZOMBIE_TORSO) ) + bConsiderZombieTorso = true; + + if ( !bConsiderZombieFast && pEntity->HasSpawnFlags(SF_WAVESPAWNER_ZOMBIE_FAST) ) + bConsiderZombieFast = true; + + if ( !bConsiderZombiePoison && pEntity->HasSpawnFlags(SF_WAVESPAWNER_ZOMBIE_POISON) ) + bConsiderZombiePoison = true; + } + else if ( waveSelected == WAVE_COMBINE ) + { + if ( !bConsiderCombine && pEntity->HasSpawnFlags(SF_WAVESPAWNER_COMBINE) ) + bConsiderCombine = true; + + if ( !bConsiderCombineMetro && pEntity->HasSpawnFlags(SF_WAVESPAWNER_COMBINE_METRO) ) + bConsiderCombineMetro = true; + + if ( !bConsiderCombineScanner && pEntity->HasSpawnFlags(SF_WAVESPAWNER_COMBINE_SCANNER) ) + bConsiderCombineScanner = true; + + if ( !bConsiderCombineManhack && pEntity->HasSpawnFlags(SF_WAVESPAWNER_COMBINE_MANHACK) ) + bConsiderCombineManhack = true; + + if ( !bConsiderCombineStalker && pEntity->HasSpawnFlags(SF_WAVESPAWNER_COMBINE_STALKER) ) + bConsiderCombineStalker = true; + } + + pEntity = gEntList.FindEntityByClassname( pEntity, "contingency_wave_spawnpoint" ); + } + + // At this point, we implicitly know exactly what NPCs we can and cannot choose + // to spawn for our challenge wave, we just need to act on that knowledge + + // We do this by mapping each of our boolean variables to an NPC classname, + // adding those NPC types we are considering to a linked list and finally + // randomly choosing an NPC classname from that linked list + + CUtlLinkedList *pConsiderationList = new CUtlLinkedList; + + /*if ( bConsiderHeadcrab ) + pConsiderationList->AddToTail( "npc_headcrab" ); + if ( bConsiderHeadcrabFast ) + pConsiderationList->AddToTail( "npc_headcrab_fast" ); + if ( bConsiderHeadcrabBlack ) + pConsiderationList->AddToTail( "npc_headcrab_black" );*/ + if ( bConsiderAntlion ) + pConsiderationList->AddToTail( "npc_antlion" ); + if ( bConsiderZombie ) + pConsiderationList->AddToTail( "npc_zombie" ); + if ( bConsiderZombieTorso ) + pConsiderationList->AddToTail( "npc_zombie_torso" ); + if ( bConsiderZombieFast ) + pConsiderationList->AddToTail( "npc_fastzombie" ); + if ( bConsiderZombiePoison ) + pConsiderationList->AddToTail( "npc_poisonzombie" ); + if ( bConsiderCombine ) + pConsiderationList->AddToTail( "npc_combine_s" ); + if ( bConsiderCombineMetro ) + pConsiderationList->AddToTail( "npc_metropolice" ); + if ( bConsiderCombineScanner ) + pConsiderationList->AddToTail( "npc_cscanner" ); + if ( bConsiderCombineManhack ) + pConsiderationList->AddToTail( "npc_manhack" ); + if ( bConsiderCombineStalker ) + pConsiderationList->AddToTail( "npc_stalker" ); + + SetPreferredNPCType( pConsiderationList->Element(random->RandomInt(0, pConsiderationList->Count() - 1)) ); + + delete pConsiderationList; // we're done with this now + + // All in all, this might not be the cleanest solution, but it works! + // ...and actually, I think it's pretty good. :D + } + // Update wave-specific variables and stuff to reflect the calculations made above SetNumEnemiesRemaining( numEnemiesToSpawnThisWave ); SetCalculatedNumEnemies( numEnemiesToSpawnThisWave ); @@ -727,9 +836,17 @@ void CContingencyRules::Think( void ) IncrementWaveNumber(); PerformWaveCalculations(); - DisplayAnnouncement( UTIL_VarArgs("WAVE %i COMMENCING...", GetWaveNumber()), 3.0f ); + if ( IsChallengeWave() ) + { + DisplayAnnouncement( UTIL_VarArgs("CHALLENGE WAVE %i COMMENCING...", GetWaveNumber()), 5.0f ); + CContingency_System_Music::PlayAnnouncementSound( "Contingency.InterimToCombatChallenge" ); + } + else + { + DisplayAnnouncement( UTIL_VarArgs("WAVE %i COMMENCING...", GetWaveNumber()), 3.0f ); + CContingency_System_Music::PlayAnnouncementSound( "Contingency.InterimToCombat" ); + } - CContingency_System_Music::PlayAnnouncementSound( "Contingency.InterimToCombat" ); CContingency_System_Music::PlayBackgroundMusic( BACKGROUND_MUSIC_COMBAT ); SetCurrentPhase( PHASE_COMBAT ); @@ -960,6 +1077,10 @@ bool CContingencyRules::ShouldCollide( int collisionGroup0, int collisionGroup1 if ( collisionGroup0 > collisionGroup1 ) swap( collisionGroup0, collisionGroup1 ); // swap so that lowest is always first + if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) && + (collisionGroup1 == COLLISION_GROUP_PLAYER || collisionGroup1 == COLLISION_GROUP_PLAYER_MOVEMENT) ) + return false; // prevent player-player collisions + return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 ); } @@ -1014,6 +1135,8 @@ CAmmoDef *GetAmmoDef() // New ammo type for turrets def.AddAmmoType("TURRET", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 5, 99999, BULLET_IMPULSE(200, 1225), 0 ); // based on the AR2 ammo type + def.AddAmmoType("hopwire", DMG_BLAST, TRACER_NONE, 0, 0, 1, 0, 0 ); + def.AddAmmoType("AlyxGun", DMG_BULLET, TRACER_LINE, "sk_plr_dmg_alyxgun", "sk_npc_dmg_alyxgun", "sk_max_alyxgun", BULLET_IMPULSE(200, 1225), 0 ); def.AddAmmoType("SniperRound", DMG_BULLET | DMG_SNIPER, TRACER_NONE, "sk_plr_dmg_sniper_round", "sk_npc_dmg_sniper_round", "sk_max_sniper_round", BULLET_IMPULSE(650, 6000), 0 ); def.AddAmmoType("SniperPenetratedRound", DMG_BULLET | DMG_SNIPER, TRACER_NONE, "sk_dmg_sniper_penetrate_plr", "sk_dmg_sniper_penetrate_npc", "sk_max_sniper_round", BULLET_IMPULSE(150, 6000), 0 ); @@ -1028,7 +1151,7 @@ CAmmoDef *GetAmmoDef() def.AddAmmoType("StriderMinigunDirect", DMG_BULLET, TRACER_LINE, 2, 2, 15, 1.0 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 1.0kg weight at 750 ft/s def.AddAmmoType("HelicopterGun", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_npc_dmg_helicopter_to_plr", "sk_npc_dmg_helicopter", "sk_max_smg1", BULLET_IMPULSE(400, 1225), AMMO_FORCE_DROP_IF_CARRIED | AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER ); #ifdef HL2_EPISODIC - def.AddAmmoType("Hopwire", DMG_BLAST, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_hopwire", 0, 0); + //def.AddAmmoType("Hopwire", DMG_BLAST, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_hopwire", 0, 0); def.AddAmmoType("CombineHeavyCannon", DMG_BULLET, TRACER_LINE, 40, 40, NULL, 10 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 10 kg weight at 750 ft/s def.AddAmmoType("ammo_proto1", DMG_BULLET, TRACER_LINE, 0, 0, 10, 0, 0 ); #endif // HL2_EPISODIC @@ -1097,6 +1220,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_ANTLION, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_BARNACLE // @@ -1132,6 +1264,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_BARNACLE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_BULLSEYE // ------------------------------------------------------------ @@ -1164,6 +1305,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_HACKED_ROLLERMINE,D_NU, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_BULLSEYE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_NU, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_BULLSQUID // ------------------------------------------------------------ @@ -1229,6 +1379,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_HACKED_ROLLERMINE,D_NU, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_CITIZEN_PASSIVE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_NU, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_CITIZEN_REBEL // ------------------------------------------------------------ @@ -1261,6 +1420,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_HACKED_ROLLERMINE,D_NU, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_CITIZEN_REBEL, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_NU, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_COMBINE // ------------------------------------------------------------ @@ -1293,6 +1461,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_COMBINE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_COMBINE_GUNSHIP // ------------------------------------------------------------ @@ -1325,6 +1502,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_GUNSHIP, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_COMBINE_GUNSHIP, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_COMBINE_HUNTER // ------------------------------------------------------------ @@ -1357,6 +1543,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_HUNTER, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE_HUNTER, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_COMBINE_HUNTER, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_CONSCRIPT // ------------------------------------------------------------ @@ -1389,6 +1584,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_HACKED_ROLLERMINE,D_NU, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_CONSCRIPT, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_NU, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_FLARE // ------------------------------------------------------------ @@ -1422,6 +1626,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_HACKED_ROLLERMINE,D_NU, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_FLARE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_NU, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_HEADCRAB // ------------------------------------------------------------ @@ -1454,6 +1667,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_HACKED_ROLLERMINE,D_FR, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_HEADCRAB, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_HOUNDEYE // ------------------------------------------------------------ @@ -1520,6 +1742,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_MANHACK, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_METROPOLICE // ------------------------------------------------------------ @@ -1552,6 +1783,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_METROPOLICE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_MILITARY // ------------------------------------------------------------ @@ -1584,6 +1824,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_MILITARY, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_MISSILE // ------------------------------------------------------------ @@ -1616,6 +1865,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_MISSILE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_NONE // ------------------------------------------------------------ @@ -1647,6 +1905,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_HACKED_ROLLERMINE,D_NU, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_NONE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_NU, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_PLAYER // ------------------------------------------------------------ @@ -1679,6 +1946,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_PLAYER_ALLY_VITAL,D_LI, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_HACKED_ROLLERMINE,D_LI, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_PLAYER, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_NU, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_PLAYER_ALLY // ------------------------------------------------------------ @@ -1711,6 +1987,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_PLAYER_ALLY_VITAL,D_LI, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY, CLASS_HACKED_ROLLERMINE,D_LI, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_PLAYER_ALLY, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_LI, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_PLAYER_ALLY_VITAL // ------------------------------------------------------------ @@ -1743,6 +2028,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_PLAYER_ALLY_VITAL,D_LI, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER_ALLY_VITAL, CLASS_HACKED_ROLLERMINE,D_LI, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_PLAYER_ALLY_VITAL, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_LI, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_SCANNER // ------------------------------------------------------------ @@ -1775,6 +2069,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_SCANNER, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_STALKER // ------------------------------------------------------------ @@ -1807,6 +2110,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_STALKER, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_VORTIGAUNT // ------------------------------------------------------------ @@ -1839,6 +2151,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_PLAYER_ALLY_VITAL,D_LI, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_HACKED_ROLLERMINE,D_LI, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_VORTIGAUNT, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_LI, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_ZOMBIE // ------------------------------------------------------------ @@ -1871,6 +2192,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_ZOMBIE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_PROTOSNIPER // ------------------------------------------------------------ @@ -1903,6 +2233,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_HACKED_ROLLERMINE,D_HT, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_PROTOSNIPER, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_EARTH_FAUNA // @@ -1938,6 +2277,15 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_HACKED_ROLLERMINE,D_NU, 0); +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_EARTH_FAUNA, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_HT, 0 ); + +///// + // ------------------------------------------------------------ // > CLASS_HACKED_ROLLERMINE // ------------------------------------------------------------ @@ -1969,6 +2317,57 @@ void CContingencyRules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_HACKED_ROLLERMINE, CLASS_PLAYER_ALLY, D_LI, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_HACKED_ROLLERMINE, CLASS_PLAYER_ALLY_VITAL,D_LI, 0); CBaseCombatCharacter::SetDefaultRelationship(CLASS_HACKED_ROLLERMINE, CLASS_HACKED_ROLLERMINE,D_LI, 0); + +///// + + // Contingency - James + // Added spawnable prop system + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_HACKED_ROLLERMINE, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_LI, 0 ); + +///// + +///// + + // Contingency - James + // Added spawnable prop system + + // ------------------------------------------------------------ + // > CLASS_CONTINGENCY_SPAWNABLE_PROP + // ------------------------------------------------------------ + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_NONE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_PLAYER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_ANTLION, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_BARNACLE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_BULLSEYE, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_BULLSQUID, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_CITIZEN_PASSIVE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_CITIZEN_REBEL, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_COMBINE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_COMBINE_GUNSHIP, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_COMBINE_HUNTER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_CONSCRIPT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_FLARE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_HEADCRAB, D_NU, 0); + //CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_HOUNDEYE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_MANHACK, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_METROPOLICE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_MILITARY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_MISSILE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_SCANNER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_STALKER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_VORTIGAUNT, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_ZOMBIE, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_PROTOSNIPER, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_EARTH_FAUNA, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_PLAYER_ALLY, D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_PLAYER_ALLY_VITAL,D_NU, 0); + CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_HACKED_ROLLERMINE,D_NU, 0); + + CBaseCombatCharacter::SetDefaultRelationship( CLASS_CONTINGENCY_SPAWNABLE_PROP, CLASS_CONTINGENCY_SPAWNABLE_PROP, D_NU, 0 ); + +///// + } //===== End Of AI Patch ====== diff --git a/src/game/shared/contingency/contingency_gamerules.h b/src/game/shared/contingency/contingency_gamerules.h index 8d07f22..689f897 100644 --- a/src/game/shared/contingency/contingency_gamerules.h +++ b/src/game/shared/contingency/contingency_gamerules.h @@ -31,6 +31,9 @@ #define CContingencyRulesProxy C_ContingencyRulesProxy #endif +// Added wave system +extern ConVar contingency_wave_challenge_frequency; + enum CONTINGENCY_TEAMS { TEAM_PLAYER = 0, @@ -226,10 +229,21 @@ class CContingencyRules : public CHL2MPRules #ifndef CLIENT_DLL void SetWaveType( int newWaveType ) { m_iWaveType = newWaveType; } void SetPreferredWaveType( int newPreferredWaveType ) { m_iPreferredWaveType = newPreferredWaveType; } +#endif + bool IsChallengeWave( void ) { + if ( contingency_wave_challenge_frequency.GetInt() <= 0 ) + return false; // avoid math headaches + + return ((GetWaveNumber() % contingency_wave_challenge_frequency.GetInt()) == 0); + } +#ifndef CLIENT_DLL + const char *GetPreferredNPCType( void ) { return m_PreferredNPCType; } + void SetPreferredNPCType( const char *newPreferredNPCType ) { m_PreferredNPCType = newPreferredNPCType; } #endif int GetNumEnemiesRemaining( void ) { return m_iNumEnemiesRemaining; } #ifndef CLIENT_DLL void SetNumEnemiesRemaining( int newNumEnemiesRemaining ) { m_iNumEnemiesRemaining = newNumEnemiesRemaining; } + void IncrementNumEnemiesRemaining( void ) { m_iNumEnemiesRemaining = m_iNumEnemiesRemaining + 1; } void DecrementNumEnemiesRemaining( void ) { m_iNumEnemiesRemaining = m_iNumEnemiesRemaining - 1; } #endif #ifndef CLIENT_DLL @@ -274,8 +288,8 @@ class CContingencyRules : public CHL2MPRules // This information is updated by a contingency_configuration entity (if one exists) when it spawns int GetMapMaxLivingNPCs( void ) { return m_iMapMaxLivingNPCs; } void SetMapMaxLivingNPCs( int iNewMapMaxLivingNPCs ) { m_iMapMaxLivingNPCs = iNewMapMaxLivingNPCs; } - bool DoesMapSupportHeadcrabs( void ) { return m_bMapHeadcrabSupport; } - void DoesMapSupportHeadcrabs( bool boolean ) { m_bMapHeadcrabSupport = boolean; } + /*bool DoesMapSupportHeadcrabs( void ) { return m_bMapHeadcrabSupport; } + void DoesMapSupportHeadcrabs( bool boolean ) { m_bMapHeadcrabSupport = boolean; }*/ bool DoesMapSupportAntlions( void ) { return m_bMapAntlionSupport; } void DoesMapSupportAntlions( bool boolean ) { m_bMapAntlionSupport = boolean; } bool DoesMapSupportZombies( void ) { return m_bMapZombieSupport; } @@ -285,8 +299,8 @@ class CContingencyRules : public CHL2MPRules // Added wave system // This information is updated by a contingency_configuration entity (if one exists) when it spawns - float GetMapHeadcrabWaveMultiplierOffset( void ) { return m_flMapHeadcrabWaveMultiplierOffset; } - void SetMapHeadcrabWaveMultiplierOffset( float newMapHeadcrabWaveMultiplierOffset ) { m_flMapHeadcrabWaveMultiplierOffset = newMapHeadcrabWaveMultiplierOffset; } + /*float GetMapHeadcrabWaveMultiplierOffset( void ) { return m_flMapHeadcrabWaveMultiplierOffset; } + void SetMapHeadcrabWaveMultiplierOffset( float newMapHeadcrabWaveMultiplierOffset ) { m_flMapHeadcrabWaveMultiplierOffset = newMapHeadcrabWaveMultiplierOffset; }*/ float GetMapAntlionWaveMultiplierOffset( void ) { return m_flMapAntlionWaveMultiplierOffset; } void SetMapAntlionWaveMultiplierOffset( float newMapAntlionWaveMultiplierOffset ) { m_flMapAntlionWaveMultiplierOffset = newMapAntlionWaveMultiplierOffset; } float GetMapZombieWaveMultiplierOffset( void ) { return m_flMapZombieWaveMultiplierOffset; } @@ -325,6 +339,7 @@ class CContingencyRules : public CHL2MPRules CNetworkVar( int, m_iPreferredWaveType ); CNetworkVar( int, m_iNumEnemiesRemaining ); #ifndef CLIENT_DLL + const char *m_PreferredNPCType; int m_iCalculatedNumEnemies; int m_iNumEnemiesSpawned; bool m_bPlayersDefeated; diff --git a/src/game/shared/contingency/contingency_system_loadout.h b/src/game/shared/contingency/contingency_system_loadout.h index 84841d7..78a1d87 100644 --- a/src/game/shared/contingency/contingency_system_loadout.h +++ b/src/game/shared/contingency/contingency_system_loadout.h @@ -6,15 +6,14 @@ static const int NUM_WEAPON_TYPE_PARAMETERS = 2; -static const int NUM_PRIMARY_WEAPON_TYPES = 6; +static const int NUM_PRIMARY_WEAPON_TYPES = 5; static const char* kPrimaryWeaponTypes[NUM_PRIMARY_WEAPON_TYPES][NUM_WEAPON_TYPE_PARAMETERS] = { { "weapon_smg1", "SMG1" }, { "weapon_alyxgun", "Alyx's Gun" }, { "weapon_shotgun", "Shotgun" }, { "weapon_ar2", "AR2" }, - { "weapon_crossbow", "Crossbow" }, - { "weapon_rpg", "RPG" } + { "weapon_crossbow", "Crossbow" } }; static const int NUM_SECONDARY_WEAPON_TYPES = 4; @@ -34,13 +33,15 @@ static const char* kMeleeWeaponTypes[NUM_MELEE_WEAPON_TYPES][NUM_WEAPON_TYPE_PAR { "weapon_stunstick", "Stunstick" } }; -static const int NUM_EQUIPMENT_TYPES = 4; +static const int NUM_EQUIPMENT_TYPES = 6; static const char* kEquipmentTypes[NUM_EQUIPMENT_TYPES][NUM_WEAPON_TYPE_PARAMETERS] = { - { "weapon_frag", "Frag Grenade" }, - { "weapon_slam", "S.L.A.M." }, + { "weapon_frag", "Frag Grenades" }, + { "weapon_slam", "S.L.A.M.s" }, { "weapon_deployableturret", "Turret" }, - { "weapon_healthkit", "Health Kit" } + { "weapon_healthkit", "Health Kit" }, + { "weapon_rpg", "RPG" }, + { "weapon_hopwire", "Vortex Grenade" } }; // Special weapons are weapons that can be picked up regardless of a player's loadout diff --git a/src/game/shared/contingency/contingency_system_propspawning.h b/src/game/shared/contingency/contingency_system_propspawning.h index 324df20..ebc428e 100644 --- a/src/game/shared/contingency/contingency_system_propspawning.h +++ b/src/game/shared/contingency/contingency_system_propspawning.h @@ -4,24 +4,25 @@ #define CONTINGENCY_SYSTEM_PROPSPAWNING_H #pragma once -static const int NUM_PROPSPAWNING_PARAMETERS = 6; +static const int NUM_PROPSPAWNING_PARAMETERS = 7; -static const int NUM_SPAWNABLEPROP_TYPES = 12; +static const int NUM_SPAWNABLEPROP_TYPES = 13; static const char* kSpawnablePropTypes[NUM_SPAWNABLEPROP_TYPES][NUM_PROPSPAWNING_PARAMETERS] = { - // Name, cost (in credits), filepath to associated image, filepath to associated mdl, height (y-vector) offset from origin, y-angle offset - { "Wooden Board", "1", "spawnableprops/woodenboard", "models/props_debris/wood_board04a.mdl", "32", "0" }, - { "Wooden Pallet", "2", "spawnableprops/woodenpallet", "models/props_junk/wood_pallet001a.mdl", "5", "0" }, - { "Wooden Chair", "2", "spawnableprops/woodenchair", "models/props_c17/FurnitureChair001a.mdl", "17", "180" }, - { "Wooden Bench", "2", "spawnableprops/woodenbench", "models/props_c17/bench01a.mdl", "20", "0" }, - { "Wooden Table", "2", "spawnableprops/woodentable", "models/props_c17/FurnitureTable002a.mdl", "18", "0" }, - { "Wooden Drawer", "2", "spawnableprops/woodendrawer", "models/props_c17/FurnitureDrawer001a.mdl", "20", "180" }, - { "Wooden Shelf", "3", "spawnableprops/woodenshelf", "models/props_interiors/Furniture_shelf01a.mdl", "43", "180" }, - { "Small Wooden Crate", "3", "spawnableprops/smallwoodencrate", "models/props_junk/wood_crate001a.mdl", "20", "0" }, - { "Large Wooden Crate", "4", "spawnableprops/largewoodencrate", "models/props_junk/wood_crate002a.mdl", "20", "0" }, - { "Wooden Fence Skeleton", "4", "spawnableprops/woodenfenceskeleton", "models/props_wasteland/wood_fence01b.mdl", "70", "90" }, - { "Small Wooden Fence", "4", "spawnableprops/smallwoodenfence", "models/props_wasteland/wood_fence02a.mdl", "65", "90" }, - { "Large Wooden Fence", "5", "spawnableprops/largewoodenfence", "models/props_wasteland/wood_fence01a.mdl", "65", "90" } + // Name, cost (in credits), filepath to associated image, filepath to associated mdl, height (y-vector) offset from origin, y-angle offset, initial "health" (HP) + { "Wooden Board", "1", "spawnableprops/woodenboard", "models/props_debris/wood_board04a.mdl", "32", "0", "10" }, + { "Wooden Chair", "2", "spawnableprops/woodenchair", "models/props_c17/FurnitureChair001a.mdl", "19", "180", "20" }, + { "Wooden Table", "2", "spawnableprops/woodentable", "models/props_c17/FurnitureTable002a.mdl", "18", "0", "20" }, + { "Wooden Pallet", "2", "spawnableprops/woodenpallet", "models/props_junk/wood_pallet001a.mdl", "5", "0", "30" }, + { "Wooden Bench", "2", "spawnableprops/woodenbench", "models/props_c17/bench01a.mdl", "20", "0", "30" }, + { "Wooden Drawer", "2", "spawnableprops/woodendrawer", "models/props_c17/FurnitureDrawer001a.mdl", "20", "180", "40" }, + { "Wooden Shelf", "3", "spawnableprops/woodenshelf", "models/props_interiors/Furniture_shelf01a.mdl", "43", "180", "40" }, + { "Small Wooden Crate", "3", "spawnableprops/smallwoodencrate", "models/props_junk/wood_crate001a.mdl", "20", "0", "50" }, + { "Large Wooden Crate", "4", "spawnableprops/largewoodencrate", "models/props_junk/wood_crate002a.mdl", "20", "0", "50" }, + { "Wooden Fence Skeleton", "4", "spawnableprops/woodenfenceskeleton", "models/props_wasteland/wood_fence01b.mdl", "70", "90", "50" }, + { "Small Wooden Fence", "4", "spawnableprops/smallwoodenfence", "models/props_wasteland/wood_fence02a.mdl", "65", "90", "75" }, + { "Large Wooden Fence", "5", "spawnableprops/largewoodenfence", "models/props_wasteland/wood_fence01a.mdl", "65", "90", "100" }, + { "Concrete Barrier", "10", "spawnableprops/concretebarrier", "models/props_c17/concrete_barrier001a.mdl", "0", "0", "200" } }; // *** IMPORTANT INFO REGARDING ADDING/REMOVING PROP TYPES FROM ABOVE LIST *** diff --git a/src/game/shared/contingency/contingency_system_wave.h b/src/game/shared/contingency/contingency_system_wave.h index 9cc106a..b44a8d4 100644 --- a/src/game/shared/contingency/contingency_system_wave.h +++ b/src/game/shared/contingency/contingency_system_wave.h @@ -7,7 +7,7 @@ enum CONTINGENCY_WAVES { WAVE_NONE = -1, - WAVE_HEADCRABS, + //WAVE_HEADCRABS, WAVE_ANTLIONS, WAVE_ZOMBIES, WAVE_COMBINE, @@ -16,13 +16,13 @@ enum CONTINGENCY_WAVES }; // Wave types -static const int NUM_HEADCRAB_NPCS = 3; +/*static const int NUM_HEADCRAB_NPCS = 3; static const char* kWaveHeadcrabsNPCTypes[NUM_HEADCRAB_NPCS] = { "npc_headcrab", "npc_headcrab_fast", "npc_headcrab_black" -}; +};*/ static const int NUM_ANTLION_NPCS = 1; static const char* kWaveAntlionsNPCTypes[NUM_ANTLION_NPCS] = { diff --git a/src/game/shared/contingency/weapon_hopwire.cpp b/src/game/shared/contingency/weapon_hopwire.cpp new file mode 100644 index 0000000..2482b96 --- /dev/null +++ b/src/game/shared/contingency/weapon_hopwire.cpp @@ -0,0 +1,662 @@ +// Ported directly from Lethal Stigma, with a few adjustments + +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +// Lethal Stigma +// ADDED: Hopwire +#include "cbase.h" +//#include "basehlcombatweapon.h" +//#include "player.h" +#include "gamerules.h" +//#include "grenade_frag.h" +//#include "npcevent.h" +#include "engine/IEngineSound.h" +//#include "items.h" +#include "in_buttons.h" +//#include "soundent.h" +//#include "grenade_hopwire.h" + +// Lethal Stigma +// ADDED: Hopwire +#ifndef CLIENT_DLL + #include "hl2mp_player.h" + #include "grenade_frag.h" + #include "items.h" + #include "soundent.h" +#endif + +// Lethal Stigma +// ADDED: Additional includes +#include "weapon_hopwire.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// Lethal Stigma +// ADDED: Hopwire +//#define GRENADE_TIMER 2.0f //Seconds +#define GRENADE_TIMER 0.5f //Seconds // compensates for a ~1.5 second delay experienced from when hopwire is thrown to final detonation + +#define GRENADE_PAUSED_NO 0 +#define GRENADE_PAUSED_PRIMARY 1 +#define GRENADE_PAUSED_SECONDARY 2 + +#define GRENADE_RADIUS 4.0f // inches + +//----------------------------------------------------------------------------- +// Fragmentation grenades +//----------------------------------------------------------------------------- +// Lethal Stigma +// ADDED: Hopwire +// Moved to new header file +/*class CWeaponHopwire: public CBaseHL2MPCombatWeapon +{ + DECLARE_CLASS( CWeaponHopwire, CBaseHL2MPCombatWeapon ); +public: + DECLARE_SERVERCLASS(); + + CWeaponHopwire(); + + void Precache( void ); + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + void PrimaryAttack( void ); + void SecondaryAttack( void ); + void DecrementAmmo( CBaseCombatCharacter *pOwner ); + void ItemPostFrame( void ); + + void HandleFireOnEmpty( void ); + bool HasAnyAmmo( void ); + bool Deploy( void ); + bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } + + bool Reload( void ); + +private: + void ThrowGrenade( CBasePlayer *pPlayer ); + void RollGrenade( CBasePlayer *pPlayer ); + void LobGrenade( CBasePlayer *pPlayer ); + // check a throw from vecSrc. If not valid, move the position back along the line to vecEye + void CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc ); + + bool m_bRedraw; //Draw the weapon again after throwing a grenade + + int m_AttackPaused; + bool m_fDrawbackFinished; + + CHandle m_hActiveHopWire; + + DECLARE_ACTTABLE(); +};*/ + +// Lethal Stigma +// ADDED: Hopwire +/*BEGIN_DATADESC( CWeaponHopwire ) + DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ), + DEFINE_FIELD( m_AttackPaused, FIELD_INTEGER ), + DEFINE_FIELD( m_fDrawbackFinished, FIELD_BOOLEAN ), + DEFINE_FIELD( m_hActiveHopWire, FIELD_EHANDLE ), +END_DATADESC()*/ + +acttable_t CWeaponHopwire::m_acttable[] = +{ + // Lethal Stigma + // ADDED: Hopwire + //{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, + + { ACT_MP_STAND_IDLE, ACT_HL2MP_IDLE_GRENADE, false }, + { ACT_MP_CROUCH_IDLE, ACT_HL2MP_IDLE_CROUCH_GRENADE, false }, + + { ACT_MP_RUN, ACT_HL2MP_RUN_GRENADE, false }, + { ACT_MP_CROUCHWALK, ACT_HL2MP_WALK_CROUCH_GRENADE, false }, + + { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE, false }, + { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE, false }, + + { ACT_MP_RELOAD_STAND, ACT_HL2MP_GESTURE_RELOAD_GRENADE, false }, + { ACT_MP_RELOAD_CROUCH, ACT_HL2MP_GESTURE_RELOAD_GRENADE, false }, + + { ACT_MP_JUMP, ACT_HL2MP_JUMP_GRENADE, false }, +}; + +IMPLEMENT_ACTTABLE(CWeaponHopwire); + +// Lethal Stigma +// ADDED: Hopwire +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponHopwire, DT_WeaponHopwire ) + +// Lethal Stigma +// ADDED: Hopwire +BEGIN_NETWORK_TABLE( CWeaponHopwire, DT_WeaponHopwire ) + +#ifdef CLIENT_DLL + RecvPropBool( RECVINFO( m_bRedraw ) ), + RecvPropBool( RECVINFO( m_fDrawbackFinished ) ), + RecvPropInt( RECVINFO( m_AttackPaused ) ), +#else + SendPropBool( SENDINFO( m_bRedraw ) ), + SendPropBool( SENDINFO( m_fDrawbackFinished ) ), + SendPropInt( SENDINFO( m_AttackPaused ) ), +#endif + +END_NETWORK_TABLE() + +// Lethal Stigma +// ADDED: Hopwire +#ifdef CLIENT_DLL +BEGIN_PREDICTION_DATA( CWeaponHopwire ) + DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_fDrawbackFinished, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_AttackPaused, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), +END_PREDICTION_DATA() +#endif + +// Lethal Stigma +// ADDED: Hopwire +/*IMPLEMENT_SERVERCLASS_ST(CWeaponHopwire, DT_WeaponHopwire) +END_SEND_TABLE()*/ + +LINK_ENTITY_TO_CLASS( weapon_hopwire, CWeaponHopwire ); +PRECACHE_WEAPON_REGISTER(weapon_hopwire); + +// Lethal Stigma +// ADDED: Hopwire +CWeaponHopwire::CWeaponHopwire( void ) : + CBaseHL2MPCombatWeapon() +{ + m_bRedraw = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponHopwire::Precache( void ) +{ + BaseClass::Precache(); + + // Lethal Stigma + // ADDED: Hopwire +#ifndef CLIENT_DLL + UTIL_PrecacheOther( "npc_grenade_hopwire" ); +#endif + + PrecacheScriptSound( "WeaponFrag.Throw" ); + PrecacheScriptSound( "WeaponFrag.Roll" ); + + m_bRedraw = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponHopwire::Deploy( void ) +{ + m_bRedraw = false; + m_fDrawbackFinished = false; + + return BaseClass::Deploy(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponHopwire::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ + // Lethal Stigma + // ADDED: Hopwire +/* + // Lethal Stigma + // ADDED: Hopwire +#ifndef CLIENT_DLL + if ( m_hActiveHopWire != NULL ) + return false; +#endif*/ + + m_bRedraw = false; + m_fDrawbackFinished = false; + + return BaseClass::Holster( pSwitchingTo ); +} + +// Lethal Stigma +// ADDED: Hopwire +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEvent - +// *pOperator - +//----------------------------------------------------------------------------- +void CWeaponHopwire::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + bool fThrewGrenade = false; + + switch( pEvent->event ) + { + case EVENT_WEAPON_SEQUENCE_FINISHED: + m_fDrawbackFinished = true; + break; + + case EVENT_WEAPON_THROW: + ThrowGrenade( pOwner ); + DecrementAmmo( pOwner ); + fThrewGrenade = true; + break; + + case EVENT_WEAPON_THROW2: + RollGrenade( pOwner ); + DecrementAmmo( pOwner ); + fThrewGrenade = true; + break; + + case EVENT_WEAPON_THROW3: + LobGrenade( pOwner ); + DecrementAmmo( pOwner ); + fThrewGrenade = true; + break; + + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } + +#define RETHROW_DELAY 0.5 + if( fThrewGrenade ) + { + m_flNextPrimaryAttack = gpGlobals->curtime + RETHROW_DELAY; + m_flNextSecondaryAttack = gpGlobals->curtime + RETHROW_DELAY; + m_flTimeWeaponIdle = FLT_MAX; //NOTE: This is set once the animation has finished up! + + // Make a sound designed to scare snipers back into their holes! + CBaseCombatCharacter *pOwner = GetOwner(); + + if( pOwner ) + { + Vector vecSrc = pOwner->Weapon_ShootPosition(); + Vector vecDir; + + AngleVectors( pOwner->EyeAngles(), &vecDir ); + + trace_t tr; + + UTIL_TraceLine( vecSrc, vecSrc + vecDir * 1024, MASK_SOLID_BRUSHONLY, pOwner, COLLISION_GROUP_NONE, &tr ); + + CSoundEnt::InsertSound( SOUND_DANGER_SNIPERONLY, tr.endpos, 384, 0.2, pOwner ); + } + } +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Override the ammo behavior so we never disallow pulling the weapon out +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +// Lethal Stigma +// ADDED: Hopwire +/*bool CWeaponHopwire::HasAnyAmmo( void ) +{ + // Lethal Stigma + // ADDED: Hopwire +#ifndef CLIENT_DLL + if ( m_hActiveHopWire != NULL ) + return true; +#endif + + return BaseClass::HasAnyAmmo(); +}*/ + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponHopwire::Reload( void ) +{ + if ( !HasPrimaryAmmo() ) + return false; + + if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) ) + { + //Redraw the weapon + SendWeaponAnim( ACT_VM_DRAW ); + + //Update our times + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); + + //Mark this as done + m_bRedraw = false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponHopwire::SecondaryAttack( void ) +{ + /* + if ( m_bRedraw ) + return; + + if ( !HasPrimaryAmmo() ) + return; + + CBaseCombatCharacter *pOwner = GetOwner(); + + if ( pOwner == NULL ) + return; + + CBasePlayer *pPlayer = ToBasePlayer( pOwner ); + + if ( pPlayer == NULL ) + return; + + // Note that this is a secondary attack and prepare the grenade attack to pause. + m_AttackPaused = GRENADE_PAUSED_SECONDARY; + SendWeaponAnim( ACT_VM_PULLBACK_LOW ); + + // Don't let weapon idle interfere in the middle of a throw! + m_flTimeWeaponIdle = FLT_MAX; + m_flNextSecondaryAttack = FLT_MAX; + + // If I'm now out of ammo, switch away + if ( !HasPrimaryAmmo() ) + { + pPlayer->SwitchToNextBestWeapon( this ); + } + */ +} + +//----------------------------------------------------------------------------- +// Purpose: Allow activation even if this is our last piece of ammo +//----------------------------------------------------------------------------- +// Lethal Stigma +// ADDED: Hopwire +/*void CWeaponHopwire::HandleFireOnEmpty( void ) +{ + // Lethal Stigma + // ADDED: Hopwire +#ifndef CLIENT_DLL + if ( m_hActiveHopWire!= NULL ) + { + // FIXME: This toggle is hokey + m_bRedraw = false; + PrimaryAttack(); + m_bRedraw = true; + } +#endif +}*/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponHopwire::PrimaryAttack( void ) +{ + if ( m_bRedraw ) + return; + + CBaseCombatCharacter *pOwner = GetOwner(); + if ( pOwner == NULL ) + return; + + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );; + if ( !pPlayer ) + return; + + // See if we're in trap mode + + // Lethal Stigma + // ADDED: Hopwire + /*if ( hopwire_trap.GetBool() && ( m_hActiveHopWire != NULL ) ) + { + // Spring the trap + m_hActiveHopWire->Detonate(); + m_hActiveHopWire = NULL; + + // Don't allow another throw for awhile + m_flTimeWeaponIdle = m_flNextPrimaryAttack = gpGlobals->curtime + 2.0f; + + return; + }*/ + + // Note that this is a primary attack and prepare the grenade attack to pause. + /* + m_AttackPaused = GRENADE_PAUSED_PRIMARY; + SendWeaponAnim( ACT_VM_PULLBACK_HIGH ); + */ + m_AttackPaused = GRENADE_PAUSED_SECONDARY; + SendWeaponAnim( ACT_VM_PULLBACK_LOW ); + + // Put both of these off indefinitely. We do not know how long + // the player will hold the grenade. + m_flTimeWeaponIdle = FLT_MAX; + m_flNextPrimaryAttack = FLT_MAX; + + // If I'm now out of ammo, switch away + /* + if ( !HasPrimaryAmmo() ) + { + pPlayer->SwitchToNextBestWeapon( this ); + } + */ + + // Lethal Stigma + // ADDED: Hopwire + // Would have uncommented above, but that might have seemed rather confusing + if ( !HasPrimaryAmmo() ) + { + pPlayer->SwitchToNextBestWeapon( this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOwner - +//----------------------------------------------------------------------------- +void CWeaponHopwire::DecrementAmmo( CBaseCombatCharacter *pOwner ) +{ + pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponHopwire::ItemPostFrame( void ) +{ + if( m_fDrawbackFinished ) + { + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if (pOwner) + { + switch( m_AttackPaused ) + { + case GRENADE_PAUSED_PRIMARY: + if( !(pOwner->m_nButtons & IN_ATTACK) ) + { + SendWeaponAnim( ACT_VM_THROW ); + + ToHL2MPPlayer(pOwner)->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY ); + + m_fDrawbackFinished = false; + } + break; + + case GRENADE_PAUSED_SECONDARY: + if( !(pOwner->m_nButtons & (IN_ATTACK|IN_ATTACK2)) ) + { + //See if we're ducking + if ( pOwner->m_nButtons & IN_DUCK ) + { + //Send the weapon animation + SendWeaponAnim( ACT_VM_SECONDARYATTACK ); + } + else + { + //Send the weapon animation + SendWeaponAnim( ACT_VM_HAULBACK ); + } + + //Tony; the grenade really should have a secondary anim. but it doesn't on the player. + ToHL2MPPlayer(pOwner)->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY ); + + m_fDrawbackFinished = false; + } + break; + + default: + break; + } + } + } + + BaseClass::ItemPostFrame(); + + if ( m_bRedraw ) + { + if ( IsViewModelSequenceFinished() ) + { + Reload(); + } + } +} + + // check a throw from vecSrc. If not valid, move the position back along the line to vecEye +void CWeaponHopwire::CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc ) +{ + trace_t tr; + + UTIL_TraceHull( vecEye, vecSrc, -Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2), Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2), + pPlayer->PhysicsSolidMaskForEntity(), pPlayer, pPlayer->GetCollisionGroup(), &tr ); + + if ( tr.DidHit() ) + { + vecSrc = tr.endpos; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +//----------------------------------------------------------------------------- +void CWeaponHopwire::ThrowGrenade( CBasePlayer *pPlayer ) +{ + // Lethal Stigma + // ADDED: Hopwire + +/*#ifndef CLIENT_DLL + Vector vecEye = pPlayer->EyePosition(); + Vector vForward, vRight; + + pPlayer->EyeVectors( &vForward, &vRight, NULL ); + Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f; + CheckThrowPosition( pPlayer, vecEye, vecSrc ); + vForward[2] += 0.1f; + + Vector vecThrow; + pPlayer->GetVelocity( &vecThrow, NULL ); + vecThrow += vForward * 1200; + m_hActiveHopWire = static_cast (HopWire_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer, GRENADE_TIMER )); +#endif + + m_bRedraw = true; + + WeaponSound( SINGLE );*/ + + LobGrenade( pPlayer ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +//----------------------------------------------------------------------------- +void CWeaponHopwire::LobGrenade( CBasePlayer *pPlayer ) +{ +#ifndef CLIENT_DLL + Vector vecEye = pPlayer->EyePosition(); + Vector vForward, vRight; + + pPlayer->EyeVectors( &vForward, &vRight, NULL ); + Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f + Vector( 0, 0, -8 ); + CheckThrowPosition( pPlayer, vecEye, vecSrc ); + + Vector vecThrow; + pPlayer->GetVelocity( &vecThrow, NULL ); + vecThrow += vForward * 350 + Vector( 0, 0, 50 ); + m_hActiveHopWire = static_cast (HopWire_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(200,random->RandomInt(-600,600),0), pPlayer, GRENADE_TIMER )); +#endif + + WeaponSound( WPN_DOUBLE ); + + m_bRedraw = true; + + // player "shoot" animation + pPlayer->SetAnimation( PLAYER_ATTACK1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +//----------------------------------------------------------------------------- +void CWeaponHopwire::RollGrenade( CBasePlayer *pPlayer ) +{ + // Lethal Stigma + // ADDED: Hopwire + +/*#ifndef CLIENT_DLL + // BUGBUG: Hardcoded grenade width of 4 - better not change the model :) + Vector vecSrc; + pPlayer->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecSrc ); + vecSrc.z += GRENADE_RADIUS; + + Vector vecFacing = pPlayer->BodyDirection2D( ); + // no up/down direction + vecFacing.z = 0; + VectorNormalize( vecFacing ); + trace_t tr; + UTIL_TraceLine( vecSrc, vecSrc - Vector(0,0,16), MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction != 1.0 ) + { + // compute forward vec parallel to floor plane and roll grenade along that + Vector tangent; + CrossProduct( vecFacing, tr.plane.normal, tangent ); + CrossProduct( tr.plane.normal, tangent, vecFacing ); + } + vecSrc += (vecFacing * 18.0); + CheckThrowPosition( pPlayer, pPlayer->WorldSpaceCenter(), vecSrc ); + + Vector vecThrow; + pPlayer->GetVelocity( &vecThrow, NULL ); + vecThrow += vecFacing * 700; + // put it on its side + QAngle orientation(0,pPlayer->GetLocalAngles().y,-90); + // roll it + AngularImpulse rotSpeed(0,0,720); + m_hActiveHopWire = static_cast (HopWire_Create( vecSrc, orientation, vecThrow, rotSpeed, pPlayer, GRENADE_TIMER )); +#endif + + WeaponSound( SPECIAL1 ); + + m_bRedraw = true;*/ + + LobGrenade( pPlayer ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponHopwire::Drop( const Vector &vecVelocity ) +{ +#ifndef CLIENT_DLL + // This weapon cannot and should not be dropped at this time + UTIL_Remove( this ); +#endif +} diff --git a/src/game/shared/contingency/weapon_hopwire.h b/src/game/shared/contingency/weapon_hopwire.h new file mode 100644 index 0000000..cd39a98 --- /dev/null +++ b/src/game/shared/contingency/weapon_hopwire.h @@ -0,0 +1,76 @@ +// Ported directly from Lethal Stigma, with a few adjustments + +// Lethal Stigma +// ADDED: Hopwire + +#ifndef HL2MP_WEAPON_HOPWIRE_H +#define HL2MP_WEAPON_HOPWIRE_H +#pragma once + +#include "weapon_hl2mpbasehlmpcombatweapon.h" +#include "npcevent.h" + +#ifndef CLIENT_DLL + #include "episodic/grenade_hopwire.h" +#endif + +#ifdef CLIENT_DLL + #define CWeaponHopwire C_WeaponHopwire +#endif + +//----------------------------------------------------------------------------- +// Fragmentation grenades +//----------------------------------------------------------------------------- +class CWeaponHopwire: public CBaseHL2MPCombatWeapon +{ + DECLARE_CLASS( CWeaponHopwire, CBaseHL2MPCombatWeapon ); +public: + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponHopwire(); + + void Precache( void ); + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + void PrimaryAttack( void ); + void SecondaryAttack( void ); + void DecrementAmmo( CBaseCombatCharacter *pOwner ); + void ItemPostFrame( void ); + + // Lethal Stigma + // ADDED: Hopwire + //void HandleFireOnEmpty( void ); + + // Lethal Stigma + // ADDED: Hopwire + //bool HasAnyAmmo( void ); + + bool Deploy( void ); + bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + + bool Reload( void ); + +private: + void ThrowGrenade( CBasePlayer *pPlayer ); + void RollGrenade( CBasePlayer *pPlayer ); + void LobGrenade( CBasePlayer *pPlayer ); + // check a throw from vecSrc. If not valid, move the position back along the line to vecEye + void CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc ); + + void Drop( const Vector &vecVelocity ); + + CNetworkVar( bool, m_bRedraw ); //Draw the weapon again after throwing a grenade + CNetworkVar( int, m_AttackPaused ); + CNetworkVar( bool, m_fDrawbackFinished ); + +#ifndef CLIENT_DLL + CHandle m_hActiveHopWire; +#endif + + CWeaponHopwire( const CWeaponHopwire & ); + + DECLARE_ACTTABLE(); +}; + + +#endif // HL2MP_WEAPON_HOPWIRE_H diff --git a/src/game/shared/contingency/weapon_wrench.cpp b/src/game/shared/contingency/weapon_wrench.cpp index b282ea4..d030616 100644 --- a/src/game/shared/contingency/weapon_wrench.cpp +++ b/src/game/shared/contingency/weapon_wrench.cpp @@ -444,8 +444,11 @@ void CWeaponWrench::WaitingOnSpawnablePropDataThink( void ) CContingency_SpawnableProp *pSpawnableProp = dynamic_cast( CreateEntityByName("contingency_spawnableprop") ); if ( pSpawnableProp ) { + pSpawnableProp->AddFlag( EF_NODRAW ); // initially, we're not visible just in case we need to be removed int iSpawnablePropIndex = pOwner->GetDesiredSpawnablePropIndex(); pSpawnableProp->SetModel( kSpawnablePropTypes[iSpawnablePropIndex][3] ); + pSpawnableProp->SetMaxHealth( Q_atoi(kSpawnablePropTypes[iSpawnablePropIndex][6]) ); + pSpawnableProp->SetHealth( pSpawnableProp->GetMaxHealth() ); pSpawnableProp->SetAbsOrigin( m_vecSpawnablePropOrigin ); pSpawnableProp->SetAbsAngles( m_angSpawnablePropAngles ); @@ -462,6 +465,24 @@ void CWeaponWrench::WaitingOnSpawnablePropDataThink( void ) DispatchSpawn( pSpawnableProp ); pSpawnableProp->Activate(); + // Do one final check to make sure we can actually spawn a prop here + trace_t trace; + Vector mins, maxs; + CPhysCollide *pSpawnablePropCollide = modelinfo->GetVCollide( pSpawnableProp->GetModelIndex() )->solids[0]; + physcollision->CollideGetAABB( &mins, &maxs, pSpawnablePropCollide, pSpawnableProp->GetAbsOrigin(), pSpawnableProp->GetAbsAngles() ); + Vector vHalfDims = ( maxs - mins ) * 0.5f; + Vector vCenter = mins + vHalfDims; + UTIL_TraceHull( vCenter, vCenter, -vHalfDims, vHalfDims, MASK_ALL, pSpawnableProp, pSpawnableProp->GetCollisionGroup(), &trace ); + if ( trace.m_pEnt && (trace.m_pEnt->IsPlayer() || trace.m_pEnt->IsNPC()) ) // as long as we don't hit a player or NPC, we're good + { + ClientPrint( pOwner, HUD_PRINTTALK, "Unable to spawn prop. There's something in the way." ); + pSpawnableProp->SUB_Remove(); + SetNextThink( NULL ); + return; + } + + pSpawnableProp->RemoveFlag( EF_NODRAW ); // we've made it past all our checks, so we should be made visible now! + pOwner->UseCredits( Q_atoi(kSpawnablePropTypes[iSpawnablePropIndex][1]) ); // spawned, so use up some of the player's credits }