halflife25-sdk/dlls/vehicle.cpp
2024-08-20 19:58:27 -07:00

1100 lines
28 KiB
C++

#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "trains.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_STATIC, (char*)STRING(pev->noise), m_flVolume, ATTN_NORM, 0, (int) flpitch);
m_soundPlaying = 1;
}
else
{
/*
// update pitch
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (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_STATIC, (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");
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;
}