diff --git a/dlls/CBasePlayer.cpp b/dlls/CBasePlayer.cpp index 39e7f822..83bcc2e6 100644 --- a/dlls/CBasePlayer.cpp +++ b/dlls/CBasePlayer.cpp @@ -49,6 +49,7 @@ #include "CTripmine.h" #include "skill.h" #include "CBreakable.h" +#include "CFuncVehicle.h" // #define DUCKFIX @@ -1733,6 +1734,11 @@ void CBasePlayer::PlayerUse ( void ) { m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW|TRAIN_OFF; + + CBaseEntity* pTrain = CBaseEntity::Instance(pev->groundentity); + if (pTrain && (pTrain->Classify() == CLASS_VEHICLE)) + ((CFuncVehicle*)pTrain)->m_pDriver = NULL; + return; } else @@ -1744,7 +1750,13 @@ void CBasePlayer::PlayerUse ( void ) m_afPhysicsFlags |= PFLAG_ONTRAIN; m_iTrain = TrainSpeed(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; } } @@ -1883,6 +1895,16 @@ void CBasePlayer::Jump() { pev->velocity = pev->velocity + pev->basevelocity; } + + // JoshA: CS behaviour does this for tracktrain + train as well, + // but let's just do this for func_vehicle to avoid breaking existing content. + // + // If you're standing on a moving train... then add the velocity of the train to yours. + if (pevGround && ( /*(!strcmp( "func_tracktrain", STRING(pevGround->classname))) || + (!strcmp( "func_train", STRING(pevGround->classname))) ) ||*/ + (!strcmp("func_vehicle", STRING(pevGround->classname))))) { + pev->velocity = pev->velocity + pevGround->velocity; + } } // This is a glorious hack to find free space when you've crouched into some solid space @@ -2252,28 +2274,59 @@ 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->Classify() == CLASS_VEHICLE) + ((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 + // and it isn't a func_vehicle. m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW|TRAIN_OFF; + if (pTrain->Classify() == CLASS_VEHICLE) + ((CFuncVehicle*)pTrain)->m_pDriver = NULL; return; } pev->velocity = g_vecZero; vel = 0; - if ( m_afButtonPressed & IN_FORWARD ) + if (pTrain->Classify() == CLASS_VEHICLE) { - vel = 1; - pTrain->Use( this, this, USE_SET, (float)vel ); + if (pev->button & IN_FORWARD) + { + vel = 1; + pTrain->Use(this, this, USE_SET, (float)vel); + } + if (pev->button & IN_BACK) + { + vel = -1; + pTrain->Use(this, this, USE_SET, (float)vel); + } + if (pev->button & IN_MOVELEFT) + { + vel = 20; + pTrain->Use(this, this, USE_SET, (float)vel); + } + if (pev->button & IN_MOVERIGHT) + { + vel = 30; + pTrain->Use(this, this, USE_SET, (float)vel); + } } - else if ( m_afButtonPressed & IN_BACK ) + else { - vel = -1; - pTrain->Use( this, this, USE_SET, (float)vel ); + 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 (vel) diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 87ca9223..33963233 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -250,6 +250,7 @@ set(FUNC_HDR func/CFuncTank.h func/CFuncTrackChange.h func/CFuncTrackTrain.h + func/CFuncVehicle.h func/CFuncWall.h func/CPlatTrigger.h ) @@ -278,6 +279,7 @@ set(FUNC_SRC func/CFuncTrackTrain.cpp func/CFuncTrain.cpp func/CFuncTrainControls.cpp + func/CFuncVehicle.cpp func/CFuncWall.cpp func/CFuncWallToggle.cpp func/CGunTarget.cpp diff --git a/dlls/cbase.h b/dlls/cbase.h index 8fd6f546..42f886e2 100644 --- a/dlls/cbase.h +++ b/dlls/cbase.h @@ -100,6 +100,7 @@ typedef void (CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCall #define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace #define CLASS_ALIEN_RACE_X 14 #define CLASS_ALIEN_RACE_X_PITDRONE 15 // TODO: use this +#define CLASS_VEHICLE 16 #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/func/CFuncVehicle.cpp b/dlls/func/CFuncVehicle.cpp new file mode 100644 index 00000000..21e6b444 --- /dev/null +++ b/dlls/func/CFuncVehicle.cpp @@ -0,0 +1,1101 @@ +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "CFuncVehicle.h" +#include "saverestore.h" +#include "weapons.h" +#include "minmax.h" + +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 ); +} + +// --------------------------------------------------------------------- +// +// Counter-Strike drivable vehicles. +// +// --------------------------------------------------------------------- + +#define VEHICLE_STARTPITCH 60 +#define VEHICLE_MAXPITCH 200 +#define VEHICLE_MAXSPEED 1500 // approx max speed for sound pitch calculation + + +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 ), +}; + +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; + } + 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.1; + 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; + + // Blocker is on-ground on the train + if ( FBitSet( pevOther->flags, FL_ONGROUND ) && VARS(pevOther->groundentity) == pev ) + { + pevOther->velocity = pev->velocity; + return; + + float deltaSpeed = fabs(pev->speed); + if ( deltaSpeed > 50 ) + deltaSpeed = 50; + if ( !pevOther->velocity.z ) + pevOther->velocity.z += deltaSpeed; + return; + } + else + { + pevOther->velocity = (pevOther->origin - pev->origin ).Normalize() * pev->dmg; + pevOther->velocity.z += 300; + + //slow down vehicle + pev->velocity = pev->velocity * 0.85; + } + + ALERT( at_console, "TRAIN(%s): Blocked by %s (dmg:%.2f)\n", STRING(pev->targetname), STRING(pOther->pev->classname), pev->dmg ); + // we can't hurt this thing, so we're not concerned with it + + UTIL_MakeVectors( pev->angles ); + Vector vFrontLeft, vFrontRight, vBackLeft, vBackRight; + Vector forward = (gpGlobals->v_forward * -1) * (m_length/2); //for some stupid reason I have to invert this... + Vector right = (gpGlobals->v_right * -1) * (m_width/2); + + vFrontLeft = pev->origin + forward - right; + vFrontRight = pev->origin + forward + right; + vBackLeft = pev->origin - forward - right; + vBackRight = pev->origin - forward + right; + + Vector vOrigin = pOther->pev->origin; + + float minx, miny, minz; + float maxx, maxy, maxz; + + minx = min(vFrontLeft.x, vBackRight.x); + maxx = max(vFrontLeft.x, vBackRight.x); + miny = min(vFrontLeft.y, vBackRight.y); + maxy = max(vFrontLeft.y, vBackRight.y); + minz = pev->origin.z; + maxz = pev->origin.z + abs(pev->mins.z - pev->maxs.z) * 2; + + // Check if the target is out of the bounds of the vehicle... + if ( ((vOrigin.x < minx) || (vOrigin.x > maxx)) + || ((vOrigin.y < miny) || (vOrigin.y > maxy)) + || ((vOrigin.z < minz) || (vOrigin.z > maxz)) ) + pOther->TakeDamage(pev, pev, 150, DMG_CRUSH); +} + + +void CFuncVehicle :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( useType != USE_SET ) + { + if ( !ShouldToggle( useType, (pev->speed != 0) ) ) + return; + + if ( pev->speed == 0 ) + { + pev->speed = m_speed * m_dir; + + Next(); + } + else + { + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + StopSound(); + SetThink( NULL ); + } + } + else + { + if (value < 10) // It's either forward or backward + { + if ( (value < 0) && (pev->speed > 145) ) + { + StopSound(); + } + + float delta = value; + float flSpeedRatio; + + if (delta > 0) // accelerating + { + flSpeedRatio = (pev->speed / m_speed); + + if (pev->speed < 0) + delta = flSpeedRatio + (0.015/3) + (m_acceleration * 0.0005); + else if (pev->speed < 10) + delta = flSpeedRatio + (0.015/7) + (m_acceleration * 0.0006); + else if (pev->speed < 20) + delta = flSpeedRatio + (0.02/6) + (m_acceleration * 0.0007); + else if (pev->speed < 30) + delta = flSpeedRatio + (0.025/6) + (m_acceleration * 0.0007); + else if (pev->speed < 45) + delta = flSpeedRatio + (0.02/5) + (m_acceleration * 0.0007); + else if (pev->speed < 60) + delta = flSpeedRatio + (0.019/5) + (m_acceleration * 0.0008); + else if (pev->speed < 80) + delta = flSpeedRatio + (0.018/4) + (m_acceleration * 0.0008); + else if (pev->speed < 100) + delta = flSpeedRatio + (0.017/4) + (m_acceleration * 0.0009); + else if (pev->speed < 150) + delta = flSpeedRatio + (0.016/6) + (m_acceleration * 0.0008); + else if (pev->speed < 225) + delta = flSpeedRatio + (0.016/7) + (m_acceleration * 0.0007); + else if (pev->speed < 300) + delta = flSpeedRatio + (0.015/8) + (m_acceleration * 0.0006); + else if (pev->speed < 400) + delta = flSpeedRatio + (0.013/9) + (m_acceleration * 0.0005); + else if (pev->speed < 550) + delta = flSpeedRatio + (0.012/10) + (m_acceleration * 0.0005); + else if (pev->speed < 800) + delta = flSpeedRatio + (0.011/12) + (m_acceleration * 0.0005); + } + else if (delta < 0) // braking + { + flSpeedRatio = pev->speed / m_speed; + + if (flSpeedRatio > 0) + delta = flSpeedRatio - 0.05 / 4; + else if ( (flSpeedRatio <= 0) && (flSpeedRatio > -0.05) ) // Just started accelerating + delta = flSpeedRatio - 0.03 / 4; + else if ( (flSpeedRatio <= 0.05) && (flSpeedRatio > -0.1) ) + delta = flSpeedRatio - 0.04 / 4; + else if ( (flSpeedRatio <= 0.15) && (flSpeedRatio > -0.15) ) + delta = flSpeedRatio - 0.05 / 4; + else if ( (flSpeedRatio <= 0.15) && (flSpeedRatio > -0.22) ) + delta = flSpeedRatio - 0.055 / 4; + else if ( (flSpeedRatio <= 0.22) && (flSpeedRatio > -0.3) ) + delta = flSpeedRatio - 0.07 / 4; + else if (flSpeedRatio <= 0.3) + delta = flSpeedRatio - 0.05 / 4; + } + + if (delta > 1) + delta = 1; + else if (delta < -0.35) + delta = -0.35; + + pev->speed = m_speed * delta; + + Next(); + + m_flAcceleratorDecay = gpGlobals->time + 0.25; + } + else + { + if (m_flCanTurnNow < gpGlobals->time) + { + if (value == 20) // turn left! + { + m_iTurnAngle += 1; + m_flSteeringWheelDecay = gpGlobals->time + 0.075; + if (m_iTurnAngle > 8) + m_iTurnAngle = 8; + } + else if (value == 30) // turn right + { + m_iTurnAngle -= 1; + m_flSteeringWheelDecay = gpGlobals->time + 0.075; + if (m_iTurnAngle < -8) + m_iTurnAngle = -8; + } + + m_flCanTurnNow = gpGlobals->time + 0.05; + } + } + } +} + + +void CFuncVehicle :: StopSound( void ) +{ + // if sound playing, stop it + if (m_soundPlaying && pev->noise) + { + unsigned short us_encode; + unsigned short us_sound = ( ( unsigned short )( m_sounds ) & 0x0007 ) << 12; + + us_encode = us_sound; + + PLAYBACK_EVENT_FULL( FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, + (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, us_encode, 0, 1, 0 ); + } + + m_soundPlaying = 0; +} + +// update pitch based on speed, start sound if not playing +// NOTE: when train goes through transition, m_soundPlaying should go to 0, +// which will cause the looped sound to restart. + +void CFuncVehicle :: UpdateSound( void ) +{ + float flpitch; + + if (!pev->noise) + return; + + flpitch = VEHICLE_STARTPITCH + (abs(pev->speed) * (VEHICLE_MAXPITCH - VEHICLE_STARTPITCH) / VEHICLE_MAXSPEED); + + if (flpitch > VEHICLE_MAXPITCH) + flpitch = VEHICLE_MAXPITCH; + + if (!m_soundPlaying) + { + // play startup sound for train + if (m_sounds < 5) + EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "plats/vehicle_brake1.wav", m_flVolume, ATTN_NORM, 0, 100); + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM, 0, (int) flpitch); + m_soundPlaying = 1; + } + else + { + // update pitch + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH, (int) flpitch); + + /* + // volume 0.0 - 1.0 - 6 bits + // m_sounds 3 bits + // flpitch = 6 bits + // 15 bits total + + unsigned short us_encode; + 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.0 ) & 0x003f ); + + us_encode = us_sound | us_pitch | us_volume; + + PLAYBACK_EVENT_FULL( FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, + (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0 ); + */ + } +} + +void CFuncVehicle :: CheckTurning (void) +{ + float maxspeed; + TraceResult tr; + + //bool bTurnIntoWall = false; + // Check to see if he's trying to turn into a wall + if (m_iTurnAngle < 0) // He's trying to turn right + { + if (pev->speed > 0) + { + UTIL_TraceLine ( m_vFrontRight, m_vFrontRight - (gpGlobals->v_right * 16), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + m_iTurnAngle = 1; + } + } + else if (pev->speed < 0) + { + UTIL_TraceLine ( m_vBackLeft, m_vBackLeft + (gpGlobals->v_right * 16), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + m_iTurnAngle = 1; + } + } + } + else if (m_iTurnAngle > 0) + { + if (pev->speed > 0) + { + UTIL_TraceLine ( m_vFrontLeft, m_vFrontLeft + (gpGlobals->v_right * 16), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + m_iTurnAngle = -1; + } + } + else if (pev->speed < 0) + { + UTIL_TraceLine ( m_vBackRight, m_vBackRight - (gpGlobals->v_right * 16), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + m_iTurnAngle = -1; + } + } + } + + if (pev->speed > 0) + { + if ( abs(m_iTurnAngle) > 4 ) // Doing a hard turn + { + if (m_flTurnStartTime == -1) //just started turning + { + m_flTurnStartTime = gpGlobals->time; + maxspeed = (float)m_speed; + } + else if ( (gpGlobals->time - m_flTurnStartTime) >= 0.0 ) + { + maxspeed = (float)m_speed * 0.98; + } + else if ( (gpGlobals->time - m_flTurnStartTime) > 0.3 ) + { + maxspeed = (float)m_speed * 0.95; + } + else if ( (gpGlobals->time - m_flTurnStartTime) > 0.6 ) + { + maxspeed = (float)m_speed * 0.9; + } + else if ( (gpGlobals->time - m_flTurnStartTime) > 0.8 ) + { + maxspeed = (float)m_speed * 0.8; + } + else if ( (gpGlobals->time - m_flTurnStartTime) > 1 ) + { + maxspeed = (float)m_speed * 0.7; + } + else if ( (gpGlobals->time - m_flTurnStartTime) > 1.2 ) + { + maxspeed = (float)m_speed * 0.5; + } + } + else if ( abs(m_iTurnAngle) > 2 ) + { + m_flTurnStartTime = -1; + maxspeed = (float)m_speed * 0.9; + } + else + { + maxspeed = (float)m_speed; + m_flTurnStartTime = -1; + } + + if (pev->speed > maxspeed) + { + pev->speed -= m_speed / 10; + } + } +} + +void CFuncVehicle :: CollisionDetection (void) +{ + TraceResult tr; + bool bHitSomething = false; + + // Check to see if there's anything in front of us + if (pev->speed < 0) // We're going backwards + { + // Check back left portion of vehicle if we hit a wall + UTIL_TraceLine ( m_vBackLeft, m_vBackLeft + (gpGlobals->v_forward * 16), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + bHitSomething = true; + if ( ( DotProduct(tr.vecPlaneNormal,gpGlobals->v_forward*-1) < 0.7) && (tr.vecPlaneNormal.z < 0.1) ) // slightly vertical wall... skid along it + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + pev->speed = pev->speed * 0.99; + } + else if ( (tr.vecPlaneNormal.z < 0.65) || (tr.fStartSolid) ) + pev->speed = pev->speed * -1; + else + m_vSurfaceNormal = tr.vecPlaneNormal; + + CBaseEntity *pHit = CBaseEntity::Instance(tr.pHit); + if (pHit && (pHit->Classify() == CLASS_VEHICLE)) + ALERT(at_console, "I hit another vehicle\n"); + } + if (bHitSomething == false) + { + UTIL_TraceLine ( m_vBackRight, m_vBackRight + (gpGlobals->v_forward * 16), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + bHitSomething = true; + if ( ( DotProduct(tr.vecPlaneNormal,gpGlobals->v_forward*-1) < 0.7) && (tr.vecPlaneNormal.z < 0.1) ) // slightly vertical wall... skid along it + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + pev->speed = pev->speed * 0.99; + } + else if ( (tr.vecPlaneNormal.z < 0.65) || (tr.fStartSolid) ) + pev->speed = pev->speed * -1; + else + m_vSurfaceNormal = tr.vecPlaneNormal; + } + } + if (bHitSomething == false) + { + UTIL_TraceLine ( m_vBack, m_vBack + (gpGlobals->v_forward * 16), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + if ( ( DotProduct(tr.vecPlaneNormal,gpGlobals->v_forward*-1) < 0.7) && (tr.vecPlaneNormal.z < 0.1) ) // slightly vertical wall... skid along it + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + pev->speed = pev->speed * 0.99; + } + else if ( (tr.vecPlaneNormal.z < 0.65) || (tr.fStartSolid) ) + pev->speed = pev->speed * -1; + else + m_vSurfaceNormal = tr.vecPlaneNormal; + } + } + } + else if (pev->speed > 0) + { + // Check front left portion of vehicle + UTIL_TraceLine ( m_vFrontLeft, m_vFrontLeft - (gpGlobals->v_forward * 16), dont_ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + bHitSomething = true; + if ( ( DotProduct(tr.vecPlaneNormal,gpGlobals->v_forward*-1) > -0.7) && (tr.vecPlaneNormal.z < 0.1) ) // slightly vertical wall... skid along it + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + pev->speed = pev->speed * 0.99; + } + else if ( (tr.vecPlaneNormal.z < 0.65) || (tr.fStartSolid) ) + pev->speed = pev->speed * -1; + else + m_vSurfaceNormal = tr.vecPlaneNormal; + + } + if (bHitSomething == false) + { + UTIL_TraceLine ( m_vFrontRight, m_vFrontRight - (gpGlobals->v_forward * 16), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + bHitSomething = true; + if ( ( DotProduct(tr.vecPlaneNormal,gpGlobals->v_forward*-1) > -0.7) && (tr.vecPlaneNormal.z < 0.1) ) // slightly vertical wall... skid along it + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + pev->speed = pev->speed * 0.99; + } + else if ( (tr.vecPlaneNormal.z < 0.65) || (tr.fStartSolid) ) + pev->speed = pev->speed * -1; + else + m_vSurfaceNormal = tr.vecPlaneNormal; + } + } + if (bHitSomething == false) + { + UTIL_TraceLine ( m_vFront, m_vFront - (gpGlobals->v_forward * 16), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + bHitSomething = true; + if ( ( DotProduct(tr.vecPlaneNormal,gpGlobals->v_forward*-1) > -0.7) && (tr.vecPlaneNormal.z < 0.1) ) // slightly vertical wall... skid along it + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + pev->speed = pev->speed * 0.99; + } + else if ( (tr.vecPlaneNormal.z < 0.65) || (tr.fStartSolid) ) + pev->speed = pev->speed * -1; + else + m_vSurfaceNormal = tr.vecPlaneNormal; + } + } + } +} + +void CFuncVehicle :: TerrainFollowing (void) +{ + TraceResult tr; + + //Get the normal of the surface we're right on top of + UTIL_TraceLine ( pev->origin, pev->origin + Vector(0,0,-1 * (m_height + 48) ), ignore_monsters, dont_ignore_glass, ENT(pev), &tr); + if (tr.flFraction != 1.0) // we hit something + { + m_vSurfaceNormal = tr.vecPlaneNormal; + } + else if (tr.fInWater) + { + m_vSurfaceNormal = Vector(0,0,1); + } + else if (tr.fStartSolid) + { +// ALERT(at_console,"I'm underground\n!"); + } +} + + +void CFuncVehicle :: Next( void ) +{ + Vector vGravityVector = g_vecZero; + + UTIL_MakeVectors( pev->angles ); + + Vector forward = (gpGlobals->v_forward * -1) * (m_length/2); //for some stupid reason I have to invert this... + Vector right = (gpGlobals->v_right * -1) * (m_width/2); + Vector 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; + + + // Check to see if we're hitting something...and alter the vehicles velocity. + CheckTurning (); + + + float time = 0.1; + + if (m_flSteeringWheelDecay < gpGlobals->time) + { + m_flSteeringWheelDecay = gpGlobals->time + 0.1; + if (m_iTurnAngle < 0) + m_iTurnAngle++; + else if (m_iTurnAngle > 0) + m_iTurnAngle--; + } + + if (m_flAcceleratorDecay < gpGlobals->time) + { + m_flAcceleratorDecay = gpGlobals->time + 0.1; + 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 ) + { + m_iTurnAngle = 0; + pev->avelocity = g_vecZero; + pev->velocity = g_vecZero; +// StopSound(); + + SetThink( &CFuncVehicle::Next ); + NextThink( pev->ltime + time, TRUE ); + return; + } + else + { + TerrainFollowing(); + CollisionDetection(); + + if (m_vSurfaceNormal != g_vecZero) + { + m_vVehicleDirection = CrossProduct(m_vSurfaceNormal, (gpGlobals->v_forward)); + m_vVehicleDirection = CrossProduct(m_vSurfaceNormal, m_vVehicleDirection); + + Vector vTargetAngle, vAngle; + float vx,vy; + + vTargetAngle = UTIL_VecToAngles (m_vVehicleDirection); + vTargetAngle.y += 180; + vAngle = pev->angles; + + 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); + + // reset launch time to -1 + m_flLaunchTime = -1; + m_flLastNormalZ = m_vSurfaceNormal.z; + } + else + { + time = 0.1; + if (m_flLaunchTime == -1) + { + m_flLaunchTime = gpGlobals->time; + vGravityVector = Vector (0,0,0); + pev->velocity = pev->velocity * 1.5; + } + else + { + float flAirTime; + + flAirTime = gpGlobals->time - m_flLaunchTime; + + vGravityVector = Vector (0,0, flAirTime * -35); + if (vGravityVector.z < -400) + vGravityVector.z = -400; + } + + m_vVehicleDirection = gpGlobals->v_forward * -1; + } + + Vector temp; + temp = UTIL_VecToAngles (m_vVehicleDirection); + + if (m_flUpdateSound < gpGlobals->time) + { + UpdateSound(); + m_flUpdateSound = gpGlobals->time + 1; + } + + if (m_vSurfaceNormal != g_vecZero) + { + pev->velocity = pev->speed * m_vVehicleDirection.Normalize(); + } + else + { + pev->velocity = pev->velocity + vGravityVector; + } + SetThink( &CFuncVehicle::Next ); + NextThink( pev->ltime + time, TRUE ); + return; + } + +/************************************************/ + +} + + +void CFuncVehicle::DeadEnd( void ) +{ + // Fire the dead-end target if there is one + CPathTrack *pTrack, *pNext; + + pTrack = m_ppath; + + ALERT( at_aiconsole, "TRAIN(%s): Dead end ", STRING(pev->targetname) ); + // Find the dead end path node + // HACKHACK -- This is bugly, but the train can actually stop moving at a different node depending on it's speed + // so we have to traverse the list to it's end. + if ( pTrack ) + { + if ( m_oldSpeed < 0 ) + { + do + { + pNext = pTrack->ValidPath( pTrack->GetPrevious(), TRUE ); + if ( pNext ) + pTrack = pNext; + } while ( pNext ); + } + else + { + do + { + pNext = pTrack->ValidPath( pTrack->GetNext(), TRUE ); + if ( pNext ) + pTrack = pNext; + } while ( pNext ); + } + } + + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + if ( pTrack ) + { + ALERT( at_aiconsole, "at %s\n", STRING(pTrack->pev->targetname) ); + if ( 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 ) +{ + Vector offset = pevTest->origin - pev->origin; + + if ( pev->spawnflags & SF_TRACKTRAIN_NOCONTROL ) + return FALSE; + + // Transform offset into local coordinates + 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 ); + + if ( 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 ) + return TRUE; + + return FALSE; +} + + +void CFuncVehicle :: Find( void ) +{ + 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_console, "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 ); + // The train actually points west + pev->angles.y += 180; + + if ( pev->spawnflags & SF_TRACKTRAIN_NOPITCH ) + pev->angles.x = 0; + UTIL_SetOrigin( pev, nextPos ); + NextThink( pev->ltime + 0.1, FALSE ); + SetThink( &CFuncVehicle::Next ); + pev->speed = m_startSpeed; + + UpdateSound(); +} + + +void CFuncVehicle :: NearestPath( void ) +{ + CBaseEntity *pTrack = NULL; + CBaseEntity *pNearest = NULL; + float dist, closest; + + closest = 1024; + + while ((pTrack = UTIL_FindEntityInSphere( pTrack, pev->origin, 1024 )) != NULL) + { + // filter out non-tracks + 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) ); + // If I'm closer to the next path_track on this path, then it's my real path + pTrack = ((CPathTrack *)pNearest)->GetNext(); + if ( pTrack ) + { + 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.1, FALSE ); + SetThink( &CFuncVehicle::Next ); + } +} + + +void CFuncVehicle::OverrideReset( void ) +{ + NextThink( pev->ltime + 0.1, FALSE ); + SetThink( &CFuncVehicle::NearestPath ); +} + + +CFuncVehicle *CFuncVehicle::Instance( edict_t *pent ) +{ + if ( FClassnameIs( pent, "func_vehicle" ) ) + return (CFuncVehicle *)GET_PRIVATE(pent); + return NULL; +} + +/*QUAKED func_train (0 .5 .8) ? +Trains are moving platforms that players can ride. +The targets origin specifies the min point of the train at each corner. +The train spawns at the first target it is pointing at. +If the train is the target of a button or trigger, it will not begin moving until activated. +speed default 100 +dmg default 2 +sounds +1) ratchet metal +*/ + +int CFuncVehicle::Classify(void) +{ + return CLASS_VEHICLE; +} + +void CFuncVehicle :: Spawn( void ) +{ + if ( pev->speed == 0 ) + m_speed = 165; + else + m_speed = pev->speed; + + if (m_sounds == 0) + 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 = 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 ); + + // Cache off placed origin for train controls + pev->oldorigin = pev->origin; + + m_controlMins = pev->mins; + m_controlMaxs = pev->maxs; + m_controlMaxs.z += 72; +// start trains on the next frame, to make sure their targets have had +// a chance to spawn/activate + NextThink( pev->ltime + 0.1, FALSE ); + SetThink( &CFuncVehicle::Find ); + Precache(); +} + +// GOOSEMAN +void CFuncVehicle :: Restart( void ) +{ + ALERT (at_console, "M_speed = %f\n", m_speed); + + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + pev->impulse = m_speed; + + m_dir = 1; + m_flTurnStartTime = -1; + m_flUpdateSound = -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_VOICE, (char*)STRING(pev->noise)); +// start trains on the next frame, to make sure their targets have had +// a chance to spawn/activate + NextThink( pev->ltime + 0.1, FALSE ); + SetThink( &CFuncVehicle::Find ); +} + + +void CFuncVehicle :: Precache( void ) +{ + if (m_flVolume == 0.0) + m_flVolume = 1.0; + + 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; + default: 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"); + PRECACHE_SOUND("plats/vehicle_ignition.wav"); + + m_usAdjustPitch = PRECACHE_EVENT( 1, "events/vehicle.sc" ); +} + + + +// This class defines the volume of space that the player must stand in to control the vehicle +class CFuncVehicleControls : public CBaseEntity +{ +public: + virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + void Spawn( void ); + void EXPORT Find( void ); +}; +LINK_ENTITY_TO_CLASS( func_vehiclecontrols, CFuncVehicleControls ); + + +void CFuncVehicleControls :: Find( void ) +{ + 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( void ) +{ + 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; +} + diff --git a/dlls/func/CFuncVehicle.h b/dlls/func/CFuncVehicle.h new file mode 100644 index 00000000..d2566517 --- /dev/null +++ b/dlls/func/CFuncVehicle.h @@ -0,0 +1,77 @@ +class CFuncVehicle : public CBaseEntity +{ +public: + void Spawn( void ); + void Precache( void ); + + void Blocked( CBaseEntity *pOther ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void KeyValue( KeyValueData* pkvd ); + + void EXPORT Next( void ); + void EXPORT Find( void ); + void EXPORT NearestPath( void ); + void EXPORT DeadEnd( void ); + + void NextThink( float thinkTime, BOOL alwaysThink ); + int Classify (void); + void CollisionDetection(void); + void TerrainFollowing(void); + void CheckTurning(void); + + void SetTrack( CPathTrack *track ) { m_ppath = track->Nearest(pev->origin); } + void SetControls( entvars_t *pevControls ); + BOOL OnControls( entvars_t *pev ); + + void StopSound ( void ); + void UpdateSound ( void ); + + static CFuncVehicle *Instance( edict_t *pent ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DIRECTIONAL_USE; } + + virtual void OverrideReset( void ); + + 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; //Time at which the vehicle has become airborne + 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; + CBasePlayer *m_pDriver; + + // GOOSEMAN + void Restart(); + +private: + unsigned short m_usAdjustPitch; +}; \ No newline at end of file