openmohaa/code/fgame/vehicle.cpp
2023-08-15 01:27:35 +02:00

6717 lines
120 KiB
C++

/*
===========================================================================
Copyright (C) 2018 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// vehicle.cpp: Script controlled Vehicles.
//
#include "g_local.h"
#include "g_phys.h"
#include "scriptslave.h"
#include "vehicle.h"
#include "player.h"
#include "specialfx.h"
#include "explosion.h"
#include "earthquake.h"
#include "gibs.h"
#include "vehicleturret.h"
#include "scriptexception.h"
#include "debuglines.h"
Event EV_Vehicle_Start
(
"start",
EV_DEFAULT,
NULL,
NULL,
"Initialize the vehicle."
);
Event EV_Vehicle_Enter
(
"enter",
EV_DEFAULT,
"eS",
"vehicle driver_anim",
"Called when someone gets into a vehicle."
);
Event EV_Vehicle_Exit
(
"exit",
EV_DEFAULT,
"e",
"entity",
"Called when driver gets out of the vehicle."
);
Event EV_Vehicle_Drivable
(
"drivable",
EV_DEFAULT,
NULL,
NULL,
"Make the vehicle drivable"
);
Event EV_Vehicle_UnDrivable
(
"undrivable",
EV_DEFAULT,
NULL,
NULL,
"Make the vehicle undrivable"
);
Event EV_Vehicle_Jumpable
(
"canjump",
EV_DEFAULT,
"b",
"jumpable",
"Sets whether or not the vehicle can jump"
);
Event EV_Vehicle_Lock
(
"lock",
EV_DEFAULT,
NULL,
NULL,
"Sets the vehicle to be locked"
);
Event EV_Vehicle_UnLock
(
"unlock",
EV_DEFAULT,
NULL,
NULL,
"Sets the vehicle to be unlocked"
);
Event EV_Vehicle_SeatAnglesOffset
(
"seatangles",
EV_DEFAULT,
"v",
"angles",
"Set the angles offset of the seat"
);
Event EV_Vehicle_SeatOffset
(
"seatoffset",
EV_DEFAULT,
"v",
"offset",
"Set the offset of the seat"
);
Event EV_Vehicle_SetWeapon
(
"setweapon",
EV_DEFAULT,
"s",
"weaponname",
"Set the weapon for the vehicle"
);
Event EV_Vehicle_ShowWeapon
(
"showweapon",
EV_DEFAULT,
NULL,
NULL,
"Set the weapon to be show in the view"
);
Event EV_Vehicle_SetSpeed
(
"vehiclespeed",
EV_DEFAULT,
"f",
"speed",
"Set the speed of the vehicle"
);
Event EV_Vehicle_SetTurnRate
(
"turnrate",
EV_DEFAULT,
"f",
"rate",
"Set the turning rate of the vehicle"
);
Event EV_Vehicle_SteerInPlace
(
"steerinplace",
EV_DEFAULT,
NULL,
NULL,
"Set the vehicle to turn in place"
);
Event EV_Vehicle_Destroyed
(
"vehicledestroyed",
EV_DEFAULT,
NULL,
NULL,
"Driver is dead"
);
Event EV_Vehicle_Mass
(
"vehiclemass",
EV_DEFAULT,
"f",
"weight",
"Sets the mass of the vehicle (backmass = frontmass = mass/2)"
);
Event EV_Vehicle_Front_Mass
(
"front_mass",
EV_DEFAULT,
"f",
"weight",
"Sets the mass of the front of the vehicle"
);
Event EV_Vehicle_Back_Mass
(
"back_mass",
EV_DEFAULT,
"f",
"weight",
"Sets the mass of the back of the vehicle"
);
Event EV_Vehicle_Tread
(
"vehicletread",
EV_DEFAULT,
"f",
"size",
"Sets the size of the wheels"
);
Event EV_Vehicle_Radius
(
"vehicleradius",
EV_DEFAULT,
"f",
"size",
"Sets the radius of the wheels"
);
Event EV_Vehicle_RollingResistance
(
"vehiclerollingresistance",
EV_DEFAULT,
"f",
"size",
"Sets the radius of the wheels"
);
Event EV_Vehicle_Drag
(
"vehicledrag",
EV_DEFAULT,
"f",
"size",
"Sets the Drag Factor"
);
Event EV_Vehicle_Drive
(
"drive",
EV_DEFAULT,
"vffffV",
"position speed acceleration reach_distance look_ahead alternate_position",
"Makes the vehicle drive to position with speed and acceleration until reached_distance close to position"
);
Event EV_Vehicle_DriveNoWait
(
"driveNo",
EV_DEFAULT,
"vfff",
"position speed acceleration reach_distance",
"Makes the vehicle drive to position with speed and acceleration until reached_distance close to position, thread doesn't wait"
);
Event EV_Vehicle_Stop
(
"stop",
EV_DEFAULT,
NULL,
NULL,
"Make the Vehicle Stop Moving... FULL BREAKS!"
);
Event EV_Vehicle_FullStop
(
"fullstop",
EV_DEFAULT,
NULL,
NULL,
"Make the Vehicle Stop Moving... Completely!"
);
Event EV_Vehicle_Init
(
"vehicleinit",
EV_DEFAULT,
NULL,
NULL,
"Initialized the Vehicle as the specified file"
);
Event EV_Vehicle_BouncyCoef
(
"vehiclebouncy",
EV_DEFAULT,
"f",
"bouncycoef",
"Sets the Bouncy Coefficient for the shocks."
);
Event EV_Vehicle_SpringyCoef
(
"vehiclespringy",
EV_DEFAULT,
"f",
"springycoef",
"Sets the Springy Coefficient for the shocks."
);
Event EV_Vehicle_Yaw
(
"vehicleYaw",
EV_DEFAULT,
"fff",
"min max coef",
"Sets the Yaw min and max and the acceleration coefficient for the shocks."
);
Event EV_Vehicle_Roll
(
"vehicleRoll",
EV_DEFAULT,
"fff",
"min max coef",
"Sets the Roll min and max and the acceleration coefficient for the shocks."
);
Event EV_Vehicle_Z
(
"vehicleZ",
EV_DEFAULT,
"fff",
"min max coef",
"Sets the Z min and max and the acceleration coefficient for the shocks."
);
Event EV_Vehicle_QueryFreePassengerSlot
(
"QueryFreePassengerSlot",
EV_DEFAULT,
NULL,
NULL,
"Returns a number that represents the first free passenger slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_QueryPassengerSlotPosition
(
"QueryPassengerSlotPosition",
EV_DEFAULT,
"i",
"slot",
"Returns the position of the specified slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_QueryPassengerSlotStatus
(
"QueryPassengerSlotStatus",
EV_DEFAULT,
"i",
"slot",
"Returns the status of the specified slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_AttachPassengerSlot
(
"AttachPassengerSlot",
EV_DEFAULT,
"ie",
"slot entity",
"Attaches an entity to the specified slot."
);
Event EV_Vehicle_QueryPassengerSlotEntity
(
"QueryPassengerSlotEntity",
EV_DEFAULT,
"i",
"slot",
"Returns an entity at the specified slot.",
EV_RETURN );
Event EV_Vehicle_DetachPassengerSlot
(
"DetachPassengerSlot",
EV_DEFAULT,
"iV",
"slot exit_",
"Detaches an entity to the specified slot."
);
Event EV_Vehicle_QueryFreeDriverSlot
(
"QueryFreeDriverSlot",
EV_DEFAULT,
NULL,
NULL,
"Returns a number that represents the first free driver slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_QueryDriverSlotPosition
(
"QueryDriverSlotPosition",
EV_DEFAULT,
"i",
"slot",
"Returns the position of the specified slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_QueryDriverSlotStatus
(
"QueryDriverSlotStatus",
EV_DEFAULT,
"i",
"slot",
"Returns the status of the specified slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_AttachDriverSlot
(
"AttachDriverSlot",
EV_DEFAULT,
"ie",
"slot entity",
"Attaches an entity to the specified slot."
);
Event EV_Vehicle_QueryDriverSlotEntity
(
"QueryDriverSlotEntity",
EV_DEFAULT,
"i",
"slot",
"Returns an entity at the specified slot.",
EV_RETURN );
Event EV_Vehicle_DetachDriverSlot
(
"DetachDriverSlot",
EV_DEFAULT,
"iV",
"slot exit_",
"Detaches an entity to the specified slot."
);
Event EV_Vehicle_QueryFreeTurretSlot
(
"QueryFreeTurretSlot",
EV_DEFAULT,
NULL,
NULL,
"Returns a number that represents the first free turret slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_QueryTurretSlotPosition
(
"QueryTurretSlotPosition",
EV_DEFAULT,
"i",
"slot",
"Returns the position of the specified slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_QueryTurretSlotStatus
(
"QueryTurretSlotStatus",
EV_DEFAULT,
"i",
"slot",
"Returns the status of the specified slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_AttachTurretSlot
(
"AttachTurretSlot",
EV_DEFAULT,
"ie",
"slot entity",
"Attaches an entity to the specified slot."
);
Event EV_Vehicle_QueryTurretSlotEntity
(
"QueryTurretSlotEntity",
EV_DEFAULT,
"i",
"slot",
"Returns an entity at the specified slot.",
EV_RETURN );
Event EV_Vehicle_DetachTurretSlot
(
"DetachTurretSlot",
EV_DEFAULT,
"iV",
"slot exit_",
"Detaches an entity to the specified slot."
);
Event EV_Vehicle_WheelCorners
(
"VehicleWheelCorners",
EV_DEFAULT,
"vv",
"size offset",
"Sets the wheel trace corners."
);
Event EV_Vehicle_QueryDriverSlotAngles
(
"QueryDriverSlotAngles",
EV_DEFAULT,
"i",
"slot",
"Returns the angles of the specified slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_QueryPassengerSlotAngles
(
"QueryDriverSlotAngles",
EV_DEFAULT,
"i",
"slot",
"Returns the angles of the specified slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_QueryTurretSlotAngles
(
"QueryDriverSlotAngles",
EV_DEFAULT,
"i",
"slot",
"Returns the angles of the specified slot on the vehicle.",
EV_RETURN );
Event EV_Vehicle_AnimationSet
(
"AnimationSet",
EV_DEFAULT,
"s",
"animset",
"Sets the Animation Set to use."
);
Event EV_Vehicle_SoundSet
(
"SoundSet",
EV_DEFAULT,
"s",
"soundset",
"Sets the Sound Set to use."
);
Event EV_Vehicle_SpawnTurret
(
"spawnturret",
EV_DEFAULT,
"is",
"slot tikifile",
"Spawns a turret with the specified model and connects it to the specified slot"
);
Event EV_Vehicle_ModifyDrive
(
"modifydrive",
EV_DEFAULT,
"fff",
"desired_speed acceleration look_ahead",
"Modifys the parameters of the current drive."
);
Event EV_Vehicle_NextDrive
(
"nextdrive",
EV_DEFAULT,
"e",
"next_path",
"appends the specified path to the current path"
);
Event EV_Vehicle_StopAtEnd
(
"stopatend",
EV_DEFAULT,
NULL,
NULL,
"Makes the vehicle slow down to a complete stop at the end of the path."
);
Event EV_Vehicle_LockMovement
(
"lockmovement",
EV_DEFAULT,
NULL,
NULL,
"The Vehicle cannot move."
);
Event EV_Vehicle_UnlockMovement
(
"unlockmovement",
EV_DEFAULT,
NULL,
NULL,
"The Vehicle can move again."
);
Event EV_Vehicle_RemoveOnDeath
(
"removeondeath",
EV_DEFAULT,
"i",
"removeondeath",
"If set to a non-zero value, vehicles will not be removed when they die"
);
Event EV_Vehicle_SetExplosionModel
(
"explosionmodel",
EV_DEFAULT,
"s",
"model",
"Sets the TIKI to call when the vehicle dies."
);
Event EV_Vehicle_SetCollisionEntity
(
"setcollisionentity",
EV_DEFAULT,
"e",
"entity",
"Sets the Collision Entity."
);
Event EV_Vehicle_CollisionEntitySetter
(
"collisionent",
EV_DEFAULT,
"e",
"entity",
"Gets the Collision Entity",
EV_SETTER );
Event EV_Vehicle_CollisionEntityGetter
(
"collisionent",
EV_DEFAULT,
NULL,
NULL,
"Gets the Collision Entity",
EV_GETTER );
Event EV_Vehicle_SetSoundParameters
(
"setsoundparameters",
EV_DEFAULT,
"ffff",
"min_speed min_pitch max_speed max_pitch",
"Sets the Sound parameters for this vehicle"
);
Event EV_Vehicle_SetVolumeParameters
(
"setvolumeparameters",
EV_DEFAULT,
"ffff",
"min_speed min_volume max_speed max_volume",
"Sets the Volume parameters for this vehicle"
);
Event EV_Vehicle_Skidding
(
"skidding",
EV_DEFAULT,
"i",
"on_off",
"Makes the vehicle skid around corners."
);
Event EV_Vehicle_ContinueSkidding
(
"_continue",
EV_DEFAULT,
NULL,
NULL,
"Continues the skidding animation of a vehicle."
);
Event EV_Vehicle_VehicleAnim
(
"vehicleanim",
EV_DEFAULT,
"sF",
"anim_name weight",
"Sets an animation to use in the LD Animation slot. \nWeight defaults to 1.0"
);
Event EV_Vehicle_VehicleAnimDone
(
"_vehicleanimdone",
EV_DEFAULT,
NULL,
NULL,
"For Internal Use Only"
);
cvar_t *g_showvehiclemovedebug;
cvar_t *g_showvehicleentrypoints;
cvar_t *g_showvehicleslotpoints;
cvar_t *g_showvehiclepath;
CLASS_DECLARATION( Animate, VehicleBase, NULL )
{
{ NULL, NULL }
};
VehicleBase::VehicleBase()
{
offset = "0 0 0";
if( LoadingSavegame )
{
// Archive function will setup all necessary data
return;
}
takedamage = DAMAGE_NO;
edict->s.renderfx &= ~RF_DONTDRAW;
edict->r.svFlags &= ~SVF_NOCLIENT;
//
// rotate the mins and maxs for the model
//
setSize( mins, maxs );
vlink = NULL;
offset = vec_zero;
PostEvent( EV_BecomeNonSolid, EV_POSTSPAWN );
}
CLASS_DECLARATION( VehicleBase, Vehicle, "script_vehicle" )
{
{ &EV_Blocked, &Vehicle::VehicleBlocked },
{ &EV_Touch, &Vehicle::VehicleTouched },
{ &EV_Use, &Vehicle::DriverUse },
{ &EV_Vehicle_Start, &Vehicle::VehicleStart },
{ &EV_Vehicle_Drivable, &Vehicle::Drivable },
{ &EV_Vehicle_UnDrivable, &Vehicle::UnDrivable },
{ &EV_Vehicle_Jumpable, &Vehicle::Jumpable },
{ &EV_Vehicle_SeatAnglesOffset, &Vehicle::SeatAnglesOffset },
{ &EV_Vehicle_SeatOffset, &Vehicle::SeatOffset },
{ &EV_Vehicle_Lock, &Vehicle::Lock },
{ &EV_Vehicle_UnLock, &Vehicle::UnLock },
{ &EV_Vehicle_SetWeapon, &Vehicle::SetWeapon },
{ &EV_Vehicle_SetSpeed, &Vehicle::SetSpeed },
{ &EV_Vehicle_SetTurnRate, &Vehicle::SetTurnRate },
{ &EV_Vehicle_SteerInPlace, &Vehicle::SteerInPlace },
{ &EV_Vehicle_ShowWeapon, &Vehicle::ShowWeaponEvent },
{ &EV_Damage, &Vehicle::EventDamage },
{ &EV_Vehicle_Destroyed, &Vehicle::VehicleDestroyed },
{ &EV_Vehicle_Mass, &Vehicle::SetMass },
{ &EV_Vehicle_Front_Mass, &Vehicle::SetFrontMass },
{ &EV_Vehicle_Back_Mass, &Vehicle::SetBackMass },
{ &EV_Vehicle_Tread, &Vehicle::SetTread },
{ &EV_Vehicle_RollingResistance, &Vehicle::SetRollingResistance },
{ &EV_Vehicle_Drag, &Vehicle::SetDrag },
{ &EV_Vehicle_Drive, &Vehicle::EventDrive },
{ &EV_Vehicle_DriveNoWait, &Vehicle::EventDriveNoWait },
{ &EV_Vehicle_Stop, &Vehicle::EventStop },
{ &EV_Vehicle_FullStop, &Vehicle::EventFullStop },
{ &EV_Vehicle_BouncyCoef, &Vehicle::BouncyCoef },
{ &EV_Vehicle_SpringyCoef, &Vehicle::SpringyCoef },
{ &EV_Vehicle_Yaw, &Vehicle::GetYaw },
{ &EV_Vehicle_Roll, &Vehicle::RollMinMax },
{ &EV_Vehicle_Z, &Vehicle::ZMinMax },
{ &EV_Vehicle_QueryFreePassengerSlot, &Vehicle::QueryFreePassengerSlot },
{ &EV_Vehicle_QueryFreeDriverSlot, &Vehicle::QueryFreeDriverSlot },
{ &EV_Vehicle_QueryFreeTurretSlot, &Vehicle::QueryFreeTurretSlot },
{ &EV_Vehicle_QueryPassengerSlotPosition, &Vehicle::QueryPassengerSlotPosition },
{ &EV_Vehicle_QueryDriverSlotPosition, &Vehicle::QueryDriverSlotPosition },
{ &EV_Vehicle_QueryTurretSlotPosition, &Vehicle::QueryTurretSlotPosition },
{ &EV_Vehicle_QueryPassengerSlotStatus, &Vehicle::QueryPassengerSlotStatus },
{ &EV_Vehicle_QueryDriverSlotStatus, &Vehicle::QueryDriverSlotStatus },
{ &EV_Vehicle_QueryTurretSlotStatus, &Vehicle::QueryTurretSlotStatus },
{ &EV_Vehicle_QueryPassengerSlotEntity, &Vehicle::QueryPassengerSlotEntity },
{ &EV_Vehicle_QueryDriverSlotEntity, &Vehicle::QueryDriverSlotEntity },
{ &EV_Vehicle_QueryTurretSlotEntity, &Vehicle::QueryTurretSlotEntity },
{ &EV_Vehicle_QueryDriverSlotAngles, &Vehicle::QueryDriverSlotAngles },
{ &EV_Vehicle_QueryPassengerSlotAngles, &Vehicle::QueryPassengerSlotAngles },
{ &EV_Vehicle_QueryTurretSlotAngles, &Vehicle::QueryTurretSlotAngles },
{ &EV_Vehicle_AttachPassengerSlot, &Vehicle::AttachPassengerSlot },
{ &EV_Vehicle_AttachDriverSlot, &Vehicle::AttachDriverSlot },
{ &EV_Vehicle_AttachTurretSlot, &Vehicle::AttachTurretSlot },
{ &EV_Vehicle_DetachPassengerSlot, &Vehicle::DetachPassengerSlot },
{ &EV_Vehicle_DetachDriverSlot, &Vehicle::DetachDriverSlot },
{ &EV_Vehicle_DetachTurretSlot, &Vehicle::DetachTurretSlot },
{ &EV_Vehicle_WheelCorners, &Vehicle::SetWheelCorners },
{ &EV_Vehicle_AnimationSet, &Vehicle::SetAnimationSet },
{ &EV_Vehicle_SoundSet, &Vehicle::SetSoundSet },
{ &EV_Vehicle_ModifyDrive, &Vehicle::EventModifyDrive },
{ &EV_Model, &Vehicle::EventModel },
{ &EV_Vehicle_NextDrive, &Vehicle::EventNextDrive },
{ &EV_Vehicle_LockMovement, &Vehicle::EventLockMovement },
{ &EV_Vehicle_UnlockMovement, &Vehicle::EventUnlockMovement },
{ &EV_Vehicle_RemoveOnDeath, &Vehicle::EventRemoveOnDeath },
{ &EV_Vehicle_SetExplosionModel, &Vehicle::EventSetExplosionModel },
{ &EV_Vehicle_SetCollisionEntity, &Vehicle::EventSetCollisionModel },
{ &EV_Vehicle_SetSoundParameters, &Vehicle::EventSetSoundParameters },
{ &EV_Vehicle_SetVolumeParameters, &Vehicle::EventSetVolumeParameters },
{ &EV_Vehicle_StopAtEnd, &Vehicle::EventStopAtEnd },
{ &EV_Vehicle_Skidding, &Vehicle::EventSkidding },
{ &EV_Vehicle_ContinueSkidding, &Vehicle::EventContinueSkidding },
{ &EV_Vehicle_CollisionEntitySetter, &Vehicle::EventSetCollisionModel },
{ &EV_Vehicle_CollisionEntityGetter, &Vehicle::EventGetCollisionModel },
{ &EV_Vehicle_VehicleAnim, &Vehicle::EventVehicleAnim },
{ &EV_Vehicle_VehicleAnimDone, &Vehicle::EventVehicleAnimDone },
{ NULL, NULL }
};
/*
====================
Vehicle::Vehicle
====================
*/
Vehicle::Vehicle()
{
entflags |= EF_VEHICLE;
g_showvehiclemovedebug = gi.Cvar_Get( "g_showvehiclemovedebug", "0", 0 );
g_showvehicleentrypoints = gi.Cvar_Get( "g_showvehicleentrypoints", "0", 0 );
g_showvehicleslotpoints = gi.Cvar_Get( "g_showvehicleslotpoints", "0", 0 );
g_showvehiclepath = gi.Cvar_Get( "g_showvehiclepath", "0", 0 );
if( LoadingSavegame )
{
// Archive function will setup all necessary data
return;
}
edict->s.eType = ET_VEHICLE;
takedamage = DAMAGE_YES;
seatangles = vec_zero;
driveroffset = Vector( 0, 0, 50 );
seatoffset = vec_zero;
currentspeed = 0;
turnangle = 0;
turnimpulse = 0;
moveimpulse = 0;
jumpimpulse = 0;
conesize = 75;
hasweapon = false;
locked = true;
steerinplace = false;
drivable = false;
jumpable = false;
showweapon = false;
hasweapon = false;
flags |= FL_TOUCH_TRIGGERS | FL_POSTTHINK | FL_THINK | FL_DIE_EXPLODE;
// touch triggers by default
gravity = 1;
mass = size.length() * 10;
health = 1000;
speed = 1200;
maxturnrate = 60.0f;
m_vAngles = angles;
prev_acceleration = vec_zero;
real_acceleration = vec_zero;
prev_origin = origin;
real_velocity = vec_zero;
prev_velocity = velocity;
m_vPrevNormal = vec_zero;
prev_moveimpulse = 0;
m_iFrameCtr = 0;
m_bFrontSlipping = qfalse;
m_bBackSlipping = qfalse;
m_vAngularVelocity = vec_zero;
m_vAngularAcceleration = vec_zero;
m_sMoveGrid = new cMoveGrid( 3, 3, 1 );
m_bAutoPilot = qfalse;
m_fIdealSpeed = 0;
m_fIdealAccel = 0;
m_fIdealDistance = 100;
m_vOriginOffset = vec_zero;
m_vOriginOffset2 = vec_zero;
m_vAnglesOffset = vec_zero;
m_fBouncyCoef = 0.2f;
m_fSpringyCoef = 0.8f;
m_fYawMin = -10.0f;
m_fYawMax = 10.0f;
m_fYawCoef = 0.1f;
m_fRollMin = -10.0f;
m_fRollMax = 10.0f;
m_fRollCoef = 0.1f;
m_fZMin = -10.0f;
m_fZMax = 10.0f;
m_fZCoef = 0.1f;
v_angle = vec_zero;
m_pCurPath = NULL;
m_bThinkCalled = qfalse;
m_iCurNode = 0;
m_pAlternatePath = NULL;
m_pNextPath = NULL;
m_iNextPathStartNode = 0;
vs.hit_obstacle = qfalse;
m_iAlternateNode = 0;
m_fLookAhead = 0;
m_fShaderOffset = 0;
vs.groundTrace.fraction = 1.0f;
edict->clipmask = MASK_VEHICLE;
m_fLeftForce = 0;
m_fRightForce = 0;
m_fForwardForce = 0;
m_fBackForce = 0;
m_fUpForce = 0;
m_fDownForce = 0;
edict->s.eFlags |= EF_LINKANGLES;
m_bMovementLocked = qfalse;
m_bRemoveOnDeath = qfalse;
m_sExplosionModel = "fx/fx_explosion.tik";
m_pCollisionEntity = NULL;
m_fSoundMinPitch = 0.95f;
m_fSoundMinSpeed = 0;
m_fSoundMaxPitch = 1.0f;
m_fSoundMaxSpeed = 200.0f;
m_fVolumeMinPitch = 1.0f;
m_fVolumeMinSpeed = 0;
m_fVolumeMaxPitch = 1.5f;
m_fVolumeMaxSpeed = 200.0f;
m_eSoundState = ST_OFF;
m_fNextSoundState = level.time;
m_pVehicleSoundEntities[ 0 ] = NULL;
m_pVehicleSoundEntities[ 1 ] = NULL;
m_pVehicleSoundEntities[ 2 ] = NULL;
m_pVehicleSoundEntities[ 3 ] = NULL;
m_bStopEnabled = qfalse;
m_bEnableSkidding = qfalse;
m_vSkidOrigin = vec_zero;
m_fSkidLeftForce = 0;
m_fSkidRightForce = 0;
m_sAnimationSet = "";
m_sSoundSet = "";
m_iLastTiresUpdate = -1;
m_vTireEnd[ 0 ] = origin;
m_bTireHit[ 0 ] = false;
m_vTireEnd[ 1 ] = origin;
m_bTireHit[ 1 ] = false;
m_vTireEnd[ 2 ] = origin;
m_bTireHit[ 2 ] = false;
m_vTireEnd[ 3 ] = origin;
m_bTireHit[ 3 ] = false;
ResetSlots();
PostEvent( EV_Vehicle_Start, EV_POSTSPAWN );
}
/*
====================
Vehicle::~Vehicle
====================
*/
Vehicle::~Vehicle()
{
for( int i = 0; i < MAX_SOUND_ENTITIES; i++ )
{
if( m_pVehicleSoundEntities[ i ] )
m_pVehicleSoundEntities[ i ]->PostEvent( EV_Remove, EV_LINKDOORS );
}
if( m_pCollisionEntity )
m_pCollisionEntity->ProcessEvent( EV_Remove );
entflags &= ~EF_VEHICLE;
}
/*
====================
Vehicle::VehicleStart
====================
*/
void Vehicle::VehicleStart
(
Event *ev
)
{
Entity *ent;
VehicleBase *last;
Vector drivemins, drivemaxs;
float max;
orientation_t orient;
// become solid
setSolidType( SOLID_BBOX );
edict->r.contents = CONTENTS_BBOX;
last = this;
setLocalOrigin( localorigin + Vector( 0.0f, 0.0f, 30.0f ) );
angles = m_vAngles;
for( ent = G_NextEntity( NULL ); ent != NULL; ent = G_NextEntity( ent ) )
{
if( ( ent != this ) && ( ent->isSubclassOf( VehicleBase ) ) )
{
if( ( ent->absmax.x >= absmin.x ) && ( ent->absmax.y >= absmin.y ) && ( ent->absmax.z >= absmin.z ) &&
( ent->absmin.x <= absmax.x ) && ( ent->absmin.y <= absmax.y ) && ( ent->absmin.z <= absmax.z ) )
{
last->vlink = ( VehicleBase * )ent;
last = ( VehicleBase * )ent;
last->offset = last->origin - origin;
last->offset = getLocalVector( last->offset );
last->edict->s.scale *= edict->s.scale;
}
}
}
last->vlink = NULL;
OpenSlotsByModel();
//
// get the seat offset
//
if( GetRawTag( "seat", &orient ) )
{
driveroffset = Vector( orient.origin );
}
driveroffset += seatoffset * edict->s.scale;
SetDriverAngles( angles + seatangles );
max_health = health;
//
// calculate drive mins and maxs
//
max = 0;
if( fabs( mins[ 0 ] ) > max )
max = fabs( mins[ 0 ] );
if( fabs( maxs[ 0 ] ) > max )
max = fabs( maxs[ 0 ] );
if( fabs( mins[ 1 ] ) > max )
max = fabs( mins[ 1 ] );
if( fabs( maxs[ 1 ] ) > max )
max = fabs( maxs[ 1 ] );
drivemins = Vector( -max, -max, mins[ 2 ] ) * edict->s.scale;
drivemaxs = Vector( max, max, maxs[ 2 ] ) * edict->s.scale;
last_origin = origin;
link();
}
/*
====================
Vehicle::Drivable
====================
*/
void Vehicle::Drivable
(
Event *ev
)
{
setMoveType( MOVETYPE_VEHICLE );
drivable = true;
}
/*
====================
Vehicle::UnDrivable
====================
*/
void Vehicle::UnDrivable
(
Event *ev
)
{
setMoveType( MOVETYPE_PUSH );
drivable = false;
}
/*
====================
Vehicle::Jumpable
====================
*/
void Vehicle::Jumpable
(
Event *ev
)
{
jumpable = true;
}
/*
====================
Vehicle::Lock
====================
*/
void Vehicle::Lock
(
Event *ev
)
{
Lock();
}
/*
====================
Vehicle::UnLock
====================
*/
void Vehicle::UnLock
(
Event *ev
)
{
UnLock();
}
/*
====================
Vehicle::SteerInPlace
====================
*/
void Vehicle::SteerInPlace
(
Event *ev
)
{
steerinplace = true;
}
/*
====================
Vehicle::SeatAnglesOffset
====================
*/
void Vehicle::SeatAnglesOffset
(
Event *ev
)
{
seatangles = ev->GetVector( 1 );
}
/*
====================
Vehicle::SeatOffset
====================
*/
void Vehicle::SeatOffset
(
Event *ev
)
{
seatoffset = ev->GetVector( 1 );
}
/*
====================
Vehicle::SetWeapon
====================
*/
void Vehicle::SetWeapon
(
Event *ev
)
{
showweapon = true;
hasweapon = true;
weaponName = ev->GetString( 1 );
}
/*
====================
Vehicle::ShowWeaponEvent
====================
*/
void Vehicle::ShowWeaponEvent
(
Event *ev
)
{
showweapon = true;
}
/*
====================
Vehicle::HasWeapon
====================
*/
qboolean Vehicle::HasWeapon
(
void
)
{
return hasweapon;
}
/*
====================
Vehicle::ShowWeapon
====================
*/
qboolean Vehicle::ShowWeapon
(
void
)
{
return showweapon;
}
/*
====================
Vehicle::SetDriverAngles
====================
*/
void Vehicle::SetDriverAngles
(
Vector angles
)
{
int i;
if( !driver.ent )
return;
for( i = 0; i < 3; i++ )
{
driver.ent->client->ps.delta_angles[ i ] = ANGLE2SHORT( angles[ i ] - driver.ent->client->cmd_angles[ i ] );
}
}
/*
====================
Vehicle::CheckWater
====================
*/
void Vehicle::CheckWater
(
void
)
{
Vector point;
int cont;
int sample1;
int sample2;
VehicleBase *v;
unlink();
v = this;
while( v->vlink )
{
v = v->vlink;
v->unlink();
}
if( driver.ent )
{
driver.ent->unlink();
}
//
// get waterlevel
//
waterlevel = 0;
watertype = 0;
sample2 = maxs[ 2 ] - mins[ 2 ];
sample1 = sample2 / 2;
point = origin;
point[ 2 ] += mins[ 2 ];
cont = gi.pointcontents( point, 0 );
if( cont & MASK_WATER )
{
watertype = cont;
waterlevel = 1;
point[ 2 ] = origin[ 2 ] + mins[ 2 ] + sample1;
cont = gi.pointcontents( point, 0 );
if( cont & MASK_WATER )
{
waterlevel = 2;
point[ 2 ] = origin[ 2 ] + mins[ 2 ] + sample2;
cont = gi.pointcontents( point, 0 );
if( cont & MASK_WATER )
{
waterlevel = 3;
}
}
}
link();
v = this;
while( v->vlink )
{
v = v->vlink;
v->link();
}
if( driver.ent )
{
driver.ent->link();
driver.ent->waterlevel = waterlevel;
driver.ent->watertype = watertype;
}
}
/*
====================
Vehicle::WorldEffects
====================
*/
void Vehicle::WorldEffects
(
void
)
{
//
// Check for earthquakes
//
if( groundentity && ( level.earthquake_magnitude > 0.0f ) )
{
velocity += Vector
(
level.earthquake_magnitude * EARTHQUAKE_STRENGTH * G_CRandom(),
level.earthquake_magnitude * EARTHQUAKE_STRENGTH * G_CRandom(),
level.earthquake_magnitude * 1.5f * G_Random()
);
}
//
// check for lava
//
if( watertype & CONTENTS_LAVA )
{
Damage( world, world, 20 * waterlevel, origin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_LAVA );
}
}
/*
====================
Vehicle::DriverUse
====================
*/
void Vehicle::DriverUse
(
Event *ev
)
{
int slot;
Vector pos;
Vector dist;
float min_length = 1e30f;
int min_slot = 0;
int min_type = -1;
Entity *ent;
Vector vExitAngles;
bool bHasExitAngles;
Vector vExitPosition;
ent = ev->GetEntity( 1 );
if( locked )
{
return;
}
if( ev->NumArgs() == 2 )
{
if( ev->IsVectorAt( 2 ) )
{
vExitPosition = ev->GetVector( 2 );
}
else if( ev->IsEntityAt( 2 ) )
{
Entity *pEnt = ev->GetEntity( 2 );
bHasExitAngles = true;
vExitAngles = pEnt->angles;
vExitPosition = pEnt->origin;
}
else if( ev->IsSimpleEntityAt( 2 ) )
{
SimpleEntity *pEnt = ev->GetSimpleEntity( 2 );
bHasExitAngles = true;
vExitAngles = pEnt->angles;
vExitPosition = pEnt->origin;
}
}
slot = FindDriverSlotByEntity( ent );
if( slot >= 0 )
{
DetachDriverSlot( slot, vec_zero, NULL );
if( ent->IsSubclassOfVehicleTank() && Turrets[ 0 ].ent->IsSubclassOfVehicleTurretGun() )
{
VehicleTurretGun *pTurret = ( VehicleTurretGun * )Turrets[ 0 ].ent.Pointer();
pTurret->m_bUseRemoteControl = false;
pTurret->m_pRemoteOwner = NULL;
}
return;
}
slot = FindPassengerSlotByEntity( ent );
if( slot >= 0 )
{
DetachPassengerSlot( slot, vec_zero, NULL );
return;
}
if( ent->IsSubclassOfWeapon() )
{
slot = FindTurretSlotByEntity( ent );
if( slot >= 0 )
{
DetachTurretSlot( slot, vec_zero, NULL );
}
}
else if( ent->IsSubclassOfPlayer() )
{
Player *player = ( Player * )ent;
if( player->m_pTurret )
{
slot = FindTurretSlotByEntity( player->m_pTurret );
if( slot >= 0 )
{
if( bHasExitAngles )
{
AttachTurretSlot( slot, player->m_pTurret, vExitPosition, &vExitAngles );
}
else
{
AttachTurretSlot( slot, player->m_pTurret, vExitPosition, NULL );
}
player->m_pVehicle = NULL;
return;
}
}
}
// Check for passengers slots
for( slot = 0; slot < MAX_PASSENGERS; slot++ )
{
if( !( Passengers[ 0 ].flags & SLOT_FREE ) )
{
continue;
}
QueryPassengerSlotPosition( slot, ( float * )&pos );
dist = pos - ent->origin;
if( dist.length() < min_length )
{
min_length = dist.length();
min_type = 0;
min_slot = slot;
}
}
// Check for turrets slots
if( ent->IsSubclassOfWeapon() )
{
for( slot = 0; slot < MAX_TURRETS; slot++ )
{
if( !( Turrets[ 0 ].flags & SLOT_FREE ) )
{
continue;
}
QueryTurretSlotPosition( slot, ( float * )&pos );
dist = pos - ent->origin;
if( dist.length() < min_length )
{
min_length = dist.length();
min_type = 1;
min_slot = slot;
}
}
}
else
{
for( slot = 0; slot < MAX_TURRETS; slot++ )
{
if( !( Turrets[ 0 ].flags & SLOT_BUSY ) )
{
continue;
}
QueryTurretSlotPosition( slot, ( float * )&pos );
dist = pos - ent->origin;
if( dist.length() < min_length )
{
min_length = dist.length();
min_type = 1;
min_slot = slot;
}
}
}
slot = 0;
// Check for driver(s) slot(s)
if( driver.flags & SLOT_FREE )
{
QueryDriverSlotPosition( slot, ( float * )&pos );
dist = pos - ent->origin;
if( dist.length() < min_length )
{
min_length = dist.length();
min_type = 2;
min_slot = slot;
}
}
switch( min_type )
{
case 0:
AttachPassengerSlot( min_slot, ent, vec_zero );
break;
case 1:
AttachTurretSlot( min_slot, ent, vec_zero, NULL );
break;
case 2:
AttachDriverSlot( min_slot, ent, vec_zero );
break;
}
}
/*
====================
Vehicle::Drive
====================
*/
qboolean Vehicle::Drive
(
usercmd_t *ucmd
)
{
Vector i, j, k;
i = velocity;
VectorNormalize( i );
if( !driver.ent || !driver.ent->isClient() )
{
return false;
}
if( !drivable )
{
driver.ent->client->ps.pm_flags |= PMF_FROZEN;
ucmd->forwardmove = 0;
ucmd->rightmove = 0;
ucmd->upmove = 0;
return false;
}
driver.ent->client->ps.pm_flags |= PMF_NO_PREDICTION;
moveimpulse = ( ( float )ucmd->forwardmove ) * ( VectorLength( i ) + 1.0 );
m_bIsBraking = ucmd->forwardmove >> 31;
m_fAccelerator += ( ( float )ucmd->forwardmove ) * 0.005;
if( m_fAccelerator < 0.0 )
m_fAccelerator = 0.0;
else if( m_fAccelerator > 1.0 )
m_fAccelerator = 1.0;
turnimpulse = ( ( float )-ucmd->rightmove );
jumpimpulse = ( ( float )ucmd->upmove * gravity ) / 350;
if( ( jumpimpulse < 0 ) || ( !jumpable ) )
jumpimpulse = 0;
turnimpulse += 8 * angledist( SHORT2ANGLE( ucmd->angles[ 1 ] ) - driver.ent->client->cmd_angles[ 1 ] );
return true;
}
/*
====================
Vehicle::Think
====================
*/
void Vehicle::Think
(
void
)
{
flags |= FL_POSTTHINK;
}
/*
====================
Vehicle::Postthink
====================
*/
void Vehicle::Postthink
(
void
)
{
float turn;
Vector i;
Vector j;
Vector k;
Vector temp;
Vector roll;
Vector acceleration;
Vector atmp;
Vector atmp2;
Vector aup;
VehicleBase *v;
VehicleBase *last;
float drivespeed;
Vector primal_angles = angles;
Vector primal_origin = origin;
Vector vTmp;
Vector vAddedAngles;
Vector n_angles;
orientation_t orient;
if( !g_vehicle->integer )
{
return;
}
if( m_pCollisionEntity )
{
setSolidType( SOLID_NOT );
m_pCollisionEntity->Solid();
}
prev_velocity = velocity;
FactorOutAnglesOffset();
FactorOutOriginOffset();
if( m_vOldMins != vec_zero && m_vOldMaxs != vec_zero )
{
mins = m_vOldMins;
maxs = m_vOldMaxs;
}
else
{
if( mins != vec_zero || maxs != vec_zero )
{
m_vOldMins = mins;
m_vOldMaxs = maxs;
}
}
SetSlotsNonSolid();
MoveVehicle();
SetSlotsSolid();
m_bThinkCalled = true;
if( !m_bAutoPilot )
{
if( !driver.ent || !driver.ent->IsSubclassOfPlayer() )
{
acceleration = velocity;
acceleration.z = 0;
if( acceleration.length() < 0.1f )
{
velocity = vec_zero;
}
}
moveimpulse *= 0.825f;
turnimpulse *= 0.825f;
}
else
{
AutoPilot();
}
currentspeed = moveimpulse;
turnangle = turnangle * 0.25f + turnimpulse;
if( turnangle > maxturnrate )
{
turnangle = maxturnrate;
}
else if( turnangle < -maxturnrate )
{
turnangle = -maxturnrate;
}
if( level.inttime <= 1200 )
{
prev_origin = origin;
}
else
{
real_velocity = origin - prev_origin;
prev_origin = origin;
prev_acceleration = real_acceleration;
real_acceleration = prev_velocity - real_velocity;
prev_velocity = real_velocity;
n_angles = real_acceleration - prev_acceleration;
}
UpdateSkidAngle();
UpdateBones();
UpdateShaderOffset();
UpdateTires();
UpdateNormals();
angles.AngleVectorsLeft( &i, &j, &k );
turn = level.frametime * turnangle;
velocity[ 0 ] *= 0.925f;
velocity[ 1 ] *= 0.925f;
velocity = Vector( orientation[ 0 ] ) * currentspeed;
drivespeed = velocity * Vector( orientation[ 0 ] );
if( drivespeed > speed )
{
drivespeed = speed;
}
velocity = Vector( orientation[ 0 ] ) * drivespeed;
velocity.z = drivespeed * jumpimpulse;
avelocity *= 0.05f;
if( steerinplace && drivespeed < 350.0f )
{
drivespeed = 350.0f;
}
avelocity[ 1 ] += turn * drivespeed;
angles += avelocity * level.frametime;
UpdateSound();
CalculateAnglesOffset( n_angles );
CalculateOriginOffset();
last_origin = origin;
vAddedAngles = angles - primal_angles;
if( vAddedAngles[ 0 ] * level.frametime > -1.0f || vAddedAngles[ 0 ] * level.frametime < 1.0f )
{
vAddedAngles[ 0 ] = 0.0f;
}
if( vAddedAngles[ 1 ] * level.frametime > -1.0f || vAddedAngles[ 1 ] * level.frametime < 1.0f )
{
vAddedAngles[ 1 ] = 0.0f;
}
if( vAddedAngles[ 2 ] * level.frametime > -1.0f || vAddedAngles[ 2 ] * level.frametime < 1.0f )
{
vAddedAngles[ 2 ] = 0.0f;
}
avelocity = vAddedAngles;
FactorInOriginOffset();
FactorInAnglesOffset( &vAddedAngles );
CalculateAnimationData( vAddedAngles, m_vOriginOffset + m_vOriginOffset2 );
if( m_pCollisionEntity )
{
n_angles = angles - m_pCollisionEntity->angles;
n_angles[ 0 ] = angledist( n_angles[ 0 ] );
n_angles[ 1 ] = angledist( n_angles[ 1 ] );
n_angles[ 2 ] = angledist( n_angles[ 2 ] );
G_PushMove( m_pCollisionEntity, origin - primal_origin, n_angles );
G_TouchTriggers( m_pCollisionEntity );
m_pCollisionEntity->setOrigin( origin );
m_pCollisionEntity->setAngles( angles );
}
SetupVehicleSoundEntities();
UpdateDriverSlot( 0 );
for( int slot = 0; slot < MAX_PASSENGERS; slot++ )
{
UpdatePassengerSlot( slot );
}
for( int slot = 0; slot < MAX_TURRETS; slot++ )
{
UpdateTurretSlot( slot );
}
atmp = angles - primal_angles;
if( g_showvehicleentrypoints->integer )
{
for( int slot = 0; slot < MAX_PASSENGERS; slot++ )
{
if( Passengers[ slot ].enter_boneindex >= 0 )
{
QueryPassengerSlotPosition( slot, ( float * )&temp );
G_DebugCircle( temp, 10.0f, 1.0f, 0.0f, 0.0f, 1.0f, true );
}
}
for( int slot = 0; slot < MAX_TURRETS; slot++ )
{
if( Turrets[ slot ].enter_boneindex >= 0 )
{
QueryTurretSlotPosition( slot, ( float * )&temp );
G_DebugCircle( temp, 10.0f, 0.0f, 1.0f, 0.0f, 1.0f, true );
}
}
if( driver.enter_boneindex >= 0 )
{
QueryTurretSlotPosition( 0, ( float * )&temp );
G_DebugCircle( temp, 10.0f, 0.0f, 0.0f, 1.0f, 1.0f, true );
}
}
if( g_showvehicleslotpoints->integer )
{
for( int slot = 0; slot < MAX_PASSENGERS; slot++ )
{
if( Passengers[ slot ].boneindex < 0 )
{
continue;
}
GetTagPositionAndOrientation( Passengers[ slot ].boneindex, &orient );
G_DebugCircle( orient.origin, 10.0, 1.0f, 0.5f, 0.5f, 1.0f, true );
}
for( int slot = 0; slot < MAX_TURRETS; slot++ )
{
if( Turrets[ slot ].boneindex < 0 )
{
continue;
}
GetTagPositionAndOrientation( Turrets[ slot ].boneindex, &orient );
G_DebugCircle( orient.origin, 10.0, 0.5f, 1.0f, 1.0f, 1.0f, true );
}
if( driver.boneindex >= 0 )
{
GetTagPositionAndOrientation( driver.boneindex, &orient );
G_DebugCircle( orient.origin, 10.0, 0.5f, 0.5f, 1.0f, 1.0f, true );
}
}
last = this;
while( last->vlink )
{
v = last->vlink;
v->setOrigin( origin + ( i * v->offset.x ) + ( j * v->offset.y ) + ( k * v->offset.z ) );
v->avelocity = avelocity;
v->velocity = velocity;
v->angles[ ROLL ] = angles[ ROLL ];
v->angles[ YAW ] = angles[ YAW ];
v->angles[ PITCH ] = ( float )( ( int )( v->angles[ PITCH ] + ( drivespeed / 4 ) ) % 360 );
v->setAngles( v->angles );
last = v;
}
CheckWater();
WorldEffects();
if( m_pCollisionEntity )
{
setSolidType( SOLID_NOT );
m_pCollisionEntity->Solid();
}
else
{
setSolidType( SOLID_BBOX );
edict->r.contents = CONTENTS_UNKNOWN2;
}
}
/*
====================
Vehicle::VehicleTouched
====================
*/
void Vehicle::VehicleTouched
(
Event *ev
)
{
Entity *other;
float speed;
Vector delta;
Vector dir;
other = ev->GetEntity( 1 );
if ( other == driver.ent )
{
return;
}
if ( other == world )
{
return;
}
if ( drivable && !driver.ent )
{
return;
}
delta = origin - last_origin;
speed = delta.length();
if ( speed > 2 )
{
Sound( "vehicle_crash", qtrue );
dir = delta * ( 1 / speed );
other->Damage( this, lastdriver.ent, speed * 8, origin, dir, vec_zero, speed*15, 0, MOD_VEHICLE );
}
}
/*
====================
Vehicle::VehicleBlocked
====================
*/
void Vehicle::VehicleBlocked
(
Event *ev
)
{
return;
/*
Entity *other;
float speed;
float damage;
Vector delta;
Vector newvel;
Vector dir;
if ( !velocity[0] && !velocity[1] )
return;
other = ev->GetEntity( 1 );
if ( other == driver.ent )
{
return;
}
if ( other->isSubclassOf( VehicleBase ) )
{
delta = other->origin - origin;
delta.normalize();
newvel = vec_zero - ( velocity) + ( other->velocity * 0.25 );
if ( newvel * delta < 0 )
{
velocity = newvel;
delta = velocity - other->velocity;
damage = delta.length()/4;
}
else
{
return;
}
}
else if ( ( velocity.length() < 350 ) )
{
other->velocity += velocity*1.25f;
other->velocity[ 2 ] += 100;
damage = velocity.length()/4;
}
else
{
damage = other->health + 1000;
}
// Gib 'em outright
speed = fabs( velocity.length() );
dir = velocity * ( 1 / speed );
other->Damage( this, lastdriver.ent, damage, origin, dir, vec_zero, speed, 0, MOD_VEHICLE, -1, -1, 1.0f );
*/
}
/*
====================
Vehicle::Driver
====================
*/
Entity *Vehicle::Driver
(
void
)
{
return driver.ent;
}
/*
====================
Vehicle::IsDrivable
====================
*/
qboolean Vehicle::IsDrivable
(
void
)
{
return drivable;
}
/*
====================
Vehicle::EventSetSpeed
====================
*/
void Vehicle::SetSpeed
(
Event *ev
)
{
speed = ev->GetFloat( 1 );
}
/*
====================
Vehicle::EventSetTurnRate
====================
*/
void Vehicle::SetTurnRate
(
Event *ev
)
{
maxturnrate = ev->GetFloat( 1 );
}
/*
====================
Vehicle::VehicleDestroyed
====================
*/
void Vehicle::VehicleDestroyed
(
Event *ev
)
{
}
void Vehicle::DetachRemoteOwner()
{
// FIXME: unimplemented
}
/*
====================
Vehicle::SetMoveInfo
====================
*/
void Vehicle::SetMoveInfo
(
vmove_t *vm
)
{
memset( vm, 0, sizeof( vmove_t ) );
VectorCopy( origin, vs.origin );
vs.useGravity = 0;
vs.entityNum = entnum;
vm->vs = &vs;
vm->frametime = level.frametime;
vm->tracemask = edict->clipmask;
VectorCopy( mins, vm->mins );
VectorCopy( maxs, vm->maxs );
vs.entityNum = edict->s.number;
vs.desired_dir[ 0 ] = velocity[ 0 ];
vs.desired_dir[ 1 ] = velocity[ 1 ];
vm->desired_speed = VectorNormalize2D( this->vs.desired_dir );
}
/*
====================
Vehicle::GetMoveInfo
====================
*/
void Vehicle::GetMoveInfo
(
vmove_t *vm
)
{
Vector newOrigin = vm->vs->origin;
if( bindmaster )
{
newOrigin = vm->vs->origin - origin;
}
setLocalOrigin( newOrigin );
groundentity = NULL;
if( vm->vs->groundEntityNum != ENTITYNUM_NONE )
{
groundentity = &g_entities[ vm->vs->groundEntityNum ];
}
}
/*
====================
Vehicle::SetCEMoveInfo
====================
*/
void Vehicle::SetCEMoveInfo
(
vmove_t *vm
)
{
SetMoveInfo( vm );
vm->mins[ 0 ] = m_pCollisionEntity->mins[ 0 ] - 24.0f;
vm->mins[ 1 ] = m_pCollisionEntity->mins[ 1 ] - 24.0f;
vm->mins[ 2 ] = m_pCollisionEntity->mins[ 2 ];
vm->maxs[ 0 ] = m_pCollisionEntity->maxs[ 0 ] + 24.0f;
vm->maxs[ 1 ] = m_pCollisionEntity->maxs[ 1 ] + 24.0f;
vm->maxs[ 2 ] = m_pCollisionEntity->maxs[ 2 ];
}
/*
====================
Vehicle::GetCEMoveInfo
====================
*/
void Vehicle::GetCEMoveInfo
(
vmove_t *vm
)
{
GetMoveInfo( vm );
}
/*
====================
Vehicle::SetViewAngles
====================
*/
void Vehicle::SetViewAngles
(
Vector newViewangles
)
{
client->ps.delta_angles[ 0 ] = ANGLE2SHORT( newViewangles.x );
client->ps.delta_angles[ 1 ] = ANGLE2SHORT( newViewangles.y );
client->ps.delta_angles[ 2 ] = ANGLE2SHORT( newViewangles.z );
AnglesToAxis( newViewangles, orientation );
yaw_forward = orientation[ 0 ];
yaw_left = orientation[ 1 ];
}
/*
====================
Vehicle::EventSetMass
====================
*/
void Vehicle::SetMass
(
Event *ev
)
{
m_fMass = ev->GetFloat( 1 );
m_fFrontMass = m_fMass * 0.5;
m_fBackMass = m_fMass * 0.5;
}
void Vehicle::SetFrontMass
(
Event *ev
)
{
m_fFrontMass = ev->GetFloat( 1 );
m_fMass = m_fFrontMass + m_fBackMass;
}
/*
====================
Vehicle::EventSetBackMass
====================
*/
void Vehicle::SetBackMass
(
Event *ev
)
{
m_fBackMass = ev->GetFloat( 1 );
m_fMass = m_fFrontMass + m_fBackMass;
}
/*
====================
Vehicle::EventSetTread
====================
*/
void Vehicle::SetTread
(
Event *ev
)
{
m_fTread = ev->GetFloat( 1 );
}
/*
====================
Vehicle::EventSetTireRadius
====================
*/
void Vehicle::SetTireRadius
(
Event *ev
)
{
m_fTireRadius = ev->GetFloat( 1 );
}
/*
====================
Vehicle::EventSetRollingResistance
====================
*/
void Vehicle::SetRollingResistance
(
Event *ev
)
{
m_fRollingResistance = ev->GetFloat( 1 );
}
/*
====================
Vehicle::EventSetDrag
====================
*/
void Vehicle::SetDrag
(
Event *ev
)
{
m_fDrag = ev->GetFloat( 1 );
}
/*
====================
Vehicle::TorqueLookup
====================
*/
float Vehicle::TorqueLookup
(
int rpm
)
{
if( rpm > 4999 )
{
if( rpm > 5999 )
{
return 0.0;
}
else
{
return ( float )( 190 * ( 6000 - rpm ) ) * 0.001;
}
}
else
{
return 190.0;
}
}
/*
====================
Vehicle::UpdateVariables
====================
*/
void Vehicle::UpdateVariables
(
Vector *acceleration,
Vector *vpn,
Vector *vup,
Vector *vright,
Vector *t_vpn,
Vector *t_vup,
Vector *t_vright
)
{
}
/*
====================
Vehicle::EventModelInit
====================
*/
void Vehicle::ModelInit
(
Event *ev
)
{
SetControllerTag( 0, gi.Tag_NumForName( edict->tiki, "tire_rotate_front_left" ) );
SetControllerTag( 1, gi.Tag_NumForName( edict->tiki, "tire_rotate_front_right" ) );
SetControllerTag( 2, gi.Tag_NumForName( edict->tiki, "steeringwheel_center" ) );
}
/*
====================
Vehicle::EventBouncyCoef
====================
*/
void Vehicle::BouncyCoef
(
Event *ev
)
{
m_fBouncyCoef = ev->GetFloat( 1 );
}
/*
====================
Vehicle::EventSpringyCoef
====================
*/
void Vehicle::SpringyCoef
(
Event *ev
)
{
m_fSpringyCoef = ev->GetFloat( 1 );
}
/*
====================
Vehicle::EventYawMinMax
====================
*/
void Vehicle::YawMinMax
(
Event *ev
)
{
if( ev->NumArgs() != 3 )
{
ScriptError( "No Parameter for YawMinMax" );
}
m_fYawMin = ev->GetFloat( 1 );
m_fYawMax = ev->GetFloat( 2 );
m_fYawCoef = ev->GetFloat( 3 );
if( m_fYawMin > m_fYawMax )
{
ScriptError( "Mismatched mins and maxs for YawMinMax" );
}
}
/*
====================
Vehicle::EventRollMinMax
====================
*/
void Vehicle::RollMinMax
(
Event *ev
)
{
if( ev->NumArgs() != 3 )
{
ScriptError( "No Parameter for RollMinMax" );
}
m_fRollMin = ev->GetFloat( 1 );
m_fRollMax = ev->GetFloat( 2 );
m_fRollCoef = ev->GetFloat( 3 );
if( m_fRollMin > m_fRollMax )
{
ScriptError( "Mismatched mins and maxs for RollMinMax" );
}
}
/*
====================
Vehicle::EventZMinMax
====================
*/
void Vehicle::ZMinMax
(
Event *ev
)
{
if( ev->NumArgs() != 3 )
{
ScriptError( "No Parameter for ZMinMax" );
}
m_fZMin = ev->GetFloat( 1 );
m_fZMax = ev->GetFloat( 2 );
m_fZCoef = ev->GetFloat( 3 );
if( m_fZMin > m_fZMax )
{
ScriptError( "Mismatched mins and maxs for ZMinMax" );
}
}
/*
====================
Vehicle::EventSetAnimationSet
====================
*/
void Vehicle::SetAnimationSet
(
Event *ev
)
{
m_sAnimationSet = ev->GetString( 1 );
}
/*
====================
Vehicle::EventSetSoundSet
====================
*/
void Vehicle::SetSoundSet
(
Event *ev
)
{
m_sSoundSet = ev->GetString( 1 );
}
/*
====================
Vehicle::EventSpawnTurret
====================
*/
void Vehicle::SpawnTurret
(
Event *ev
)
{
VehicleTurretGun *pTurret;
int slot;
pTurret = new VehicleTurretGun;
pTurret->SetBaseOrientation( orientation, NULL );
pTurret->setModel( ev->GetString( 2 ) );
slot = ev->GetInteger( 1 );
AttachTurretSlot( slot, pTurret, vec_zero, NULL );
pTurret->SetVehicleOwner( this );
Event *event = new Event( EV_TakeDamage );
pTurret->PostEvent( event, EV_POSTSPAWN );
UpdateTurretSlot( slot );
pTurret->ProcessPendingEvents();
}
/*
====================
Vehicle::EventLockMovement
====================
*/
void Vehicle::EventLockMovement
(
Event *ev
)
{
m_bMovementLocked = true;
}
/*
====================
Vehicle::EventUnlockMovement
====================
*/
void Vehicle::EventUnlockMovement
(
Event *ev
)
{
m_bMovementLocked = false;
}
/*
====================
Vehicle::EventQueryFreePassengerSlot
====================
*/
void Vehicle::QueryFreePassengerSlot
(
Event *ev
)
{
ev->AddInteger( QueryFreePassengerSlot() );
}
/*
====================
Vehicle::EventQueryFreeDriverSlot
====================
*/
void Vehicle::QueryFreeDriverSlot
(
Event *ev
)
{
ev->AddInteger( QueryFreeDriverSlot() );
}
/*
====================
Vehicle::EventQueryFreeTurretSlot
====================
*/
void Vehicle::QueryFreeTurretSlot
(
Event *ev
)
{
ev->AddInteger( QueryFreeTurretSlot() );
}
/*
====================
Vehicle::EventQueryPassengerSlotPosition
====================
*/
void Vehicle::QueryPassengerSlotPosition
(
Event *ev
)
{
Vector vPos;
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_PASSENGERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
QueryPassengerSlotPosition( iSlot, vPos );
ev->AddVector( vPos );
}
/*
====================
Vehicle::EventQueryDriverSlotPosition
====================
*/
void Vehicle::QueryDriverSlotPosition
(
Event *ev
)
{
Vector vPos;
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_DRIVERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
QueryDriverSlotPosition( iSlot, vPos );
ev->AddVector( vPos );
}
/*
====================
Vehicle::EventQueryTurretSlotPosition
====================
*/
void Vehicle::QueryTurretSlotPosition
(
Event *ev
)
{
Vector vPos;
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_TURRETS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
QueryTurretSlotPosition( iSlot, vPos );
ev->AddVector( vPos );
}
/*
====================
Vehicle::EventQueryPassengerSlotAngles
====================
*/
void Vehicle::QueryPassengerSlotAngles
(
Event *ev
)
{
Vector vAngles;
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_PASSENGERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
QueryPassengerSlotAngles( iSlot, vAngles );
ev->AddVector( vAngles );
}
/*
====================
Vehicle::EventQueryDriverSlotAngles
====================
*/
void Vehicle::QueryDriverSlotAngles
(
Event *ev
)
{
Vector vAngles;
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_DRIVERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
QueryDriverSlotAngles( iSlot, vAngles );
ev->AddVector( vAngles );
}
/*
====================
Vehicle::EventQueryTurretSlotAngles
====================
*/
void Vehicle::QueryTurretSlotAngles
(
Event *ev
)
{
Vector vAngles;
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_TURRETS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
QueryTurretSlotAngles( iSlot, vAngles );
ev->AddVector( vAngles );
}
/*
====================
Vehicle::EventQueryPassengerSlotStatus
====================
*/
void Vehicle::QueryPassengerSlotStatus
(
Event *ev
)
{
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_PASSENGERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
ev->AddInteger( QueryPassengerSlotStatus( iSlot ) );
}
/*
====================
Vehicle::EventQueryDriverSlotStatus
====================
*/
void Vehicle::QueryDriverSlotStatus
(
Event *ev
)
{
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_DRIVERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
ev->AddInteger( QueryDriverSlotStatus( iSlot ) );
}
/*
====================
Vehicle::EventQueryTurretSlotStatus
====================
*/
void Vehicle::QueryTurretSlotStatus
(
Event *ev
)
{
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_TURRETS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
ev->AddInteger( QueryTurretSlotStatus( iSlot ) );
}
/*
====================
Vehicle::EventQueryPassengerSlotEntity
====================
*/
void Vehicle::QueryPassengerSlotEntity
(
Event *ev
)
{
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_PASSENGERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
ev->AddEntity( QueryPassengerSlotEntity( iSlot ) );
}
/*
====================
Vehicle::EventQueryDriverSlotEntity
====================
*/
void Vehicle::QueryDriverSlotEntity
(
Event *ev
)
{
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_DRIVERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
ev->AddEntity( QueryDriverSlotEntity( iSlot ) );
}
/*
====================
Vehicle::EventQueryTurretSlotEntity
====================
*/
void Vehicle::QueryTurretSlotEntity
(
Event *ev
)
{
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_TURRETS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
ev->AddEntity( QueryTurretSlotEntity( iSlot ) );
}
/*
====================
Vehicle::EventAttachPassengerSlot
====================
*/
void Vehicle::AttachPassengerSlot
(
Event *ev
)
{
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_PASSENGERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
AttachPassengerSlot( iSlot, ev->GetEntity( 2 ), vec_zero );
UpdatePassengerSlot( iSlot );
}
/*
====================
Vehicle::EventAttachDriverSlot
====================
*/
void Vehicle::AttachDriverSlot
(
Event *ev
)
{
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_DRIVERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
AttachDriverSlot( 0, ev->GetEntity( 2 ), vec_zero );
UpdateDriverSlot( 0 );
}
/*
====================
Vehicle::EventAttachTurretSlot
====================
*/
void Vehicle::AttachTurretSlot
(
Event *ev
)
{
int iSlot;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_TURRETS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
AttachTurretSlot( iSlot, ev->GetEntity( 2 ), vec_zero, NULL );
UpdateTurretSlot( iSlot );
}
/*
====================
Vehicle::EventDetachPassengerSlot
====================
*/
void Vehicle::DetachPassengerSlot
(
Event *ev
)
{
int iSlot;
Vector vExitPosition;
Vector vExitAngles;
bool bHasExitAngles = false;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_PASSENGERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
if( ev->NumArgs() == 2 )
{
if( ev->IsVectorAt( 2 ) )
{
vExitPosition = ev->GetVector( 2 );
}
else if( ev->IsEntityAt( 2 ) )
{
vExitPosition = ev->GetEntity( 2 )->origin;
bHasExitAngles = true;
}
else if( ev->IsSimpleEntityAt( 2 ) )
{
vExitPosition = ev->GetSimpleEntity( 2 )->origin;
}
DetachPassengerSlot( iSlot, vExitPosition, bHasExitAngles ? &vExitAngles : NULL );
}
else
{
DetachPassengerSlot( iSlot, vec_zero, NULL );
}
}
/*
====================
Vehicle::EventDetachDriverSlot
====================
*/
void Vehicle::DetachDriverSlot
(
Event *ev
)
{
int iSlot;
Vector vExitPosition;
Vector vExitAngles;
bool bHasExitAngles = false;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_DRIVERS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
if( ev->NumArgs() == 2 )
{
if( ev->IsVectorAt( 2 ) )
{
vExitPosition = ev->GetVector( 2 );
}
else if( ev->IsEntityAt( 2 ) )
{
vExitPosition = ev->GetEntity( 2 )->origin;
bHasExitAngles = true;
}
else if( ev->IsSimpleEntityAt( 2 ) )
{
vExitPosition = ev->GetSimpleEntity( 2 )->origin;
}
DetachDriverSlot( iSlot, vExitPosition, bHasExitAngles ? &vExitAngles : NULL );
}
else
{
DetachDriverSlot( iSlot, vec_zero, NULL );
}
}
/*
====================
Vehicle::EventDetachTurretSlot
====================
*/
void Vehicle::DetachTurretSlot
(
Event *ev
)
{
int iSlot;
Vector vExitPosition;
Vector vExitAngles;
bool bHasExitAngles = false;
iSlot = ev->GetInteger( 1 );
if( iSlot >= MAX_TURRETS )
{
ScriptError( "Slot Specified is greater than maximum allowed for that parameter\n" );
}
if( ev->NumArgs() == 2 )
{
if( ev->IsVectorAt( 2 ) )
{
vExitPosition = ev->GetVector( 2 );
}
else if( ev->IsEntityAt( 2 ) )
{
vExitPosition = ev->GetEntity( 2 )->origin;
bHasExitAngles = true;
}
else if( ev->IsSimpleEntityAt( 2 ) )
{
vExitPosition = ev->GetSimpleEntity( 2 )->origin;
}
DetachTurretSlot( iSlot, vExitPosition, bHasExitAngles ? &vExitAngles : NULL );
}
else
{
DetachTurretSlot( iSlot, vec_zero, NULL );
}
}
/*
====================
Vehicle::SetWheelCorners
====================
*/
void Vehicle::SetWheelCorners
(
Event *ev
)
{
Vector size;
size = ev->GetVector( 1 );
m_vOriginCornerOffset = ev->GetVector( 2 );
maxtracedist = size[ 2 ];
Corners[ 0 ][ 0 ] = -( size[ 0 ] * 0.5 );
Corners[ 0 ][ 1 ] = ( size[ 1 ] * 0.5 );
Corners[ 0 ][ 2 ] = size[ 2 ];
Corners[ 1 ][ 0 ] = ( size[ 0 ] * 0.5 );
Corners[ 1 ][ 1 ] = ( size[ 1 ] * 0.5 );
Corners[ 1 ][ 2 ] = size[ 2 ];
Corners[ 2 ][ 0 ] = -( size[ 0 ] * 0.5 );
Corners[ 2 ][ 1 ] = -( size[ 1 ] * 0.5 );
Corners[ 2 ][ 2 ] = size[ 2 ];
Corners[ 3 ][ 0 ] = ( size[ 0 ] * 0.5 );
Corners[ 3 ][ 1 ] = -( size[ 1 ] * 0.5 );
Corners[ 3 ][ 2 ] = size[ 2 ];
SetupVehicleSoundEntities();
}
/*
====================
Vehicle::EventDriveInternal
====================
*/
void Vehicle::EventDriveInternal
(
Event *ev,
bool wait
)
{
SimpleEntity *path;
SimpleEntity *alternate_path = NULL;
m_fIdealDistance = 100.0f;
m_fLookAhead = 256.0f;
m_fIdealAccel = 35.0f;
m_fIdealSpeed = 250.0f;
switch( ev->NumArgs() )
{
case 6:
alternate_path = ev->GetSimpleEntity( 6 );
case 5:
m_fLookAhead = ev->GetFloat( 5 );
case 4:
m_fIdealDistance = ev->GetFloat( 4 );
case 3:
m_fIdealAccel = ev->GetFloat( 3 );
case 2:
m_fIdealSpeed = ev->GetFloat( 2 );
break;
case 1:
break;
default:
ScriptError( "wrong number of arguments" );
}
path = ev->GetSimpleEntity( 1 );
if( path )
{
ScriptError( "Vehicle Given Drive Command with NULL path." );
}
if( !m_pCurPath )
{
m_pCurPath = new cSpline < 4, 512 > ;
}
if( !m_pAlternatePath )
{
m_pAlternatePath = new cSpline < 4, 512 > ;
}
SetupPath( m_pCurPath, path );
// Setup the alternate path
if( alternate_path )
{
SetupPath( m_pAlternatePath, alternate_path );
}
m_bAutoPilot = true;
m_iCurNode = 0;
m_iAlternateNode = 0;
Sound( m_sSoundSet + "snd_start" );
}
/*
====================
Vehicle::EventDrive
====================
*/
void Vehicle::EventDrive
(
Event *ev
)
{
EventDriveInternal( ev, true );
}
/*
====================
Vehicle::EventDriveNoWait
====================
*/
void Vehicle::EventDriveNoWait
(
Event *ev
)
{
EventDriveInternal( ev, false );
}
/*
====================
Vehicle::EventStop
====================
*/
void Vehicle::EventStop
(
Event *ev
)
{
m_bStopEnabled = false;
m_bAutoPilot = false;
m_bIsSkidding = false;
moveimpulse = 0;
turnimpulse = 0;
m_iCurNode = 0;
Unregister( STRING_DRIVE );
}
/*
====================
Vehicle::EventFullStop
====================
*/
void Vehicle::EventFullStop
(
Event *ev
)
{
m_bStopEnabled = 0;
m_bIsSkidding = 0;
m_bAutoPilot = 0;
moveimpulse = 0;
turnimpulse = 0;
velocity = vec_zero;
m_iCurNode = 0;
Unregister( STRING_DRIVE );
}
/*
====================
Vehicle::EventModifyDrive
====================
*/
void Vehicle::EventModifyDrive
(
Event *ev
)
{
if( !level.Spawned() )
{
ScriptError( "ModifyDrive used improperly... (used before the level is spawned)" );
}
if( !m_bAutoPilot || !this->m_pCurPath )
{
ScriptError( "ModifyDrive used when not driving!" );
}
if( ev->NumArgs() < 1 || ev->NumArgs() > 3 )
{
ScriptError( "wrong number of arguments" );
}
m_fIdealSpeed = ev->GetFloat( 1 );
if( ev->NumArgs() >= 2 )
{
m_fIdealAccel = ev->GetFloat( 2 );
}
if( ev->NumArgs() >= 3 )
{
m_fLookAhead = ev->GetFloat( 3 );
}
}
/*
====================
Vehicle::EventNextDrive
====================
*/
void Vehicle::EventNextDrive
(
Event *ev
)
{
SimpleEntity *path;
float *i_fTmp;
float o_fTmp[ 4 ];
Vector org1;
Vector org2;
path = ev->GetSimpleEntity( 1 );
if( !m_bAutoPilot )
{
ScriptError( "Cannot Set Next Path because Not Currently Driving a Path." );
}
if( !m_pCurPath || m_pCurPath->m_iPoints == 0 )
{
ScriptError( "Cannot Set Next Path because Current Path is Empty." );
}
if( !m_pNextPath )
{
m_pNextPath = new cSpline < 4, 512 > ;
}
SetupPath( m_pNextPath, path );
i_fTmp = m_pCurPath->GetByNode( m_pCurPath->m_iPoints, NULL );
org1 = ( i_fTmp + 1 );
i_fTmp = m_pNextPath->GetByNode( 0.0f, NULL );
org2 = ( i_fTmp + 1 );
o_fTmp[ 0 ] = ( org2 - org1 ).length();
VectorClear( o_fTmp + 1 );
m_pNextPath->UniformAdd( o_fTmp );
m_iNextPathStartNode = m_pCurPath->Append( m_pNextPath );
}
/*
====================
Vehicle::EventModel
====================
*/
void Vehicle::EventModel
(
Event *ev
)
{
SetModelEvent( ev );
}
/*
====================
Vehicle::EventRemoveOnDeath
====================
*/
void Vehicle::EventRemoveOnDeath
(
Event *ev
)
{
m_bRemoveOnDeath = ev->GetBoolean( 1 );
}
/*
====================
Vehicle::EventSetExplosionModel
====================
*/
void Vehicle::EventSetExplosionModel
(
Event *ev
)
{
m_sExplosionModel = ev->GetString( 1 );
}
/*
====================
Vehicle::EventSetCollisionModel
====================
*/
void Vehicle::EventSetCollisionModel
(
Event *ev
)
{
Entity *pColEnt = ev->GetEntity( 1 );
if( !pColEnt )
{
ScriptError( "Trying to set a collision model with a NULL entity." );
}
if( m_pCollisionEntity )
{
m_pCollisionEntity->PostEvent( EV_Remove, EV_VEHICLE );
}
m_pCollisionEntity = new VehicleCollisionEntity( this );
m_pCollisionEntity->setModel( pColEnt->model );
if( !m_pCollisionEntity->model.length() || *m_pCollisionEntity->model != '*' )
{
// Re-post the event with the correct time
m_pCollisionEntity->CancelEventsOfType( EV_Remove );
m_pCollisionEntity->PostEvent( EV_Remove, EV_VEHICLE );
m_pCollisionEntity = NULL;
ScriptError( "Model for Entity not of a valid type. Must be B-Model." );
}
m_pCollisionEntity->setOrigin( origin );
m_pCollisionEntity->setAngles( angles );
}
/*
====================
Vehicle::EventGetCollisionModel
====================
*/
void Vehicle::EventGetCollisionModel
(
Event *ev
)
{
ev->AddEntity( m_pCollisionEntity );
}
/*
====================
Vehicle::EventSetSoundParameters
====================
*/
void Vehicle::EventSetSoundParameters
(
Event *ev
)
{
m_fSoundMinSpeed = ev->GetFloat( 1 );
m_fSoundMinPitch = ev->GetFloat( 2 );
m_fSoundMaxSpeed = ev->GetFloat( 3 );
m_fSoundMaxPitch = ev->GetFloat( 4 );
}
/*
====================
Vehicle::EventSetVolumeParameters
====================
*/
void Vehicle::EventSetVolumeParameters
(
Event *ev
)
{
m_fVolumeMinSpeed = ev->GetFloat( 1 );
m_fVolumeMinPitch = ev->GetFloat( 2 );
m_fVolumeMaxSpeed = ev->GetFloat( 3 );
m_fVolumeMaxPitch = ev->GetFloat( 4 );
}
/*
====================
Vehicle::EventDamage
====================
*/
void Vehicle::EventDamage
(
Event *ev
)
{
Vector vDirection;
float fForce;
int i;
if( !IsDamagedBy( ev->GetEntity( 3 ) ) )
{
return;
}
Event *event = new Event( EV_Damage );
vDirection = ev->GetVector( 5 );
fForce = ev->GetFloat( 7 );
VectorNormalizeFast( vDirection );
m_fForwardForce += DotProduct( orientation[ 1 ], vDirection ) * fForce;
m_fLeftForce += DotProduct( orientation[ 0 ], vDirection ) * fForce;
for( i = 1; i <= ev->NumArgs(); i++ )
{
if( i == 7 )
{
event->AddFloat( 0 );
}
else
{
event->AddValue( ev->GetValue( i ) );
}
}
if( driver.ent )
{
if( driver.ent->IsSubclassOfPlayer() )
{
Player *player = ( Player * )driver.ent.Pointer();
Vector dir = ev->GetVector( 5 );
if( player->camera )
{
player->damage_yaw = AngleSubtract( player->camera->angles[ 1 ], dir.toYaw() ) + 180.5f;
}
else
{
player->damage_yaw = AngleSubtract( player->GetVAngles()[ 1 ], dir.toYaw() ) + 180.5f;
}
}
}
DamageEvent( event );
delete event;
}
/*
====================
Vehicle::EventStopAtEnd
====================
*/
void Vehicle::EventStopAtEnd
(
Event *ev
)
{
if( !m_pCurPath )
{
ScriptError( "Tried to Stop at end of path on a vehicle who is not driving a path!" );
}
m_fStopStartDistance = GetPathPosition( m_pCurPath, m_iCurNode );
m_fStopStartSpeed = moveimpulse;
m_fStopEndDistance = *m_pCurPath->GetByNode( m_pCurPath->m_vPoints[ 0 ][ 0 ], NULL );
m_bStopEnabled = 1;
}
/*
====================
Vehicle::EventSkidding
====================
*/
void Vehicle::EventSkidding
(
Event *ev
)
{
if( ev->NumArgs() == 1 )
{
m_bEnableSkidding = ev->GetInteger( 1 );
}
else
{
m_bEnableSkidding = true;
}
ProcessEvent( EV_Vehicle_ContinueSkidding );
}
/*
====================
Vehicle::EventContinueSkidding
====================
*/
void Vehicle::EventContinueSkidding
(
Event *ev
)
{
if( m_bEnableSkidding )
{
if( HasAnim( "skidding" ) )
{
NewAnim( "skidding", EV_Vehicle_ContinueSkidding, 7, 0.000001f );
return;
}
else
{
assert( !"Vehicle without skidding animation." );
}
}
else
{
if( HasAnim( "idle" ) )
{
NewAnim( "idle", 0, 7, 0.000001f );
return;
}
else
{
assert( !"Vehicle without idle animation." );
}
}
}
/*
====================
Vehicle::EventVehicleAnim
====================
*/
void Vehicle::EventVehicleAnim
(
Event *ev
)
{
float weight;
if( ev->NumArgs() > 1 )
{
weight = ev->GetFloat( 2 );
}
else
{
weight = 1.0f;
}
NewAnim( ev->GetString( 1 ), EV_Vehicle_VehicleAnimDone, 8, weight );
}
/*
====================
Vehicle::EventVehicleAnimDone
====================
*/
void Vehicle::EventVehicleAnimDone
(
Event *ev
)
{
Unregister( STRING_VEHICLEANIMDONE );
}
/*
====================
Vehicle::TouchStuff
====================
*/
void Vehicle::TouchStuff
(
vmove_t *vm
)
{
int i, j;
gentity_t *other;
Event *event;
if (driver.ent)
G_TouchTriggers(driver.ent);
for (int i = 0; i < MAX_PASSENGERS; i++)
{
if (Passengers[i].ent)
{
G_TouchTriggers(Passengers[i].ent);
}
}
for (int i = 0; i < MAX_TURRETS; i++)
{
if (Turrets[i].ent)
{
G_TouchTriggers(Turrets[i].ent);
}
}
if (getMoveType() != MOVETYPE_NOCLIP)
{
G_TouchTriggers(this);
}
for (i = 0; i < vm->numtouch; i++)
{
other = &g_entities[vm->touchents[i]];
for (j = 0; j < i; j++)
{
gentity_t *ge = &g_entities[j];
if (ge == other)
break;
}
if (j != i)
{
// duplicated
continue;
}
// Don't bother touching the world
if ((!other->entity) || (other->entity == world))
{
continue;
}
event = new Event(EV_Touch);
event->AddEntity(this);
other->entity->ProcessEvent(event);
event = new Event(EV_Touch);
event->AddEntity(other->entity);
ProcessEvent(event);
}
}
/*
====================
Vehicle::ResetSlots
====================
*/
void Vehicle::ResetSlots
(
void
)
{
driver.ent = NULL;
driver.boneindex = -1;
driver.enter_boneindex = -1;
driver.flags = SLOT_UNUSED;
lastdriver.ent = NULL;
lastdriver.boneindex = -1;
lastdriver.enter_boneindex = -1;
lastdriver.flags = SLOT_UNUSED;
for( int i = 0; i < MAX_PASSENGERS; i++ )
{
Passengers[ i ].ent = NULL;
Passengers[ i ].boneindex = -1;
Passengers[ i ].enter_boneindex = -1;
Passengers[ i ].flags = SLOT_UNUSED;
}
for( int i = 0; i < MAX_TURRETS; i++ )
{
Turrets[ i ].ent = NULL;
Turrets[ i ].boneindex = -1;
Turrets[ i ].enter_boneindex = -1;
Turrets[ i ].flags = SLOT_UNUSED;
}
}
/*
====================
Vehicle::OpenSlotsByModel
====================
*/
void Vehicle::OpenSlotsByModel
(
void
)
{
str bonename;
int bonenum;
int boneindex;
driver.boneindex = gi.Tag_NumForName( edict->tiki, "driver" );
driver.enter_boneindex = gi.Tag_NumForName( edict->tiki, "driver_enter" );
if( driver.flags & SLOT_UNUSED )
{
driver.ent = NULL;
driver.flags = SLOT_FREE;
}
numPassengers = 0;
for( bonenum = 0; bonenum < MAX_PASSENGERS; bonenum++ )
{
str bonenumstr = bonenum;
bonename = "passenger" + bonenumstr;
boneindex = gi.Tag_NumForName( edict->tiki, bonename.c_str() );
if( boneindex >= 0 )
{
numPassengers++;
Passengers[ bonenum ].boneindex = boneindex;
Passengers[ bonenum ].enter_boneindex = gi.Tag_NumForName( edict->tiki, "passenger_enter" + bonenumstr);
if( Passengers[ bonenum ].flags & SLOT_UNUSED )
{
Passengers[ bonenum ].ent = NULL;
Passengers[ bonenum ].flags = SLOT_FREE;
}
}
}
numTurrets = 0;
for( bonenum = 0; bonenum < MAX_TURRETS; bonenum++ )
{
str bonenumstr = bonenum;
bonename = "turret" + bonenumstr;
boneindex = gi.Tag_NumForName( edict->tiki, bonename.c_str() );
if( boneindex >= 0 )
{
numTurrets++;
Turrets[ bonenum ].boneindex = boneindex;
Turrets[ bonenum ].enter_boneindex = gi.Tag_NumForName( edict->tiki, "turret_enter" + bonenumstr);
if( Turrets[ bonenum ].flags & SLOT_UNUSED )
{
Turrets[ bonenum ].ent = NULL;
Turrets[ bonenum ].flags = SLOT_FREE;
}
}
}
}
/*
====================
Vehicle::MoveVehicle
====================
*/
void Vehicle::MoveVehicle
(
void
)
{
trace_t tr;
Vector vecStart;
Vector vecStart2;
Vector vecEnd;
Vector vecDelta;
Vector vecAng;
int i;
gridpoint_t *gp;
vmove_t vm;
float flMoveFrac = 1.0f;
float fSpeed;
bool bDoGravity = true;
bool bHitPerson = false;
Entity *pSkippedEntities[ MAX_SKIPPED_ENTITIES ];
int iContentsEntities[ MAX_SKIPPED_ENTITIES ];
solid_t solidEntities[ MAX_SKIPPED_ENTITIES ];
int iNumSkippedEntities = 0;
Event* event = nullptr;
if( m_bMovementLocked )
{
return;
}
setAngles();
if( m_pCollisionEntity )
{
SetCEMoveInfo( &vm );
}
else
{
SetMoveInfo( &vm );
}
m_sMoveGrid->SetMoveInfo( &vm );
m_sMoveGrid->SetOrientation( orientation );
m_sMoveGrid->CalculateBoxPoints();
CheckGround();
VmoveSingle( &vm );
setOrigin( vm.vs->origin );
if( velocity.length() > 0.5f )
{
fSpeed = DotProduct( velocity, orientation[ 0 ] );
vecDelta = velocity * level.frametime;
for( i = 0; i < 3; i++ )
{
if( fSpeed > 0.0f )
{
gp = m_sMoveGrid->GetGridPoint( 0, i, 0 );
vecStart = gp->origin + origin;
}
else
{
gp = m_sMoveGrid->GetGridPoint( 2, i, 0 );
vecStart = gp->origin + origin;
}
if( real_velocity.length() > 0.5f )
{
vecStart2 = vecDelta + vecStart;
for( ;; )
{
tr = G_Trace( vecStart2, Vector( vm.mins ) - Vector( -32, -32, -32 ), Vector( vm.maxs ) + Vector( 32, 32, 32 ), vecStart2, this, 0x6001382, false, "Vehicle::MoveVehicle" );
if( !tr.ent || !tr.ent->entity || tr.ent->entity == world )
{
break;
}
tr.ent->entity->CheckGround();
if (!tr.ent->entity->groundentity || (tr.ent->entity->groundentity == edict &&
(!m_pCollisionEntity || tr.ent->entity != m_pCollisionEntity)))
{
Event *event = new Event( EV_Touch );
event->AddEntity( this );
tr.ent->entity->ProcessEvent( event );
event = new Event( EV_Touch );
event->AddEntity( tr.ent->entity );
ProcessEvent( event );
if( tr.ent->entity->IsSubclassOfSentient() )
bHitPerson = true;
if( g_showvehiclemovedebug->integer )
{
Com_Printf( "Vehicle Hit(MV0): %s : %s\n", tr.ent->entity->getClassname(), tr.ent->entity->targetname.c_str() );
}
break;
}
pSkippedEntities[ iNumSkippedEntities ] = tr.ent->entity;
iContentsEntities[ iNumSkippedEntities ] = tr.ent->r.contents;
solidEntities[ iNumSkippedEntities ] = tr.ent->solid;
iNumSkippedEntities++;
if( iNumSkippedEntities >= MAX_SKIPPED_ENTITIES )
{
gi.Error( ERR_DROP, "MAX_SKIPPED_ENTITIES hit in VehicleMove.\n" );
return;
}
tr.ent->entity->setSolidType( SOLID_NOT );
if( g_showvehiclemovedebug->integer )
{
Com_Printf( "Vehicle Skipped(MV0): %s : %s\n", tr.ent->entity->getClassname(), tr.ent->entity->targetname.c_str() );
}
}
}
if( g_showvehiclemovedebug->integer )
{
G_DebugBBox( vecStart, vm.mins, vm.maxs, 1.0f, 0.0f, 0.0f, 1.0f );
G_DebugBBox( vecStart + Vector( 0, 0, 64.0f ), vm.mins, vm.maxs, 1.0f, 0.0f, 0.0f, 1.0f );
}
vecEnd = vecStart + vecDelta;
for( ;; )
{
tr = G_Trace( vecStart, Vector( vm.mins ), Vector( vm.maxs ), vecEnd, this, edict->clipmask, false, "Vehicle::MoveVehicle" );
if( tr.fraction == 1.0f && !tr.allsolid && !tr.startsolid )
{
break;
}
if( !tr.ent || !tr.ent->entity || tr.ent->entity == world )
{
if( g_showvehiclemovedebug->integer )
{
if( flMoveFrac > tr.fraction )
{
flMoveFrac = tr.fraction - 0.1f;
}
G_DebugBBox( tr.endpos, vm.mins, vm.maxs, 0.0f, 1.0f, 0.0f, 1.0f );
}
vecStart = tr.endpos;
vecEnd = tr.endpos;
for( ;; )
{
tr = G_Trace( vecStart, Vector( vm.mins ), Vector( vm.maxs ), vecEnd, this, edict->clipmask, false, "Vehicle::MoveVehicle" );
if( tr.fraction != 1.0f || tr.plane.normal[ 2 ] < 0.7f || tr.allsolid )
{
flMoveFrac = 0.0f;
}
if( !tr.ent || !tr.ent->entity || tr.ent->entity == world )
{
goto _label1;
break;
}
tr.ent->entity->CheckGround();
if( !tr.ent->entity->groundentity )
{
break;
}
if( tr.ent != edict )
{
if( !m_pCollisionEntity )
{
break;
}
if( tr.ent->entity != m_pCollisionEntity )
{
break;
}
}
pSkippedEntities[ iNumSkippedEntities ] = tr.ent->entity;
iContentsEntities[ iNumSkippedEntities ] = tr.ent->r.contents;
solidEntities[ iNumSkippedEntities ] = tr.ent->solid;
iNumSkippedEntities++;
if( iNumSkippedEntities >= MAX_SKIPPED_ENTITIES )
{
gi.Error( ERR_DROP, "MAX_SKIPPED_ENTITIES hit in VehicleMove.\n" );
return;
}
tr.ent->entity->setSolidType( SOLID_NOT );
if( g_showvehiclemovedebug->integer )
{
Com_Printf( "Vehicle Skipped(MV2): %s : %s\n", tr.ent->entity->getClassname(), tr.ent->entity->targetname.c_str() );
}
}
event = new Event( EV_Touch );
event->AddEntity( this );
tr.ent->entity->ProcessEvent( event );
event = new Event( EV_Touch );
event->AddEntity( tr.ent->entity );
ProcessEvent( event );
if( tr.ent->entity->IsSubclassOfPlayer() )
{
bHitPerson = true;
}
_label1:
if( g_showvehiclemovedebug->integer )
{
Com_Printf( "Vehicle Hit(MV2): %s : %s\n", tr.ent->entity->getClassname(), tr.ent->entity->targetname.c_str() );
G_DebugBBox( vecStart, gp->vm.mins, gp->vm.maxs, 0.0f, 0.0f, 1.0f, 1.0f );
}
if( ( !tr.ent || !tr.ent->entity->IsSubclassOfProjectile() ) && driver.ent && driver.ent->IsSubclassOfPlayer() )
{
if( fSpeed > 0.0f )
{
if( i )
{
if( i == 2 && turnimpulse >= 0.0f )
{
turnimpulse -= 800.0f * level.frametime;
}
continue;
}
if( turnimpulse > 0.0f )
{
continue;
}
turnimpulse += 800 * level.frametime;
continue;
}
if( i == 0 )
{
if( turnimpulse < 0.0f )
continue;
turnimpulse -= 800 * level.frametime;
continue;
}
if( i == 2 && turnimpulse <= 0.0f )
{
turnimpulse += 800 * level.frametime;
continue;
}
}
if( flMoveFrac < 0.1f )
{
bDoGravity = false;
}
break;
}
tr.ent->entity->CheckGround();
if (!tr.ent->entity->groundentity || (tr.ent->entity->groundentity == edict &&
(!m_pCollisionEntity || tr.ent->entity != m_pCollisionEntity)))
{
Event *event = new Event( EV_Touch );
event->AddEntity( this );
tr.ent->entity->ProcessEvent( event );
event = new Event( EV_Touch );
event->AddEntity( tr.ent->entity );
ProcessEvent( event );
if( tr.ent->entity->IsSubclassOfSentient() )
bHitPerson = true;
if( g_showvehiclemovedebug->integer )
{
Com_Printf( "Vehicle Hit(MV): %s : %s\n", tr.ent->entity->getClassname(), tr.ent->entity->targetname.c_str() );
}
break;
}
pSkippedEntities[ iNumSkippedEntities ] = tr.ent->entity;
iContentsEntities[ iNumSkippedEntities ] = tr.ent->r.contents;
solidEntities[ iNumSkippedEntities ] = tr.ent->solid;
iNumSkippedEntities++;
if( iNumSkippedEntities >= MAX_SKIPPED_ENTITIES )
{
gi.Error( ERR_DROP, "MAX_SKIPPED_ENTITIES hit in VehicleMove.\n" );
return;
}
tr.ent->entity->setSolidType( SOLID_NOT );
if( g_showvehiclemovedebug->integer )
{
Com_Printf( "Vehicle Skipped(MV): %s : %s\n", tr.ent->entity->getClassname(), tr.ent->entity->targetname.c_str() );
}
}
}
}
}
/*
====================
Vehicle::AssertMove
====================
*/
bool Vehicle::AssertMove
(
Vector vNewOrigin,
Vector vOldOrigin
)
{
Entity *check;
gentity_t *edict;
int touch[ MAX_GENTITIES ];
int i;
int num;
if( m_pCollisionEntity )
{
num = gi.AreaEntities( m_pCollisionEntity->mins, m_pCollisionEntity->maxs, touch, MAX_GENTITIES );
}
else
{
num = gi.AreaEntities( mins, maxs, touch, MAX_GENTITIES );
}
if( num <= 0 )
{
return true;
}
for( i = 0; i < num; i++ )
{
edict = &g_entities[ touch[ i ] ];
check = edict->entity;
if( check->edict->s.number != edict->s.number && edict->solid && check->movetype != MOVETYPE_STOP )
{
if( check->movetype != MOVETYPE_NONE
&& check->movetype != MOVETYPE_NOCLIP
&& edict->r.contents != CONTENTS_PLAYERCLIP
&& IsTouching( check )
&& G_TestEntityPosition( check, check->origin ) )
{
return false;
}
}
}
return true;
}
/*
====================
Vehicle::AssertRotation
====================
*/
bool Vehicle::AssertRotation
(
Vector vNewAngles,
Vector vOldAngles
)
{
Vector i;
Vector j;
Vector k;
Vector i2;
Vector j2;
Vector k2;
Vector vAngleDiff;
float mAngleDiff[ 3 ][ 3 ];
AngleVectorsLeft( vOldAngles, i, j, k );
AngleVectorsLeft( vNewAngles, i2, j2, k2 );
AnglesSubtract( vOldAngles, vNewAngles, vAngleDiff );
AngleVectorsLeft( vAngleDiff, mAngleDiff[ 0 ], mAngleDiff[ 1 ], mAngleDiff[ 2 ] );
// FIXME: not sure what it is supposed to do. Should we put an assert there ?
return true;
}
/*
====================
Vehicle::NoMove
====================
*/
void Vehicle::NoMove
(
void
)
{
vmove_t vm;
SetMoveInfo( &vm );
VectorClear2D( vs.desired_dir );
VmoveSingle( &vm );
GetMoveInfo( &vm );
}
/*
====================
Vehicle::SlidePush
====================
*/
void Vehicle::SlidePush
(
Vector vPush
)
{
vmove_t vm;
int i;
int j;
Entity *pSkippedEntities[ MAX_SKIPPED_ENTITIES ];
int iContentsEntities[ MAX_SKIPPED_ENTITIES ];
solid_t solidEntities[ MAX_SKIPPED_ENTITIES ];
int iNumSkippedEntities = 0;
int iNumSkipped = 0;
gentity_t *other;
Vector newOrigin;
do
{
SetMoveInfo( &vm );
VectorCopy( vPush, vs.velocity );
vs.desired_dir[ 0 ] = vPush[ 0 ];
vs.desired_dir[ 1 ] = vPush[ 1 ];
VectorNormalize2D( vs.desired_dir );
if( g_showvehiclemovedebug->integer )
{
G_DebugBBox( origin, vm.mins, vm.maxs, 1.0f, 0.0f, 0.0f, 1.0f );
G_DebugBBox( origin, vm.mins, vm.maxs, 0.0f, 1.0f, 0.0f, 1.0f );
}
VmoveSingle( &vm );
iNumSkippedEntities = 0;
for( i = 0; i < vm.numtouch; i++ )
{
other = &g_entities[ vm.touchents[ i ] ];
for( j = 0; j < i; j++ )
{
if( &g_entities[ j ] == other )
{
break;
}
}
if( j == i && other->entity )
{
other->entity->CheckGround();
if( other->entity->groundentity && ( other->entity->groundentity == edict || other->entity == m_pCollisionEntity ) )
{
// save the entity
pSkippedEntities[ iNumSkipped ] = other->entity;
iContentsEntities[ iNumSkipped ] = other->r.contents;
solidEntities[ iNumSkipped ] = other->solid;
iNumSkipped++;
if( iNumSkipped >= MAX_SKIPPED_ENTITIES )
{
gi.Error( ERR_DROP, "MAX_SKIPPED_ENTITIES hit in VehicleMove.\n" );
return;
}
other->entity->setSolidType( SOLID_NOT );
iNumSkippedEntities++;
}
if( g_showvehiclemovedebug->integer )
{
Com_Printf( "Vehicle Hit(SP): %s : %s\n", other->entity->getClassname(), other->entity->targetname.c_str() );
}
}
}
} while( iNumSkippedEntities != 0 );
if( bindmaster )
{
newOrigin = vm.vs->origin - bindmaster->origin;
}
else
{
newOrigin = vm.vs->origin;
}
setLocalOrigin( newOrigin );
if( g_showvehiclemovedebug->integer )
{
G_DebugBBox( origin, vm.mins, vm.maxs, 0, 0, 1.0f, 1.0f );
}
TouchStuff( &vm );
for( i = 0; i < iNumSkipped; i++ )
{
pSkippedEntities[ i ]->setSolidType( solidEntities[ i ] );
pSkippedEntities[ i ]->edict->r.contents = iContentsEntities[ i ];
}
}
/*
====================
Vehicle::SetupPath
====================
*/
void Vehicle::SetupPath
(
cSpline<4, 512> *pPath,
SimpleEntity *se
)
{
Vector vLastOrigin;
SimpleEntity *ent;
float fCurLength = 0.0f;
int i = 1;
if( !pPath )
{
return;
}
pPath->Reset();
if( !se->target.length() || !*se->target )
{
return;
}
vLastOrigin = se->origin;
for( ent = se; ent != NULL; ent = ent->Next(), i++ )
{
Vector vDelta = vLastOrigin - ent->origin;
float vTmp[ 4 ];
if( vDelta.length() == 0.0f && i > 1 )
{
Com_Printf( "^~^~^Warning: Vehicle Driving with a Path that contains 2 equal points\n" );
}
else
{
vLastOrigin = ent->origin;
fCurLength += vDelta.length();
vTmp[ 0 ] = fCurLength;
VectorCopy( vTmp + 1, ent->origin );
if( ent->IsSubclassOfVehiclePoint() )
{
pPath->Add( vTmp, ( ( VehiclePoint * )ent )->spawnflags );
}
else
{
pPath->Add( vTmp, 0 );
}
}
if( ent == se && i > 1 )
{
break;
}
}
}
/*
====================
Vehicle::UpdateSound
====================
*/
void Vehicle::UpdateSound
(
void
)
{
float pitch;
float volume;
if( level.time < m_fNextSoundState )
{
return;
}
// Calculate the pitch based on the vehicle's speed
pitch = ( velocity.length() - m_fSoundMinSpeed ) / ( m_fSoundMaxSpeed - m_fSoundMinSpeed );
if( pitch > 1.0f )
{
pitch = 1.0f;
}
else if( pitch < 0.0f )
{
pitch = 0.0f;
}
pitch = m_fSoundMinPitch + ( m_fSoundMaxPitch - m_fSoundMinPitch ) * pitch;
volume = ( velocity.length() - m_fVolumeMinSpeed ) / ( m_fVolumeMaxSpeed - m_fVolumeMinSpeed );
if( volume > 1.0f )
{
volume = 1.0f;
}
else if( volume < 0.0f )
{
volume = 0.0f;
}
volume = this->m_fVolumeMinPitch + ( this->m_fVolumeMaxPitch - this->m_fVolumeMinPitch ) * volume;
switch( m_eSoundState )
{
case ST_OFF:
StopLoopSound();
TurnOffVehicleSoundEntities();
m_fNextSoundState = level.time;
if( driver.ent || m_bAutoPilot )
{
m_eSoundState = ST_OFF_TRANS_IDLE;
}
break;
case ST_OFF_TRANS_IDLE:
m_fNextSoundState = level.time;
m_eSoundState = ST_IDLE;
Sound( m_sSoundSet + "snd_on" );
LoopSound( m_sSoundSet + "snd_idle" );
break;
case ST_IDLE_TRANS_OFF:
m_fNextSoundState = level.time;
m_eSoundState = ST_OFF;
Sound( m_sSoundSet + "snd_off" );
StopLoopSound();
break;
case ST_IDLE:
m_fNextSoundState = level.time;
if( driver.ent || m_bAutoPilot )
{
if( fabs( DotProduct( orientation[ 0 ], velocity ) ) > 10.0f )
{
m_eSoundState = ST_IDLE_TRANS_RUN;
}
}
else
{
m_eSoundState = ST_IDLE_TRANS_OFF;
}
LoopSound( m_sSoundSet + "snd_idle" );
TurnOffVehicleSoundEntities();
break;
case ST_IDLE_TRANS_RUN:
m_fNextSoundState = level.time;
m_eSoundState = ST_RUN;
Sound( m_sSoundSet + "snd_revup" );
LoopSound( m_sSoundSet + "snd_run", -1.0f, -1.0f, -1.0f, pitch );
break;
case ST_RUN:
m_fNextSoundState = level.time;
if( fabs( DotProduct( orientation[ 0 ], velocity ) ) < 10.0f )
{
m_eSoundState = ST_RUN_TRANS_IDLE;
}
TurnOnVehicleSoundEntities();
LoopSound( m_sSoundSet + "snd_run", volume, -1.0f, -1.0f, pitch );
break;
case ST_RUN_TRANS_IDLE:
m_fNextSoundState = level.time;
m_eSoundState = ST_IDLE;
Sound( m_sSoundSet + "snd_revdown" );
LoopSound( m_sSoundSet + "snd_idle" );
break;
default:
m_fNextSoundState = level.time;
m_eSoundState = ST_OFF;
break;
}
}
/*
====================
Vehicle::SetupVehicleSoundEntities
====================
*/
void Vehicle::SetupVehicleSoundEntities
(
void
)
{
int i;
Vector a;
Vector b;
Vector c;
Vector start;
angles.AngleVectorsLeft( &a, &b, &c );
// place the sound entities in the vehicle wheels
for( i = 0; i < MAX_SOUND_ENTITIES; i++ )
{
if( !m_pVehicleSoundEntities[ i ] )
{
m_pVehicleSoundEntities[ i ] = new VehicleSoundEntity( this );
}
start = origin + a * Corners[ i ][ 0 ] + b * Corners[ i ][ 1 ] + c * Corners[ i ][ 2 ];
m_pVehicleSoundEntities[ i ]->setOrigin( start );
}
}
/*
====================
Vehicle::RemoveVehicleSoundEntities
====================
*/
void Vehicle::RemoveVehicleSoundEntities
(
void
)
{
for( int i = 0; i < MAX_SOUND_ENTITIES; i++ )
{
if( !m_pVehicleSoundEntities[ i ] )
{
continue;
}
m_pVehicleSoundEntities[ i ]->PostEvent( EV_Remove, EV_VEHICLE );
}
}
/*
====================
Vehicle::TurnOnVehicleSoundEntities
====================
*/
void Vehicle::TurnOnVehicleSoundEntities
(
void
)
{
for( int i = 0; i < MAX_SOUND_ENTITIES; i++ )
{
if( !m_pVehicleSoundEntities[ i ] )
{
m_pVehicleSoundEntities[ i ] = new VehicleSoundEntity( this );
}
m_pVehicleSoundEntities[ i ]->Start();
}
}
/*
====================
Vehicle::TurnOffVehicleSoundEntities
====================
*/
void Vehicle::TurnOffVehicleSoundEntities
(
void
)
{
for( int i = 0; i < MAX_SOUND_ENTITIES; i++ )
{
if( !m_pVehicleSoundEntities[ i ] )
{
m_pVehicleSoundEntities[ i ] = new VehicleSoundEntity( this );
}
m_pVehicleSoundEntities[ i ]->Stop();
}
}
/*
====================
Vehicle::UpdateTires
====================
*/
void Vehicle::UpdateTires
(
void
)
{
int index;
trace_t trace;
Vector a;
Vector b;
Vector c;
Vector vTmp;
Vector t_mins;
Vector t_maxs;
Vector start;
Vector end;
Vector boxoffset;
Entity *pSkippedEntities[ MAX_SKIPPED_ENTITIES ];
int iContentsEntities[ MAX_SKIPPED_ENTITIES ];
solid_t solidEntities[ MAX_SKIPPED_ENTITIES ];
int iNumSkippedEntities;
int iNumSkipped = 0;
t_mins = mins * 0.25f;
t_maxs = maxs * 0.25f;
if( real_velocity.length() <= 0.5f && m_iLastTiresUpdate != -1 )
{
if( m_iLastTiresUpdate + 1000 > level.inttime )
return;
}
m_iLastTiresUpdate = level.inttime;
vTmp[ 1 ] = angles[ 1 ] + m_fSkidAngle;
AngleVectors( vTmp, a, b, c );
// Temporary make slots non-solid for G_Trace
SetSlotsNonSolid();
do
{
iNumSkippedEntities = 0;
for( index = 0; index < MAX_CORNERS; index++ )
{
boxoffset = Corners[ index ];
start = origin + a * boxoffset[ 0 ] + b * boxoffset[ 1 ] + c * boxoffset[ 2 ];
end = start;
end[ 2 ] -= 400.0f;
trace = G_Trace( start, t_mins, t_maxs, end, this, MASK_VEHICLE, false, "Vehicle::PostThink Corners" );
if( g_showvehiclemovedebug->integer )
{
G_DebugBBox( origin, start, end, 1.0f, 1.0f, 1.0f, 1.0f );
G_DebugBBox( origin, start, trace.endpos, 1.0f, 0.0f, 0.0f, 1.0f );
}
if( trace.ent && trace.ent->entity && trace.ent->entity->isSubclassOf( VehicleCollisionEntity ) )
{
// save the entity
pSkippedEntities[ iNumSkipped ] = trace.ent->entity;
iContentsEntities[ iNumSkipped ] = trace.ent->r.contents;
solidEntities[ iNumSkipped ] = trace.ent->solid;
iNumSkipped++;
if( iNumSkipped >= MAX_SKIPPED_ENTITIES )
{
gi.Error( ERR_DROP, "MAX_SKIPPED_ENTITIES hit in VehicleMove.\n" );
return;
}
trace.ent->entity->setSolidType( SOLID_NOT );
iNumSkippedEntities++;
}
if( trace.fraction == 1.0 )
{
m_bTireHit[ index ] = false;
}
else
{
m_vTireEnd[ index ] = trace.endpos;
m_bTireHit[ index ] = true;
}
}
} while( iNumSkippedEntities != 0 );
for( index = 0; index < iNumSkipped; index++ )
{
pSkippedEntities[ index ]->setSolidType( solidEntities[ index ] );
pSkippedEntities[ index ]->edict->r.contents = iContentsEntities[ index ];
}
// Turn slots back into a solid state
SetSlotsSolid();
}
/*
====================
Vehicle::UpdateNormals
====================
*/
void Vehicle::UpdateNormals
(
void
)
{
Vector vDist1;
Vector vDist2;
Vector vCross;
Vector temp;
Vector pitch;
Vector i;
Vector j;
if (real_velocity.length() <= 0.5)
{
if (m_iLastTiresUpdate != -1 && m_iLastTiresUpdate + 1000 > level.inttime)
return;
}
AngleVectorsLeft(angles, NULL, pitch, NULL);
m_vNormalSum = vec_zero;
pitch = -pitch;
m_iNumNormals = 0;
if (m_bTireHit[0] && m_bTireHit[1] && m_bTireHit[2])
{
vDist1 = m_vTireEnd[1] - m_vTireEnd[0];
vDist2 = m_vTireEnd[1] - m_vTireEnd[2];
vCross.CrossProduct(vDist1, vDist2);
VectorNormalize(vCross);
m_vNormalSum += vCross;
m_iNumNormals++;
}
if (m_bTireHit[1] && m_bTireHit[2] && m_bTireHit[3])
{
vDist1 = m_vTireEnd[2] - m_vTireEnd[1];
vDist2 = m_vTireEnd[2] - m_vTireEnd[3];
vCross.CrossProduct(vDist1, vDist2);
VectorNormalize(vCross);
m_vNormalSum += vCross;
m_iNumNormals++;
}
if (m_bTireHit[2] && m_bTireHit[3] && m_bTireHit[0])
{
vDist1 = m_vTireEnd[3] - m_vTireEnd[0];
vDist2 = m_vTireEnd[3] - m_vTireEnd[2];
vCross.CrossProduct(vDist1, vDist2);
VectorNormalize(vCross);
m_vNormalSum += vCross;
m_iNumNormals++;
}
if (m_bTireHit[3] && m_bTireHit[0] && m_bTireHit[1])
{
vDist1 = m_vTireEnd[0] - m_vTireEnd[3];
vDist2 = m_vTireEnd[0] - m_vTireEnd[1];
vCross.CrossProduct(vDist1, vDist2);
VectorNormalize(vCross);
m_vNormalSum += vCross;
m_iNumNormals++;
}
if (m_iNumNormals > 1)
{
temp = m_vNormalSum / m_iNumNormals;
i.CrossProduct(temp, pitch);
angles[0] = i.toPitch();
j.CrossProduct(temp, i);
angles[2] = j.toPitch();
}
}
/*
====================
Vehicle::UpdateBones
====================
*/
void Vehicle::UpdateBones
(
void
)
{
float fNewTurnAngle = AngleNormalize180( turnangle - m_fSkidAngle );
if( fabs( fNewTurnAngle ) > maxturnrate )
{
fNewTurnAngle = maxturnrate;
}
SetControllerAngles( 0, Vector( 0, fNewTurnAngle, 0 ) );
SetControllerAngles( 1, Vector( 0, fNewTurnAngle, 0 ) );
}
/*
====================
Vehicle::UpdateShaderOffset
====================
*/
void Vehicle::UpdateShaderOffset
(
void
)
{
m_fShaderOffset -= orientation[ 0 ] * real_velocity * 0.25 * level.frametime;
edict->s.shader_time = m_fShaderOffset;
}
/*
====================
Vehicle::UpdateTurretSlot
====================
*/
void Vehicle::UpdateTurretSlot
(
int iSlot
)
{
orientation_t orient;
if( !( Turrets[ iSlot ].flags & SLOT_BUSY ) || !Turrets[ iSlot ].ent )
{
return;
}
if( Turrets[ iSlot ].boneindex != -1 )
{
GetTag( Turrets[ iSlot ].boneindex, &orient );
if( Turrets[ iSlot ].ent->IsSubclassOfActor() )
{
Turrets[ iSlot ].ent->setOriginEvent( orient.origin );
}
else
{
Turrets[ iSlot ].ent->setOrigin( orient.origin );
}
}
else
{
Vector forward = orientation[ 0 ];
Vector left = orientation[ 1 ];
Vector up = orientation[ 2 ];
if( Turrets[ iSlot ].ent->IsSubclassOfActor() )
{
Turrets[ iSlot ].ent->setOriginEvent( origin );
}
else
{
Turrets[ iSlot ].ent->setOrigin( origin );
}
}
Turrets[ iSlot ].ent->avelocity = avelocity;
Turrets[ iSlot ].ent->velocity = velocity;
if( !Turrets[ iSlot ].ent->IsSubclassOfActor() || ( ( Actor * )Turrets[ iSlot ].ent.Pointer() )->m_Enemy )
{
Turrets[ iSlot ].ent->setAngles( angles );
}
}
/*
====================
Vehicle::UpdatePassengerSlot
====================
*/
void Vehicle::UpdatePassengerSlot
(
int iSlot
)
{
orientation_t orient;
if( !( Passengers[ iSlot ].flags & SLOT_BUSY ) || !Passengers[ iSlot ].ent )
{
return;
}
if( Passengers[ iSlot ].boneindex != -1 )
{
GetTag( Passengers[ iSlot ].boneindex, &orient );
if( Passengers[ iSlot ].ent->IsSubclassOfActor() )
{
Passengers[ iSlot ].ent->setOriginEvent( orient.origin );
}
else
{
Passengers[ iSlot ].ent->setOrigin( orient.origin );
}
}
else
{
if( Passengers[ iSlot ].ent->IsSubclassOfActor() )
{
Passengers[ iSlot ].ent->setOriginEvent( origin );
}
else
{
Passengers[ iSlot ].ent->setOrigin( origin );
}
}
Passengers[ iSlot ].ent->avelocity = avelocity;
Passengers[ iSlot ].ent->velocity = velocity;
if( !Passengers[ iSlot ].ent->IsSubclassOfActor() || ( ( Actor * )Passengers[ iSlot ].ent.Pointer() )->m_Enemy )
{
Passengers[ iSlot ].ent->setAngles( angles );
}
}
/*
====================
Vehicle::UpdateDriverSlot
====================
*/
void Vehicle::UpdateDriverSlot
(
int iSlot
)
{
orientation_t orient;
if( !( driver.flags & SLOT_BUSY ) || !driver.ent )
{
return;
}
if( driver.boneindex != -1 )
{
GetTag( driver.boneindex, &orient );
if( driver.ent->IsSubclassOfActor() )
{
driver.ent->setOriginEvent( orient.origin );
}
else
{
driver.ent->setOrigin( orient.origin );
}
}
else
{
Vector forward = orientation[ 0 ];
Vector left = orientation[ 1 ];
Vector up = orientation[ 2 ];
if( driver.ent->IsSubclassOfActor() )
{
driver.ent->setOriginEvent( origin + forward * driveroffset[ 0 ] + left * driveroffset[ 1 ] + up * driveroffset[ 2 ] );
}
else
{
driver.ent->setOrigin( origin + forward * driveroffset[ 0 ] + left * driveroffset[ 1 ] + up * driveroffset[ 2 ] );
}
}
if( drivable )
{
driver.ent->avelocity = avelocity;
driver.ent->velocity = velocity;
driver.ent->setAngles( angles );
}
}
/*
====================
Vehicle::UpdateSkidAngle
====================
*/
void Vehicle::UpdateSkidAngle
(
void
)
{
if( m_bEnableSkidding )
{
if( g_showvehiclemovedebug && g_showvehiclemovedebug->integer )
{
Com_Printf( "Skidding!\n" );
}
m_fSkidLeftForce += velocity.length() / 150.0f * turnangle;
m_fSkidRightForce += -m_fSkidAngle * 0.2;
m_fSkidRightForce *= 0.3f;
m_fSkidAngle = m_fSkidAngle + ( m_fSkidLeftForce + m_fSkidRightForce ) * 22.0f * level.frametime;
m_vSkidOrigin[ 0 ] = -fabs( m_fSkidAngle );
}
else
{
m_fSkidAngle = 0;
}
}
/*
====================
Vehicle::GetPathPosition
====================
*/
float Vehicle::GetPathPosition
(
cSpline<4, 512> *pPath,
int iNode
)
{
float *vTmp;
float vPrev[ 3 ];
float vCur[ 3 ];
float vTotal[ 3 ];
Vector vDelta;
float fTotal;
float fCoef;
vTmp = pPath->GetByNode( iNode, NULL );
VectorCopy( vCur, vTmp + 1 );
if( g_showvehiclemovedebug->integer )
{
G_DebugString( Vector( vTmp[ 1 ], vTmp[ 2 ], vTmp[ 3 ] ), 3.0f, 1.0f, 1.0f, 1.0f, "%f", vTmp[ 0 ] );
}
vTmp = pPath->GetByNode( iNode - 1, NULL );
VectorCopy( vPrev, vTmp + 1 );
if( g_showvehiclemovedebug->integer )
{
G_DebugString( Vector( vTmp[ 1 ], vTmp[ 2 ], vTmp[ 3 ] ), 3.0f, 1.0f, 1.0f, 1.0f, "%f", vTmp[ 0 ] );
}
VectorCopy( Vector( vCur ) - Vector( vPrev ), vTotal );
m_vIdealDir = vTotal;
fTotal = m_vIdealDir.length();
VectorNormalize( m_vIdealDir );
angles.AngleVectorsLeft( &vDelta, NULL, NULL );
fCoef = ProjectLineOnPlane( vDelta, DotProduct( vDelta, origin ), vPrev, vCur, NULL );
if( g_showvehiclemovedebug->integer )
{
G_DebugBBox( vPrev, Vector( -32.0f, -32.0f, -32.0f ), Vector( 32.0f, 32.0f, 32.0f ), 0, 1.0f, 1.0f, 1.0f );
G_DebugBBox( vCur, Vector( -32.0f, -32.0f, -32.0f ), Vector( 32.0f, 32.0f, 32.0f ), 1.0f, 1.0f, 0, 1.0f );
G_DebugArrow( vCur, m_vIdealDir * -1.0f, ( 1.0 - fCoef ) * fTotal, 0, 1.0f, 0, 1.0f );
G_DebugArrow( vPrev, m_vIdealDir, fCoef * fTotal, 0, 0, 1.0f, 1.0f );
}
return *pPath->GetByNode( iNode - ( 1.0 - fCoef ), NULL );
}
/*
====================
Vehicle::FactorInSkidOrigin
====================
*/
void Vehicle::FactorInSkidOrigin
(
void
)
{
Vector vNewOrigin;
vNewOrigin[ 0 ] = orientation[ 0 ][ 0 ] * m_vSkidOrigin[ 0 ] + orientation[ 1 ][ 0 ] * m_vSkidOrigin[ 1 ] + orientation[ 2 ][ 0 ] * m_vSkidOrigin[ 2 ];
vNewOrigin[ 1 ] = orientation[ 0 ][ 1 ] * m_vSkidOrigin[ 0 ] + orientation[ 1 ][ 1 ] * m_vSkidOrigin[ 1 ] + orientation[ 2 ][ 1 ] * m_vSkidOrigin[ 2 ];
vNewOrigin[ 2 ] = orientation[ 0 ][ 2 ] * m_vSkidOrigin[ 0 ] + orientation[ 1 ][ 2 ] * m_vSkidOrigin[ 1 ] + orientation[ 2 ][ 2 ] * m_vSkidOrigin[ 2 ];
m_vOriginOffset2 += vNewOrigin;
}
/*
====================
Vehicle::FactorInOriginOffset
====================
*/
void Vehicle::FactorInOriginOffset
(
void
)
{
origin += m_vOriginOffset;
setOrigin( origin );
}
/*
====================
Vehicle::CalculateOriginOffset
====================
*/
void Vehicle::CalculateOriginOffset
(
void
)
{
int index;
Vector vTireAvg;
Vector vMissHit;
Vector temp;
int iNum = 0;
Vector acceleration;
m_vOriginOffset += m_vOriginOffset2;
m_vOriginOffset2 = vec_zero;
for( index = 0; index < MAX_CORNERS; index++ )
{
if( m_bTireHit[ index ] )
{
iNum++;
temp = m_vTireEnd[ index ];
vTireAvg += origin - temp;
}
else
{
temp = Corners[ index ];
vMissHit = temp;
}
}
if( iNum == 3 )
{
temp = m_vNormalSum * ( 1.0f / m_iNumNormals );
ProjectPointOnPlane( acceleration, vMissHit, temp );
vTireAvg += acceleration;
}
else if( iNum == 4 )
{
vTireAvg *= 0.25f;
MatrixTransformVector( m_vOriginCornerOffset, orientation, acceleration );
vTireAvg -= acceleration;
m_vOriginOffset2 += vTireAvg;
}
m_vOriginOffset2[ 0 ] += orientation[ 0 ][ 0 ] * m_vSkidOrigin[ 0 ] + orientation[ 1 ][ 0 ] * m_vSkidOrigin[ 1 ] + orientation[ 2 ][ 0 ] * m_vSkidOrigin[ 2 ];
m_vOriginOffset2[ 1 ] += orientation[ 0 ][ 1 ] * m_vSkidOrigin[ 0 ] + orientation[ 1 ][ 1 ] * m_vSkidOrigin[ 1 ] + orientation[ 2 ][ 1 ] * m_vSkidOrigin[ 2 ];
m_vOriginOffset2[ 2 ] += orientation[ 0 ][ 2 ] * m_vSkidOrigin[ 0 ] + orientation[ 1 ][ 2 ] * m_vSkidOrigin[ 1 ] + orientation[ 2 ][ 2 ] * m_vSkidOrigin[ 2 ];
Vector vTmp = real_acceleration - prev_acceleration;
m_fDownForce = vTmp[ 2 ] * m_fZCoef;
if( m_fDownForce > m_fZMax )
{
m_fDownForce = m_fZMax;
}
else if( m_fDownForce < m_fZMin )
{
m_fDownForce = m_fZMin;
}
m_fUpForce = ( -m_vOriginOffset[ 2 ] * m_fBouncyCoef + m_fUpForce ) * m_fSpringyCoef;
m_vOriginOffset2[ 2 ] += ( m_fDownForce + m_fUpForce ) * 12.0 * level.frametime;
if( m_vOriginOffset2[ 2 ] > m_fZMax )
{
m_vOriginOffset2[ 2 ] = m_fZMax;
}
else if( m_vOriginOffset2[ 2 ] < m_fZMin )
{
m_vOriginOffset2[ 2 ] = m_fZMin;
}
m_vOriginOffset -= m_vOriginOffset2;
}
/*
====================
Vehicle::FactorOutOriginOffset
====================
*/
void Vehicle::FactorOutOriginOffset
(
void
)
{
origin -= m_vOriginOffset;
setOrigin( origin );
}
/*
====================
Vehicle::FactorInAnglesOffset
====================
*/
void Vehicle::FactorInAnglesOffset
(
Vector *vAddedAngles
)
{
( *vAddedAngles ) += m_vAnglesOffset;
( *vAddedAngles )[ 1 ] += m_fSkidAngle;
}
/*
====================
Vehicle::CalculateAnglesOffset
====================
*/
void Vehicle::CalculateAnglesOffset
(
Vector acceleration
)
{
if( level.time <= 1200 )
{
return;
}
m_fForwardForce += DotProduct( orientation[ 0 ], acceleration ) * m_fYawCoef;
m_fBackForce = ( -m_vAnglesOffset[ 0 ] * m_fBouncyCoef + m_fBackForce ) * m_fSpringyCoef;
m_vAnglesOffset[ 0 ] += m_fForwardForce + m_fBackForce * 12.0 * level.frametime;
if( m_vAnglesOffset[ 0 ] > m_fYawMax )
{
m_vAnglesOffset[ 0 ] = m_fYawMax;
}
else if( m_vAnglesOffset[ 0 ] < m_fYawMin )
{
m_vAnglesOffset[ 0 ] = m_fYawMin;
}
m_fForwardForce = 0;
m_fLeftForce += DotProduct( orientation[ 1 ], acceleration ) * m_fRollCoef;
m_fRightForce = ( -m_vAnglesOffset[ 2 ] * m_fBouncyCoef + m_fRightForce ) * m_fSpringyCoef;
m_vAnglesOffset[ 2 ] += 12.0 * ( m_fLeftForce + m_fRightForce ) *level.frametime;
if( m_vAnglesOffset[ 2 ] > m_fRollMax )
{
m_vAnglesOffset[ 2 ] = m_fRollMax;
}
else if( m_vAnglesOffset[ 2 ] < m_fRollMin )
{
m_vAnglesOffset[ 2 ] = m_fRollMin;
}
m_fLeftForce = 0;
}
/*
====================
Vehicle::FactorOutAnglesOffset
====================
*/
void Vehicle::FactorOutAnglesOffset
(
void
)
{
}
/*
====================
Vehicle::GetTagPositionAndOrientation
====================
*/
qboolean Vehicle::GetTagPositionAndOrientation
(
str tagname,
orientation_t *new_or
)
{
int tagnum = gi.Tag_NumForName( edict->tiki, tagname.c_str() );
if( tagnum < 0 )
{
warning( "Vehicle::GetTagPositionAndOrientation", "Could not find tag \"%s\"", tagname.c_str() );
return false;
}
else
{
return GetTagPositionAndOrientation( tagnum, new_or );
}
}
/*
====================
Vehicle::GetTagPositionAndOrientation
====================
*/
qboolean Vehicle::GetTagPositionAndOrientation
(
int tagnum,
orientation_t *new_or
)
{
int i;
orientation_t tag_or;
float axis[ 3 ][ 3 ];
GetRawTag( tagnum, &tag_or );
AnglesToAxis( angles, axis );
VectorCopy( origin, new_or->origin );
for( i = 0; i < 3; i++ )
{
VectorMA( new_or->origin, tag_or.origin[ i ], tag_or.axis[ i ], new_or->origin );
}
MatrixMultiply( tag_or.axis, axis, new_or->axis );
return true;
}
/*
====================
Vehicle::CalculateAnimationData
====================
*/
void Vehicle::CalculateAnimationData
(
Vector vAngles,
Vector vOrigin
)
{
float fLeft = fEpsilon();
float fRight = fEpsilon();
float fForward = 0;
float fBack = fEpsilon();
float fLow = fEpsilon();
if( vAngles[ 1 ] < 0.0 )
{
fBack = vAngles[ 1 ] / m_fYawMin;
}
else if( vAngles[ 1 ] > 0.0 )
{
fForward = vAngles[ 1 ] / m_fYawMax;
}
if( vAngles[ 2 ] > 0.0 )
{
fRight = vAngles[ 2 ] / m_fRollMin;
}
else if( vAngles[ 2 ] < 0.0 )
{
fLeft = vAngles[ 2 ] / m_fRollMax;
}
if( vOrigin[ 2 ] < 0.0 )
{
fBack = vOrigin[ 2 ] / m_fZMin;
}
else if( vOrigin[ 2 ] > 0.0 )
{
fForward = vOrigin[ 2 ] / m_fZMax;
}
NewAnim( "idle", 0 );
NewAnim( "lean_left", 0, 3, fLeft );
NewAnim( "lean_right", 0, 4, fRight );
NewAnim( "lean_forward", 0, 1, fForward );
NewAnim( "lean_back", 0, 2, fBack );
NewAnim( "high", 0, 6, fEpsilon() );
NewAnim( "low", 0, 5, fEpsilon() );
}
/*
====================
Vehicle::IsDamagedBy
Returns whether or not the vehicle is damaged by the specified entity.
====================
*/
bool Vehicle::IsDamagedBy
(
Entity *ent
)
{
int i = FindDriverSlotByEntity( ent );
if( i == -1 )
{
i = FindPassengerSlotByEntity( ent );
if( i == -1 )
{
i = FindTurretSlotByEntity( ent );
if( i == -1 )
{
for( i = 0; i < MAX_TURRETS; i++ )
{
TurretGun *pTurret = ( TurretGun * )Turrets[ i ].ent.Pointer();
if( !pTurret )
{
continue;
}
if( pTurret->IsSubclassOfTurretGun() && pTurret->GetOwner() == ent )
{
return false;
}
}
return true;
}
}
}
return false;
}
/*
====================
Vehicle::AutoPilot
====================
*/
void Vehicle::AutoPilot
(
void
)
{
float *vTmp;
if (!m_pCurPath || m_pCurPath->m_iPoints == 0)
{
m_bAutoPilot = false;
return;
}
if (g_showvehiclepath && g_showvehiclepath->integer)
{
int iFlags = 0;
float fZ;
Vector vTmp1;
Vector vTmp2;
for (int i = 0; i < m_pCurPath->m_iPoints; i++)
{
vTmp = m_pCurPath->GetByNode(i, &iFlags);
vTmp1 = vTmp + 1;
fZ = 0;
//FIXME: macros
if (iFlags & 1)
{
fZ = 1;
G_DebugString(vTmp1 + Vector(0, 0, 32), sin(level.time) + 3, 1, 1, 0, "START_STOPPING");
}
if (iFlags & 2)
{
G_DebugString(vTmp1 + Vector(0, 0, (fZ + 1) * 32), sin(level.time) + 3, 0, 1, 0, "START_SKIDDING");
fZ++;
}
if (iFlags & 4)
{
G_DebugString(vTmp1 + Vector(0, 0, (fZ + 1) * 32), sin(level.time) + 3, 0, 0, 1, "STOP_SKIDDING");
}
Vector vMaxs = Vector(sin(level.time), sin(level.time), sin(level.time)) * 16;
Vector vMins = vMaxs * -1;
vMaxs = Vector(sin(level.time), sin(level.time), sin(level.time)) * 16;
G_DebugBBox(vTmp1, vMins, vMaxs, 0, 0, 1, 1);
vTmp = m_pCurPath->GetByNode(i + 1, NULL);
vTmp2 = vTmp + 1;
G_DebugLine(vTmp1, vTmp2, 0, 1, 0, 1);
}
}
// FIXME: stub
}
/*
====================
Vehicle::GetSoundSet
====================
*/
str Vehicle::GetSoundSet
(
void
)
{
return m_sSoundSet;
}
/*
====================
Vehicle::QueryFreePassengerSlot
====================
*/
int Vehicle::QueryFreePassengerSlot
(
void
)
{
for( int i = 0; i < numPassengers; i++ )
{
if( Passengers[ i ].flags & SLOT_FREE )
{
return i;
}
}
return -1;
}
/*
====================
Vehicle::QueryFreeDriverSlot
====================
*/
int Vehicle::QueryFreeDriverSlot
(
void
)
{
return ( driver.flags & SLOT_FREE ) ? 0 : -1;
}
/*
====================
Vehicle::QueryFreeTurretSlot
====================
*/
int Vehicle::QueryFreeTurretSlot
(
void
)
{
for( int i = 0; i < numTurrets; i++ )
{
if( Turrets[ i ].flags & SLOT_FREE )
{
return i;
}
}
return -1;
}
/*
====================
Vehicle::QueryPassengerSlotPosition
====================
*/
void Vehicle::QueryPassengerSlotPosition
(
int slot,
float *pos
)
{
orientation_t orient;
if( Passengers[ slot ].enter_boneindex >= 0 )
{
GetTagPositionAndOrientation( Passengers[ slot ].enter_boneindex, &orient );
VectorCopy( orient.origin, pos );
}
else
{
VectorCopy( origin, pos );
}
}
/*
====================
Vehicle::QueryDriverSlotPosition
====================
*/
void Vehicle::QueryDriverSlotPosition
(
int slot,
float *pos
)
{
orientation_t orient;
if( driver.enter_boneindex >= 0 )
{
GetTagPositionAndOrientation( driver.enter_boneindex, &orient );
VectorCopy( orient.origin, pos );
}
else
{
VectorCopy( origin, pos );
}
}
/*
====================
Vehicle::QueryTurretSlotPosition
====================
*/
void Vehicle::QueryTurretSlotPosition
(
int slot,
float *pos
)
{
orientation_t orient;
if( Turrets[ slot ].enter_boneindex >= 0 )
{
GetTagPositionAndOrientation( Turrets[ slot ].enter_boneindex, &orient );
VectorCopy( orient.origin, pos );
}
else
{
VectorCopy( origin, pos );
}
}
/*
====================
Vehicle::QueryPassengerSlotAngles
====================
*/
void Vehicle::QueryPassengerSlotAngles
(
int slot,
float *ang
)
{
orientation_t orient;
GetTagPositionAndOrientation( Passengers[ slot ].enter_boneindex, &orient );
MatrixToEulerAngles( orient.axis, ang );
}
/*
====================
Vehicle::QueryDriverSlotAngles
====================
*/
void Vehicle::QueryDriverSlotAngles
(
int slot,
float *ang
)
{
orientation_t orient;
GetTagPositionAndOrientation( driver.enter_boneindex, &orient );
MatrixToEulerAngles( orient.axis, ang );
}
/*
====================
Vehicle::QueryTurretSlotAngles
====================
*/
void Vehicle::QueryTurretSlotAngles
(
int slot,
float *ang
)
{
orientation_t orient;
GetTagPositionAndOrientation( Turrets[ slot ].enter_boneindex, &orient );
MatrixToEulerAngles( orient.axis, ang );
}
/*
====================
Vehicle::QueryPassengerSlotStatus
====================
*/
int Vehicle::QueryPassengerSlotStatus
(
int slot
)
{
return Passengers[ slot ].flags;
}
/*
====================
Vehicle::QueryDriverSlotStatus
====================
*/
int Vehicle::QueryDriverSlotStatus
(
int slot
)
{
return driver.flags;
}
/*
====================
Vehicle::QueryTurretSlotStatus
====================
*/
int Vehicle::QueryTurretSlotStatus
(
int slot
)
{
return Turrets[ slot ].flags;
}
/*
====================
Vehicle::QueryPassengerSlotEntity
====================
*/
Entity *Vehicle::QueryPassengerSlotEntity
(
int slot
)
{
return Passengers[ slot ].ent;
}
/*
====================
Vehicle::QueryDriverSlotEntity
====================
*/
Entity *Vehicle::QueryDriverSlotEntity
(
int slot
)
{
return driver.ent;
}
/*
====================
Vehicle::QueryTurretSlotEntity
====================
*/
Entity *Vehicle::QueryTurretSlotEntity
(
int slot
)
{
return Turrets[ slot ].ent;
}
/*
====================
Vehicle::AttachPassengerSlot
====================
*/
void Vehicle::AttachPassengerSlot
(
int slot,
Entity *ent,
Vector vExitPosition
)
{
Entity *passenger;
str sName;
if( !ent )
{
return;
}
passenger = Passengers[ slot ].ent;
if( !passenger )
{
Passengers[ slot ].ent = ent;
Passengers[ slot ].flags = SLOT_BUSY;
sName = m_sSoundSet + "snd_doorclose";
Sound( sName );
Event *event = new Event( EV_Vehicle_Enter );
event->AddEntity( this );
driver.ent->ProcessEvent( event );
offset = ent->origin - origin;
flags |= FL_POSTTHINK;
SetDriverAngles( seatangles + angles );
}
else if( !isLocked() && ent == passenger )
{
DetachPassengerSlot( slot, vec_zero, NULL );
}
}
/*
====================
Vehicle::AttachDriverSlot
====================
*/
void Vehicle::AttachDriverSlot
(
int slot,
Entity *ent,
Vector vExitPosition
)
{
Entity *d;
str sName;
if( !ent || !ent->IsSubclassOfSentient() )
{
return;
}
d = Driver();
if( !d )
{
driver.ent = ent;
driver.flags = SLOT_BUSY;
lastdriver.ent = driver.ent;
sName = m_sSoundSet + "snd_doorclose";
Sound( sName );
sName = m_sSoundSet + "snd_start";
Sound( sName );
Event *event = new Event( EV_Vehicle_Enter );
event->AddEntity( this );
driver.ent->ProcessEvent( event );
offset = ent->origin - origin;
flags |= FL_POSTTHINK;
SetDriverAngles( seatangles + angles );
}
else if( !isLocked() && ent == d )
{
DetachDriverSlot( slot, vec_zero, NULL );
}
}
/*
====================
Vehicle::AttachTurretSlot
Attach a turret or a sentient that will use the turret to the vehicle.
====================
*/
void Vehicle::AttachTurretSlot
(
int slot,
Entity *ent,
Vector vExitPosition,
Vector *vExitAngles
)
{
TurretGun *pTurret;
VehicleTurretGun *pVehicleTurret;
str sName;
if( !ent )
{
return;
}
pTurret = ( TurretGun * )Turrets[ slot ].ent.Pointer();
if( pTurret && ent->IsSubclassOfWeapon() )
{
if( !isLocked() )
DetachTurretSlot( slot, vec_zero, NULL );
}
else
{
if( ent->IsSubclassOfWeapon() )
{
Turrets[ slot ].ent = ent;
Turrets[ slot ].flags = SLOT_BUSY;
pTurret = ( TurretGun * )ent;
ent->takedamage = DAMAGE_NO;
ent->setSolidType( SOLID_NOT );
Event *event = new Event( EV_Vehicle_Enter );
event->AddEntity( this );
Turrets[ slot ].ent->ProcessEvent( event );
offset = ent->origin - origin;
flags |= FL_POSTTHINK;
Turrets[ slot ].ent->setAngles( angles );
if( pTurret->IsSubclassOfTurretGun() )
{
pTurret->m_bUsable = false;
pTurret->m_bRestable = false;
}
}
else if( pTurret )
{
Entity *pTurretOwner = NULL;
Entity *pRemoteTurretOwner = NULL;
if( pTurret->IsSubclassOfTurretGun() )
{
pTurretOwner = pTurret->GetOwner();
}
if( pTurret->IsSubclassOfVehicleTurretGun() )
{
pVehicleTurret = ( VehicleTurretGun * )pTurret;
pRemoteTurretOwner = pVehicleTurret->GetRemoteOwner();
}
if( pTurret->IsSubclassOfTurretGun() )
{
if( pTurret->IsSubclassOfVehicleTurretGun() && pVehicleTurret->isLocked() )
{
ScriptError( "Turret is locked, cannot attach to turret slot." );
}
pTurret->m_bUsable = true;
}
Event *event = new Event( EV_Use );
event->AddEntity( ent );
pTurret->ProcessEvent( event );
if( ent->IsSubclassOfPlayer() )
{
Player *pPlayer = ( Player * )ent;
pPlayer->m_pVehicle = this;
}
if( pTurret->IsSubclassOfTurretGun() )
{
pTurret->m_bUsable = false;
}
if( pTurretOwner == ent || pRemoteTurretOwner == ent )
{
if( pRemoteTurretOwner )
{
pVehicleTurret->SetRemoteOwner( NULL );
}
if( vExitPosition != vec_zero )
{
Vector pos;
trace_t trace;
pos = vExitPosition;
trace = G_Trace(
pos,
ent->mins,
ent->maxs,
pos,
NULL,
edict->clipmask,
false,
"Vehicle::AttachTurretSlot" );
if( !trace.allsolid && !trace.startsolid )
{
trace = G_Trace(
pos,
ent->mins,
ent->maxs,
pos - Vector( 0, 0, 128 ),
NULL,
edict->clipmask,
false,
"Vehicle::AttachTurretSlot" );
if( trace.fraction < 1.0f )
{
if( vExitAngles )
{
ent->setAngles( *vExitAngles );
}
ent->setOrigin( trace.endpos );
velocity = vec_zero;
Event *event = new Event( EV_Vehicle_Exit );
event->AddEntity( this );
ent->ProcessEvent( event );
}
}
}
else
{
int height;
int ang;
Vector angles;
Vector forward;
Vector pos;
float ofs;
trace_t trace;
if( locked )
return;
//
// place the turret on the ground
//
ofs = size.length() * 0.5f;
for( height = 0; height < 100; height += 16 )
{
for( ang = 0; ang < 360; ang += 30 )
{
angles[ 1 ] = ent->angles[ 1 ] + ang + 90;
angles.AngleVectors( &forward, NULL, NULL );
pos = origin + ( forward * ofs );
pos[ 2 ] += height;
trace = G_Trace( pos, ent->mins, ent->maxs, pos, NULL, MASK_PLAYERSOLID, false, "Vehicle::AttachTurretSlot" );
if( !trace.startsolid && !trace.allsolid )
{
Vector end;
end = pos;
end[ 2 ] -= 128;
trace = G_Trace( pos, ent->mins, ent->maxs, end, NULL, MASK_PLAYERSOLID, false, "Vehicle::AttachTurretSlot" );
if( trace.fraction < 1.0f )
{
ent->setOrigin( vExitPosition );
ent->velocity = vec_zero;
Event *ev = new Event( EV_Vehicle_Exit );
ev->AddEntity( this );
ent->ProcessEvent( ev );
}
}
}
}
}
}
}
}
}
/*
====================
Vehicle::DetachPassengerSlot
====================
*/
void Vehicle::DetachPassengerSlot
(
int slot,
Vector vExitPosition,
Vector *vExitAngles
)
{
Entity *passenger = Passengers[ slot ].ent;
if( !passenger )
{
return;
}
if( vExitPosition == vec_zero )
{
int height;
int ang;
Vector angles;
Vector forward;
Vector pos;
float ofs;
trace_t trace;
if( locked )
return;
//
// place the passenger on the ground
//
ofs = size.length() * 0.5f;
for( height = 0; height < 100; height += 16 )
{
for( ang = 0; ang < 360; ang += 30 )
{
angles[ 1 ] = passenger->angles[ 1 ] + ang + 90;
angles.AngleVectors( &forward, NULL, NULL );
pos = origin + ( forward * ofs );
pos[ 2 ] += height;
trace = G_Trace( pos, passenger->mins, passenger->maxs, pos, NULL, MASK_PLAYERSOLID, false, "Vehicle::DetachPassengerSlot" );
if( !trace.startsolid && !trace.allsolid )
{
Vector end;
end = pos;
end[ 2 ] -= 128;
trace = G_Trace( pos, passenger->mins, passenger->maxs, end, NULL, MASK_PLAYERSOLID, false, "Vehicle::DetachPassengerSlot" );
if( trace.fraction < 1.0f )
{
passenger->setOrigin( pos );
passenger->velocity = vec_zero;
Event *ev = new Event( EV_Vehicle_Exit );
ev->AddEntity( this );
passenger->ProcessEvent( ev );
Sound( m_sSoundSet + "snd_dooropen" );
}
}
}
}
}
else
{
if( vExitAngles )
{
passenger->setAngles( *vExitAngles );
}
passenger->setOrigin( vExitPosition );
passenger->velocity = vec_zero;
Event *ev = new Event( EV_Vehicle_Exit );
ev->AddEntity( this );
passenger->ProcessEvent( ev );
}
Passengers[ slot ].ent = NULL;
Passengers[ slot ].flags = SLOT_FREE;
}
/*
====================
Vehicle::DetachDriverSlot
====================
*/
void Vehicle::DetachDriverSlot
(
int slot,
Vector vExitPosition,
Vector *vExitAngles
)
{
Entity *other = driver.ent;
if( !other )
{
return;
}
if( vExitPosition == vec_zero )
{
int height;
int ang;
Vector angles;
Vector forward;
Vector pos;
float ofs;
trace_t trace;
if( other != driver.ent )
{
return;
}
if( locked )
return;
//
// place the driver.ent on the ground
//
ofs = size.length() * 0.5f;
for( height = 0; height < 100; height += 16 )
{
for( ang = 0; ang < 360; ang += 30 )
{
angles[ 1 ] = driver.ent->angles[ 1 ] + ang + 90;
angles.AngleVectors( &forward, NULL, NULL );
pos = origin + ( forward * ofs );
pos[ 2 ] += height;
trace = G_Trace( pos, driver.ent->mins, driver.ent->maxs, pos, NULL, MASK_PLAYERSOLID, false, "Vehicle::DetachDriverSlot" );
if( !trace.startsolid && !trace.allsolid )
{
Vector end;
end = pos;
end[ 2 ] -= 128;
trace = G_Trace( pos, driver.ent->mins, driver.ent->maxs, end, NULL, MASK_PLAYERSOLID, false, "Vehicle::DetachDriverSlot" );
if( trace.fraction < 1.0f )
{
driver.ent->setOrigin( pos );
turnimpulse = 0;
moveimpulse = 0;
jumpimpulse = 0;
Event *event = new Event( EV_Vehicle_Exit );
event->AddEntity( this );
driver.ent->ProcessEvent( event );
if( hasweapon )
{
Player *player = ( Player * )driver.ent.Pointer();
player->takeItem( weaponName.c_str() );
}
if( drivable )
{
StopLoopSound();
Sound( "snd_dooropen", CHAN_BODY );
Sound( "snd_stop", CHAN_VOICE );
driver.ent->setSolidType( SOLID_BBOX );
}
}
}
}
}
}
else
{
if( vExitAngles )
{
other->setAngles( *vExitAngles );
}
other->setOrigin( vExitPosition );
other->velocity = vec_zero;
Event *ev = new Event( EV_Vehicle_Exit );
ev->AddEntity( this );
other->ProcessEvent( ev );
}
driver.ent = NULL;
driver.flags = SLOT_FREE;
}
/*
====================
Vehicle::DetachTurretSlot
Detach a turret or a sentient.
====================
*/
void Vehicle::DetachTurretSlot
(
int slot,
Vector vExitPosition,
Vector *vExitAngles
)
{
Entity *passenger = Turrets[ slot ].ent;
if( !passenger )
{
return;
}
if( vExitPosition == vec_zero )
{
int height;
int ang;
Vector angles;
Vector forward;
Vector pos;
float ofs;
trace_t trace;
if( locked )
return;
//
// place the turret on the ground
//
ofs = size.length() * 0.5f;
for( height = 0; height < 100; height += 16 )
{
for( ang = 0; ang < 360; ang += 30 )
{
angles[ 1 ] = passenger->angles[ 1 ] + ang + 90;
angles.AngleVectors( &forward, NULL, NULL );
pos = origin + ( forward * ofs );
pos[ 2 ] += height;
trace = G_Trace( pos, passenger->mins, passenger->maxs, pos, NULL, MASK_PLAYERSOLID, false, "Vehicle::DetachTurretSlot" );
if( !trace.startsolid && !trace.allsolid )
{
Vector end;
end = pos;
end[ 2 ] -= 128;
trace = G_Trace( pos, passenger->mins, passenger->maxs, end, NULL, MASK_PLAYERSOLID, false, "Vehicle::DetachTurretSlot" );
if( trace.fraction < 1.0f )
{
passenger->setOrigin( pos );
passenger->velocity = vec_zero;
turnimpulse = 0;
moveimpulse = 0;
jumpimpulse = 0;
Event *ev = new Event( EV_Vehicle_Exit );
ev->AddEntity( this );
passenger->ProcessEvent( ev );
Sound( m_sSoundSet + "snd_dooropen" );
TurretGun *pTurret = ( TurretGun * )passenger;
if( pTurret->IsSubclassOfTurretGun() )
{
pTurret->m_bUsable = true;
pTurret->m_bRestable = true;
}
}
}
}
}
}
else
{
if( vExitAngles )
{
passenger->setAngles( *vExitAngles );
}
passenger->setOrigin( vExitPosition );
passenger->velocity = vec_zero;
Event *ev = new Event( EV_Vehicle_Exit );
ev->AddEntity( this );
passenger->ProcessEvent( ev );
}
Turrets[ slot ].ent = NULL;
Turrets[ slot ].flags = SLOT_FREE;
}
/*
====================
Vehicle::FindPassengerSlotByEntity
====================
*/
int Vehicle::FindPassengerSlotByEntity
(
Entity *ent
)
{
for( int i = 0; i < MAX_PASSENGERS; i++ )
{
if( Passengers[ i ].ent == ent )
{
return i;
}
}
return -1;
}
/*
====================
Vehicle::FindDriverSlotByEntity
====================
*/
int Vehicle::FindDriverSlotByEntity
(
Entity *ent
)
{
return driver.ent == ent ? 0 : -1;
}
/*
====================
Vehicle::FindTurretSlotByEntity
====================
*/
int Vehicle::FindTurretSlotByEntity
(
Entity *ent
)
{
for( int i = 0; i < MAX_TURRETS; i++ )
{
if( Turrets[ i ].ent == ent )
{
return i;
}
}
return -1;
}
/*
====================
Vehicle::SetSlotsNonSolid
====================
*/
void Vehicle::SetSlotsNonSolid
(
void
)
{
for( int i = 0; i < MAX_PASSENGERS; i++ )
{
Passengers[ i ].NotSolid();
}
for( int i = 0; i < MAX_TURRETS; i++ )
{
Turrets[ i ].NotSolid();
}
driver.NotSolid();
if( m_pCollisionEntity )
{
m_pCollisionEntity->NotSolid();
}
}
/*
====================
Vehicle::SetSlotsSolid
====================
*/
void Vehicle::SetSlotsSolid
(
void
)
{
for( int i = 0; i < MAX_PASSENGERS; i++ )
{
Passengers[ i ].Solid();
}
for( int i = 0; i < MAX_TURRETS; i++ )
{
Turrets[ i ].Solid();
}
driver.Solid();
if( m_pCollisionEntity )
{
m_pCollisionEntity->Solid();
}
}
/*
====================
Vehicle::KickSuspension
====================
*/
void Vehicle::KickSuspension
(
Vector vDirection,
float fForce
)
{
VectorNormalizeFast( vDirection );
m_fForwardForce += DotProduct( vDirection, orientation[ 1 ] ) * fForce;
m_fLeftForce += DotProduct( vDirection, orientation[ 0 ] ) * fForce;
}
/*
====================
Vehicle::isLocked
====================
*/
qboolean Vehicle::isLocked
(
void
)
{
return locked;
}
/*
====================
Vehicle::Lock
====================
*/
void Vehicle::Lock
(
void
)
{
locked = true;
}
/*
====================
Vehicle::UnLock
====================
*/
void Vehicle::UnLock
(
void
)
{
locked = false;
}
/*
====================
Vehicle::GetCollisionEntity
====================
*/
VehicleCollisionEntity *Vehicle::GetCollisionEntity
(
void
)
{
return m_pCollisionEntity;
}
CLASS_DECLARATION( Vehicle, DrivableVehicle, "script_drivablevehicle" )
{
{ &EV_Damage, &Entity::DamageEvent },
{ &EV_Killed, &DrivableVehicle::Killed },
{ NULL, NULL }
};
/*
====================
DrivableVehicle::DrivableVehicle
====================
*/
DrivableVehicle::DrivableVehicle()
{
if( LoadingSavegame )
{
// Archive function will setup all necessary data
return;
}
drivable = true;
flags |= FL_POSTTHINK | FL_THINK;
setMoveType( MOVETYPE_VEHICLE );
}
/*
====================
DrivableVehicle::Killed
====================
*/
void DrivableVehicle::Killed
(
Event *ev
)
{
Entity * ent;
Entity * attacker;
Vector dir;
Event * event;
const char * name;
VehicleBase *last;
takedamage = DAMAGE_NO;
setSolidType( SOLID_NOT );
hideModel();
attacker = ev->GetEntity( 1 );
//
// kill the driver.ent
//
if( driver.ent )
{
Vector dir;
SentientPtr sent;
Event * event;
velocity = vec_zero;
sent = ( Sentient * )driver.ent.Pointer();
event = new Event( EV_Use );
event->AddEntity( sent );
ProcessEvent( event );
dir = sent->origin - origin;
dir[ 2 ] += 64;
dir.normalize();
sent->Damage( this, this, sent->health * 2, origin, dir, vec_zero, 50, 0, MOD_VEHICLE );
}
if( flags & FL_DIE_EXPLODE )
{
CreateExplosion( origin, 150 * edict->s.scale, this, this, this );
}
if( flags & FL_DIE_GIBS )
{
setSolidType( SOLID_NOT );
hideModel();
CreateGibs( this, -150, edict->s.scale, 3 );
}
//
// kill all my wheels
//
last = this;
while( last->vlink )
{
last->vlink->PostEvent( EV_Remove, 0 );
last = last->vlink;
}
//
// kill the killtargets
//
name = KillTarget();
if( name && strcmp( name, "" ) )
{
ent = NULL;
do
{
ent = ( Entity * )G_FindTarget( ent, name );
if( !ent )
{
break;
}
ent->PostEvent( EV_Remove, 0 );
} while( 1 );
}
//
// fire targets
//
name = Target();
if( name && strcmp( name, "" ) )
{
ent = NULL;
do
{
ent = ( Entity * )G_FindTarget( ent, name );
if( !ent )
{
break;
}
event = new Event( EV_Activate );
event->AddEntity( attacker );
ent->ProcessEvent( event );
} while( 1 );
}
PostEvent( EV_Remove, 0 );
}