From ccbb5b763edfde7d03fa834a529d481133c4edc4 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 28 Jan 2024 09:14:47 +0000 Subject: [PATCH] Port func_vehicle implementation from cs16-client/regamedll_cs. (#428) --- cl_dll/ev_hldm.cpp | 60 +++ cl_dll/hl/hl_events.cpp | 2 + contrib/iZarif/premake5.lua | 1 + dlls/Android.mk | 1 + dlls/CMakeLists.txt | 1 + dlls/cbase.h | 1 + dlls/client.cpp | 2 + dlls/compile.bat | 1 + dlls/multiplay_gamerules.cpp | 11 + dlls/player.cpp | 83 ++- dlls/trains.h | 85 +++ dlls/vehicle.cpp | 996 +++++++++++++++++++++++++++++++++++ 12 files changed, 1229 insertions(+), 15 deletions(-) create mode 100644 dlls/vehicle.cpp diff --git a/cl_dll/ev_hldm.cpp b/cl_dll/ev_hldm.cpp index 9f5ede08..381ee25b 100644 --- a/cl_dll/ev_hldm.cpp +++ b/cl_dll/ev_hldm.cpp @@ -70,6 +70,7 @@ void EV_TripmineFire( struct event_args_s *args ); void EV_SnarkFire( struct event_args_s *args ); void EV_TrainPitchAdjust( struct event_args_s *args ); +void EV_VehiclePitchAdjust( event_args_t *args ); } #define VECTOR_CONE_1DEGREES Vector( 0.00873f, 0.00873f, 0.00873f ) @@ -1752,6 +1753,65 @@ void EV_TrainPitchAdjust( event_args_t *args ) } } +void EV_VehiclePitchAdjust( event_args_t *args ) +{ + int idx; + vec3_t origin; + + unsigned short us_params; + int noise; + float m_flVolume; + int pitch; + int stop; + + const char *pszSound; + + idx = args->entindex; + + VectorCopy( args->origin, origin ); + + us_params = (unsigned short)args->iparam1; + stop = args->bparam1; + + m_flVolume = (float)( us_params & 0x003f ) / 40.0f; + noise = (int)( ( ( us_params ) >> 12 ) & 0x0007 ); + pitch = (int)( 10.0f * (float)( ( us_params >> 6 ) & 0x003f ) ); + + switch( noise ) + { + case 1: + pszSound = "plats/vehicle1.wav"; + break; + case 2: + pszSound = "plats/vehicle2.wav"; + break; + case 3: + pszSound = "plats/vehicle3.wav"; + break; + case 4: + pszSound = "plats/vehicle4.wav"; + break; + case 5: + pszSound = "plats/vehicle6.wav"; + break; + case 6: + pszSound = "plats/vehicle7.wav"; + break; + default: + // no sound + return; + } + + if( stop ) + { + gEngfuncs.pEventAPI->EV_StopSound( idx, CHAN_STATIC, pszSound ); + } + else + { + gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_STATIC, pszSound, m_flVolume, ATTN_NORM, SND_CHANGE_PITCH, pitch ); + } +} + int EV_TFC_IsAllyTeam( int iTeam1, int iTeam2 ) { return 0; diff --git a/cl_dll/hl/hl_events.cpp b/cl_dll/hl/hl_events.cpp index c79279dd..e68fabf9 100644 --- a/cl_dll/hl/hl_events.cpp +++ b/cl_dll/hl/hl_events.cpp @@ -40,6 +40,7 @@ void EV_TripmineFire( struct event_args_s *args ); void EV_SnarkFire( struct event_args_s *args ); void EV_TrainPitchAdjust( struct event_args_s *args ); +void EV_VehiclePitchAdjust( event_args_t *args ); } /* @@ -76,4 +77,5 @@ void Game_HookEvents( void ) gEngfuncs.pfnHookEvent( "events/firehornet.sc", EV_HornetGunFire ); gEngfuncs.pfnHookEvent( "events/tripfire.sc", EV_TripmineFire ); gEngfuncs.pfnHookEvent( "events/snarkfire.sc", EV_SnarkFire ); + gEngfuncs.pfnHookEvent( "events/vehicle.sc", EV_VehiclePitchAdjust ); } diff --git a/contrib/iZarif/premake5.lua b/contrib/iZarif/premake5.lua index 91817da5..d959d9ad 100644 --- a/contrib/iZarif/premake5.lua +++ b/contrib/iZarif/premake5.lua @@ -219,6 +219,7 @@ files{"dlls/agrunt.cpp", "dlls/triggers.cpp", "dlls/turret.cpp", "dlls/util.cpp", +"dlls/vehicle.cpp", "dlls/weapons.cpp", "dlls/world.cpp", "dlls/xen.cpp", diff --git a/dlls/Android.mk b/dlls/Android.mk index 1dc79616..80934949 100644 --- a/dlls/Android.mk +++ b/dlls/Android.mk @@ -122,6 +122,7 @@ LOCAL_SRC_FILES := agrunt.cpp airtank.cpp \ tripmine.cpp \ turret.cpp \ util.cpp \ + vehicle.cpp \ weapons.cpp \ world.cpp \ xen.cpp \ diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 7da665d9..9c518d39 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -144,6 +144,7 @@ set (SVDLL_SOURCES tripmine.cpp turret.cpp util.cpp + vehicle.cpp weapons.cpp world.cpp xen.cpp diff --git a/dlls/cbase.h b/dlls/cbase.h index d64c0f5c..88a161e0 100644 --- a/dlls/cbase.h +++ b/dlls/cbase.h @@ -103,6 +103,7 @@ typedef void(CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCalle #define CLASS_PLAYER_ALLY 11 #define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players #define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace +#define CLASS_VEHICLE 14 #define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. class CBaseEntity; diff --git a/dlls/client.cpp b/dlls/client.cpp index c27b22d1..3ce82cfb 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -893,6 +893,8 @@ void ClientPrecache( void ) PRECACHE_SOUND( "plats/train_use1.wav" ); // use a train + PRECACHE_SOUND( "plats/vehicle_ignition.wav" ); + PRECACHE_SOUND( "buttons/spark5.wav" ); // hit computer texture PRECACHE_SOUND( "buttons/spark6.wav" ); PRECACHE_SOUND( "debris/glass1.wav" ); diff --git a/dlls/compile.bat b/dlls/compile.bat index 4eba92c7..20b7cc66 100644 --- a/dlls/compile.bat +++ b/dlls/compile.bat @@ -103,6 +103,7 @@ set SOURCES=agrunt.cpp ^ tripmine.cpp ^ turret.cpp ^ util.cpp ^ + vehicle.cpp ^ weapons.cpp ^ world.cpp ^ xen.cpp ^ diff --git a/dlls/multiplay_gamerules.cpp b/dlls/multiplay_gamerules.cpp index b9f35d41..49e24ab3 100644 --- a/dlls/multiplay_gamerules.cpp +++ b/dlls/multiplay_gamerules.cpp @@ -30,6 +30,7 @@ #include "voice_gamemgr.h" #endif #include "hltv.h" +#include "trains.h" extern DLL_GLOBAL CGameRules *g_pGameRules; extern DLL_GLOBAL BOOL g_fGameOver; @@ -647,6 +648,16 @@ void CHalfLifeMultiplay::PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, CBaseEntity *ktmp = CBaseEntity::Instance( pKiller ); if( ktmp && (ktmp->Classify() == CLASS_PLAYER ) ) peKiller = (CBasePlayer*)ktmp; + else if( ktmp && ktmp->Classify() == CLASS_VEHICLE ) + { + CBasePlayer *pDriver = (CBasePlayer *)( (CFuncVehicle *)ktmp )->m_pDriver; + + if( pDriver != NULL ) + { + pKiller = pDriver->pev; + peKiller = (CBasePlayer *)pDriver; + } + } if( pVictim->pev == pKiller ) { diff --git a/dlls/player.cpp b/dlls/player.cpp index 0d984b12..7f5df559 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -1494,18 +1494,31 @@ void CBasePlayer::PlayerUse( void ) { m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW|TRAIN_OFF; + + CBaseEntity *pTrain = Instance( pev->groundentity ); + if( pTrain && pTrain->Classify() == CLASS_VEHICLE ) + { + ( (CFuncVehicle *)pTrain )->m_pDriver = NULL; + } return; } else { // Start controlling the train! CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity ); - if( pTrain && !( pev->button & IN_JUMP ) && FBitSet( pev->flags, FL_ONGROUND ) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE ) && pTrain->OnControls( pev ) ) + if( pTrain && !( pev->button & IN_JUMP ) && FBitSet( pev->flags, FL_ONGROUND ) && ( pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE ) && pTrain->OnControls( pev ) ) { m_afPhysicsFlags |= PFLAG_ONTRAIN; m_iTrain = TrainSpeed( (int)pTrain->pev->speed, pTrain->pev->impulse ); m_iTrain |= TRAIN_NEW; - EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM ); + + if( pTrain->Classify() == CLASS_VEHICLE ) + { + EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/vehicle_ignition.wav", 0.8, ATTN_NORM ); + ( (CFuncVehicle *)pTrain )->m_pDriver = this; + } + else + EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM ); return; } } @@ -1619,9 +1632,17 @@ void CBasePlayer::Jump() // If you're standing on a conveyor, add it's velocity to yours (for momentum) entvars_t *pevGround = VARS( pev->groundentity ); - if( pevGround && ( pevGround->flags & FL_CONVEYOR ) ) + if( pevGround ) { - pev->velocity = pev->velocity + pev->basevelocity; + if( pevGround->flags & FL_CONVEYOR ) + { + pev->velocity = pev->velocity + pev->basevelocity; + } + + if( FClassnameIs( pevGround, "func_vehicle" )) + { + pev->velocity = pevGround->velocity + pev->velocity; + } } } @@ -1886,30 +1907,62 @@ void CBasePlayer::PreThink( void ) //ALERT( at_error, "In train mode with no train!\n" ); m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW|TRAIN_OFF; + if( pTrain ) + ( (CFuncVehicle *)pTrain )->m_pDriver = NULL; return; } } - else if( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) || ( pev->button & ( IN_MOVELEFT | IN_MOVERIGHT ) ) ) + else if( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) + || ( ( pev->button & ( IN_MOVELEFT | IN_MOVERIGHT )) && pTrain->Classify() != CLASS_VEHICLE )) { // Turn off the train if you jump, strafe, or the train controls go dead m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW | TRAIN_OFF; + ( (CFuncVehicle *)pTrain )->m_pDriver = NULL; return; } pev->velocity = g_vecZero; vel = 0; - if( m_afButtonPressed & IN_FORWARD ) - { - vel = 1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } - else if( m_afButtonPressed & IN_BACK ) - { - vel = -1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } + if( pTrain->Classify() == CLASS_VEHICLE ) + { + if( pev->button & IN_FORWARD ) + { + vel = 1; + pTrain->Use( this, this, USE_SET, vel ); + } + + if( pev->button & IN_BACK ) + { + vel = -1; + pTrain->Use( this, this, USE_SET, vel ); + } + + if( pev->button & IN_MOVELEFT ) + { + vel = 20; + pTrain->Use( this, this, USE_SET, vel ); + } + if( pev->button & IN_MOVERIGHT ) + { + vel = 30; + pTrain->Use( this, this, USE_SET, vel ); + } + } + else + { + if( m_afButtonPressed & IN_FORWARD ) + { + vel = 1; + pTrain->Use( this, this, USE_SET, vel ); + } + else if( m_afButtonPressed & IN_BACK ) + { + vel = -1; + pTrain->Use( this, this, USE_SET, vel ); + } + } iGearId = TrainSpeed( pTrain->pev->speed, pTrain->pev->impulse ); if( iGearId != ( m_iTrain & 0x0F ) ) // Vit_amiN: speed changed diff --git a/dlls/trains.h b/dlls/trains.h index 046dbc24..652ecbc4 100644 --- a/dlls/trains.h +++ b/dlls/trains.h @@ -122,4 +122,89 @@ public: private: unsigned short m_usAdjustPitch; }; + +class CFuncVehicle: public CBaseEntity +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual void Restart(); + virtual void KeyValue( KeyValueData *pkvd ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + virtual int ObjectCaps() { return ( CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION ) | FCAP_DIRECTIONAL_USE; } + virtual int Classify(); + virtual void OverrideReset(); + virtual BOOL OnControls( entvars_t *pev ); + virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + virtual void Blocked( CBaseEntity *pOther ); + +public: + void EXPORT Next(); + void EXPORT Find(); + void EXPORT NearestPath(); + void EXPORT DeadEnd(); + + void NextThink( float thinkTime, BOOL alwaysThink ); + void CollisionDetection(); + void TerrainFollowing(); + void CheckTurning(); + + void SetTrack( CPathTrack *track ) { m_ppath = track->Nearest( pev->origin ); } + void SetControls( entvars_t *pevControls ); + + void StopSound(); + void UpdateSound(); + +public: + static CFuncVehicle *Instance( edict_t *pent ); + static TYPEDESCRIPTION m_SaveData[12]; + + CPathTrack *m_ppath; + float m_length; + float m_width; + float m_height; + float m_speed; + float m_dir; + float m_startSpeed; + Vector m_controlMins; + Vector m_controlMaxs; + int m_soundPlaying; + int m_sounds; + int m_acceleration; + float m_flVolume; + float m_flBank; + float m_oldSpeed; + int m_iTurnAngle; + float m_flSteeringWheelDecay; + float m_flAcceleratorDecay; + float m_flTurnStartTime; + float m_flLaunchTime; + float m_flLastNormalZ; + float m_flCanTurnNow; + float m_flUpdateSound; + Vector m_vFrontLeft; + Vector m_vFront; + Vector m_vFrontRight; + Vector m_vBackLeft; + Vector m_vBack; + Vector m_vBackRight; + Vector m_vSurfaceNormal; + Vector m_vVehicleDirection; + CBaseEntity *m_pDriver; + +private: + unsigned short m_usAdjustPitch; +}; + +class CFuncVehicleControls: public CBaseEntity +{ +public: + virtual void Spawn(); + virtual int ObjectCaps() { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + +public: + void EXPORT Find(); +}; + #endif diff --git a/dlls/vehicle.cpp b/dlls/vehicle.cpp new file mode 100644 index 00000000..66e5cc2f --- /dev/null +++ b/dlls/vehicle.cpp @@ -0,0 +1,996 @@ +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "trains.h" +#include "saverestore.h" + +#define VEHICLE_SPEED0_ACCELERATION 0.005000000000000000 +#define VEHICLE_SPEED1_ACCELERATION 0.002142857142857143 +#define VEHICLE_SPEED2_ACCELERATION 0.003333333333333334 +#define VEHICLE_SPEED3_ACCELERATION 0.004166666666666667 +#define VEHICLE_SPEED4_ACCELERATION 0.004000000000000000 +#define VEHICLE_SPEED5_ACCELERATION 0.003800000000000000 +#define VEHICLE_SPEED6_ACCELERATION 0.004500000000000000 +#define VEHICLE_SPEED7_ACCELERATION 0.004250000000000000 +#define VEHICLE_SPEED8_ACCELERATION 0.002666666666666667 +#define VEHICLE_SPEED9_ACCELERATION 0.002285714285714286 +#define VEHICLE_SPEED10_ACCELERATION 0.001875000000000000 +#define VEHICLE_SPEED11_ACCELERATION 0.001444444444444444 +#define VEHICLE_SPEED12_ACCELERATION 0.001200000000000000 +#define VEHICLE_SPEED13_ACCELERATION 0.000916666666666666 + +#define VEHICLE_STARTPITCH 60 +#define VEHICLE_MAXPITCH 200 +#define VEHICLE_MAXSPEED 1500 + +TYPEDESCRIPTION CFuncVehicle::m_SaveData[] = +{ + DEFINE_FIELD( CFuncVehicle, m_ppath, FIELD_CLASSPTR ), + DEFINE_FIELD( CFuncVehicle, m_length, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_height, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_speed, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_dir, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_startSpeed, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_controlMins, FIELD_VECTOR ), + DEFINE_FIELD( CFuncVehicle, m_controlMaxs, FIELD_VECTOR ), + DEFINE_FIELD( CFuncVehicle, m_sounds, FIELD_INTEGER ), + DEFINE_FIELD( CFuncVehicle, m_flVolume, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_flBank, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_oldSpeed, FIELD_FLOAT ), +}; + +static float Fix2( float angle ) +{ + while( angle < 0 ) + angle += 360; + + while( angle > 360 ) + angle -= 360; + + return angle; +} + +static void FixupAngles2( Vector &v ) +{ + v.x = Fix2( v.x ); + v.y = Fix2( v.y ); + v.z = Fix2( v.z ); +} + +IMPLEMENT_SAVERESTORE( CFuncVehicle, CBaseEntity ) + +LINK_ENTITY_TO_CLASS( func_vehicle, CFuncVehicle ) + +void CFuncVehicle::KeyValue( KeyValueData *pkvd ) +{ + if( FStrEq( pkvd->szKeyName, "length" )) + { + m_length = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "width" )) + { + m_width = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "height" )) + { + m_height = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "startspeed" )) + { + m_startSpeed = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "sounds" )) + { + m_sounds = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "volume" )) + { + m_flVolume = (float)atoi( pkvd->szValue ); + m_flVolume *= 0.1f; + + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "bank" )) + { + m_flBank = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "acceleration" )) + { + m_acceleration = atoi( pkvd->szValue ); + + if( m_acceleration < 1 ) + m_acceleration = 1; + + else if( m_acceleration > 10 ) + m_acceleration = 10; + + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + +void CFuncVehicle::NextThink( float thinkTime, BOOL alwaysThink ) +{ + if( alwaysThink ) + pev->flags |= FL_ALWAYSTHINK; + else + pev->flags &= ~FL_ALWAYSTHINK; + + pev->nextthink = thinkTime; +} + +void CFuncVehicle::Blocked( CBaseEntity *pOther ) +{ + entvars_t *pevOther = pOther->pev; + + if( ( pevOther->flags & FL_ONGROUND ) && VARS( pevOther->groundentity ) == pev ) + { + pevOther->velocity = pev->velocity; + return; + } + + pevOther->velocity = ( pevOther->origin - pev->origin ).Normalize() * pev->dmg; + pevOther->velocity.z += 300; + pev->velocity = pev->velocity * 0.85f; + + ALERT( at_aiconsole, "TRAIN(%s): Blocked by %s (dmg:%.2f)\n", STRING( pev->targetname ), STRING( pOther->pev->classname ), pev->dmg ); + UTIL_MakeVectors( pev->angles ); + + Vector forward, right, vOrigin; + Vector vFrontLeft = ( gpGlobals->v_forward * -1 ) * ( m_length * 0.5f ); + Vector vFrontRight = ( gpGlobals->v_right * -1 ) * ( m_width * 0.5f ); + + Vector vBackLeft = pev->origin + vFrontLeft - vFrontRight; + Vector vBackRight = pev->origin - vFrontLeft + vFrontRight; + + float minx = Q_min( vBackLeft.x, vBackRight.x ); + float miny = Q_min( vBackLeft.y, vBackRight.y ); + float maxx = Q_max( vBackLeft.x, vBackRight.x ); + float maxy = Q_max( vBackLeft.y, vBackRight.y ); + + float minz = pev->origin.z; + float maxz = pev->origin.z + ( 2 * abs( (int)( pev->mins.z - pev->maxs.z ))); + + if ( pOther->pev->origin.x < minx + || pOther->pev->origin.x > maxx + || pOther->pev->origin.y < miny + || pOther->pev->origin.y > maxy + || pOther->pev->origin.z < pev->origin.z + || pOther->pev->origin.z > maxz ) + { + pOther->TakeDamage( pev, pev, 150, DMG_CRUSH ); + } +} + +void CFuncVehicle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + float delta = value; + + if( useType != USE_SET ) + { + if( ShouldToggle( useType, pev->speed != 0 )) + { + if( pev->speed == 0 ) + { + pev->speed = m_dir * m_speed; + Next(); + } + else + { + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + + StopSound(); + SetThink( NULL ); + } + } + + return; + } + + if( delta < 10 ) + { + if( delta < 0 ) + { + if( pev->speed > 145 ) + { + StopSound(); + } + } + + float flSpeedRatio = delta; + + if( delta > 0 ) + { + flSpeedRatio = (float)( pev->speed / m_speed ); + + if( pev->speed < 0 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED0_ACCELERATION; + else if( pev->speed < 10 ) flSpeedRatio = m_acceleration * 0.0006 + flSpeedRatio + VEHICLE_SPEED1_ACCELERATION; + else if( pev->speed < 20 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED2_ACCELERATION; + else if( pev->speed < 30 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED3_ACCELERATION; + else if( pev->speed < 45 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED4_ACCELERATION; + else if( pev->speed < 60 ) flSpeedRatio = m_acceleration * 0.0008 + flSpeedRatio + VEHICLE_SPEED5_ACCELERATION; + else if( pev->speed < 80 ) flSpeedRatio = m_acceleration * 0.0008 + flSpeedRatio + VEHICLE_SPEED6_ACCELERATION; + else if( pev->speed < 100 ) flSpeedRatio = m_acceleration * 0.0009 + flSpeedRatio + VEHICLE_SPEED7_ACCELERATION; + else if( pev->speed < 150 ) flSpeedRatio = m_acceleration * 0.0008 + flSpeedRatio + VEHICLE_SPEED8_ACCELERATION; + else if( pev->speed < 225 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED9_ACCELERATION; + else if( pev->speed < 300 ) flSpeedRatio = m_acceleration * 0.0006 + flSpeedRatio + VEHICLE_SPEED10_ACCELERATION; + else if( pev->speed < 400 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED11_ACCELERATION; + else if( pev->speed < 550 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED12_ACCELERATION; + else if( pev->speed < 800 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED13_ACCELERATION; + } + else if( delta < 0 ) + { + flSpeedRatio = pev->speed / m_speed; + + // TODO: fix float for test demo + if( flSpeedRatio > 0 ) flSpeedRatio = (float)flSpeedRatio - 0.0125f; + else if( flSpeedRatio <= 0 && flSpeedRatio > -0.05f ) flSpeedRatio = (float)flSpeedRatio - 0.0075f; + else if( flSpeedRatio <= 0.05f && flSpeedRatio > -0.1f ) flSpeedRatio = (float)flSpeedRatio - 0.01f; + else if( flSpeedRatio <= 0.15f && flSpeedRatio > -0.15f ) flSpeedRatio = (float)flSpeedRatio - 0.0125f; + else if( flSpeedRatio <= 0.15f && flSpeedRatio > -0.22f ) flSpeedRatio = (float)flSpeedRatio - 0.01375f; + else if( flSpeedRatio <= 0.22f && flSpeedRatio > -0.3f ) flSpeedRatio = (float)flSpeedRatio - 0.0175f; + else if( flSpeedRatio <= 0.3f ) flSpeedRatio = (float)flSpeedRatio - 0.0125f; + } + + if( flSpeedRatio > 1 ) + { + flSpeedRatio = 1; + } + else if( flSpeedRatio < -0.35f ) + { + flSpeedRatio = -0.35f; + } + + pev->speed = flSpeedRatio * m_speed; + Next(); + m_flAcceleratorDecay = gpGlobals->time + 0.25f; + } + else if( m_flCanTurnNow < gpGlobals->time ) + { + if( delta == 20 ) + { + m_iTurnAngle++; + m_flSteeringWheelDecay = gpGlobals->time + 0.075f; + + if (m_iTurnAngle > 8) + { + m_iTurnAngle = 8; + } + } + else if( delta == 30 ) + { + m_iTurnAngle--; + m_flSteeringWheelDecay = gpGlobals->time + 0.075f; + + if( m_iTurnAngle < -8 ) + { + m_iTurnAngle = -8; + } + } + + m_flCanTurnNow = gpGlobals->time + 0.05f; + } +} + +void CFuncVehicle::StopSound() +{ + if( m_soundPlaying && pev->noise ) + { + unsigned short us_sound = ( (unsigned short)m_sounds & 0x0007 ) << 12; + unsigned short us_encode = us_sound; + + PLAYBACK_EVENT_FULL( FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0, g_vecZero, g_vecZero, 0, 0, us_encode, 0, 1, 0 ); + } + + m_soundPlaying = 0; +} + +void CFuncVehicle::UpdateSound() +{ + if( !pev->noise ) + return; + + float flpitch = VEHICLE_STARTPITCH + ( abs( (int)pev->speed ) * ( VEHICLE_MAXPITCH - VEHICLE_STARTPITCH ) / VEHICLE_MAXSPEED ); + + if( flpitch > 200 ) + flpitch = 200; + + if( !m_soundPlaying ) + { + if( m_sounds < 5 ) + { + EMIT_SOUND_DYN( ENT(pev), CHAN_ITEM, "plats/vehicle_brake1.wav", m_flVolume, ATTN_NORM, 0, PITCH_NORM ); + } + + EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, STRING( pev->noise ), m_flVolume, ATTN_NORM, 0, (int)flpitch ); + m_soundPlaying = 1; + } + else + { + unsigned short us_sound = ( (unsigned short)( m_sounds ) & 0x0007 ) << 12; + unsigned short us_pitch = ( (unsigned short)( flpitch / 10.0 ) & 0x003F ) << 6; + unsigned short us_volume = ( (unsigned short)( m_flVolume * 40 ) & 0x003F ); + unsigned short us_encode = us_sound | us_pitch | us_volume; + + PLAYBACK_EVENT_FULL( FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, g_vecZero, g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0 ); + } +} + +void CFuncVehicle::CheckTurning() +{ + float maxspeed; + TraceResult tr; + bool bTurnIntoWall = false; + + if( m_iTurnAngle < 0 ) + { + if( pev->speed > 0 ) + { + UTIL_TraceLine( m_vFrontRight, m_vFrontRight - ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + } + else if( pev->speed < 0 ) + { + UTIL_TraceLine( m_vBackLeft, m_vBackLeft + ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + } + + if( tr.flFraction != 1.0f ) + { + m_iTurnAngle = 1; + } + } + else if( m_iTurnAngle > 0 ) + { + if( pev->speed > 0 ) + { + UTIL_TraceLine( m_vFrontLeft, m_vFrontLeft + ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + } + else if( pev->speed < 0 ) + { + UTIL_TraceLine( m_vBackRight, m_vBackRight - ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + } + + if( tr.flFraction != 1.0f ) + { + m_iTurnAngle = -1; + } + } + + if( pev->speed > 0 ) + { + int iCountTurn = abs( m_iTurnAngle ); + + if( iCountTurn > 4 ) + { + if ( m_flTurnStartTime != -1 ) + { + float flTurnTime = gpGlobals->time - m_flTurnStartTime; + + if( flTurnTime >= 0 ) maxspeed = m_speed * 0.98f; + else if( flTurnTime > 0.3f ) maxspeed = m_speed * 0.95f; + else if( flTurnTime > 0.6f ) maxspeed = m_speed * 0.9f; + else if( flTurnTime > 0.8f ) maxspeed = m_speed * 0.8f; + else if( flTurnTime > 1 ) maxspeed = m_speed * 0.7f; + else if( flTurnTime > 1.2f ) maxspeed = m_speed * 0.5f; + else maxspeed = flTurnTime; + } + else + { + m_flTurnStartTime = gpGlobals->time; + maxspeed = m_speed; + } + } + else + { + m_flTurnStartTime = -1; + + if( iCountTurn > 2 ) + maxspeed = m_speed * 0.9f; + else + maxspeed = m_speed; + } + + if( maxspeed < pev->speed ) + { + pev->speed -= m_speed * 0.1f; + } + } +} + +void CFuncVehicle::CollisionDetection() +{ + TraceResult tr; + bool bHitSomething = false; + + if( pev->speed < 0 ) + { + UTIL_TraceLine( m_vBackLeft, m_vBackLeft + ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + UTIL_TraceLine( m_vBackRight, m_vBackRight + ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + UTIL_TraceLine( m_vBack, m_vBack + ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + return; + } + } + + if( DotProduct( gpGlobals->v_forward, tr.vecPlaneNormal * -1.0f ) < 0.7f && tr.vecPlaneNormal.z < 0.1f ) + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + + pev->speed *= 0.99f; + } + else if( tr.vecPlaneNormal.z < 0.65f || tr.fStartSolid ) + { + pev->speed *= -1.0f; + } + else + { + m_vSurfaceNormal = tr.vecPlaneNormal; + } + } + else + { + if( DotProduct( gpGlobals->v_forward, tr.vecPlaneNormal * -1.0f ) < 0.7f && tr.vecPlaneNormal.z < 0.1f ) + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + + pev->speed *= 0.99f; + } + else if( tr.vecPlaneNormal[2] < 0.65f || tr.fStartSolid ) + { + pev->speed *= -1.0f; + } + else + { + m_vSurfaceNormal = tr.vecPlaneNormal; + } + + CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); + + if( pHit && pHit->Classify() == CLASS_VEHICLE ) + { + bHitSomething = true; + ALERT( at_console, "I hit another vehicle\n" ); + } + } + } + else if( pev->speed > 0 ) + { + UTIL_TraceLine( m_vFrontLeft, m_vFrontLeft - ( gpGlobals->v_forward * 16.0f ), dont_ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + UTIL_TraceLine( m_vFrontRight, m_vFrontRight - ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + UTIL_TraceLine( m_vFront, m_vFront - ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + return; + } + } + } + + if( DotProduct( gpGlobals->v_forward, tr.vecPlaneNormal * -1.0f ) > -0.7f && tr.vecPlaneNormal.z < 0.1f ) + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + + pev->speed *= 0.99f; + } + else if( tr.vecPlaneNormal.z < 0.65f || tr.fStartSolid ) + { + pev->speed *= -1.0f; + } + else + { + m_vSurfaceNormal = tr.vecPlaneNormal; + } + } +} + +void CFuncVehicle::TerrainFollowing() +{ + TraceResult tr; + UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, ( m_height + 48 ) * -1 ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction != 1.0f ) + { + m_vSurfaceNormal = tr.vecPlaneNormal; + } + else if( tr.fInWater ) + { + m_vSurfaceNormal = Vector( 0, 0, 1 ); + } +} + +void CFuncVehicle::Next() +{ + Vector vGravityVector, forward, right, up; + float time = 0.1f; + + vGravityVector = g_vecZero; + UTIL_MakeVectors( pev->angles ); + + forward = ( gpGlobals->v_forward * -1 ) * ( m_length * 0.5f ); + right = ( gpGlobals->v_right * -1 ) * ( m_width * 0.5f ); + up = gpGlobals->v_up * 16; + + m_vFrontLeft = pev->origin + forward - right + up; + m_vFrontRight = pev->origin + forward + right + up; + m_vFront = pev->origin + forward + up; + m_vBackLeft = pev->origin - forward - right + up; + m_vBackRight = pev->origin - forward + right + up; + m_vBack = pev->origin - forward + up; + m_vSurfaceNormal = g_vecZero; + + CheckTurning(); + + if( m_flSteeringWheelDecay < gpGlobals->time ) + { + m_flSteeringWheelDecay = gpGlobals->time + 0.1f; + + if( m_iTurnAngle < 0 ) + m_iTurnAngle++; + + else if( m_iTurnAngle > 0 ) + m_iTurnAngle--; + } + + if( m_flAcceleratorDecay < gpGlobals->time ) + { + m_flAcceleratorDecay = gpGlobals->time + 0.1f; + + if( pev->speed < 0 ) + { + pev->speed += 20; + + if( pev->speed > 0 ) + pev->speed = 0; + } + else if( pev->speed > 0 ) + { + pev->speed -= 20; + + if( pev->speed < 0 ) + pev->speed = 0; + } + } + + if( pev->speed == 0 ) + { + m_iTurnAngle = 0; + pev->avelocity = g_vecZero; + pev->velocity = g_vecZero; + + SetThink( &CFuncVehicle::Next ); + NextThink( pev->ltime + time, TRUE ); + return; + } + + TerrainFollowing(); + CollisionDetection(); + + Vector temp; + if( m_vSurfaceNormal != g_vecZero ) + { + Vector vTargetAngle, vAngle; + + float vx; + float vy; + + m_vVehicleDirection = CrossProduct( m_vSurfaceNormal, gpGlobals->v_forward ); + m_vVehicleDirection = CrossProduct( m_vSurfaceNormal, m_vVehicleDirection ); + + vTargetAngle = UTIL_VecToAngles( m_vVehicleDirection ); + vAngle = pev->angles; + + vTargetAngle.y += 180; + + if( m_iTurnAngle != 0 ) + { + vTargetAngle.y += m_iTurnAngle; + } + + FixupAngles2( vTargetAngle ); + FixupAngles2( vAngle ); + + vx = UTIL_AngleDistance( vTargetAngle.x, vAngle.x ); + vy = UTIL_AngleDistance( vTargetAngle.y, vAngle.y ); + + if( vx > 10 ) + vx = 10; + else if( vx < -10 ) + vx = -10; + + if( vy > 10 ) + vy = 10; + else if( vy < -10 ) + vy = -10; + + pev->avelocity.y = (int)( vy * 10 ); + pev->avelocity.x = (int)( vx * 10 ); + + m_flLaunchTime = -1; + m_flLastNormalZ = m_vSurfaceNormal.z; + } + else + { + if( m_flLaunchTime != -1 ) + { + vGravityVector.x = 0; + vGravityVector.y = 0; + vGravityVector.z = ( gpGlobals->time - m_flLaunchTime ) * -35; + + if( vGravityVector.z < -400 ) + { + vGravityVector.z = -400; + } + } + else + { + m_flLaunchTime = gpGlobals->time; + vGravityVector = Vector( 0, 0, 0 ); + pev->velocity = pev->velocity * 1.5f; + } + + m_vVehicleDirection = gpGlobals->v_forward * -1; + } + + UTIL_VecToAngles( m_vVehicleDirection ); + + if( m_flUpdateSound < gpGlobals->time ) + { + UpdateSound(); + m_flUpdateSound = gpGlobals->time + 1.0f; + } + + if( m_vSurfaceNormal != g_vecZero ) + { + pev->velocity = m_vVehicleDirection.Normalize() * pev->speed; + } + else + { + pev->velocity = pev->velocity + vGravityVector; + } + + SetThink( &CFuncVehicle::Next ); + NextThink( pev->ltime + time, TRUE ); +} + +void CFuncVehicle::DeadEnd() +{ + CPathTrack *pTrack = m_ppath; + ALERT( at_aiconsole, "TRAIN(%s): Dead end ", STRING( pev->targetname )); + + if( pTrack != NULL ) + { + CPathTrack *pNext; + + if( m_oldSpeed < 0 ) + { + do + { + pNext = pTrack->ValidPath( pTrack->GetPrevious(), TRUE ); + + if( pNext != NULL ) + { + pTrack = pNext; + } + } + while( pNext != NULL ); + } + else + { + do + { + pNext = pTrack->ValidPath( pTrack->GetNext(), TRUE ); + + if( pNext != NULL ) + { + pTrack = pNext; + } + } + while( pNext != NULL ); + } + } + + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + + if( pTrack != NULL ) + { + ALERT( at_aiconsole, "at %s\n", STRING( pTrack->pev->targetname )); + + if( !FStringNull( pTrack->pev->netname )) + { + FireTargets( STRING( pTrack->pev->netname ), this, this, USE_TOGGLE, 0 ); + } + } + else + ALERT( at_aiconsole, "\n" ); +} + +void CFuncVehicle::SetControls(entvars_t *pevControls) +{ + Vector offset = pevControls->origin - pev->oldorigin; + m_controlMins = pevControls->mins + offset; + m_controlMaxs = pevControls->maxs + offset; +} + +BOOL CFuncVehicle::OnControls(entvars_t *pevTest) +{ + if( pev->spawnflags & SF_TRACKTRAIN_NOCONTROL ) + return FALSE; + + Vector offset = pevTest->origin - pev->origin; + + UTIL_MakeVectors( pev->angles ); + + Vector local; + local.x = DotProduct( offset, gpGlobals->v_forward ); + local.y = -DotProduct( offset, gpGlobals->v_right ); + local.z = DotProduct( offset, gpGlobals->v_up ); + + return ( local.x >= m_controlMins.x && local.y >= m_controlMins.y && local.z >= m_controlMins.z + && local.x <= m_controlMaxs.x && local.y <= m_controlMaxs.y && local.z <= m_controlMaxs.z ); +} + +void CFuncVehicle::Find() +{ + m_ppath = CPathTrack::Instance( FIND_ENTITY_BY_TARGETNAME( NULL, STRING( pev->target ))); + + if( !m_ppath ) + return; + + entvars_t *pevTarget = m_ppath->pev; + + if( !FClassnameIs( pevTarget, "path_track" )) + { + ALERT( at_error, "func_track_train must be on a path of path_track\n" ); + m_ppath = NULL; + return; + } + + Vector nextPos = pevTarget->origin; + nextPos.z += m_height; + + Vector look = nextPos; + look.z -= m_height; + m_ppath->LookAhead( &look, m_length, 0 ); + look.z += m_height; + + pev->angles = UTIL_VecToAngles( look - nextPos ); + pev->angles.y += 180; + + if( pev->spawnflags & SF_TRACKTRAIN_NOPITCH ) + { + pev->angles.x = 0; + } + + UTIL_SetOrigin( pev, nextPos ); + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::Next ); + pev->speed = m_startSpeed; + UpdateSound(); +} + +void CFuncVehicle::NearestPath() +{ + CBaseEntity *pTrack = NULL; + CBaseEntity *pNearest = NULL; + float dist; + float closest = 1024; + + while( ( pTrack = UTIL_FindEntityInSphere( pTrack, pev->origin, 1024 )) != NULL ) + { + if( !( pTrack->pev->flags & ( FL_CLIENT | FL_MONSTER )) && FClassnameIs( pTrack->pev, "path_track" )) + { + dist = ( pev->origin - pTrack->pev->origin ).Length(); + + if( dist < closest ) + { + closest = dist; + pNearest = pTrack; + } + } + } + + if( !pNearest ) + { + ALERT( at_console, "Can't find a nearby track !!!\n" ); + SetThink( NULL ); + return; + } + + ALERT( at_aiconsole, "TRAIN: %s, Nearest track is %s\n", STRING( pev->targetname ), STRING( pNearest->pev->targetname )); + pTrack = ( (CPathTrack *)pNearest )->GetNext(); + + if( pTrack != NULL ) + { + if( ( pev->origin - pTrack->pev->origin ).Length() < ( pev->origin - pNearest->pev->origin ).Length()) + { + pNearest = pTrack; + } + } + + m_ppath = (CPathTrack *)pNearest; + if( pev->speed != 0 ) + { + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::Next ); + } +} + +void CFuncVehicle::OverrideReset() +{ + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::NearestPath ); +} + +CFuncVehicle *CFuncVehicle::Instance(edict_t *pent) +{ + if( FClassnameIs( pent, "func_vehicle" )) + { + return (CFuncVehicle *)GET_PRIVATE( pent ); + } + + return NULL; +} + +int CFuncVehicle::Classify() +{ + return CLASS_VEHICLE; +} + +void CFuncVehicle::Spawn() +{ + if( pev->speed == 0 ) + m_speed = 165; + else + m_speed = pev->speed; + + if( !m_sounds ) + m_sounds = 3; + + ALERT( at_console, "M_speed = %f\n", m_speed ); + + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + + pev->impulse = (int)m_speed; + m_acceleration = 5; + + m_dir = 1; + m_flTurnStartTime = -1; + + if( FStringNull( pev->target )) + { + ALERT( at_console, "Vehicle with no target" ); + } + + if( pev->spawnflags & SF_TRACKTRAIN_PASSABLE ) + pev->solid = SOLID_NOT; + else + pev->solid = SOLID_BSP; + + pev->movetype = MOVETYPE_PUSH; + + SET_MODEL( ENT( pev ), STRING(pev->model )); + UTIL_SetSize( pev, pev->mins, pev->maxs ); + UTIL_SetOrigin( pev, pev->origin ); + + pev->oldorigin = pev->origin; + + m_controlMins = pev->mins; + m_controlMaxs = pev->maxs; + m_controlMaxs.z += 72; + + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::Find ); + Precache(); +} + +void CFuncVehicle::Restart() +{ + ALERT( at_console, "M_speed = %f\n", m_speed ); + + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + + pev->impulse = (int)m_speed; + m_flTurnStartTime = -1; + m_flUpdateSound = -1; + m_dir = 1; + m_pDriver = NULL; + + if( FStringNull( pev->target )) + { + ALERT( at_console, "Vehicle with no target" ); + } + + UTIL_SetOrigin( pev, pev->oldorigin ); + STOP_SOUND( ENT( pev ), CHAN_STATIC, STRING( pev->noise )); + + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::Find ); +} + +void CFuncVehicle::Precache() +{ + if( m_flVolume == 0.0f ) + m_flVolume = 1.0f; + + switch( m_sounds ) + { + case 1: PRECACHE_SOUND( "plats/vehicle1.wav" );pev->noise = MAKE_STRING( "plats/vehicle1.wav" ); break; + case 2: PRECACHE_SOUND( "plats/vehicle2.wav" );pev->noise = MAKE_STRING( "plats/vehicle2.wav" ); break; + case 3: PRECACHE_SOUND( "plats/vehicle3.wav" );pev->noise = MAKE_STRING( "plats/vehicle3.wav" ); break; + case 4: PRECACHE_SOUND( "plats/vehicle4.wav" );pev->noise = MAKE_STRING( "plats/vehicle4.wav" ); break; + case 5: PRECACHE_SOUND( "plats/vehicle6.wav" );pev->noise = MAKE_STRING( "plats/vehicle6.wav" ); break; + case 6: PRECACHE_SOUND( "plats/vehicle7.wav" );pev->noise = MAKE_STRING( "plats/vehicle7.wav" ); break; + } + + PRECACHE_SOUND( "plats/vehicle_brake1.wav" ); + PRECACHE_SOUND( "plats/vehicle_start1.wav" ); + + m_usAdjustPitch = PRECACHE_EVENT( 1, "events/vehicle.sc" ); +} + +LINK_ENTITY_TO_CLASS( func_vehiclecontrols, CFuncVehicleControls ); + +void CFuncVehicleControls::Find() +{ + edict_t *pTarget = NULL; + + do + { + pTarget = FIND_ENTITY_BY_TARGETNAME( pTarget, STRING( pev->target )); + } + while( !FNullEnt( pTarget ) && !FClassnameIs( pTarget, "func_vehicle" )); + + if( FNullEnt( pTarget )) + { + ALERT( at_console, "No vehicle %s\n", STRING( pev->target )); + return; + } + + CFuncVehicle *pvehicle = CFuncVehicle::Instance( pTarget ); + + pvehicle->SetControls( pev ); + UTIL_Remove( this ); +} + +void CFuncVehicleControls::Spawn() +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + SET_MODEL( ENT( pev ), STRING( pev->model )); + + UTIL_SetSize( pev, pev->mins, pev->maxs ); + UTIL_SetOrigin( pev, pev->origin ); + + SetThink( &CFuncVehicleControls::Find ); + pev->nextthink = gpGlobals->time; +}