aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/episodic/vehicle_jeep_episodic.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/episodic/vehicle_jeep_episodic.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/episodic/vehicle_jeep_episodic.cpp')
-rw-r--r--mp/src/game/server/episodic/vehicle_jeep_episodic.cpp3528
1 files changed, 1764 insertions, 1764 deletions
diff --git a/mp/src/game/server/episodic/vehicle_jeep_episodic.cpp b/mp/src/game/server/episodic/vehicle_jeep_episodic.cpp
index 345284df..368f1b9f 100644
--- a/mp/src/game/server/episodic/vehicle_jeep_episodic.cpp
+++ b/mp/src/game/server/episodic/vehicle_jeep_episodic.cpp
@@ -1,1764 +1,1764 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-//
-//
-//=============================================================================
-
-#include "cbase.h"
-#include "vehicle_jeep_episodic.h"
-#include "collisionutils.h"
-#include "npc_alyx_episodic.h"
-#include "particle_parse.h"
-#include "particle_system.h"
-#include "hl2_player.h"
-#include "in_buttons.h"
-#include "vphysics/friction.h"
-#include "vphysicsupdateai.h"
-#include "physics_npc_solver.h"
-#include "Sprite.h"
-#include "weapon_striderbuster.h"
-#include "npc_strider.h"
-#include "vguiscreen.h"
-#include "hl2_vehicle_radar.h"
-#include "props.h"
-#include "ai_dynamiclink.h"
-
-extern ConVar phys_upimpactforcescale;
-
-ConVar jalopy_blocked_exit_max_speed( "jalopy_blocked_exit_max_speed", "50" );
-
-#define JEEP_AMMOCRATE_HITGROUP 5
-#define JEEP_AMMO_CRATE_CLOSE_DELAY 2.0f
-
-// Bodygroups
-#define JEEP_RADAR_BODYGROUP 1
-#define JEEP_HOPPER_BODYGROUP 2
-#define JEEP_CARBAR_BODYGROUP 3
-
-#define RADAR_PANEL_MATERIAL "vgui/screens/radar"
-#define RADAR_PANEL_WRITEZ "engine/writez"
-
-static const char *s_szHazardSprite = "sprites/light_glow01.vmt";
-
-enum
-{
- RADAR_MODE_NORMAL = 0,
- RADAR_MODE_STICKY,
-};
-
-//=========================================================
-//=========================================================
-class CRadarTarget : public CPointEntity
-{
- DECLARE_CLASS( CRadarTarget, CPointEntity );
-
-public:
- void Spawn();
-
- bool IsDisabled() { return m_bDisabled; }
- int GetType() { return m_iType; }
- int GetMode() { return m_iMode; }
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
- int ObjectCaps();
-
-private:
- bool m_bDisabled;
- int m_iType;
- int m_iMode;
-
-public:
- float m_flRadius;
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS( info_radar_target, CRadarTarget );
-
-BEGIN_DATADESC( CRadarTarget )
- DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
- DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
- DEFINE_KEYFIELD( m_iType, FIELD_INTEGER, "type" ),
- DEFINE_KEYFIELD( m_iMode, FIELD_INTEGER, "mode" ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable",InputDisable ),
-END_DATADESC();
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CRadarTarget::Spawn()
-{
- BaseClass::Spawn();
-
- AddEffects( EF_NODRAW );
- SetMoveType( MOVETYPE_NONE );
- SetSolid( SOLID_NONE );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CRadarTarget::InputEnable( inputdata_t &inputdata )
-{
- m_bDisabled = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CRadarTarget::InputDisable( inputdata_t &inputdata )
-{
- m_bDisabled = true;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-int CRadarTarget::ObjectCaps()
-{
- return BaseClass::ObjectCaps() | FCAP_ACROSS_TRANSITION;
-}
-
-
-
-
-//
-// Trigger which detects entities placed in the cargo hold of the jalopy
-//
-
-class CVehicleCargoTrigger : public CBaseEntity
-{
- DECLARE_CLASS( CVehicleCargoTrigger, CBaseEntity );
-
-public:
-
- //
- // Creates a trigger with the specified bounds
-
- static CVehicleCargoTrigger *Create( const Vector &vecOrigin, const Vector &vecMins, const Vector &vecMaxs, CBaseEntity *pOwner )
- {
- CVehicleCargoTrigger *pTrigger = (CVehicleCargoTrigger *) CreateEntityByName( "trigger_vehicle_cargo" );
- if ( pTrigger == NULL )
- return NULL;
-
- UTIL_SetOrigin( pTrigger, vecOrigin );
- UTIL_SetSize( pTrigger, vecMins, vecMaxs );
- pTrigger->SetOwnerEntity( pOwner );
- pTrigger->SetParent( pOwner );
-
- pTrigger->Spawn();
-
- return pTrigger;
- }
-
- //
- // Handles the trigger touching its intended quarry
-
- void CargoTouch( CBaseEntity *pOther )
- {
- // Cannot be ignoring touches
- if ( ( m_hIgnoreEntity == pOther ) || ( m_flIgnoreDuration >= gpGlobals->curtime ) )
- return;
-
- // Make sure this object is being held by the player
- if ( pOther->VPhysicsGetObject() == NULL || (pOther->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) == false )
- return;
-
- if ( StriderBuster_NumFlechettesAttached( pOther ) > 0 )
- return;
-
- AddCargo( pOther );
- }
-
- bool AddCargo( CBaseEntity *pOther )
- {
- // For now, only bother with strider busters
- if ( (FClassnameIs( pOther, "weapon_striderbuster" ) == false) &&
- (FClassnameIs( pOther, "npc_grenade_magna" ) == false)
- )
- return false;
-
- // Must be a physics prop
- CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pOther);
- if ( pOther == NULL )
- return false;
-
- CPropJeepEpisodic *pJeep = dynamic_cast< CPropJeepEpisodic * >( GetOwnerEntity() );
- if ( pJeep == NULL )
- return false;
-
- // Make the player release the item
- Pickup_ForcePlayerToDropThisObject( pOther );
-
- // Stop colliding with things
- pOther->VPhysicsDestroyObject();
- pOther->SetSolidFlags( FSOLID_NOT_SOLID );
- pOther->SetMoveType( MOVETYPE_NONE );
-
- // Parent the object to our owner
- pOther->SetParent( GetOwnerEntity() );
-
- // The car now owns the entity
- pJeep->AddPropToCargoHold( pProp );
-
- // Notify the buster that it's been added to the cargo hold.
- StriderBuster_OnAddToCargoHold( pProp );
-
- // Stop touching this item
- Disable();
-
- return true;
- }
-
- //
- // Setup the entity
-
- void Spawn( void )
- {
- BaseClass::Spawn();
-
- SetSolid( SOLID_BBOX );
- SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID );
-
- SetTouch( &CVehicleCargoTrigger::CargoTouch );
- }
-
- void Activate()
- {
- BaseClass::Activate();
- SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID ); // Fixes up old savegames
- }
-
- //
- // When we've stopped touching this entity, we ignore it
-
- void EndTouch( CBaseEntity *pOther )
- {
- if ( pOther == m_hIgnoreEntity )
- {
- m_hIgnoreEntity = NULL;
- }
-
- BaseClass::EndTouch( pOther );
- }
-
- //
- // Disables the trigger for a set duration
-
- void IgnoreTouches( CBaseEntity *pIgnoreEntity )
- {
- m_hIgnoreEntity = pIgnoreEntity;
- m_flIgnoreDuration = gpGlobals->curtime + 0.5f;
- }
-
- void Disable( void )
- {
- SetTouch( NULL );
- }
-
- void Enable( void )
- {
- SetTouch( &CVehicleCargoTrigger::CargoTouch );
- }
-
-protected:
-
- float m_flIgnoreDuration;
- CHandle <CBaseEntity> m_hIgnoreEntity;
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS( trigger_vehicle_cargo, CVehicleCargoTrigger );
-
-BEGIN_DATADESC( CVehicleCargoTrigger )
- DEFINE_FIELD( m_flIgnoreDuration, FIELD_TIME ),
- DEFINE_FIELD( m_hIgnoreEntity, FIELD_EHANDLE ),
- DEFINE_ENTITYFUNC( CargoTouch ),
-END_DATADESC();
-
-//
-// Transition reference point for the vehicle
-//
-
-class CInfoTargetVehicleTransition : public CPointEntity
-{
-public:
- DECLARE_CLASS( CInfoTargetVehicleTransition, CPointEntity );
-
- void Enable( void ) { m_bDisabled = false; }
- void Disable( void ) { m_bDisabled = true; }
-
- bool IsDisabled( void ) const { return m_bDisabled; }
-
-private:
-
- void InputEnable( inputdata_t &data ) { Enable(); }
- void InputDisable( inputdata_t &data ) { Disable(); }
-
- bool m_bDisabled;
-
- DECLARE_DATADESC();
-};
-
-BEGIN_DATADESC( CInfoTargetVehicleTransition )
- DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable",InputDisable ),
-END_DATADESC();
-
-LINK_ENTITY_TO_CLASS( info_target_vehicle_transition, CInfoTargetVehicleTransition );
-
-//
-// CPropJeepEpisodic
-//
-
-LINK_ENTITY_TO_CLASS( prop_vehicle_jeep, CPropJeepEpisodic );
-
-BEGIN_DATADESC( CPropJeepEpisodic )
-
- DEFINE_FIELD( m_bEntranceLocked, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bExitLocked, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_hCargoProp, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hCargoTrigger, FIELD_EHANDLE ),
- DEFINE_FIELD( m_bAddingCargo, FIELD_BOOLEAN ),
- DEFINE_ARRAY( m_hWheelDust, FIELD_EHANDLE, NUM_WHEEL_EFFECTS ),
- DEFINE_ARRAY( m_hWheelWater, FIELD_EHANDLE, NUM_WHEEL_EFFECTS ),
- DEFINE_ARRAY( m_hHazardLights, FIELD_EHANDLE, NUM_HAZARD_LIGHTS ),
- DEFINE_FIELD( m_flCargoStartTime, FIELD_TIME ),
- DEFINE_FIELD( m_bBlink, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bRadarEnabled, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bRadarDetectsEnemies, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_hRadarScreen, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hLinkControllerFront, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hLinkControllerRear, FIELD_EHANDLE ),
- DEFINE_KEYFIELD( m_bBusterHopperVisible, FIELD_BOOLEAN, "CargoVisible" ),
- // m_flNextAvoidBroadcastTime
- DEFINE_FIELD( m_flNextWaterSound, FIELD_TIME ),
- DEFINE_FIELD( m_flNextRadarUpdateTime, FIELD_TIME ),
- DEFINE_FIELD( m_iNumRadarContacts, FIELD_INTEGER ),
- DEFINE_ARRAY( m_vecRadarContactPos, FIELD_POSITION_VECTOR, RADAR_MAX_CONTACTS ),
- DEFINE_ARRAY( m_iRadarContactType, FIELD_INTEGER, RADAR_MAX_CONTACTS ),
-
- DEFINE_THINKFUNC( HazardBlinkThink ),
-
- DEFINE_OUTPUT( m_OnCompanionEnteredVehicle, "OnCompanionEnteredVehicle" ),
- DEFINE_OUTPUT( m_OnCompanionExitedVehicle, "OnCompanionExitedVehicle" ),
- DEFINE_OUTPUT( m_OnHostileEnteredVehicle, "OnHostileEnteredVehicle" ),
- DEFINE_OUTPUT( m_OnHostileExitedVehicle, "OnHostileExitedVehicle" ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "LockEntrance", InputLockEntrance ),
- DEFINE_INPUTFUNC( FIELD_VOID, "UnlockEntrance", InputUnlockEntrance ),
- DEFINE_INPUTFUNC( FIELD_VOID, "LockExit", InputLockExit ),
- DEFINE_INPUTFUNC( FIELD_VOID, "UnlockExit", InputUnlockExit ),
- DEFINE_INPUTFUNC( FIELD_VOID, "EnableRadar", InputEnableRadar ),
- DEFINE_INPUTFUNC( FIELD_VOID, "DisableRadar", InputDisableRadar ),
- DEFINE_INPUTFUNC( FIELD_VOID, "EnableRadarDetectEnemies", InputEnableRadarDetectEnemies ),
- DEFINE_INPUTFUNC( FIELD_VOID, "AddBusterToCargo", InputAddBusterToCargo ),
- DEFINE_INPUTFUNC( FIELD_VOID, "OutsideTransition", InputOutsideTransition ),
- DEFINE_INPUTFUNC( FIELD_VOID, "DisablePhysGun", InputDisablePhysGun ),
- DEFINE_INPUTFUNC( FIELD_VOID, "EnablePhysGun", InputEnablePhysGun ),
- DEFINE_INPUTFUNC( FIELD_VOID, "CreateLinkController", InputCreateLinkController ),
- DEFINE_INPUTFUNC( FIELD_VOID, "DestroyLinkController", InputDestroyLinkController ),
-
- DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetCargoHopperVisibility", InputSetCargoVisibility ),
-
-END_DATADESC();
-
-IMPLEMENT_SERVERCLASS_ST(CPropJeepEpisodic, DT_CPropJeepEpisodic)
- //CNetworkVar( int, m_iNumRadarContacts );
- SendPropInt( SENDINFO(m_iNumRadarContacts), 8 ),
-
- //CNetworkArray( Vector, m_vecRadarContactPos, RADAR_MAX_CONTACTS );
- SendPropArray( SendPropVector( SENDINFO_ARRAY(m_vecRadarContactPos), -1, SPROP_COORD), m_vecRadarContactPos ),
-
- //CNetworkArray( int, m_iRadarContactType, RADAR_MAX_CONTACTS );
- SendPropArray( SendPropInt(SENDINFO_ARRAY(m_iRadarContactType), RADAR_CONTACT_TYPE_BITS ), m_iRadarContactType ),
-END_SEND_TABLE()
-
-
-//=============================================================================
-// Episodic jeep
-
-CPropJeepEpisodic::CPropJeepEpisodic( void ) :
-m_bEntranceLocked( false ),
-m_bExitLocked( false ),
-m_bAddingCargo( false ),
-m_flNextAvoidBroadcastTime( 0.0f )
-{
- m_bHasGun = false;
- m_bUnableToFire = true;
- m_bRadarDetectsEnemies = false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::UpdateOnRemove( void )
-{
- BaseClass::UpdateOnRemove();
-
- // Kill our wheel dust
- for ( int i = 0; i < NUM_WHEEL_EFFECTS; i++ )
- {
- if ( m_hWheelDust[i] != NULL )
- {
- UTIL_Remove( m_hWheelDust[i] );
- }
-
- if ( m_hWheelWater[i] != NULL )
- {
- UTIL_Remove( m_hWheelWater[i] );
- }
- }
-
- DestroyHazardLights();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::Precache( void )
-{
- PrecacheMaterial( RADAR_PANEL_MATERIAL );
- PrecacheMaterial( RADAR_PANEL_WRITEZ );
- PrecacheModel( s_szHazardSprite );
- PrecacheScriptSound( "JNK_Radar_Ping_Friendly" );
- PrecacheScriptSound( "Physics.WaterSplash" );
-
- PrecacheParticleSystem( "WheelDust" );
- PrecacheParticleSystem( "WheelSplash" );
-
- BaseClass::Precache();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPlayer -
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::EnterVehicle( CBaseCombatCharacter *pPassenger )
-{
- BaseClass::EnterVehicle( pPassenger );
-
- // Turn our hazards off!
- DestroyHazardLights();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::Spawn( void )
-{
- BaseClass::Spawn();
-
- SetBlocksLOS( false );
-
- CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
- if ( pPlayer != NULL )
- {
- pPlayer->m_Local.m_iHideHUD |= HIDEHUD_VEHICLE_CROSSHAIR;
- }
-
-
- SetBodygroup( JEEP_HOPPER_BODYGROUP, m_bBusterHopperVisible ? 1 : 0);
- CreateCargoTrigger();
-
- // carbar bodygroup is always on
- SetBodygroup( JEEP_CARBAR_BODYGROUP, 1 );
-
- m_bRadarDetectsEnemies = false;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::Activate()
-{
- m_iNumRadarContacts = 0; // Force first contact tone
- BaseClass::Activate();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::NPC_FinishedEnterVehicle( CAI_BaseNPC *pPassenger, bool bCompanion )
-{
- // FIXME: This will be moved to the NPCs entering and exiting
- // Fire our outputs
- if ( bCompanion )
- {
- m_OnCompanionEnteredVehicle.FireOutput( this, pPassenger );
- }
- else
- {
- m_OnHostileEnteredVehicle.FireOutput( this, pPassenger );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::NPC_FinishedExitVehicle( CAI_BaseNPC *pPassenger, bool bCompanion )
-{
- // FIXME: This will be moved to the NPCs entering and exiting
- // Fire our outputs
- if ( bCompanion )
- {
- m_OnCompanionExitedVehicle.FireOutput( this, pPassenger );
- }
- else
- {
- m_OnHostileExitedVehicle.FireOutput( this, pPassenger );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPassenger -
-// bCompanion -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CPropJeepEpisodic::NPC_CanEnterVehicle( CAI_BaseNPC *pPassenger, bool bCompanion )
-{
- // Must be unlocked
- if ( bCompanion && m_bEntranceLocked )
- return false;
-
- return BaseClass::NPC_CanEnterVehicle( pPassenger, bCompanion );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPassenger -
-// bCompanion -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CPropJeepEpisodic::NPC_CanExitVehicle( CAI_BaseNPC *pPassenger, bool bCompanion )
-{
- // Must be unlocked
- if ( bCompanion && m_bExitLocked )
- return false;
-
- return BaseClass::NPC_CanExitVehicle( pPassenger, bCompanion );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputLockEntrance( inputdata_t &data )
-{
- m_bEntranceLocked = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputUnlockEntrance( inputdata_t &data )
-{
- m_bEntranceLocked = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputLockExit( inputdata_t &data )
-{
- m_bExitLocked = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputUnlockExit( inputdata_t &data )
-{
- m_bExitLocked = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Turn on the Jalopy radar device
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputEnableRadar( inputdata_t &data )
-{
- if( m_bRadarEnabled )
- return; // Already enabled
-
- SetBodygroup( JEEP_RADAR_BODYGROUP, 1 );
-
- SpawnRadarPanel();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Turn off the Jalopy radar device
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputDisableRadar( inputdata_t &data )
-{
- if( !m_bRadarEnabled )
- return; // Already disabled
-
- SetBodygroup( JEEP_RADAR_BODYGROUP, 0 );
-
- DestroyRadarPanel();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Allow the Jalopy radar to detect Hunters and Striders
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputEnableRadarDetectEnemies( inputdata_t &data )
-{
- m_bRadarDetectsEnemies = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputAddBusterToCargo( inputdata_t &data )
-{
- if ( m_hCargoProp != NULL)
- {
- ReleasePropFromCargoHold();
- m_hCargoProp = NULL;
- }
-
- CBaseEntity *pNewBomb = CreateEntityByName( "weapon_striderbuster" );
- if ( pNewBomb )
- {
- DispatchSpawn( pNewBomb );
- pNewBomb->Teleport( &m_hCargoTrigger->GetAbsOrigin(), NULL, NULL );
- m_hCargoTrigger->AddCargo( pNewBomb );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CPropJeepEpisodic::PassengerInTransition( void )
-{
- // FIXME: Big hack - we need a way to bridge this data better
- // TODO: Get a list of passengers we can traverse instead
- CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx();
- if ( pAlyx )
- {
- if ( pAlyx->GetPassengerState() == PASSENGER_STATE_ENTERING ||
- pAlyx->GetPassengerState() == PASSENGER_STATE_EXITING )
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override velocity if our passenger is transitioning or we're upside-down
-//-----------------------------------------------------------------------------
-Vector CPropJeepEpisodic::PhysGunLaunchVelocity( const Vector &forward, float flMass )
-{
- // Disallow
- if ( PassengerInTransition() )
- return vec3_origin;
-
- Vector vecPuntDir = BaseClass::PhysGunLaunchVelocity( forward, flMass );
- vecPuntDir.z = 150.0f;
- vecPuntDir *= 600.0f;
- return vecPuntDir;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Rolls the vehicle when its trying to upright itself from a punt
-//-----------------------------------------------------------------------------
-AngularImpulse CPropJeepEpisodic::PhysGunLaunchAngularImpulse( void )
-{
- if ( IsOverturned() )
- return AngularImpulse( 0, 300, 0 );
-
- // Don't spin randomly, always spin reliably
- return AngularImpulse( 0, 0, 0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the upright strength based on what state we're in
-//-----------------------------------------------------------------------------
-float CPropJeepEpisodic::GetUprightStrength( void )
-{
- // Lesser if overturned
- if ( IsOverturned() )
- return 2.0f;
-
- return 0.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::CreateCargoTrigger( void )
-{
- if ( m_hCargoTrigger != NULL )
- return;
-
- int nAttachment = LookupAttachment( "cargo" );
- if ( nAttachment )
- {
- Vector vecAttachOrigin;
- Vector vecForward, vecRight, vecUp;
- GetAttachment( nAttachment, vecAttachOrigin, &vecForward, &vecRight, &vecUp );
-
- // Approx size of the hold
- Vector vecMins( -8.0, -6.0, 0 );
- Vector vecMaxs( 8.0, 6.0, 4.0 );
-
- // NDebugOverlay::BoxDirection( vecAttachOrigin, vecMins, vecMaxs, vecForward, 255, 0, 0, 64, 4.0f );
-
- // Create a trigger that lives for a small amount of time
- m_hCargoTrigger = CVehicleCargoTrigger::Create( vecAttachOrigin, vecMins, vecMaxs, this );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: If the player uses the jeep while at the back, he gets ammo from the crate instead
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
-{
- // Fall back and get in the vehicle instead, skip giving ammo
- BaseClass::BaseClass::Use( pActivator, pCaller, useType, value );
-}
-
-#define MIN_WHEEL_DUST_SPEED 5
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::UpdateWheelDust( void )
-{
- // See if this wheel should emit dust
- const vehicleparams_t *vehicleData = m_pServerVehicle->GetVehicleParams();
- const vehicle_operatingparams_t *carState = m_pServerVehicle->GetVehicleOperatingParams();
- bool bAllowDust = vehicleData->steering.dustCloud;
-
- // Car must be active
- bool bCarOn = m_VehiclePhysics.IsOn();
-
- // Must be moving quickly enough or skidding along the ground
- bool bCreateDust = ( bCarOn &&
- bAllowDust &&
- ( m_VehiclePhysics.GetSpeed() >= MIN_WHEEL_DUST_SPEED || carState->skidSpeed > DEFAULT_SKID_THRESHOLD ) );
-
- // Update our wheel dust
- Vector vecPos;
- for ( int i = 0; i < NUM_WHEEL_EFFECTS; i++ )
- {
- m_pServerVehicle->GetWheelContactPoint( i, vecPos );
-
- // Make sure the effect is created
- if ( m_hWheelDust[i] == NULL )
- {
- // Create the dust effect in place
- m_hWheelDust[i] = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
- if ( m_hWheelDust[i] == NULL )
- continue;
-
- // Setup our basic parameters
- m_hWheelDust[i]->KeyValue( "start_active", "0" );
- m_hWheelDust[i]->KeyValue( "effect_name", "WheelDust" );
- m_hWheelDust[i]->SetParent( this );
- m_hWheelDust[i]->SetLocalOrigin( vec3_origin );
- DispatchSpawn( m_hWheelDust[i] );
- if ( gpGlobals->curtime > 0.5f )
- m_hWheelDust[i]->Activate();
- }
-
- // Make sure the effect is created
- if ( m_hWheelWater[i] == NULL )
- {
- // Create the dust effect in place
- m_hWheelWater[i] = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
- if ( m_hWheelWater[i] == NULL )
- continue;
-
- // Setup our basic parameters
- m_hWheelWater[i]->KeyValue( "start_active", "0" );
- m_hWheelWater[i]->KeyValue( "effect_name", "WheelSplash" );
- m_hWheelWater[i]->SetParent( this );
- m_hWheelWater[i]->SetLocalOrigin( vec3_origin );
- DispatchSpawn( m_hWheelWater[i] );
- if ( gpGlobals->curtime > 0.5f )
- m_hWheelWater[i]->Activate();
- }
-
- // Turn the dust on or off
- if ( bCreateDust )
- {
- // Angle the dust out away from the wheels
- Vector vecForward, vecRight, vecUp;
- GetVectors( &vecForward, &vecRight, &vecUp );
-
- const vehicle_controlparams_t *vehicleControls = m_pServerVehicle->GetVehicleControlParams();
- float flWheelDir = ( i & 1 ) ? 1.0f : -1.0f;
- QAngle vecAngles;
- vecForward += vecRight * flWheelDir;
- vecForward += vecRight * (vehicleControls->steering*0.5f) * flWheelDir;
- vecForward += vecUp;
- VectorAngles( vecForward, vecAngles );
-
- // NDebugOverlay::Axis( vecPos, vecAngles, 8.0f, true, 0.1f );
-
- if ( m_WaterData.m_bWheelInWater[i] )
- {
- m_hWheelDust[i]->StopParticleSystem();
-
- // Set us up in the right position
- m_hWheelWater[i]->StartParticleSystem();
- m_hWheelWater[i]->SetAbsAngles( vecAngles );
- m_hWheelWater[i]->SetAbsOrigin( vecPos + Vector( 0, 0, 8 ) );
-
- if ( m_flNextWaterSound < gpGlobals->curtime )
- {
- m_flNextWaterSound = gpGlobals->curtime + random->RandomFloat( 0.25f, 1.0f );
- EmitSound( "Physics.WaterSplash" );
- }
- }
- else
- {
- m_hWheelWater[i]->StopParticleSystem();
-
- // Set us up in the right position
- m_hWheelDust[i]->StartParticleSystem();
- m_hWheelDust[i]->SetAbsAngles( vecAngles );
- m_hWheelDust[i]->SetAbsOrigin( vecPos + Vector( 0, 0, 8 ) );
- }
- }
- else
- {
- // Stop emitting
- m_hWheelDust[i]->StopParticleSystem();
- m_hWheelWater[i]->StopParticleSystem();
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-ConVar jalopy_radar_test_ent( "jalopy_radar_test_ent", "none" );
-
-//-----------------------------------------------------------------------------
-// Purpose: Search for things that the radar detects, and stick them in the
-// UTILVector that gets sent to the client for radar display.
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::UpdateRadar( bool forceUpdate )
-{
- bool bDetectedDog = false;
-
- if( !m_bRadarEnabled )
- return;
-
- if( !forceUpdate && gpGlobals->curtime < m_flNextRadarUpdateTime )
- return;
-
- // Count the targets on radar. If any more targets come on the radar, we beep.
- int m_iNumOldRadarContacts = m_iNumRadarContacts;
-
- m_flNextRadarUpdateTime = gpGlobals->curtime + RADAR_UPDATE_FREQUENCY;
- m_iNumRadarContacts = 0;
-
- CBaseEntity *pEnt = gEntList.FirstEnt();
- string_t iszRadarTarget = FindPooledString( "info_radar_target" );
- string_t iszStriderName = FindPooledString( "npc_strider" );
- string_t iszHunterName = FindPooledString( "npc_hunter" );
-
- string_t iszTestName = FindPooledString( jalopy_radar_test_ent.GetString() );
-
- Vector vecJalopyOrigin = WorldSpaceCenter();
-
- while( pEnt != NULL )
- {
- int type = RADAR_CONTACT_NONE;
-
- if( pEnt->m_iClassname == iszRadarTarget )
- {
- CRadarTarget *pTarget = dynamic_cast<CRadarTarget*>(pEnt);
-
- if( pTarget != NULL && !pTarget->IsDisabled() )
- {
- if( pTarget->m_flRadius < 0 || vecJalopyOrigin.DistToSqr(pTarget->GetAbsOrigin()) <= Square(pTarget->m_flRadius) )
- {
- // This item has been detected.
- type = pTarget->GetType();
-
- if( type == RADAR_CONTACT_DOG )
- bDetectedDog = true;// used to prevent Alyx talking about the radar (see below)
-
- if( pTarget->GetMode() == RADAR_MODE_STICKY )
- {
- // This beacon was just detected. Now change the radius to infinite
- // so that it will never go off the radar due to distance.
- pTarget->m_flRadius = -1;
- }
- }
- }
- }
- else if ( m_bRadarDetectsEnemies )
- {
- if ( pEnt->m_iClassname == iszStriderName )
- {
- CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider*>(pEnt);
-
- if( !pStrider || !pStrider->CarriedByDropship() )
- {
- // Ignore striders which are carried by dropships.
- type = RADAR_CONTACT_LARGE_ENEMY;
- }
- }
-
- if ( pEnt->m_iClassname == iszHunterName )
- {
- type = RADAR_CONTACT_ENEMY;
- }
- }
-
- if( type != RADAR_CONTACT_NONE )
- {
- Vector vecPos = pEnt->WorldSpaceCenter();
-
- m_vecRadarContactPos.Set( m_iNumRadarContacts, vecPos );
- m_iRadarContactType.Set( m_iNumRadarContacts, type );
- m_iNumRadarContacts++;
-
- if( m_iNumRadarContacts == RADAR_MAX_CONTACTS )
- break;
- }
-
- pEnt = gEntList.NextEnt(pEnt);
- }
-
- if( m_iNumRadarContacts > m_iNumOldRadarContacts )
- {
- // Play a bleepy sound
- if( !bDetectedDog )
- {
- EmitSound( "JNK_Radar_Ping_Friendly" );
- }
-
- //Notify Alyx so she can talk about the radar contact
- CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx();
-
- if( !bDetectedDog && pAlyx != NULL && pAlyx->GetVehicle() )
- {
- pAlyx->SpeakIfAllowed( TLK_PASSENGER_NEW_RADAR_CONTACT );
- }
- }
-
- if( bDetectedDog )
- {
- // Update the radar much more frequently when dog is around.
- m_flNextRadarUpdateTime = gpGlobals->curtime + RADAR_UPDATE_FREQUENCY_FAST;
- }
-
- //Msg("Server detected %d objects\n", m_iNumRadarContacts );
-
- CBasePlayer *pPlayer = AI_GetSinglePlayer();
- CSingleUserRecipientFilter filter(pPlayer);
- UserMessageBegin( filter, "UpdateJalopyRadar" );
- WRITE_BYTE( 0 ); // end marker
- MessageEnd(); // send message
-}
-
-ConVar jalopy_cargo_anim_time( "jalopy_cargo_anim_time", "1.0" );
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::UpdateCargoEntry( void )
-{
- // Don't bother if we have no prop to move
- if ( m_hCargoProp == NULL )
- return;
-
- // If we're past our animation point, then we're already done
- if ( m_flCargoStartTime + jalopy_cargo_anim_time.GetFloat() < gpGlobals->curtime )
- {
- // Close the hold immediately if we're finished
- if ( m_bAddingCargo )
- {
- m_flAmmoCrateCloseTime = gpGlobals->curtime;
- m_bAddingCargo = false;
- }
-
- return;
- }
-
- // Get our target point
- int nAttachment = LookupAttachment( "cargo" );
- Vector vecTarget, vecOut;
- QAngle vecAngles;
- GetAttachmentLocal( nAttachment, vecTarget, vecAngles );
-
- // Find where we are in the blend and bias it for a fast entry and slow ease-out
- float flPerc = (jalopy_cargo_anim_time.GetFloat()) ? (( gpGlobals->curtime - m_flCargoStartTime ) / jalopy_cargo_anim_time.GetFloat()) : 1.0f;
- flPerc = Bias( flPerc, 0.75f );
- VectorLerp( m_hCargoProp->GetLocalOrigin(), vecTarget, flPerc, vecOut );
-
- // Get our target orientation
- CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(m_hCargoProp.Get());
- if ( pProp == NULL )
- return;
-
- // Slerp our quaternions to find where we are this frame
- Quaternion qtTarget;
- QAngle qa( 0, 90, 0 );
- qa += pProp->PreferredCarryAngles();
- AngleQuaternion( qa, qtTarget ); // FIXME: Find the real offset to make this sit properly
- Quaternion qtCurrent;
- AngleQuaternion( pProp->GetLocalAngles(), qtCurrent );
-
- Quaternion qtOut;
- QuaternionSlerp( qtCurrent, qtTarget, flPerc, qtOut );
-
- // Put it back to angles
- QuaternionAngles( qtOut, vecAngles );
-
- // Finally, take these new position
- m_hCargoProp->SetLocalOrigin( vecOut );
- m_hCargoProp->SetLocalAngles( vecAngles );
-
- // Push the closing out into the future to make sure we don't try and close at the same time
- m_flAmmoCrateCloseTime += gpGlobals->frametime;
-}
-
-#define VEHICLE_AVOID_BROADCAST_RATE 0.5f
-
-//-----------------------------------------------------------------------------
-// Purpose: This function isn't really what we want
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::CreateAvoidanceZone( void )
-{
- if ( m_flNextAvoidBroadcastTime > gpGlobals->curtime )
- return;
-
- // Only do this when we're stopped
- if ( m_VehiclePhysics.GetSpeed() > 5.0f )
- return;
-
- float flHullRadius = CollisionProp()->BoundingRadius2D();
-
- Vector vecPos;
- CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.33f, 0.25f ), &vecPos );
- CSoundEnt::InsertSound( SOUND_MOVE_AWAY, vecPos, (flHullRadius*0.4f), VEHICLE_AVOID_BROADCAST_RATE, this );
- // NDebugOverlay::Sphere( vecPos, vec3_angle, flHullRadius*0.4f, 255, 0, 0, 0, true, VEHICLE_AVOID_BROADCAST_RATE );
-
- CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.66f, 0.25f ), &vecPos );
- CSoundEnt::InsertSound( SOUND_MOVE_AWAY, vecPos, (flHullRadius*0.4f), VEHICLE_AVOID_BROADCAST_RATE, this );
- // NDebugOverlay::Sphere( vecPos, vec3_angle, flHullRadius*0.4f, 255, 0, 0, 0, true, VEHICLE_AVOID_BROADCAST_RATE );
-
- // Don't broadcast again until these are done
- m_flNextAvoidBroadcastTime = gpGlobals->curtime + VEHICLE_AVOID_BROADCAST_RATE;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::Think( void )
-{
- BaseClass::Think();
-
- // If our passenger is transitioning, then don't let the player drive off
- CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx();
- if ( pAlyx && pAlyx->GetPassengerState() == PASSENGER_STATE_EXITING )
- {
- m_throttleDisableTime = gpGlobals->curtime + 0.25f;
- }
-
- // Update our cargo entering our hold
- UpdateCargoEntry();
-
- // See if the wheel dust should be on or off
- UpdateWheelDust();
-
- // Update the radar, of course.
- UpdateRadar();
-
- if ( m_hCargoTrigger && !m_hCargoProp && !m_hCargoTrigger->m_pfnTouch )
- {
- m_hCargoTrigger->Enable();
- }
-
- CreateAvoidanceZone();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEntity -
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::AddPropToCargoHold( CPhysicsProp *pProp )
-{
- // The hold must be empty to add something to it
- if ( m_hCargoProp != NULL )
- {
- Assert( 0 );
- return;
- }
-
- // Take the prop as our cargo
- m_hCargoProp = pProp;
- m_flCargoStartTime = gpGlobals->curtime;
- m_bAddingCargo = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Drops the cargo from the hold
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::ReleasePropFromCargoHold( void )
-{
- // Pull the object free!
- m_hCargoProp->SetParent( NULL );
- m_hCargoProp->CreateVPhysics();
-
- if ( m_hCargoTrigger )
- {
- m_hCargoTrigger->Enable();
- m_hCargoTrigger->IgnoreTouches( m_hCargoProp );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: If the player is trying to pull the cargo out of the hold using the physcannon, let him
-// Output : Returns the cargo to pick up, if all the conditions are met
-//-----------------------------------------------------------------------------
-CBaseEntity *CPropJeepEpisodic::OnFailedPhysGunPickup( Vector vPhysgunPos )
-{
- // Make sure we're available to open
- if ( m_hCargoProp != NULL )
- {
- // Player's forward direction
- Vector vecPlayerForward;
- CBasePlayer *pPlayer = AI_GetSinglePlayer();
- if ( pPlayer == NULL )
- return NULL;
-
- pPlayer->EyeVectors( &vecPlayerForward );
-
- // Origin and facing of the cargo hold
- Vector vecCargoOrigin;
- Vector vecCargoForward;
- GetAttachment( "cargo", vecCargoOrigin, &vecCargoForward );
-
- // Direction from the cargo to the player's position
- Vector vecPickupDir = ( vecCargoOrigin - vPhysgunPos );
- float flDist = VectorNormalize( vecPickupDir );
-
- // We need to make sure the player's position is within a cone near the opening and that they're also facing the right way
- bool bInCargoRange = ( (flDist < (15.0f * 12.0f)) && DotProduct( vecCargoForward, vecPickupDir ) < 0.1f );
- bool bFacingCargo = DotProduct( vecPlayerForward, vecPickupDir ) > 0.975f;
-
- // If we're roughly pulling at the item, pick that up
- if ( bInCargoRange && bFacingCargo )
- {
- // Save this for later
- CBaseEntity *pCargo = m_hCargoProp;
-
- // Drop the cargo
- ReleasePropFromCargoHold();
-
- // Forget the item but pass it back as the object to pick up
- m_hCargoProp = NULL;
- return pCargo;
- }
- }
-
- return BaseClass::OnFailedPhysGunPickup( vPhysgunPos );
-}
-
-// adds a collision solver for any small props that are stuck under the vehicle
-static void SolveBlockingProps( CPropJeepEpisodic *pVehicleEntity, IPhysicsObject *pVehiclePhysics )
-{
- CUtlVector<CBaseEntity *> solveList;
- float vehicleMass = pVehiclePhysics->GetMass();
- Vector vehicleUp;
- pVehicleEntity->GetVectors( NULL, NULL, &vehicleUp );
- IPhysicsFrictionSnapshot *pSnapshot = pVehiclePhysics->CreateFrictionSnapshot();
- while ( pSnapshot->IsValid() )
- {
- IPhysicsObject *pOther = pSnapshot->GetObject(1);
- float otherMass = pOther->GetMass();
- CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
- Assert(pOtherEntity);
- if ( pOtherEntity && pOtherEntity->GetMoveType() == MOVETYPE_VPHYSICS && pOther->IsMoveable() && (otherMass*4.0f) < vehicleMass )
- {
- Vector normal;
- pSnapshot->GetSurfaceNormal(normal);
- // this points down in the car's reference frame, then it's probably trapped under the car
- if ( DotProduct(normal, vehicleUp) < -0.9f )
- {
- Vector point, pointLocal;
- pSnapshot->GetContactPoint(point);
- VectorITransform( point, pVehicleEntity->EntityToWorldTransform(), pointLocal );
- Vector bottomPoint = physcollision->CollideGetExtent( pVehiclePhysics->GetCollide(), vec3_origin, vec3_angle, Vector(0,0,-1) );
- // make sure it's under the bottom of the car
- float bottomPlane = DotProduct(bottomPoint,vehicleUp)+8; // 8 inches above bottom
- if ( DotProduct( pointLocal, vehicleUp ) <= bottomPlane )
- {
- //Msg("Solved %s\n", pOtherEntity->GetClassname());
- if ( solveList.Find(pOtherEntity) < 0 )
- {
- solveList.AddToTail(pOtherEntity);
- }
- }
- }
- }
- pSnapshot->NextFrictionData();
- }
- pVehiclePhysics->DestroyFrictionSnapshot( pSnapshot );
- if ( solveList.Count() )
- {
- for ( int i = 0; i < solveList.Count(); i++ )
- {
- EntityPhysics_CreateSolver( pVehicleEntity, solveList[i], true, 4.0f );
- }
- pVehiclePhysics->RecheckContactPoints();
- }
-}
-
-static void SimpleCollisionResponse( Vector velocityIn, const Vector &normal, float coefficientOfRestitution, Vector *pVelocityOut )
-{
- Vector Vn = DotProduct(velocityIn,normal) * normal;
- Vector Vt = velocityIn - Vn;
- *pVelocityOut = Vt - coefficientOfRestitution * Vn;
-}
-
-static void KillBlockingEnemyNPCs( CBasePlayer *pPlayer, CBaseEntity *pVehicleEntity, IPhysicsObject *pVehiclePhysics )
-{
- Vector velocity;
- pVehiclePhysics->GetVelocity( &velocity, NULL );
- float vehicleMass = pVehiclePhysics->GetMass();
-
- // loop through the contacts and look for enemy NPCs that we're pushing on
- CUtlVector<CAI_BaseNPC *> npcList;
- CUtlVector<Vector> forceList;
- CUtlVector<Vector> contactList;
- IPhysicsFrictionSnapshot *pSnapshot = pVehiclePhysics->CreateFrictionSnapshot();
- while ( pSnapshot->IsValid() )
- {
- IPhysicsObject *pOther = pSnapshot->GetObject(1);
- float otherMass = pOther->GetMass();
- CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
- CAI_BaseNPC *pNPC = pOtherEntity ? pOtherEntity->MyNPCPointer() : NULL;
- // Is this an enemy NPC with a small enough mass?
- if ( pNPC && pPlayer->IRelationType(pNPC) != D_LI && ((otherMass*2.0f) < vehicleMass) )
- {
- // accumulate the stress force for this NPC in the lsit
- float force = pSnapshot->GetNormalForce();
- Vector normal;
- pSnapshot->GetSurfaceNormal(normal);
- normal *= force;
- int index = npcList.Find(pNPC);
- if ( index < 0 )
- {
- vphysicsupdateai_t *pUpdate = NULL;
- if ( pNPC->VPhysicsGetObject() && pNPC->VPhysicsGetObject()->GetShadowController() && pNPC->GetMoveType() == MOVETYPE_STEP )
- {
- if ( pNPC->HasDataObjectType(VPHYSICSUPDATEAI) )
- {
- pUpdate = static_cast<vphysicsupdateai_t *>(pNPC->GetDataObject(VPHYSICSUPDATEAI));
- // kill this guy if I've been pushing him for more than half a second and I'm
- // still pushing in his direction
- if ( (gpGlobals->curtime - pUpdate->startUpdateTime) > 0.5f && DotProduct(velocity,normal) > 0)
- {
- index = npcList.AddToTail(pNPC);
- forceList.AddToTail( normal );
- Vector pos;
- pSnapshot->GetContactPoint(pos);
- contactList.AddToTail(pos);
- }
- }
- else
- {
- pUpdate = static_cast<vphysicsupdateai_t *>(pNPC->CreateDataObject( VPHYSICSUPDATEAI ));
- pUpdate->startUpdateTime = gpGlobals->curtime;
- }
- // update based on vphysics for the next second
- // this allows the car to push the NPC
- pUpdate->stopUpdateTime = gpGlobals->curtime + 1.0f;
- float maxAngular;
- pNPC->VPhysicsGetObject()->GetShadowController()->GetMaxSpeed( &pUpdate->savedShadowControllerMaxSpeed, &maxAngular );
- pNPC->VPhysicsGetObject()->GetShadowController()->MaxSpeed( 1.0f, maxAngular );
- }
- }
- else
- {
- forceList[index] += normal;
- }
- }
- pSnapshot->NextFrictionData();
- }
- pVehiclePhysics->DestroyFrictionSnapshot( pSnapshot );
- // now iterate the list and check each cumulative force against the threshold
- if ( npcList.Count() )
- {
- for ( int i = npcList.Count(); --i >= 0; )
- {
- Vector damageForce;
- npcList[i]->VPhysicsGetObject()->GetVelocity( &damageForce, NULL );
- Vector vel;
- pVehiclePhysics->GetVelocityAtPoint( contactList[i], &vel );
- damageForce -= vel;
- Vector normal = forceList[i];
- VectorNormalize(normal);
- SimpleCollisionResponse( damageForce, normal, 1.0, &damageForce );
- damageForce += (normal * 300.0f);
- damageForce *= npcList[i]->VPhysicsGetObject()->GetMass();
- float len = damageForce.Length();
- damageForce.z += len*phys_upimpactforcescale.GetFloat();
- Vector vehicleForce = -damageForce;
-
- CTakeDamageInfo dmgInfo( pVehicleEntity, pVehicleEntity, damageForce, contactList[i], 200.0f, DMG_CRUSH|DMG_VEHICLE );
- npcList[i]->TakeDamage( dmgInfo );
- pVehiclePhysics->ApplyForceOffset( vehicleForce, contactList[i] );
- PhysCollisionSound( pVehicleEntity, npcList[i]->VPhysicsGetObject(), CHAN_BODY, pVehiclePhysics->GetMaterialIndex(), npcList[i]->VPhysicsGetObject()->GetMaterialIndex(), gpGlobals->frametime, 200.0f );
- }
- }
-}
-
-void CPropJeepEpisodic::DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased )
-{
- /* The car headlight hurts perf, there's no timer to turn it off automatically,
- and we haven't built any gameplay around it.
-
- Furthermore, I don't think I've ever seen a playtester turn it on.
-
- if ( ucmd->impulse == 100 )
- {
- if (HeadlightIsOn())
- {
- HeadlightTurnOff();
- }
- else
- {
- HeadlightTurnOn();
- }
- }*/
-
- if ( ucmd->forwardmove != 0.0f )
- {
- //Msg("Push V: %.2f, %.2f, %.2f\n", ucmd->forwardmove, carState->engineRPM, carState->speed );
- CBasePlayer *pPlayer = ToBasePlayer(GetDriver());
-
- if ( pPlayer && VPhysicsGetObject() )
- {
- KillBlockingEnemyNPCs( pPlayer, this, VPhysicsGetObject() );
- SolveBlockingProps( this, VPhysicsGetObject() );
- }
- }
- BaseClass::DriveVehicle(flFrameTime, ucmd, iButtonsDown, iButtonsReleased);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::CreateHazardLights( void )
-{
- static const char *s_szAttach[NUM_HAZARD_LIGHTS] =
- {
- "rearlight_r",
- "rearlight_l",
- "headlight_r",
- "headlight_l",
- };
-
- // Turn on the hazards!
- for ( int i = 0; i < NUM_HAZARD_LIGHTS; i++ )
- {
- if ( m_hHazardLights[i] == NULL )
- {
- m_hHazardLights[i] = CSprite::SpriteCreate( s_szHazardSprite, GetLocalOrigin(), false );
- if ( m_hHazardLights[i] )
- {
- m_hHazardLights[i]->SetTransparency( kRenderWorldGlow, 255, 220, 40, 255, kRenderFxNoDissipation );
- m_hHazardLights[i]->SetAttachment( this, LookupAttachment( s_szAttach[i] ) );
- m_hHazardLights[i]->SetGlowProxySize( 2.0f );
- m_hHazardLights[i]->TurnOff();
- if ( i < 2 )
- {
- // Rear lights are red
- m_hHazardLights[i]->SetColor( 255, 0, 0 );
- m_hHazardLights[i]->SetScale( 1.0f );
- }
- else
- {
- // Font lights are white
- m_hHazardLights[i]->SetScale( 1.0f );
- }
- }
- }
- }
-
- // We start off
- m_bBlink = false;
-
- // Setup our blink
- SetContextThink( &CPropJeepEpisodic::HazardBlinkThink, gpGlobals->curtime + 0.1f, "HazardBlink" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::DestroyHazardLights( void )
-{
- for ( int i = 0; i < NUM_HAZARD_LIGHTS; i++ )
- {
- if ( m_hHazardLights[i] != NULL )
- {
- UTIL_Remove( m_hHazardLights[i] );
- }
- }
-
- SetContextThink( NULL, gpGlobals->curtime, "HazardBlink" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : nRole -
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::ExitVehicle( int nRole )
-{
- BaseClass::ExitVehicle( nRole );
-
- CreateHazardLights();
-}
-
-void CPropJeepEpisodic::SetBusterHopperVisibility(bool visible)
-{
- // if we're there already do nothing
- if (visible == m_bBusterHopperVisible)
- return;
-
- SetBodygroup( JEEP_HOPPER_BODYGROUP, visible ? 1 : 0);
- m_bBusterHopperVisible = visible;
-}
-
-
-void CPropJeepEpisodic::InputSetCargoVisibility( inputdata_t &data )
-{
- bool visible = data.value.Bool();
-
- SetBusterHopperVisibility( visible );
-}
-
-//-----------------------------------------------------------------------------
-// THIS CODE LIFTED RIGHT OUT OF TF2, to defer the pain of making vgui-on-an-entity
-// code available to all CBaseAnimating.
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::SpawnRadarPanel()
-{
- // FIXME: Deal with dynamically resizing control panels?
-
- // If we're attached to an entity, spawn control panels on it instead of use
- CBaseAnimating *pEntityToSpawnOn = this;
- char *pOrgLL = "controlpanel0_ll";
- char *pOrgUR = "controlpanel0_ur";
-
- Assert( pEntityToSpawnOn );
-
- // Lookup the attachment point...
- int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(pOrgLL);
-
- if (nLLAttachmentIndex <= 0)
- {
- return;
- }
-
- int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(pOrgUR);
- if (nURAttachmentIndex <= 0)
- {
- return;
- }
-
- const char *pScreenName = "jalopy_radar_panel";
- const char *pScreenClassname = "vgui_screen";
-
- // Compute the screen size from the attachment points...
- matrix3x4_t panelToWorld;
- pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld );
-
- matrix3x4_t worldToPanel;
- MatrixInvert( panelToWorld, worldToPanel );
-
- // Now get the lower right position + transform into panel space
- Vector lr, lrlocal;
- pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld );
- MatrixGetColumn( panelToWorld, 3, lr );
- VectorTransform( lr, worldToPanel, lrlocal );
-
- float flWidth = lrlocal.x;
- float flHeight = lrlocal.y;
-
- CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
- pScreen->SetActualSize( flWidth, flHeight );
- pScreen->SetActive( true );
- pScreen->SetOverlayMaterial( RADAR_PANEL_WRITEZ );
- pScreen->SetTransparency( true );
-
- m_hRadarScreen.Set( pScreen );
-
- m_bRadarEnabled = true;
- m_iNumRadarContacts = 0;
- m_flNextRadarUpdateTime = gpGlobals->curtime - 1.0f;
-}
-
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::DestroyRadarPanel()
-{
- Assert( m_hRadarScreen != NULL );
- m_hRadarScreen->SUB_Remove();
- m_bRadarEnabled = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::HazardBlinkThink( void )
-{
- if ( m_bBlink )
- {
- for ( int i = 0; i < NUM_HAZARD_LIGHTS; i++ )
- {
- if ( m_hHazardLights[i] )
- {
- m_hHazardLights[i]->SetBrightness( 0, 0.1f );
- }
- }
-
- SetContextThink( &CPropJeepEpisodic::HazardBlinkThink, gpGlobals->curtime + 0.25f, "HazardBlink" );
- }
- else
- {
- for ( int i = 0; i < NUM_HAZARD_LIGHTS; i++ )
- {
- if ( m_hHazardLights[i] )
- {
- m_hHazardLights[i]->SetBrightness( 255, 0.1f );
- m_hHazardLights[i]->TurnOn();
- }
- }
-
- SetContextThink( &CPropJeepEpisodic::HazardBlinkThink, gpGlobals->curtime + 0.5f, "HazardBlink" );
- }
-
- m_bBlink = !m_bBlink;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::HandleWater( void )
-{
- // Only check the wheels and engine in water if we have a driver (player).
- if ( !GetDriver() )
- return;
-
- // Update our internal state
- CheckWater();
-
- // Save of data from last think.
- for ( int iWheel = 0; iWheel < JEEP_WHEEL_COUNT; ++iWheel )
- {
- m_WaterData.m_bWheelWasInWater[iWheel] = m_WaterData.m_bWheelInWater[iWheel];
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Report our lock state
-//-----------------------------------------------------------------------------
-int CPropJeepEpisodic::DrawDebugTextOverlays( void )
-{
- int text_offset = BaseClass::DrawDebugTextOverlays();
-
- if ( m_debugOverlays & OVERLAY_TEXT_BIT )
- {
- EntityText( text_offset, CFmtStr("Entrance: %s", m_bEntranceLocked ? "Locked" : "Unlocked" ), 0 );
- text_offset++;
-
- EntityText( text_offset, CFmtStr("Exit: %s", m_bExitLocked ? "Locked" : "Unlocked" ), 0 );
- text_offset++;
- }
-
- return text_offset;
-}
-
-#define TRANSITION_SEARCH_RADIUS (100*12)
-
-//-----------------------------------------------------------------------------
-// Purpose: Teleport the car to a destination that will cause it to transition if it's not going to otherwise
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputOutsideTransition( inputdata_t &inputdata )
-{
- // Teleport into the new map
- CBasePlayer *pPlayer = AI_GetSinglePlayer();
- Vector vecTeleportPos;
- QAngle vecTeleportAngles;
-
- // Get our bounds
- Vector vecSurroundMins, vecSurroundMaxs;
- CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
- vecSurroundMins -= WorldSpaceCenter();
- vecSurroundMaxs -= WorldSpaceCenter();
-
- Vector vecBestPos;
- QAngle vecBestAngles;
-
- CInfoTargetVehicleTransition *pEntity = NULL;
- bool bSucceeded = false;
-
- // Find all entities of the correct name and try and sit where they're at
- while ( ( pEntity = (CInfoTargetVehicleTransition *) gEntList.FindEntityByClassname( pEntity, "info_target_vehicle_transition" ) ) != NULL )
- {
- // Must be enabled
- if ( pEntity->IsDisabled() )
- continue;
-
- // Must be within range
- if ( ( pEntity->GetAbsOrigin() - pPlayer->GetAbsOrigin() ).LengthSqr() > Square( TRANSITION_SEARCH_RADIUS ) )
- continue;
-
- vecTeleportPos = pEntity->GetAbsOrigin();
- vecTeleportAngles = pEntity->GetAbsAngles() + QAngle( 0, -90, 0 ); // Vehicle is always off by 90 degrees
-
- // Rotate to face the destination angles
- Vector vecMins;
- Vector vecMaxs;
- VectorRotate( vecSurroundMins, vecTeleportAngles, vecMins );
- VectorRotate( vecSurroundMaxs, vecTeleportAngles, vecMaxs );
-
- if ( vecMaxs.x < vecMins.x )
- V_swap( vecMins.x, vecMaxs.x );
-
- if ( vecMaxs.y < vecMins.y )
- V_swap( vecMins.y, vecMaxs.y );
-
- if ( vecMaxs.z < vecMins.z )
- V_swap( vecMins.z, vecMaxs.z );
-
- // Move up
- vecTeleportPos.z += ( vecMaxs.z - vecMins.z );
-
- trace_t tr;
- UTIL_TraceHull( vecTeleportPos, vecTeleportPos - Vector( 0, 0, 128 ), vecMins, vecMaxs, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
- if ( tr.startsolid == false && tr.allsolid == false && tr.fraction < 1.0f )
- {
- // Store this off
- vecBestPos = tr.endpos;
- vecBestAngles = vecTeleportAngles;
- bSucceeded = true;
-
- // If this point isn't visible, then stop looking and use it
- if ( pPlayer->FInViewCone( tr.endpos ) == false )
- break;
- }
- }
-
- // See if we're finished
- if ( bSucceeded )
- {
- Teleport( &vecTeleportPos, &vecTeleportAngles, NULL );
- return;
- }
-
- // TODO: We found no valid teleport points, so try to find them dynamically
- Warning("No valid vehicle teleport points!\n");
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Stop players punting the car around.
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputDisablePhysGun( inputdata_t &data )
-{
- AddEFlags( EFL_NO_PHYSCANNON_INTERACTION );
-}
-//-----------------------------------------------------------------------------
-// Purpose: Return to normal
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputEnablePhysGun( inputdata_t &data )
-{
- RemoveEFlags( EFL_NO_PHYSCANNON_INTERACTION );
-}
-
-//-----------------------------------------------------------------------------
-// Create and parent two radial node link controllers.
-//-----------------------------------------------------------------------------
-void CPropJeepEpisodic::InputCreateLinkController( inputdata_t &data )
-{
- Vector vecFront, vecRear;
- Vector vecWFL, vecWFR; // Front wheels
- Vector vecWRL, vecWRR; // Back wheels
-
- GetAttachment( "wheel_fr", vecWFR );
- GetAttachment( "wheel_fl", vecWFL );
-
- GetAttachment( "wheel_rr", vecWRR );
- GetAttachment( "wheel_rl", vecWRL );
-
- vecFront = (vecWFL + vecWFR) * 0.5f;
- vecRear = (vecWRL + vecWRR) * 0.5f;
-
- float flRadius = ( (vecFront - vecRear).Length() ) * 0.6f;
-
- CAI_RadialLinkController *pLinkController = (CAI_RadialLinkController *)CreateEntityByName( "info_radial_link_controller" );
- if( pLinkController != NULL && m_hLinkControllerFront.Get() == NULL )
- {
- pLinkController->m_flRadius = flRadius;
- pLinkController->Spawn();
- pLinkController->SetAbsOrigin( vecFront );
- pLinkController->SetOwnerEntity( this );
- pLinkController->SetParent( this );
- pLinkController->Activate();
- m_hLinkControllerFront.Set( pLinkController );
-
- //NDebugOverlay::Circle( vecFront, Vector(0,1,0), Vector(1,0,0), flRadius, 255, 255, 255, 128, false, 100 );
- }
-
- pLinkController = (CAI_RadialLinkController *)CreateEntityByName( "info_radial_link_controller" );
- if( pLinkController != NULL && m_hLinkControllerRear.Get() == NULL )
- {
- pLinkController->m_flRadius = flRadius;
- pLinkController->Spawn();
- pLinkController->SetAbsOrigin( vecRear );
- pLinkController->SetOwnerEntity( this );
- pLinkController->SetParent( this );
- pLinkController->Activate();
- m_hLinkControllerRear.Set( pLinkController );
-
- //NDebugOverlay::Circle( vecRear, Vector(0,1,0), Vector(1,0,0), flRadius, 255, 255, 255, 128, false, 100 );
- }
-}
-
-void CPropJeepEpisodic::InputDestroyLinkController( inputdata_t &data )
-{
- if( m_hLinkControllerFront.Get() != NULL )
- {
- CAI_RadialLinkController *pLinkController = dynamic_cast<CAI_RadialLinkController*>(m_hLinkControllerFront.Get());
- if( pLinkController != NULL )
- {
- pLinkController->ModifyNodeLinks(false);
- UTIL_Remove( pLinkController );
- m_hLinkControllerFront.Set(NULL);
- }
- }
-
- if( m_hLinkControllerRear.Get() != NULL )
- {
- CAI_RadialLinkController *pLinkController = dynamic_cast<CAI_RadialLinkController*>(m_hLinkControllerRear.Get());
- if( pLinkController != NULL )
- {
- pLinkController->ModifyNodeLinks(false);
- UTIL_Remove( pLinkController );
- m_hLinkControllerRear.Set(NULL);
- }
- }
-}
-
-
-bool CPropJeepEpisodic::AllowBlockedExit( CBaseCombatCharacter *pPassenger, int nRole )
-{
- // Wait until we've settled down before we resort to blocked exits.
- // This keeps us from doing blocked exits in mid-jump, which can cause mayhem like
- // sticking the player through player clips or into geometry.
- return GetSmoothedVelocity().IsLengthLessThan( jalopy_blocked_exit_max_speed.GetFloat() );
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "vehicle_jeep_episodic.h"
+#include "collisionutils.h"
+#include "npc_alyx_episodic.h"
+#include "particle_parse.h"
+#include "particle_system.h"
+#include "hl2_player.h"
+#include "in_buttons.h"
+#include "vphysics/friction.h"
+#include "vphysicsupdateai.h"
+#include "physics_npc_solver.h"
+#include "Sprite.h"
+#include "weapon_striderbuster.h"
+#include "npc_strider.h"
+#include "vguiscreen.h"
+#include "hl2_vehicle_radar.h"
+#include "props.h"
+#include "ai_dynamiclink.h"
+
+extern ConVar phys_upimpactforcescale;
+
+ConVar jalopy_blocked_exit_max_speed( "jalopy_blocked_exit_max_speed", "50" );
+
+#define JEEP_AMMOCRATE_HITGROUP 5
+#define JEEP_AMMO_CRATE_CLOSE_DELAY 2.0f
+
+// Bodygroups
+#define JEEP_RADAR_BODYGROUP 1
+#define JEEP_HOPPER_BODYGROUP 2
+#define JEEP_CARBAR_BODYGROUP 3
+
+#define RADAR_PANEL_MATERIAL "vgui/screens/radar"
+#define RADAR_PANEL_WRITEZ "engine/writez"
+
+static const char *s_szHazardSprite = "sprites/light_glow01.vmt";
+
+enum
+{
+ RADAR_MODE_NORMAL = 0,
+ RADAR_MODE_STICKY,
+};
+
+//=========================================================
+//=========================================================
+class CRadarTarget : public CPointEntity
+{
+ DECLARE_CLASS( CRadarTarget, CPointEntity );
+
+public:
+ void Spawn();
+
+ bool IsDisabled() { return m_bDisabled; }
+ int GetType() { return m_iType; }
+ int GetMode() { return m_iMode; }
+ void InputEnable( inputdata_t &inputdata );
+ void InputDisable( inputdata_t &inputdata );
+ int ObjectCaps();
+
+private:
+ bool m_bDisabled;
+ int m_iType;
+ int m_iMode;
+
+public:
+ float m_flRadius;
+
+ DECLARE_DATADESC();
+};
+
+LINK_ENTITY_TO_CLASS( info_radar_target, CRadarTarget );
+
+BEGIN_DATADESC( CRadarTarget )
+ DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
+ DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
+ DEFINE_KEYFIELD( m_iType, FIELD_INTEGER, "type" ),
+ DEFINE_KEYFIELD( m_iMode, FIELD_INTEGER, "mode" ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable",InputDisable ),
+END_DATADESC();
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CRadarTarget::Spawn()
+{
+ BaseClass::Spawn();
+
+ AddEffects( EF_NODRAW );
+ SetMoveType( MOVETYPE_NONE );
+ SetSolid( SOLID_NONE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CRadarTarget::InputEnable( inputdata_t &inputdata )
+{
+ m_bDisabled = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CRadarTarget::InputDisable( inputdata_t &inputdata )
+{
+ m_bDisabled = true;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int CRadarTarget::ObjectCaps()
+{
+ return BaseClass::ObjectCaps() | FCAP_ACROSS_TRANSITION;
+}
+
+
+
+
+//
+// Trigger which detects entities placed in the cargo hold of the jalopy
+//
+
+class CVehicleCargoTrigger : public CBaseEntity
+{
+ DECLARE_CLASS( CVehicleCargoTrigger, CBaseEntity );
+
+public:
+
+ //
+ // Creates a trigger with the specified bounds
+
+ static CVehicleCargoTrigger *Create( const Vector &vecOrigin, const Vector &vecMins, const Vector &vecMaxs, CBaseEntity *pOwner )
+ {
+ CVehicleCargoTrigger *pTrigger = (CVehicleCargoTrigger *) CreateEntityByName( "trigger_vehicle_cargo" );
+ if ( pTrigger == NULL )
+ return NULL;
+
+ UTIL_SetOrigin( pTrigger, vecOrigin );
+ UTIL_SetSize( pTrigger, vecMins, vecMaxs );
+ pTrigger->SetOwnerEntity( pOwner );
+ pTrigger->SetParent( pOwner );
+
+ pTrigger->Spawn();
+
+ return pTrigger;
+ }
+
+ //
+ // Handles the trigger touching its intended quarry
+
+ void CargoTouch( CBaseEntity *pOther )
+ {
+ // Cannot be ignoring touches
+ if ( ( m_hIgnoreEntity == pOther ) || ( m_flIgnoreDuration >= gpGlobals->curtime ) )
+ return;
+
+ // Make sure this object is being held by the player
+ if ( pOther->VPhysicsGetObject() == NULL || (pOther->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) == false )
+ return;
+
+ if ( StriderBuster_NumFlechettesAttached( pOther ) > 0 )
+ return;
+
+ AddCargo( pOther );
+ }
+
+ bool AddCargo( CBaseEntity *pOther )
+ {
+ // For now, only bother with strider busters
+ if ( (FClassnameIs( pOther, "weapon_striderbuster" ) == false) &&
+ (FClassnameIs( pOther, "npc_grenade_magna" ) == false)
+ )
+ return false;
+
+ // Must be a physics prop
+ CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pOther);
+ if ( pOther == NULL )
+ return false;
+
+ CPropJeepEpisodic *pJeep = dynamic_cast< CPropJeepEpisodic * >( GetOwnerEntity() );
+ if ( pJeep == NULL )
+ return false;
+
+ // Make the player release the item
+ Pickup_ForcePlayerToDropThisObject( pOther );
+
+ // Stop colliding with things
+ pOther->VPhysicsDestroyObject();
+ pOther->SetSolidFlags( FSOLID_NOT_SOLID );
+ pOther->SetMoveType( MOVETYPE_NONE );
+
+ // Parent the object to our owner
+ pOther->SetParent( GetOwnerEntity() );
+
+ // The car now owns the entity
+ pJeep->AddPropToCargoHold( pProp );
+
+ // Notify the buster that it's been added to the cargo hold.
+ StriderBuster_OnAddToCargoHold( pProp );
+
+ // Stop touching this item
+ Disable();
+
+ return true;
+ }
+
+ //
+ // Setup the entity
+
+ void Spawn( void )
+ {
+ BaseClass::Spawn();
+
+ SetSolid( SOLID_BBOX );
+ SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID );
+
+ SetTouch( &CVehicleCargoTrigger::CargoTouch );
+ }
+
+ void Activate()
+ {
+ BaseClass::Activate();
+ SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID ); // Fixes up old savegames
+ }
+
+ //
+ // When we've stopped touching this entity, we ignore it
+
+ void EndTouch( CBaseEntity *pOther )
+ {
+ if ( pOther == m_hIgnoreEntity )
+ {
+ m_hIgnoreEntity = NULL;
+ }
+
+ BaseClass::EndTouch( pOther );
+ }
+
+ //
+ // Disables the trigger for a set duration
+
+ void IgnoreTouches( CBaseEntity *pIgnoreEntity )
+ {
+ m_hIgnoreEntity = pIgnoreEntity;
+ m_flIgnoreDuration = gpGlobals->curtime + 0.5f;
+ }
+
+ void Disable( void )
+ {
+ SetTouch( NULL );
+ }
+
+ void Enable( void )
+ {
+ SetTouch( &CVehicleCargoTrigger::CargoTouch );
+ }
+
+protected:
+
+ float m_flIgnoreDuration;
+ CHandle <CBaseEntity> m_hIgnoreEntity;
+
+ DECLARE_DATADESC();
+};
+
+LINK_ENTITY_TO_CLASS( trigger_vehicle_cargo, CVehicleCargoTrigger );
+
+BEGIN_DATADESC( CVehicleCargoTrigger )
+ DEFINE_FIELD( m_flIgnoreDuration, FIELD_TIME ),
+ DEFINE_FIELD( m_hIgnoreEntity, FIELD_EHANDLE ),
+ DEFINE_ENTITYFUNC( CargoTouch ),
+END_DATADESC();
+
+//
+// Transition reference point for the vehicle
+//
+
+class CInfoTargetVehicleTransition : public CPointEntity
+{
+public:
+ DECLARE_CLASS( CInfoTargetVehicleTransition, CPointEntity );
+
+ void Enable( void ) { m_bDisabled = false; }
+ void Disable( void ) { m_bDisabled = true; }
+
+ bool IsDisabled( void ) const { return m_bDisabled; }
+
+private:
+
+ void InputEnable( inputdata_t &data ) { Enable(); }
+ void InputDisable( inputdata_t &data ) { Disable(); }
+
+ bool m_bDisabled;
+
+ DECLARE_DATADESC();
+};
+
+BEGIN_DATADESC( CInfoTargetVehicleTransition )
+ DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable",InputDisable ),
+END_DATADESC();
+
+LINK_ENTITY_TO_CLASS( info_target_vehicle_transition, CInfoTargetVehicleTransition );
+
+//
+// CPropJeepEpisodic
+//
+
+LINK_ENTITY_TO_CLASS( prop_vehicle_jeep, CPropJeepEpisodic );
+
+BEGIN_DATADESC( CPropJeepEpisodic )
+
+ DEFINE_FIELD( m_bEntranceLocked, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bExitLocked, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_hCargoProp, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hCargoTrigger, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bAddingCargo, FIELD_BOOLEAN ),
+ DEFINE_ARRAY( m_hWheelDust, FIELD_EHANDLE, NUM_WHEEL_EFFECTS ),
+ DEFINE_ARRAY( m_hWheelWater, FIELD_EHANDLE, NUM_WHEEL_EFFECTS ),
+ DEFINE_ARRAY( m_hHazardLights, FIELD_EHANDLE, NUM_HAZARD_LIGHTS ),
+ DEFINE_FIELD( m_flCargoStartTime, FIELD_TIME ),
+ DEFINE_FIELD( m_bBlink, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bRadarEnabled, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bRadarDetectsEnemies, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_hRadarScreen, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hLinkControllerFront, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hLinkControllerRear, FIELD_EHANDLE ),
+ DEFINE_KEYFIELD( m_bBusterHopperVisible, FIELD_BOOLEAN, "CargoVisible" ),
+ // m_flNextAvoidBroadcastTime
+ DEFINE_FIELD( m_flNextWaterSound, FIELD_TIME ),
+ DEFINE_FIELD( m_flNextRadarUpdateTime, FIELD_TIME ),
+ DEFINE_FIELD( m_iNumRadarContacts, FIELD_INTEGER ),
+ DEFINE_ARRAY( m_vecRadarContactPos, FIELD_POSITION_VECTOR, RADAR_MAX_CONTACTS ),
+ DEFINE_ARRAY( m_iRadarContactType, FIELD_INTEGER, RADAR_MAX_CONTACTS ),
+
+ DEFINE_THINKFUNC( HazardBlinkThink ),
+
+ DEFINE_OUTPUT( m_OnCompanionEnteredVehicle, "OnCompanionEnteredVehicle" ),
+ DEFINE_OUTPUT( m_OnCompanionExitedVehicle, "OnCompanionExitedVehicle" ),
+ DEFINE_OUTPUT( m_OnHostileEnteredVehicle, "OnHostileEnteredVehicle" ),
+ DEFINE_OUTPUT( m_OnHostileExitedVehicle, "OnHostileExitedVehicle" ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "LockEntrance", InputLockEntrance ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "UnlockEntrance", InputUnlockEntrance ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "LockExit", InputLockExit ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "UnlockExit", InputUnlockExit ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "EnableRadar", InputEnableRadar ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "DisableRadar", InputDisableRadar ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "EnableRadarDetectEnemies", InputEnableRadarDetectEnemies ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "AddBusterToCargo", InputAddBusterToCargo ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "OutsideTransition", InputOutsideTransition ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "DisablePhysGun", InputDisablePhysGun ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "EnablePhysGun", InputEnablePhysGun ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "CreateLinkController", InputCreateLinkController ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "DestroyLinkController", InputDestroyLinkController ),
+
+ DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetCargoHopperVisibility", InputSetCargoVisibility ),
+
+END_DATADESC();
+
+IMPLEMENT_SERVERCLASS_ST(CPropJeepEpisodic, DT_CPropJeepEpisodic)
+ //CNetworkVar( int, m_iNumRadarContacts );
+ SendPropInt( SENDINFO(m_iNumRadarContacts), 8 ),
+
+ //CNetworkArray( Vector, m_vecRadarContactPos, RADAR_MAX_CONTACTS );
+ SendPropArray( SendPropVector( SENDINFO_ARRAY(m_vecRadarContactPos), -1, SPROP_COORD), m_vecRadarContactPos ),
+
+ //CNetworkArray( int, m_iRadarContactType, RADAR_MAX_CONTACTS );
+ SendPropArray( SendPropInt(SENDINFO_ARRAY(m_iRadarContactType), RADAR_CONTACT_TYPE_BITS ), m_iRadarContactType ),
+END_SEND_TABLE()
+
+
+//=============================================================================
+// Episodic jeep
+
+CPropJeepEpisodic::CPropJeepEpisodic( void ) :
+m_bEntranceLocked( false ),
+m_bExitLocked( false ),
+m_bAddingCargo( false ),
+m_flNextAvoidBroadcastTime( 0.0f )
+{
+ m_bHasGun = false;
+ m_bUnableToFire = true;
+ m_bRadarDetectsEnemies = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::UpdateOnRemove( void )
+{
+ BaseClass::UpdateOnRemove();
+
+ // Kill our wheel dust
+ for ( int i = 0; i < NUM_WHEEL_EFFECTS; i++ )
+ {
+ if ( m_hWheelDust[i] != NULL )
+ {
+ UTIL_Remove( m_hWheelDust[i] );
+ }
+
+ if ( m_hWheelWater[i] != NULL )
+ {
+ UTIL_Remove( m_hWheelWater[i] );
+ }
+ }
+
+ DestroyHazardLights();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::Precache( void )
+{
+ PrecacheMaterial( RADAR_PANEL_MATERIAL );
+ PrecacheMaterial( RADAR_PANEL_WRITEZ );
+ PrecacheModel( s_szHazardSprite );
+ PrecacheScriptSound( "JNK_Radar_Ping_Friendly" );
+ PrecacheScriptSound( "Physics.WaterSplash" );
+
+ PrecacheParticleSystem( "WheelDust" );
+ PrecacheParticleSystem( "WheelSplash" );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPlayer -
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::EnterVehicle( CBaseCombatCharacter *pPassenger )
+{
+ BaseClass::EnterVehicle( pPassenger );
+
+ // Turn our hazards off!
+ DestroyHazardLights();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ SetBlocksLOS( false );
+
+ CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
+ if ( pPlayer != NULL )
+ {
+ pPlayer->m_Local.m_iHideHUD |= HIDEHUD_VEHICLE_CROSSHAIR;
+ }
+
+
+ SetBodygroup( JEEP_HOPPER_BODYGROUP, m_bBusterHopperVisible ? 1 : 0);
+ CreateCargoTrigger();
+
+ // carbar bodygroup is always on
+ SetBodygroup( JEEP_CARBAR_BODYGROUP, 1 );
+
+ m_bRadarDetectsEnemies = false;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::Activate()
+{
+ m_iNumRadarContacts = 0; // Force first contact tone
+ BaseClass::Activate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::NPC_FinishedEnterVehicle( CAI_BaseNPC *pPassenger, bool bCompanion )
+{
+ // FIXME: This will be moved to the NPCs entering and exiting
+ // Fire our outputs
+ if ( bCompanion )
+ {
+ m_OnCompanionEnteredVehicle.FireOutput( this, pPassenger );
+ }
+ else
+ {
+ m_OnHostileEnteredVehicle.FireOutput( this, pPassenger );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::NPC_FinishedExitVehicle( CAI_BaseNPC *pPassenger, bool bCompanion )
+{
+ // FIXME: This will be moved to the NPCs entering and exiting
+ // Fire our outputs
+ if ( bCompanion )
+ {
+ m_OnCompanionExitedVehicle.FireOutput( this, pPassenger );
+ }
+ else
+ {
+ m_OnHostileExitedVehicle.FireOutput( this, pPassenger );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPassenger -
+// bCompanion -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CPropJeepEpisodic::NPC_CanEnterVehicle( CAI_BaseNPC *pPassenger, bool bCompanion )
+{
+ // Must be unlocked
+ if ( bCompanion && m_bEntranceLocked )
+ return false;
+
+ return BaseClass::NPC_CanEnterVehicle( pPassenger, bCompanion );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPassenger -
+// bCompanion -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CPropJeepEpisodic::NPC_CanExitVehicle( CAI_BaseNPC *pPassenger, bool bCompanion )
+{
+ // Must be unlocked
+ if ( bCompanion && m_bExitLocked )
+ return false;
+
+ return BaseClass::NPC_CanExitVehicle( pPassenger, bCompanion );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputLockEntrance( inputdata_t &data )
+{
+ m_bEntranceLocked = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputUnlockEntrance( inputdata_t &data )
+{
+ m_bEntranceLocked = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputLockExit( inputdata_t &data )
+{
+ m_bExitLocked = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputUnlockExit( inputdata_t &data )
+{
+ m_bExitLocked = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn on the Jalopy radar device
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputEnableRadar( inputdata_t &data )
+{
+ if( m_bRadarEnabled )
+ return; // Already enabled
+
+ SetBodygroup( JEEP_RADAR_BODYGROUP, 1 );
+
+ SpawnRadarPanel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn off the Jalopy radar device
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputDisableRadar( inputdata_t &data )
+{
+ if( !m_bRadarEnabled )
+ return; // Already disabled
+
+ SetBodygroup( JEEP_RADAR_BODYGROUP, 0 );
+
+ DestroyRadarPanel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allow the Jalopy radar to detect Hunters and Striders
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputEnableRadarDetectEnemies( inputdata_t &data )
+{
+ m_bRadarDetectsEnemies = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputAddBusterToCargo( inputdata_t &data )
+{
+ if ( m_hCargoProp != NULL)
+ {
+ ReleasePropFromCargoHold();
+ m_hCargoProp = NULL;
+ }
+
+ CBaseEntity *pNewBomb = CreateEntityByName( "weapon_striderbuster" );
+ if ( pNewBomb )
+ {
+ DispatchSpawn( pNewBomb );
+ pNewBomb->Teleport( &m_hCargoTrigger->GetAbsOrigin(), NULL, NULL );
+ m_hCargoTrigger->AddCargo( pNewBomb );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CPropJeepEpisodic::PassengerInTransition( void )
+{
+ // FIXME: Big hack - we need a way to bridge this data better
+ // TODO: Get a list of passengers we can traverse instead
+ CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx();
+ if ( pAlyx )
+ {
+ if ( pAlyx->GetPassengerState() == PASSENGER_STATE_ENTERING ||
+ pAlyx->GetPassengerState() == PASSENGER_STATE_EXITING )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override velocity if our passenger is transitioning or we're upside-down
+//-----------------------------------------------------------------------------
+Vector CPropJeepEpisodic::PhysGunLaunchVelocity( const Vector &forward, float flMass )
+{
+ // Disallow
+ if ( PassengerInTransition() )
+ return vec3_origin;
+
+ Vector vecPuntDir = BaseClass::PhysGunLaunchVelocity( forward, flMass );
+ vecPuntDir.z = 150.0f;
+ vecPuntDir *= 600.0f;
+ return vecPuntDir;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Rolls the vehicle when its trying to upright itself from a punt
+//-----------------------------------------------------------------------------
+AngularImpulse CPropJeepEpisodic::PhysGunLaunchAngularImpulse( void )
+{
+ if ( IsOverturned() )
+ return AngularImpulse( 0, 300, 0 );
+
+ // Don't spin randomly, always spin reliably
+ return AngularImpulse( 0, 0, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the upright strength based on what state we're in
+//-----------------------------------------------------------------------------
+float CPropJeepEpisodic::GetUprightStrength( void )
+{
+ // Lesser if overturned
+ if ( IsOverturned() )
+ return 2.0f;
+
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::CreateCargoTrigger( void )
+{
+ if ( m_hCargoTrigger != NULL )
+ return;
+
+ int nAttachment = LookupAttachment( "cargo" );
+ if ( nAttachment )
+ {
+ Vector vecAttachOrigin;
+ Vector vecForward, vecRight, vecUp;
+ GetAttachment( nAttachment, vecAttachOrigin, &vecForward, &vecRight, &vecUp );
+
+ // Approx size of the hold
+ Vector vecMins( -8.0, -6.0, 0 );
+ Vector vecMaxs( 8.0, 6.0, 4.0 );
+
+ // NDebugOverlay::BoxDirection( vecAttachOrigin, vecMins, vecMaxs, vecForward, 255, 0, 0, 64, 4.0f );
+
+ // Create a trigger that lives for a small amount of time
+ m_hCargoTrigger = CVehicleCargoTrigger::Create( vecAttachOrigin, vecMins, vecMaxs, this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If the player uses the jeep while at the back, he gets ammo from the crate instead
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ // Fall back and get in the vehicle instead, skip giving ammo
+ BaseClass::BaseClass::Use( pActivator, pCaller, useType, value );
+}
+
+#define MIN_WHEEL_DUST_SPEED 5
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::UpdateWheelDust( void )
+{
+ // See if this wheel should emit dust
+ const vehicleparams_t *vehicleData = m_pServerVehicle->GetVehicleParams();
+ const vehicle_operatingparams_t *carState = m_pServerVehicle->GetVehicleOperatingParams();
+ bool bAllowDust = vehicleData->steering.dustCloud;
+
+ // Car must be active
+ bool bCarOn = m_VehiclePhysics.IsOn();
+
+ // Must be moving quickly enough or skidding along the ground
+ bool bCreateDust = ( bCarOn &&
+ bAllowDust &&
+ ( m_VehiclePhysics.GetSpeed() >= MIN_WHEEL_DUST_SPEED || carState->skidSpeed > DEFAULT_SKID_THRESHOLD ) );
+
+ // Update our wheel dust
+ Vector vecPos;
+ for ( int i = 0; i < NUM_WHEEL_EFFECTS; i++ )
+ {
+ m_pServerVehicle->GetWheelContactPoint( i, vecPos );
+
+ // Make sure the effect is created
+ if ( m_hWheelDust[i] == NULL )
+ {
+ // Create the dust effect in place
+ m_hWheelDust[i] = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
+ if ( m_hWheelDust[i] == NULL )
+ continue;
+
+ // Setup our basic parameters
+ m_hWheelDust[i]->KeyValue( "start_active", "0" );
+ m_hWheelDust[i]->KeyValue( "effect_name", "WheelDust" );
+ m_hWheelDust[i]->SetParent( this );
+ m_hWheelDust[i]->SetLocalOrigin( vec3_origin );
+ DispatchSpawn( m_hWheelDust[i] );
+ if ( gpGlobals->curtime > 0.5f )
+ m_hWheelDust[i]->Activate();
+ }
+
+ // Make sure the effect is created
+ if ( m_hWheelWater[i] == NULL )
+ {
+ // Create the dust effect in place
+ m_hWheelWater[i] = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
+ if ( m_hWheelWater[i] == NULL )
+ continue;
+
+ // Setup our basic parameters
+ m_hWheelWater[i]->KeyValue( "start_active", "0" );
+ m_hWheelWater[i]->KeyValue( "effect_name", "WheelSplash" );
+ m_hWheelWater[i]->SetParent( this );
+ m_hWheelWater[i]->SetLocalOrigin( vec3_origin );
+ DispatchSpawn( m_hWheelWater[i] );
+ if ( gpGlobals->curtime > 0.5f )
+ m_hWheelWater[i]->Activate();
+ }
+
+ // Turn the dust on or off
+ if ( bCreateDust )
+ {
+ // Angle the dust out away from the wheels
+ Vector vecForward, vecRight, vecUp;
+ GetVectors( &vecForward, &vecRight, &vecUp );
+
+ const vehicle_controlparams_t *vehicleControls = m_pServerVehicle->GetVehicleControlParams();
+ float flWheelDir = ( i & 1 ) ? 1.0f : -1.0f;
+ QAngle vecAngles;
+ vecForward += vecRight * flWheelDir;
+ vecForward += vecRight * (vehicleControls->steering*0.5f) * flWheelDir;
+ vecForward += vecUp;
+ VectorAngles( vecForward, vecAngles );
+
+ // NDebugOverlay::Axis( vecPos, vecAngles, 8.0f, true, 0.1f );
+
+ if ( m_WaterData.m_bWheelInWater[i] )
+ {
+ m_hWheelDust[i]->StopParticleSystem();
+
+ // Set us up in the right position
+ m_hWheelWater[i]->StartParticleSystem();
+ m_hWheelWater[i]->SetAbsAngles( vecAngles );
+ m_hWheelWater[i]->SetAbsOrigin( vecPos + Vector( 0, 0, 8 ) );
+
+ if ( m_flNextWaterSound < gpGlobals->curtime )
+ {
+ m_flNextWaterSound = gpGlobals->curtime + random->RandomFloat( 0.25f, 1.0f );
+ EmitSound( "Physics.WaterSplash" );
+ }
+ }
+ else
+ {
+ m_hWheelWater[i]->StopParticleSystem();
+
+ // Set us up in the right position
+ m_hWheelDust[i]->StartParticleSystem();
+ m_hWheelDust[i]->SetAbsAngles( vecAngles );
+ m_hWheelDust[i]->SetAbsOrigin( vecPos + Vector( 0, 0, 8 ) );
+ }
+ }
+ else
+ {
+ // Stop emitting
+ m_hWheelDust[i]->StopParticleSystem();
+ m_hWheelWater[i]->StopParticleSystem();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+ConVar jalopy_radar_test_ent( "jalopy_radar_test_ent", "none" );
+
+//-----------------------------------------------------------------------------
+// Purpose: Search for things that the radar detects, and stick them in the
+// UTILVector that gets sent to the client for radar display.
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::UpdateRadar( bool forceUpdate )
+{
+ bool bDetectedDog = false;
+
+ if( !m_bRadarEnabled )
+ return;
+
+ if( !forceUpdate && gpGlobals->curtime < m_flNextRadarUpdateTime )
+ return;
+
+ // Count the targets on radar. If any more targets come on the radar, we beep.
+ int m_iNumOldRadarContacts = m_iNumRadarContacts;
+
+ m_flNextRadarUpdateTime = gpGlobals->curtime + RADAR_UPDATE_FREQUENCY;
+ m_iNumRadarContacts = 0;
+
+ CBaseEntity *pEnt = gEntList.FirstEnt();
+ string_t iszRadarTarget = FindPooledString( "info_radar_target" );
+ string_t iszStriderName = FindPooledString( "npc_strider" );
+ string_t iszHunterName = FindPooledString( "npc_hunter" );
+
+ string_t iszTestName = FindPooledString( jalopy_radar_test_ent.GetString() );
+
+ Vector vecJalopyOrigin = WorldSpaceCenter();
+
+ while( pEnt != NULL )
+ {
+ int type = RADAR_CONTACT_NONE;
+
+ if( pEnt->m_iClassname == iszRadarTarget )
+ {
+ CRadarTarget *pTarget = dynamic_cast<CRadarTarget*>(pEnt);
+
+ if( pTarget != NULL && !pTarget->IsDisabled() )
+ {
+ if( pTarget->m_flRadius < 0 || vecJalopyOrigin.DistToSqr(pTarget->GetAbsOrigin()) <= Square(pTarget->m_flRadius) )
+ {
+ // This item has been detected.
+ type = pTarget->GetType();
+
+ if( type == RADAR_CONTACT_DOG )
+ bDetectedDog = true;// used to prevent Alyx talking about the radar (see below)
+
+ if( pTarget->GetMode() == RADAR_MODE_STICKY )
+ {
+ // This beacon was just detected. Now change the radius to infinite
+ // so that it will never go off the radar due to distance.
+ pTarget->m_flRadius = -1;
+ }
+ }
+ }
+ }
+ else if ( m_bRadarDetectsEnemies )
+ {
+ if ( pEnt->m_iClassname == iszStriderName )
+ {
+ CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider*>(pEnt);
+
+ if( !pStrider || !pStrider->CarriedByDropship() )
+ {
+ // Ignore striders which are carried by dropships.
+ type = RADAR_CONTACT_LARGE_ENEMY;
+ }
+ }
+
+ if ( pEnt->m_iClassname == iszHunterName )
+ {
+ type = RADAR_CONTACT_ENEMY;
+ }
+ }
+
+ if( type != RADAR_CONTACT_NONE )
+ {
+ Vector vecPos = pEnt->WorldSpaceCenter();
+
+ m_vecRadarContactPos.Set( m_iNumRadarContacts, vecPos );
+ m_iRadarContactType.Set( m_iNumRadarContacts, type );
+ m_iNumRadarContacts++;
+
+ if( m_iNumRadarContacts == RADAR_MAX_CONTACTS )
+ break;
+ }
+
+ pEnt = gEntList.NextEnt(pEnt);
+ }
+
+ if( m_iNumRadarContacts > m_iNumOldRadarContacts )
+ {
+ // Play a bleepy sound
+ if( !bDetectedDog )
+ {
+ EmitSound( "JNK_Radar_Ping_Friendly" );
+ }
+
+ //Notify Alyx so she can talk about the radar contact
+ CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx();
+
+ if( !bDetectedDog && pAlyx != NULL && pAlyx->GetVehicle() )
+ {
+ pAlyx->SpeakIfAllowed( TLK_PASSENGER_NEW_RADAR_CONTACT );
+ }
+ }
+
+ if( bDetectedDog )
+ {
+ // Update the radar much more frequently when dog is around.
+ m_flNextRadarUpdateTime = gpGlobals->curtime + RADAR_UPDATE_FREQUENCY_FAST;
+ }
+
+ //Msg("Server detected %d objects\n", m_iNumRadarContacts );
+
+ CBasePlayer *pPlayer = AI_GetSinglePlayer();
+ CSingleUserRecipientFilter filter(pPlayer);
+ UserMessageBegin( filter, "UpdateJalopyRadar" );
+ WRITE_BYTE( 0 ); // end marker
+ MessageEnd(); // send message
+}
+
+ConVar jalopy_cargo_anim_time( "jalopy_cargo_anim_time", "1.0" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::UpdateCargoEntry( void )
+{
+ // Don't bother if we have no prop to move
+ if ( m_hCargoProp == NULL )
+ return;
+
+ // If we're past our animation point, then we're already done
+ if ( m_flCargoStartTime + jalopy_cargo_anim_time.GetFloat() < gpGlobals->curtime )
+ {
+ // Close the hold immediately if we're finished
+ if ( m_bAddingCargo )
+ {
+ m_flAmmoCrateCloseTime = gpGlobals->curtime;
+ m_bAddingCargo = false;
+ }
+
+ return;
+ }
+
+ // Get our target point
+ int nAttachment = LookupAttachment( "cargo" );
+ Vector vecTarget, vecOut;
+ QAngle vecAngles;
+ GetAttachmentLocal( nAttachment, vecTarget, vecAngles );
+
+ // Find where we are in the blend and bias it for a fast entry and slow ease-out
+ float flPerc = (jalopy_cargo_anim_time.GetFloat()) ? (( gpGlobals->curtime - m_flCargoStartTime ) / jalopy_cargo_anim_time.GetFloat()) : 1.0f;
+ flPerc = Bias( flPerc, 0.75f );
+ VectorLerp( m_hCargoProp->GetLocalOrigin(), vecTarget, flPerc, vecOut );
+
+ // Get our target orientation
+ CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(m_hCargoProp.Get());
+ if ( pProp == NULL )
+ return;
+
+ // Slerp our quaternions to find where we are this frame
+ Quaternion qtTarget;
+ QAngle qa( 0, 90, 0 );
+ qa += pProp->PreferredCarryAngles();
+ AngleQuaternion( qa, qtTarget ); // FIXME: Find the real offset to make this sit properly
+ Quaternion qtCurrent;
+ AngleQuaternion( pProp->GetLocalAngles(), qtCurrent );
+
+ Quaternion qtOut;
+ QuaternionSlerp( qtCurrent, qtTarget, flPerc, qtOut );
+
+ // Put it back to angles
+ QuaternionAngles( qtOut, vecAngles );
+
+ // Finally, take these new position
+ m_hCargoProp->SetLocalOrigin( vecOut );
+ m_hCargoProp->SetLocalAngles( vecAngles );
+
+ // Push the closing out into the future to make sure we don't try and close at the same time
+ m_flAmmoCrateCloseTime += gpGlobals->frametime;
+}
+
+#define VEHICLE_AVOID_BROADCAST_RATE 0.5f
+
+//-----------------------------------------------------------------------------
+// Purpose: This function isn't really what we want
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::CreateAvoidanceZone( void )
+{
+ if ( m_flNextAvoidBroadcastTime > gpGlobals->curtime )
+ return;
+
+ // Only do this when we're stopped
+ if ( m_VehiclePhysics.GetSpeed() > 5.0f )
+ return;
+
+ float flHullRadius = CollisionProp()->BoundingRadius2D();
+
+ Vector vecPos;
+ CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.33f, 0.25f ), &vecPos );
+ CSoundEnt::InsertSound( SOUND_MOVE_AWAY, vecPos, (flHullRadius*0.4f), VEHICLE_AVOID_BROADCAST_RATE, this );
+ // NDebugOverlay::Sphere( vecPos, vec3_angle, flHullRadius*0.4f, 255, 0, 0, 0, true, VEHICLE_AVOID_BROADCAST_RATE );
+
+ CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.66f, 0.25f ), &vecPos );
+ CSoundEnt::InsertSound( SOUND_MOVE_AWAY, vecPos, (flHullRadius*0.4f), VEHICLE_AVOID_BROADCAST_RATE, this );
+ // NDebugOverlay::Sphere( vecPos, vec3_angle, flHullRadius*0.4f, 255, 0, 0, 0, true, VEHICLE_AVOID_BROADCAST_RATE );
+
+ // Don't broadcast again until these are done
+ m_flNextAvoidBroadcastTime = gpGlobals->curtime + VEHICLE_AVOID_BROADCAST_RATE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::Think( void )
+{
+ BaseClass::Think();
+
+ // If our passenger is transitioning, then don't let the player drive off
+ CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx();
+ if ( pAlyx && pAlyx->GetPassengerState() == PASSENGER_STATE_EXITING )
+ {
+ m_throttleDisableTime = gpGlobals->curtime + 0.25f;
+ }
+
+ // Update our cargo entering our hold
+ UpdateCargoEntry();
+
+ // See if the wheel dust should be on or off
+ UpdateWheelDust();
+
+ // Update the radar, of course.
+ UpdateRadar();
+
+ if ( m_hCargoTrigger && !m_hCargoProp && !m_hCargoTrigger->m_pfnTouch )
+ {
+ m_hCargoTrigger->Enable();
+ }
+
+ CreateAvoidanceZone();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEntity -
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::AddPropToCargoHold( CPhysicsProp *pProp )
+{
+ // The hold must be empty to add something to it
+ if ( m_hCargoProp != NULL )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ // Take the prop as our cargo
+ m_hCargoProp = pProp;
+ m_flCargoStartTime = gpGlobals->curtime;
+ m_bAddingCargo = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Drops the cargo from the hold
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::ReleasePropFromCargoHold( void )
+{
+ // Pull the object free!
+ m_hCargoProp->SetParent( NULL );
+ m_hCargoProp->CreateVPhysics();
+
+ if ( m_hCargoTrigger )
+ {
+ m_hCargoTrigger->Enable();
+ m_hCargoTrigger->IgnoreTouches( m_hCargoProp );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If the player is trying to pull the cargo out of the hold using the physcannon, let him
+// Output : Returns the cargo to pick up, if all the conditions are met
+//-----------------------------------------------------------------------------
+CBaseEntity *CPropJeepEpisodic::OnFailedPhysGunPickup( Vector vPhysgunPos )
+{
+ // Make sure we're available to open
+ if ( m_hCargoProp != NULL )
+ {
+ // Player's forward direction
+ Vector vecPlayerForward;
+ CBasePlayer *pPlayer = AI_GetSinglePlayer();
+ if ( pPlayer == NULL )
+ return NULL;
+
+ pPlayer->EyeVectors( &vecPlayerForward );
+
+ // Origin and facing of the cargo hold
+ Vector vecCargoOrigin;
+ Vector vecCargoForward;
+ GetAttachment( "cargo", vecCargoOrigin, &vecCargoForward );
+
+ // Direction from the cargo to the player's position
+ Vector vecPickupDir = ( vecCargoOrigin - vPhysgunPos );
+ float flDist = VectorNormalize( vecPickupDir );
+
+ // We need to make sure the player's position is within a cone near the opening and that they're also facing the right way
+ bool bInCargoRange = ( (flDist < (15.0f * 12.0f)) && DotProduct( vecCargoForward, vecPickupDir ) < 0.1f );
+ bool bFacingCargo = DotProduct( vecPlayerForward, vecPickupDir ) > 0.975f;
+
+ // If we're roughly pulling at the item, pick that up
+ if ( bInCargoRange && bFacingCargo )
+ {
+ // Save this for later
+ CBaseEntity *pCargo = m_hCargoProp;
+
+ // Drop the cargo
+ ReleasePropFromCargoHold();
+
+ // Forget the item but pass it back as the object to pick up
+ m_hCargoProp = NULL;
+ return pCargo;
+ }
+ }
+
+ return BaseClass::OnFailedPhysGunPickup( vPhysgunPos );
+}
+
+// adds a collision solver for any small props that are stuck under the vehicle
+static void SolveBlockingProps( CPropJeepEpisodic *pVehicleEntity, IPhysicsObject *pVehiclePhysics )
+{
+ CUtlVector<CBaseEntity *> solveList;
+ float vehicleMass = pVehiclePhysics->GetMass();
+ Vector vehicleUp;
+ pVehicleEntity->GetVectors( NULL, NULL, &vehicleUp );
+ IPhysicsFrictionSnapshot *pSnapshot = pVehiclePhysics->CreateFrictionSnapshot();
+ while ( pSnapshot->IsValid() )
+ {
+ IPhysicsObject *pOther = pSnapshot->GetObject(1);
+ float otherMass = pOther->GetMass();
+ CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
+ Assert(pOtherEntity);
+ if ( pOtherEntity && pOtherEntity->GetMoveType() == MOVETYPE_VPHYSICS && pOther->IsMoveable() && (otherMass*4.0f) < vehicleMass )
+ {
+ Vector normal;
+ pSnapshot->GetSurfaceNormal(normal);
+ // this points down in the car's reference frame, then it's probably trapped under the car
+ if ( DotProduct(normal, vehicleUp) < -0.9f )
+ {
+ Vector point, pointLocal;
+ pSnapshot->GetContactPoint(point);
+ VectorITransform( point, pVehicleEntity->EntityToWorldTransform(), pointLocal );
+ Vector bottomPoint = physcollision->CollideGetExtent( pVehiclePhysics->GetCollide(), vec3_origin, vec3_angle, Vector(0,0,-1) );
+ // make sure it's under the bottom of the car
+ float bottomPlane = DotProduct(bottomPoint,vehicleUp)+8; // 8 inches above bottom
+ if ( DotProduct( pointLocal, vehicleUp ) <= bottomPlane )
+ {
+ //Msg("Solved %s\n", pOtherEntity->GetClassname());
+ if ( solveList.Find(pOtherEntity) < 0 )
+ {
+ solveList.AddToTail(pOtherEntity);
+ }
+ }
+ }
+ }
+ pSnapshot->NextFrictionData();
+ }
+ pVehiclePhysics->DestroyFrictionSnapshot( pSnapshot );
+ if ( solveList.Count() )
+ {
+ for ( int i = 0; i < solveList.Count(); i++ )
+ {
+ EntityPhysics_CreateSolver( pVehicleEntity, solveList[i], true, 4.0f );
+ }
+ pVehiclePhysics->RecheckContactPoints();
+ }
+}
+
+static void SimpleCollisionResponse( Vector velocityIn, const Vector &normal, float coefficientOfRestitution, Vector *pVelocityOut )
+{
+ Vector Vn = DotProduct(velocityIn,normal) * normal;
+ Vector Vt = velocityIn - Vn;
+ *pVelocityOut = Vt - coefficientOfRestitution * Vn;
+}
+
+static void KillBlockingEnemyNPCs( CBasePlayer *pPlayer, CBaseEntity *pVehicleEntity, IPhysicsObject *pVehiclePhysics )
+{
+ Vector velocity;
+ pVehiclePhysics->GetVelocity( &velocity, NULL );
+ float vehicleMass = pVehiclePhysics->GetMass();
+
+ // loop through the contacts and look for enemy NPCs that we're pushing on
+ CUtlVector<CAI_BaseNPC *> npcList;
+ CUtlVector<Vector> forceList;
+ CUtlVector<Vector> contactList;
+ IPhysicsFrictionSnapshot *pSnapshot = pVehiclePhysics->CreateFrictionSnapshot();
+ while ( pSnapshot->IsValid() )
+ {
+ IPhysicsObject *pOther = pSnapshot->GetObject(1);
+ float otherMass = pOther->GetMass();
+ CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
+ CAI_BaseNPC *pNPC = pOtherEntity ? pOtherEntity->MyNPCPointer() : NULL;
+ // Is this an enemy NPC with a small enough mass?
+ if ( pNPC && pPlayer->IRelationType(pNPC) != D_LI && ((otherMass*2.0f) < vehicleMass) )
+ {
+ // accumulate the stress force for this NPC in the lsit
+ float force = pSnapshot->GetNormalForce();
+ Vector normal;
+ pSnapshot->GetSurfaceNormal(normal);
+ normal *= force;
+ int index = npcList.Find(pNPC);
+ if ( index < 0 )
+ {
+ vphysicsupdateai_t *pUpdate = NULL;
+ if ( pNPC->VPhysicsGetObject() && pNPC->VPhysicsGetObject()->GetShadowController() && pNPC->GetMoveType() == MOVETYPE_STEP )
+ {
+ if ( pNPC->HasDataObjectType(VPHYSICSUPDATEAI) )
+ {
+ pUpdate = static_cast<vphysicsupdateai_t *>(pNPC->GetDataObject(VPHYSICSUPDATEAI));
+ // kill this guy if I've been pushing him for more than half a second and I'm
+ // still pushing in his direction
+ if ( (gpGlobals->curtime - pUpdate->startUpdateTime) > 0.5f && DotProduct(velocity,normal) > 0)
+ {
+ index = npcList.AddToTail(pNPC);
+ forceList.AddToTail( normal );
+ Vector pos;
+ pSnapshot->GetContactPoint(pos);
+ contactList.AddToTail(pos);
+ }
+ }
+ else
+ {
+ pUpdate = static_cast<vphysicsupdateai_t *>(pNPC->CreateDataObject( VPHYSICSUPDATEAI ));
+ pUpdate->startUpdateTime = gpGlobals->curtime;
+ }
+ // update based on vphysics for the next second
+ // this allows the car to push the NPC
+ pUpdate->stopUpdateTime = gpGlobals->curtime + 1.0f;
+ float maxAngular;
+ pNPC->VPhysicsGetObject()->GetShadowController()->GetMaxSpeed( &pUpdate->savedShadowControllerMaxSpeed, &maxAngular );
+ pNPC->VPhysicsGetObject()->GetShadowController()->MaxSpeed( 1.0f, maxAngular );
+ }
+ }
+ else
+ {
+ forceList[index] += normal;
+ }
+ }
+ pSnapshot->NextFrictionData();
+ }
+ pVehiclePhysics->DestroyFrictionSnapshot( pSnapshot );
+ // now iterate the list and check each cumulative force against the threshold
+ if ( npcList.Count() )
+ {
+ for ( int i = npcList.Count(); --i >= 0; )
+ {
+ Vector damageForce;
+ npcList[i]->VPhysicsGetObject()->GetVelocity( &damageForce, NULL );
+ Vector vel;
+ pVehiclePhysics->GetVelocityAtPoint( contactList[i], &vel );
+ damageForce -= vel;
+ Vector normal = forceList[i];
+ VectorNormalize(normal);
+ SimpleCollisionResponse( damageForce, normal, 1.0, &damageForce );
+ damageForce += (normal * 300.0f);
+ damageForce *= npcList[i]->VPhysicsGetObject()->GetMass();
+ float len = damageForce.Length();
+ damageForce.z += len*phys_upimpactforcescale.GetFloat();
+ Vector vehicleForce = -damageForce;
+
+ CTakeDamageInfo dmgInfo( pVehicleEntity, pVehicleEntity, damageForce, contactList[i], 200.0f, DMG_CRUSH|DMG_VEHICLE );
+ npcList[i]->TakeDamage( dmgInfo );
+ pVehiclePhysics->ApplyForceOffset( vehicleForce, contactList[i] );
+ PhysCollisionSound( pVehicleEntity, npcList[i]->VPhysicsGetObject(), CHAN_BODY, pVehiclePhysics->GetMaterialIndex(), npcList[i]->VPhysicsGetObject()->GetMaterialIndex(), gpGlobals->frametime, 200.0f );
+ }
+ }
+}
+
+void CPropJeepEpisodic::DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased )
+{
+ /* The car headlight hurts perf, there's no timer to turn it off automatically,
+ and we haven't built any gameplay around it.
+
+ Furthermore, I don't think I've ever seen a playtester turn it on.
+
+ if ( ucmd->impulse == 100 )
+ {
+ if (HeadlightIsOn())
+ {
+ HeadlightTurnOff();
+ }
+ else
+ {
+ HeadlightTurnOn();
+ }
+ }*/
+
+ if ( ucmd->forwardmove != 0.0f )
+ {
+ //Msg("Push V: %.2f, %.2f, %.2f\n", ucmd->forwardmove, carState->engineRPM, carState->speed );
+ CBasePlayer *pPlayer = ToBasePlayer(GetDriver());
+
+ if ( pPlayer && VPhysicsGetObject() )
+ {
+ KillBlockingEnemyNPCs( pPlayer, this, VPhysicsGetObject() );
+ SolveBlockingProps( this, VPhysicsGetObject() );
+ }
+ }
+ BaseClass::DriveVehicle(flFrameTime, ucmd, iButtonsDown, iButtonsReleased);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::CreateHazardLights( void )
+{
+ static const char *s_szAttach[NUM_HAZARD_LIGHTS] =
+ {
+ "rearlight_r",
+ "rearlight_l",
+ "headlight_r",
+ "headlight_l",
+ };
+
+ // Turn on the hazards!
+ for ( int i = 0; i < NUM_HAZARD_LIGHTS; i++ )
+ {
+ if ( m_hHazardLights[i] == NULL )
+ {
+ m_hHazardLights[i] = CSprite::SpriteCreate( s_szHazardSprite, GetLocalOrigin(), false );
+ if ( m_hHazardLights[i] )
+ {
+ m_hHazardLights[i]->SetTransparency( kRenderWorldGlow, 255, 220, 40, 255, kRenderFxNoDissipation );
+ m_hHazardLights[i]->SetAttachment( this, LookupAttachment( s_szAttach[i] ) );
+ m_hHazardLights[i]->SetGlowProxySize( 2.0f );
+ m_hHazardLights[i]->TurnOff();
+ if ( i < 2 )
+ {
+ // Rear lights are red
+ m_hHazardLights[i]->SetColor( 255, 0, 0 );
+ m_hHazardLights[i]->SetScale( 1.0f );
+ }
+ else
+ {
+ // Font lights are white
+ m_hHazardLights[i]->SetScale( 1.0f );
+ }
+ }
+ }
+ }
+
+ // We start off
+ m_bBlink = false;
+
+ // Setup our blink
+ SetContextThink( &CPropJeepEpisodic::HazardBlinkThink, gpGlobals->curtime + 0.1f, "HazardBlink" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::DestroyHazardLights( void )
+{
+ for ( int i = 0; i < NUM_HAZARD_LIGHTS; i++ )
+ {
+ if ( m_hHazardLights[i] != NULL )
+ {
+ UTIL_Remove( m_hHazardLights[i] );
+ }
+ }
+
+ SetContextThink( NULL, gpGlobals->curtime, "HazardBlink" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : nRole -
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::ExitVehicle( int nRole )
+{
+ BaseClass::ExitVehicle( nRole );
+
+ CreateHazardLights();
+}
+
+void CPropJeepEpisodic::SetBusterHopperVisibility(bool visible)
+{
+ // if we're there already do nothing
+ if (visible == m_bBusterHopperVisible)
+ return;
+
+ SetBodygroup( JEEP_HOPPER_BODYGROUP, visible ? 1 : 0);
+ m_bBusterHopperVisible = visible;
+}
+
+
+void CPropJeepEpisodic::InputSetCargoVisibility( inputdata_t &data )
+{
+ bool visible = data.value.Bool();
+
+ SetBusterHopperVisibility( visible );
+}
+
+//-----------------------------------------------------------------------------
+// THIS CODE LIFTED RIGHT OUT OF TF2, to defer the pain of making vgui-on-an-entity
+// code available to all CBaseAnimating.
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::SpawnRadarPanel()
+{
+ // FIXME: Deal with dynamically resizing control panels?
+
+ // If we're attached to an entity, spawn control panels on it instead of use
+ CBaseAnimating *pEntityToSpawnOn = this;
+ char *pOrgLL = "controlpanel0_ll";
+ char *pOrgUR = "controlpanel0_ur";
+
+ Assert( pEntityToSpawnOn );
+
+ // Lookup the attachment point...
+ int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(pOrgLL);
+
+ if (nLLAttachmentIndex <= 0)
+ {
+ return;
+ }
+
+ int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(pOrgUR);
+ if (nURAttachmentIndex <= 0)
+ {
+ return;
+ }
+
+ const char *pScreenName = "jalopy_radar_panel";
+ const char *pScreenClassname = "vgui_screen";
+
+ // Compute the screen size from the attachment points...
+ matrix3x4_t panelToWorld;
+ pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld );
+
+ matrix3x4_t worldToPanel;
+ MatrixInvert( panelToWorld, worldToPanel );
+
+ // Now get the lower right position + transform into panel space
+ Vector lr, lrlocal;
+ pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld );
+ MatrixGetColumn( panelToWorld, 3, lr );
+ VectorTransform( lr, worldToPanel, lrlocal );
+
+ float flWidth = lrlocal.x;
+ float flHeight = lrlocal.y;
+
+ CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
+ pScreen->SetActualSize( flWidth, flHeight );
+ pScreen->SetActive( true );
+ pScreen->SetOverlayMaterial( RADAR_PANEL_WRITEZ );
+ pScreen->SetTransparency( true );
+
+ m_hRadarScreen.Set( pScreen );
+
+ m_bRadarEnabled = true;
+ m_iNumRadarContacts = 0;
+ m_flNextRadarUpdateTime = gpGlobals->curtime - 1.0f;
+}
+
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::DestroyRadarPanel()
+{
+ Assert( m_hRadarScreen != NULL );
+ m_hRadarScreen->SUB_Remove();
+ m_bRadarEnabled = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::HazardBlinkThink( void )
+{
+ if ( m_bBlink )
+ {
+ for ( int i = 0; i < NUM_HAZARD_LIGHTS; i++ )
+ {
+ if ( m_hHazardLights[i] )
+ {
+ m_hHazardLights[i]->SetBrightness( 0, 0.1f );
+ }
+ }
+
+ SetContextThink( &CPropJeepEpisodic::HazardBlinkThink, gpGlobals->curtime + 0.25f, "HazardBlink" );
+ }
+ else
+ {
+ for ( int i = 0; i < NUM_HAZARD_LIGHTS; i++ )
+ {
+ if ( m_hHazardLights[i] )
+ {
+ m_hHazardLights[i]->SetBrightness( 255, 0.1f );
+ m_hHazardLights[i]->TurnOn();
+ }
+ }
+
+ SetContextThink( &CPropJeepEpisodic::HazardBlinkThink, gpGlobals->curtime + 0.5f, "HazardBlink" );
+ }
+
+ m_bBlink = !m_bBlink;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::HandleWater( void )
+{
+ // Only check the wheels and engine in water if we have a driver (player).
+ if ( !GetDriver() )
+ return;
+
+ // Update our internal state
+ CheckWater();
+
+ // Save of data from last think.
+ for ( int iWheel = 0; iWheel < JEEP_WHEEL_COUNT; ++iWheel )
+ {
+ m_WaterData.m_bWheelWasInWater[iWheel] = m_WaterData.m_bWheelInWater[iWheel];
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Report our lock state
+//-----------------------------------------------------------------------------
+int CPropJeepEpisodic::DrawDebugTextOverlays( void )
+{
+ int text_offset = BaseClass::DrawDebugTextOverlays();
+
+ if ( m_debugOverlays & OVERLAY_TEXT_BIT )
+ {
+ EntityText( text_offset, CFmtStr("Entrance: %s", m_bEntranceLocked ? "Locked" : "Unlocked" ), 0 );
+ text_offset++;
+
+ EntityText( text_offset, CFmtStr("Exit: %s", m_bExitLocked ? "Locked" : "Unlocked" ), 0 );
+ text_offset++;
+ }
+
+ return text_offset;
+}
+
+#define TRANSITION_SEARCH_RADIUS (100*12)
+
+//-----------------------------------------------------------------------------
+// Purpose: Teleport the car to a destination that will cause it to transition if it's not going to otherwise
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputOutsideTransition( inputdata_t &inputdata )
+{
+ // Teleport into the new map
+ CBasePlayer *pPlayer = AI_GetSinglePlayer();
+ Vector vecTeleportPos;
+ QAngle vecTeleportAngles;
+
+ // Get our bounds
+ Vector vecSurroundMins, vecSurroundMaxs;
+ CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
+ vecSurroundMins -= WorldSpaceCenter();
+ vecSurroundMaxs -= WorldSpaceCenter();
+
+ Vector vecBestPos;
+ QAngle vecBestAngles;
+
+ CInfoTargetVehicleTransition *pEntity = NULL;
+ bool bSucceeded = false;
+
+ // Find all entities of the correct name and try and sit where they're at
+ while ( ( pEntity = (CInfoTargetVehicleTransition *) gEntList.FindEntityByClassname( pEntity, "info_target_vehicle_transition" ) ) != NULL )
+ {
+ // Must be enabled
+ if ( pEntity->IsDisabled() )
+ continue;
+
+ // Must be within range
+ if ( ( pEntity->GetAbsOrigin() - pPlayer->GetAbsOrigin() ).LengthSqr() > Square( TRANSITION_SEARCH_RADIUS ) )
+ continue;
+
+ vecTeleportPos = pEntity->GetAbsOrigin();
+ vecTeleportAngles = pEntity->GetAbsAngles() + QAngle( 0, -90, 0 ); // Vehicle is always off by 90 degrees
+
+ // Rotate to face the destination angles
+ Vector vecMins;
+ Vector vecMaxs;
+ VectorRotate( vecSurroundMins, vecTeleportAngles, vecMins );
+ VectorRotate( vecSurroundMaxs, vecTeleportAngles, vecMaxs );
+
+ if ( vecMaxs.x < vecMins.x )
+ V_swap( vecMins.x, vecMaxs.x );
+
+ if ( vecMaxs.y < vecMins.y )
+ V_swap( vecMins.y, vecMaxs.y );
+
+ if ( vecMaxs.z < vecMins.z )
+ V_swap( vecMins.z, vecMaxs.z );
+
+ // Move up
+ vecTeleportPos.z += ( vecMaxs.z - vecMins.z );
+
+ trace_t tr;
+ UTIL_TraceHull( vecTeleportPos, vecTeleportPos - Vector( 0, 0, 128 ), vecMins, vecMaxs, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+ if ( tr.startsolid == false && tr.allsolid == false && tr.fraction < 1.0f )
+ {
+ // Store this off
+ vecBestPos = tr.endpos;
+ vecBestAngles = vecTeleportAngles;
+ bSucceeded = true;
+
+ // If this point isn't visible, then stop looking and use it
+ if ( pPlayer->FInViewCone( tr.endpos ) == false )
+ break;
+ }
+ }
+
+ // See if we're finished
+ if ( bSucceeded )
+ {
+ Teleport( &vecTeleportPos, &vecTeleportAngles, NULL );
+ return;
+ }
+
+ // TODO: We found no valid teleport points, so try to find them dynamically
+ Warning("No valid vehicle teleport points!\n");
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop players punting the car around.
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputDisablePhysGun( inputdata_t &data )
+{
+ AddEFlags( EFL_NO_PHYSCANNON_INTERACTION );
+}
+//-----------------------------------------------------------------------------
+// Purpose: Return to normal
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputEnablePhysGun( inputdata_t &data )
+{
+ RemoveEFlags( EFL_NO_PHYSCANNON_INTERACTION );
+}
+
+//-----------------------------------------------------------------------------
+// Create and parent two radial node link controllers.
+//-----------------------------------------------------------------------------
+void CPropJeepEpisodic::InputCreateLinkController( inputdata_t &data )
+{
+ Vector vecFront, vecRear;
+ Vector vecWFL, vecWFR; // Front wheels
+ Vector vecWRL, vecWRR; // Back wheels
+
+ GetAttachment( "wheel_fr", vecWFR );
+ GetAttachment( "wheel_fl", vecWFL );
+
+ GetAttachment( "wheel_rr", vecWRR );
+ GetAttachment( "wheel_rl", vecWRL );
+
+ vecFront = (vecWFL + vecWFR) * 0.5f;
+ vecRear = (vecWRL + vecWRR) * 0.5f;
+
+ float flRadius = ( (vecFront - vecRear).Length() ) * 0.6f;
+
+ CAI_RadialLinkController *pLinkController = (CAI_RadialLinkController *)CreateEntityByName( "info_radial_link_controller" );
+ if( pLinkController != NULL && m_hLinkControllerFront.Get() == NULL )
+ {
+ pLinkController->m_flRadius = flRadius;
+ pLinkController->Spawn();
+ pLinkController->SetAbsOrigin( vecFront );
+ pLinkController->SetOwnerEntity( this );
+ pLinkController->SetParent( this );
+ pLinkController->Activate();
+ m_hLinkControllerFront.Set( pLinkController );
+
+ //NDebugOverlay::Circle( vecFront, Vector(0,1,0), Vector(1,0,0), flRadius, 255, 255, 255, 128, false, 100 );
+ }
+
+ pLinkController = (CAI_RadialLinkController *)CreateEntityByName( "info_radial_link_controller" );
+ if( pLinkController != NULL && m_hLinkControllerRear.Get() == NULL )
+ {
+ pLinkController->m_flRadius = flRadius;
+ pLinkController->Spawn();
+ pLinkController->SetAbsOrigin( vecRear );
+ pLinkController->SetOwnerEntity( this );
+ pLinkController->SetParent( this );
+ pLinkController->Activate();
+ m_hLinkControllerRear.Set( pLinkController );
+
+ //NDebugOverlay::Circle( vecRear, Vector(0,1,0), Vector(1,0,0), flRadius, 255, 255, 255, 128, false, 100 );
+ }
+}
+
+void CPropJeepEpisodic::InputDestroyLinkController( inputdata_t &data )
+{
+ if( m_hLinkControllerFront.Get() != NULL )
+ {
+ CAI_RadialLinkController *pLinkController = dynamic_cast<CAI_RadialLinkController*>(m_hLinkControllerFront.Get());
+ if( pLinkController != NULL )
+ {
+ pLinkController->ModifyNodeLinks(false);
+ UTIL_Remove( pLinkController );
+ m_hLinkControllerFront.Set(NULL);
+ }
+ }
+
+ if( m_hLinkControllerRear.Get() != NULL )
+ {
+ CAI_RadialLinkController *pLinkController = dynamic_cast<CAI_RadialLinkController*>(m_hLinkControllerRear.Get());
+ if( pLinkController != NULL )
+ {
+ pLinkController->ModifyNodeLinks(false);
+ UTIL_Remove( pLinkController );
+ m_hLinkControllerRear.Set(NULL);
+ }
+ }
+}
+
+
+bool CPropJeepEpisodic::AllowBlockedExit( CBaseCombatCharacter *pPassenger, int nRole )
+{
+ // Wait until we've settled down before we resort to blocked exits.
+ // This keeps us from doing blocked exits in mid-jump, which can cause mayhem like
+ // sticking the player through player clips or into geometry.
+ return GetSmoothedVelocity().IsLengthLessThan( jalopy_blocked_exit_max_speed.GetFloat() );
+}
+