openmohaa/code/fgame/vehicle.cpp
smallmodel 8b6b1597d7
Check for bHitPerson in MV0, MV1 and MV2
This checks if the sentient being hit is dead, and skip it when moving the vehicle so the vehicle doesn't get stuck for when a sentient is dying
2024-11-23 22:58:51 +01:00

7034 lines
174 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.",
EV_NORMAL
);
Event EV_Vehicle_Enter
(
"enter",
EV_DEFAULT,
"eS",
"vehicle driver_anim",
"Called when someone gets into a vehicle.",
EV_NORMAL
);
Event EV_Vehicle_Exit
(
"exit",
EV_DEFAULT,
"e",
"vehicle",
"Called when driver gets out of the vehicle.",
EV_NORMAL
);
Event EV_Vehicle_Drivable
(
"drivable",
EV_DEFAULT,
NULL,
NULL,
"Make the vehicle drivable",
EV_NORMAL
);
Event EV_Vehicle_UnDrivable
(
"undrivable",
EV_DEFAULT,
NULL,
NULL,
"Make the vehicle undrivable",
EV_NORMAL
);
Event EV_Vehicle_PathDrivable
(
"pathDrivable",
EV_DEFAULT,
"b",
"pathDrivable",
"Make the vehicle drivable along a path",
EV_NORMAL
);
Event EV_Vehicle_Jumpable
(
"canjump",
EV_DEFAULT,
"b",
"jumpable",
"Sets whether or not the vehicle can jump",
EV_NORMAL
);
Event EV_Vehicle_Lock
(
"lock",
EV_DEFAULT,
NULL,
NULL,
"Sets the vehicle to be locked",
EV_NORMAL
);
Event EV_Vehicle_UnLock
(
"unlock",
EV_DEFAULT,
NULL,
NULL,
"Sets the vehicle to be unlocked",
EV_NORMAL
);
Event EV_Vehicle_SeatAnglesOffset
(
"seatanglesoffset",
EV_DEFAULT,
"v",
"angles",
"Set the angles offset of the seat",
EV_NORMAL
);
Event EV_Vehicle_SeatOffset
(
"seatoffset",
EV_DEFAULT,
"v",
"offset",
"Set the offset of the seat",
EV_NORMAL
);
Event EV_Vehicle_SetWeapon
(
"setweapon",
EV_DEFAULT,
"s",
"weaponname",
"Set the weapon for the vehicle",
EV_NORMAL
);
Event EV_Vehicle_SetName
(
"name",
EV_DEFAULT,
"s",
"vehicleName",
"Set the name for the vehicle",
EV_NORMAL
);
Event EV_Vehicle_ShowWeapon
(
"showweapon",
EV_DEFAULT,
NULL,
NULL,
"Set the weapon to be show in the view",
EV_NORMAL
);
Event EV_Vehicle_SetSpeed
(
"vehiclespeed",
EV_DEFAULT,
"f",
"speed",
"Set the speed of the vehicle",
EV_NORMAL
);
Event EV_Vehicle_SetTurnRate
(
"turnrate",
EV_DEFAULT,
"f",
"rate",
"Set the turning rate of the vehicle",
EV_NORMAL
);
Event EV_Vehicle_SteerInPlace
(
"steerinplace",
EV_DEFAULT,
NULL,
NULL,
"Set the vehicle to turn in place",
EV_NORMAL
);
Event EV_Vehicle_Destroyed
(
"vehicledestroyed",
EV_DEFAULT,
NULL,
NULL,
"Driver is dead",
EV_NORMAL
);
Event EV_Vehicle_Mass
(
"vehiclemass",
EV_DEFAULT,
"f",
"weight",
"Sets the mass of the vehicle (backmass = frontmass = mass/2)",
EV_NORMAL
);
Event EV_Vehicle_Front_Mass
(
"front_mass",
EV_DEFAULT,
"f",
"weight",
"Sets the mass of the front of the vehicle",
EV_NORMAL
);
Event EV_Vehicle_Back_Mass
(
"back_mass",
EV_DEFAULT,
"f",
"weight",
"Sets the mass of the back of the vehicle",
EV_NORMAL
);
Event EV_Vehicle_Tread
(
"vehicletread",
EV_DEFAULT,
"f",
"size",
"Sets the size of the wheels",
EV_NORMAL
);
Event EV_Vehicle_Radius
(
"vehicleradius",
EV_DEFAULT,
"f",
"size",
"Sets the radius of the wheels",
EV_NORMAL
);
Event EV_Vehicle_RollingResistance
(
"vehiclerollingresistance",
EV_DEFAULT,
"f",
"size",
"Sets the radius of the wheels",
EV_NORMAL
);
Event EV_Vehicle_Drag
(
"vehicledrag",
EV_DEFAULT,
"f",
"size",
"Sets the Drag Factor",
EV_NORMAL
);
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",
EV_NORMAL
);
Event EV_Vehicle_DriveNoWait
(
"driveNoWait",
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",
EV_NORMAL
);
Event EV_Vehicle_Stop
(
"stop",
EV_DEFAULT,
NULL,
NULL,
"Make the Vehicle Stop Moving... FULL BREAKS!",
EV_NORMAL
);
Event EV_Vehicle_FullStop
(
"fullstop",
EV_DEFAULT,
NULL,
NULL,
"Make the Vehicle Stop Moving... Completely!",
EV_NORMAL
);
Event EV_Vehicle_Init
(
"vehicleinit",
EV_DEFAULT,
NULL,
NULL,
"Initialized the Vehicle as the specified file",
EV_NORMAL
);
Event EV_Vehicle_BouncyCoef
(
"vehiclebouncy",
EV_DEFAULT,
"f",
"bouncycoef",
"Sets the Bouncy Coefficient for the shocks.",
EV_NORMAL
);
Event EV_Vehicle_SpringyCoef
(
"vehiclespringy",
EV_DEFAULT,
"f",
"springycoef",
"Sets the Springy Coefficient for the shocks.",
EV_NORMAL
);
Event EV_Vehicle_Yaw
(
"vehicleYaw",
EV_DEFAULT,
"fff",
"min max coef",
"Sets the Yaw min and max and the acceleration coefficient for the shocks.",
EV_NORMAL
);
Event EV_Vehicle_Roll
(
"vehicleRoll",
EV_DEFAULT,
"fff",
"min max coef",
"Sets the Roll min and max and the acceleration coefficient for the shocks.",
EV_NORMAL
);
Event EV_Vehicle_Z
(
"vehicleZ",
EV_DEFAULT,
"fff",
"min max coef",
"Sets the Z min and max and the acceleration coefficient for the shocks.",
EV_NORMAL
);
Event EV_Vehicle_QuerySpeed
(
"QuerySpeed",
EV_DEFAULT,
"f",
"speed",
"Returns the current speed of the vehicle.",
EV_RETURN
);
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_BounceForwardsVelocity
(
"BounceForwardsVelocity",
EV_DEFAULT,
NULL,
NULL,
"For vehicles on rails that are moving forwards, reverse our velocity.",
EV_NORMAL
);
Event EV_Vehicle_BounceBackwardsVelocity
(
"BounceBackwardsVelocity",
EV_DEFAULT,
"i",
"bStayFullSpeed",
"For vehicles on rails that are moving backwards, reverse our velocity. (Optionally pass bStayFullSpeed to keep "
"vehic"
"le at full speed after the bounce)",
EV_NORMAL
);
Event EV_Vehicle_StopForwardsVelocity
(
"StopForwardsVelocity",
EV_DEFAULT,
NULL,
NULL,
"Stops any forward motion for vehicles on rails.",
EV_NORMAL
);
Event EV_Vehicle_StopBackwardsVelocity
(
"StopBackwardsVelocity",
EV_DEFAULT,
NULL,
NULL,
"Stops any backwards motion for vehicles on rails.",
EV_NORMAL
);
Event EV_Vehicle_AttachPassengerSlot
(
"AttachPassengerSlot",
EV_DEFAULT,
"ie",
"slot entity",
"Attaches an entity to the specified slot.",
EV_NORMAL
);
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_position",
"Detaches an entity to the specified slot.",
EV_NORMAL
);
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.",
EV_NORMAL
);
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_position",
"Detaches an entity to the specified slot.",
EV_NORMAL
);
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.",
EV_NORMAL
);
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_position",
"Detaches an entity to the specified slot.",
EV_NORMAL
);
Event EV_Vehicle_WheelCorners
(
"VehicleWheelCorners",
EV_DEFAULT,
"vv",
"size offset",
"Sets the wheel trace corners.",
EV_NORMAL
);
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.",
EV_NORMAL
);
Event EV_Vehicle_SoundSet
(
"SoundSet",
EV_DEFAULT,
"s",
"soundset",
"Sets the Sound Set to use.",
EV_NORMAL
);
Event EV_Vehicle_SpawnTurret
(
"spawnturret",
EV_DEFAULT,
"is",
"slot tikifile",
"Spawns a turret with the specified model and connects it to the specified slot",
EV_NORMAL
);
Event EV_Vehicle_ModifyDrive
(
"modifydrive",
EV_DEFAULT,
"fff",
"desired_speed acceleration look_ahead",
"Modifys the parameters of the current drive.",
EV_NORMAL
);
Event EV_Vehicle_NextDrive
(
"nextdrive",
EV_DEFAULT,
"e",
"next_path",
"appends the specified path to the current path",
EV_NORMAL
);
Event EV_Vehicle_StopAtEnd
(
"stopatend",
EV_DEFAULT,
NULL,
NULL,
"Makes the vehicle slow down to a complete stop at the end of the path.",
EV_NORMAL
);
Event EV_Vehicle_LockMovement
(
"lockmovement",
EV_DEFAULT,
NULL,
NULL,
"The Vehicle cannot move.",
EV_NORMAL
);
Event EV_Vehicle_UnlockMovement
(
"unlockmovement",
EV_DEFAULT,
NULL,
NULL,
"The Vehicle can move again.",
EV_NORMAL
);
Event EV_Vehicle_RemoveOnDeath
(
"removeondeath",
EV_DEFAULT,
"i",
"removeondeath",
"If set to a non-zero value, vehicles will not be removed when they die",
EV_NORMAL
);
Event EV_Vehicle_SetExplosionModel
(
"explosionmodel",
EV_DEFAULT,
"s",
"model",
"Sets the TIKI to call when the vehicle dies.",
EV_NORMAL
);
Event EV_Vehicle_SetCollisionEntity
(
"setcollisionentity",
EV_DEFAULT,
"e",
"entity",
"Sets the Collision Entity.",
EV_NORMAL
);
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",
EV_NORMAL
);
Event EV_Vehicle_SetVolumeParameters
(
"setvolumeparameters",
EV_DEFAULT,
"ffff",
"min_speed min_volume max_speed max_volume",
"Sets the Volume parameters for this vehicle",
EV_NORMAL
);
Event EV_Vehicle_Skidding
(
"skidding",
EV_DEFAULT,
"i",
"on_off",
"Makes the vehicle skid around corners.",
EV_NORMAL
);
Event EV_Vehicle_ContinueSkidding
(
"_continueskidding",
EV_DEFAULT,
NULL,
NULL,
"Continues the skidding animation of a vehicle.",
EV_NORMAL
);
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",
EV_NORMAL
);
Event EV_Vehicle_VehicleAnimDone
(
"_vehicleanimdone",
EV_DEFAULT,
NULL,
NULL,
"For Internal Use Only",
EV_NORMAL
);
Event EV_Vehicle_VehicleMoveAnim
(
"moveanim",
EV_DEFAULT,
"s",
"anim_name",
"move the vehicle with an animation",
EV_NORMAL
);
Event EV_Vehicle_VehicleMoveAnimDone
(
"_moveanimdone",
EV_DEFAULT,
NULL,
NULL,
"For Internal Use Only",
EV_NORMAL
);
Event EV_Vehicle_DamageSounds
(
"damagesounds",
EV_DEFAULT,
"i",
"on_off",
"Makes the vehicle play damaged sounds.",
EV_NORMAL
);
Event EV_Vehicle_RunSounds
(
"runsounds",
EV_DEFAULT,
"i",
"on_off",
"Makes the vehicle play running sounds.",
EV_NORMAL
);
Event EV_Vehicle_ProjectileVulnerable
(
"projectilevulnerable",
EV_DEFAULT,
"I",
"number_of_hits",
"Make vehicle vulnerable to being one-shot by projectiles. If number_of_hits is given, it will take this many "
"shots.",
EV_NORMAL
);
Event EV_Vehicle_CanUse
(
"canuse",
EV_DEFAULT,
"e",
"entity",
"Returns 1 if passed entity can 'use' this vehicle.",
EV_RETURN
);
cvar_t *g_showvehiclemovedebug;
cvar_t *g_showvehicleentrypoints;
cvar_t *g_showvehicleslotpoints;
cvar_t *g_showvehicletags;
cvar_t *g_showvehiclepath;
CLASS_DECLARATION(Animate, VehicleBase, NULL) {
{NULL, NULL}
};
VehicleBase::VehicleBase()
{
if (LoadingSavegame) {
// Archive function will setup all necessary data
return;
}
takedamage = DAMAGE_NO;
showModel();
//
// 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_PathDrivable, &Vehicle::PathDrivable },
{&EV_Vehicle_ProjectileVulnerable, &Vehicle::SetProjectileVulnerable },
{&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_SetName, &Vehicle::SetName },
{&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_Radius, &Vehicle::SetTireRadius },
{&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_Init, &Vehicle::ModelInit },
{&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_QuerySpeed, &Vehicle::QuerySpeed },
{&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_BounceForwardsVelocity, &Vehicle::BounceForwardsVelocity },
{&EV_Vehicle_BounceBackwardsVelocity, &Vehicle::BounceBackwardsVelocity },
{&EV_Vehicle_StopBackwardsVelocity, &Vehicle::StopBackwardsVelocity },
{&EV_Vehicle_StopBackwardsVelocity, &Vehicle::StopBackwardsVelocity },
{&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_SpawnTurret, &Vehicle::SpawnTurret },
{&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 },
{&EV_Vehicle_VehicleMoveAnim, &Vehicle::EventVehicleMoveAnim },
{&EV_Vehicle_VehicleMoveAnimDone, &Vehicle::EventVehicleMoveAnimDone },
{&EV_Vehicle_DamageSounds, &Vehicle::EventDamageSounds },
{&EV_Vehicle_RunSounds, &Vehicle::EventRunSounds },
{&EV_Remove, &Vehicle::Remove },
{&EV_Turret_SetMaxUseAngle, &Vehicle::EventSetMaxUseAngle },
{&EV_Vehicle_CanUse, &Vehicle::EventCanUse },
{NULL, NULL }
};
/*
====================
Vehicle::Vehicle
====================
*/
Vehicle::Vehicle()
{
int i;
entflags |= ECF_VEHICLE;
AddWaitTill(STRING_DRIVE);
AddWaitTill(STRING_VEHICLEANIMDONE);
AddWaitTill(STRING_ANIMDONE);
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_showvehicletags = gi.Cvar_Get("g_showvehicletags", "0", 0);
g_showvehiclepath = gi.Cvar_Get("g_showvehiclepath", "0", 0);
for (i = 0; i < NUM_VEHICLE_TIRES; i++) {
VectorClear(m_vTireEnd[i]);
}
m_bTireHit[0] = false;
VectorClear(m_vNormalSum);
m_iNumNormals = 0;
if (LoadingSavegame) {
// Archive function will setup all necessary data
return;
}
edict->s.eType = ET_VEHICLE;
moveresult = 0;
isBlocked = false;
m_iGear = 1;
m_iRPM = 0;
maxtracedist = 0;
airspeed = 0;
for (i = 0; i < 6; i++) {
m_fGearRatio[i] = 0;
}
m_fMass = 0;
m_fFrontMass = 0;
m_fBackMass = 0;
m_fWheelBase = 0.0;
m_fWheelFrontLoad = 0.0;
m_fWheelFrontInnerLoad = 0.0;
m_fWheelFrontOutterLoad = 0.0;
m_fWheelFrontDist = 0.0;
m_fWheelFrontSuspension = 0.0;
m_fWheelBackLoad = 0.0;
m_fWheelBackInnerLoad = 0.0;
m_fWheelBackOutterLoad = 0.0;
m_fWheelBackDist = 0.0;
m_fWheelBackSuspension = 0.0;
m_fCGHeight = 0.0;
m_fBankAngle = 0.0;
m_fTread = 0.0;
m_fTrackWidth = 0.0;
m_fTireFriction = 0.0;
m_fDrag = 0.0;
m_fTireRadius = 0.0;
m_fFrontBrakes = 0.0;
m_fBackBrakes = 0.0;
m_fRollingResistance = 0.0;
m_fTireRotationalSpeed = 0.0;
m_fFrontBrakingForce = 0.0;
m_fBackBrakingForce = 0.0;
m_fBrakingPerformance = 0.0;
m_fLastTurn = 0.0;
m_fTangForce = 0.0;
m_fInertia = 0.0;
m_fDifferentialRatio = 0.0;
m_fGearEfficiency = 0.0;
m_fMaxTraction = 0.0;
m_fTractionForce = 0.0;
m_fAccelerator = 0.0;
m_fTorque = 0.0;
m_fStopStartDistance = 0.0;
m_fStopStartSpeed = 0.0;
m_fStopEndDistance = 0.0;
m_bWheelSpinning = 0;
m_bIsSkidding = 0;
m_bIsBraking = 0;
m_bAutomatic = 0;
m_iNumNormals = 0;
memset(&vs, 0, sizeof(vs));
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;
pathDrivable = false;
jumpable = false;
showweapon = false;
m_iProjectileHitsRemaining = 0;
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_fMaxSpeed = 0;
m_fIdealAccel = 0;
m_fIdealDistance = 100;
m_bBounceBackwards = false;
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;
m_vAngularVelocity = vec_zero;
m_vAngularAcceleration = vec_zero;
v_angle = vec_zero;
m_bThinkCalled = qfalse;
m_pCurPath = NULL;
m_iCurNode = 0;
m_pAlternatePath = NULL;
m_iAlternateNode = 0;
m_pNextPath = NULL;
m_iNextPathStartNode = -1;
m_fLookAhead = 0;
m_fShaderOffset = 0;
vs.hit_obstacle = false;
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 = qtrue;
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_fSkidAngle = 0;
m_vSkidOrigin = vec_zero;
m_fSkidLeftForce = 0;
m_fSkidRightForce = 0;
m_sAnimationSet = "";
m_sSoundSet = "";
m_iLastTiresUpdate = -1;
for (i = 0; i < NUM_VEHICLE_TIRES; i++) {
m_vTireEnd[i] = origin;
m_bTireHit[i] = false;
}
m_bAnimMove = false;
m_fMaxUseAngle = 0;
m_bBounceStayFullSpeed = false;
ResetSlots();
PostEvent(EV_Vehicle_Start, EV_POSTSPAWN);
}
/*
====================
Vehicle::~Vehicle
====================
*/
Vehicle::~Vehicle()
{
RemoveVehicleSoundEntities();
if (m_pCollisionEntity) {
m_pCollisionEntity->ProcessEvent(EV_Remove);
}
entflags &= ~ECF_VEHICLE;
}
/*
====================
Vehicle::ResetSlots
====================
*/
void Vehicle::ResetSlots(void)
{
driver.ent = NULL;
driver.boneindex = -1;
driver.enter_boneindex = -1;
driver.flags = 0;
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::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::VehicleStart
====================
*/
void Vehicle::VehicleStart(Event *ev)
{
Entity *ent;
VehicleBase *last;
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.x = m_vAngles.x;
angles.z = m_vAngles.z;
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;
//
// since 2.0: set targetname for all busy turrets
//
for (int i = 0; i < numTurrets; i++) {
if (Turrets[i].flags & SLOT_BUSY) {
Turrets[i].ent->SetTargetName(TargetName() + "_turret" + str(i));
}
}
last_origin = origin;
link();
}
/*
====================
Vehicle::SetWheelCorners
====================
*/
void Vehicle::SetWheelCorners(Event *ev)
{
Vector size;
Vector offset;
size = ev->GetVector(1);
offset = ev->GetVector(2);
m_vOriginCornerOffset = offset;
maxtracedist = size[2];
Corners[0][0] = -(size[0] / 2);
Corners[0][1] = (size[1] / 2);
Corners[0][2] = size[2];
Corners[1][0] = (size[0] / 2);
Corners[1][1] = (size[1] / 2);
Corners[1][2] = size[2];
Corners[2][0] = -(size[0] / 2);
Corners[2][1] = -(size[1] / 2);
Corners[2][2] = size[2];
Corners[3][0] = (size[0] / 2);
Corners[3][1] = -(size[1] / 2);
Corners[3][2] = size[2];
SetupVehicleSoundEntities();
}
/*
====================
Vehicle::QuerySpeed
====================
*/
float Vehicle::QuerySpeed(void)
{
return currentspeed;
}
/*
====================
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 if (Turrets[slot].boneindex >= 0) {
GetTagPositionAndOrientation(Turrets[slot].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::QuerySpeed
====================
*/
void Vehicle::QuerySpeed(Event *ev)
{
ev->AddFloat(QuerySpeed());
}
/*
====================
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)) {
vExitAngles = ev->GetEntity(2)->angles;
bHasExitAngles = true;
vExitPosition = ev->GetEntity(2)->origin;
} else if (ev->IsSimpleEntityAt(2)) {
vExitPosition = ev->GetSimpleEntity(2)->origin;
}
}
DetachTurretSlot(iSlot, vExitPosition, bHasExitAngles ? &vExitAngles : NULL);
}
/*
====================
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::FindExitPosition
====================
*/
bool Vehicle::FindExitPosition(Entity *pEnt, const Vector& vOrigin, const Vector *vAngles)
{
Event *ev;
Vector yawAngles;
Vector forward;
Vector offset;
float radius;
int i, j;
trace_t trace;
if (!pEnt) {
return false;
}
if ((g_target_game < target_game_e::TG_MOHTA || !pEnt->IsSubclassOfPlayer()) && vOrigin != vec_zero) {
if (vAngles) {
pEnt->setAngles(*vAngles);
}
pEnt->setOrigin(vOrigin);
pEnt->velocity = vec_zero;
ev = new Event(EV_Vehicle_Exit);
ev->AddEntity(this);
pEnt->ProcessEvent(ev);
return true;
}
radius = (pEnt->size.length() + size.length()) * 0.5;
for (i = 0; i < 128; i += 32) {
for (j = 0; j < 360; j += 30) {
yawAngles = vec_zero;
yawAngles[1] = j + angles[1] + 180;
yawAngles.AngleVectorsLeft(&forward);
offset = origin + forward * radius;
offset[2] += i;
trace = G_Trace(
offset,
pEnt->mins,
pEnt->maxs,
offset,
NULL,
pEnt->edict->clipmask,
pEnt->IsSubclassOfPlayer() ? qtrue : qfalse,
" Vehicle::FindExitPosition"
);
if (!trace.startsolid && !trace.allsolid) {
Vector end;
offset = trace.endpos;
end = offset;
end[2] -= 128;
trace = G_Trace(
offset,
pEnt->mins,
pEnt->maxs,
end,
NULL,
pEnt->edict->clipmask,
pEnt->IsSubclassOfPlayer() ? qtrue : qfalse,
" Vehicle::FindExitPosition"
);
if (!trace.startsolid && !trace.allsolid && trace.fraction < 1) {
pEnt->setOrigin(trace.endpos);
pEnt->velocity = vec_zero;
if (vAngles) {
pEnt->setAngles(*vAngles);
}
ev = new Event(EV_Vehicle_Exit);
ev->AddEntity(this);
pEnt->ProcessEvent(ev);
return true;
}
}
}
}
return false;
}
/*
====================
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);
Passengers[slot].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);
DriverAdded();
} else if (ent == d && !isLocked()) {
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 (ent == pTurret && !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->GetRawRemoteOwner();
}
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);
}
FindExitPosition(ent, vExitPosition, vExitAngles);
}
}
}
/*
====================
Vehicle::DetachPassengerSlot
====================
*/
void Vehicle::DetachPassengerSlot(int slot, Vector vExitPosition, Vector *vExitAngles)
{
Entity *passenger = Passengers[slot].ent;
if (!passenger) {
return;
}
if (!FindExitPosition(passenger, vExitPosition, vExitAngles)) {
// cannot exit
return;
}
Sound(m_sSoundSet + "snd_dooropen");
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 (!FindExitPosition(other, vExitPosition, vExitAngles)) {
// cannot exit
return;
}
turnimpulse = 0;
moveimpulse = 0;
jumpimpulse = 0;
if (drivable) {
StopLoopSound();
Sound("snd_dooropen", CHAN_BODY);
Sound("snd_stop", CHAN_VOICE);
driver.ent->setSolidType(SOLID_BBOX);
}
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 (!FindExitPosition(passenger, vExitPosition, vExitAngles)) {
// cannot exit
return;
}
turnimpulse = 0;
moveimpulse = 0;
jumpimpulse = 0;
passenger->setSolidType(SOLID_BBOX);
Sound(m_sSoundSet + "snd_dooropen");
if (passenger->IsSubclassOfTurretGun()) {
TurretGun *pTurret = static_cast<TurretGun *>(passenger);
pTurret->m_bUsable = true;
pTurret->m_bRestable = true;
}
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::Drivable
====================
*/
void Vehicle::Drivable(Event *ev)
{
setMoveType(MOVETYPE_VEHICLE);
drivable = true;
}
/*
====================
Vehicle::PathDrivable
====================
*/
void Vehicle::PathDrivable(Event *ev)
{
pathDrivable = ev->GetBoolean(1);
}
/*
====================
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::SetName
====================
*/
void Vehicle::SetName(Event *ev)
{
vehicleName = 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 || !driver.ent->client) {
return;
}
for (i = 0; i < 3; i++) {
driver.ent->client->ps.delta_angles[i] = ANGLE2SHORT(angles[i] - driver.ent->client->cmd_angles[i]);
}
}
/*
====================
Vehicle::Driver
====================
*/
Entity *Vehicle::Driver(void)
{
return driver.ent;
}
/*
====================
Vehicle::IsDrivable
====================
*/
qboolean Vehicle::IsDrivable(void)
{
return drivable;
}
/*
====================
Vehicle::BounceForwardsVelocity
====================
*/
void Vehicle::BounceForwardsVelocity(Event *ev)
{
if (m_bBounceBackwards) {
m_bBounceStayFullSpeed = false;
m_bBounceBackwards = false;
currentspeed = -currentspeed;
moveimpulse = -moveimpulse;
}
}
/*
====================
Vehicle::BounceBackwardsVelocity
====================
*/
void Vehicle::BounceBackwardsVelocity(Event *ev)
{
if (!m_bBounceBackwards) {
m_bBounceBackwards = true;
currentspeed = -currentspeed;
moveimpulse = -moveimpulse;
if (ev->NumArgs() > 0) {
m_bBounceStayFullSpeed = ev->GetBoolean(1);
}
}
}
/*
====================
Vehicle::StopForwardsVelocity
====================
*/
void Vehicle::StopForwardsVelocity(Event *ev)
{
if (!m_bBounceBackwards) {
moveimpulse = 0;
}
}
/*
====================
Vehicle::StopBackwardsVelocity
====================
*/
void Vehicle::StopBackwardsVelocity(Event *ev)
{
if (m_bBounceBackwards) {
moveimpulse = 0;
}
}
/*
====================
Vehicle::EventSetSpeed
====================
*/
void Vehicle::SetSpeed(Event *ev)
{
speed = ev->GetFloat(1);
}
/*
====================
Vehicle::EventSetTurnRate
====================
*/
void Vehicle::SetTurnRate(Event *ev)
{
maxturnrate = ev->GetFloat(1);
}
/*
====================
Vehicle::SetMass
====================
*/
void Vehicle::SetMass(Event *ev)
{
m_fMass = ev->GetFloat(1);
m_fFrontMass = m_fMass * 0.5;
m_fBackMass = m_fMass * 0.5;
}
/*
====================
Vehicle::SetFrontMass
====================
*/
void Vehicle::SetFrontMass(Event *ev)
{
m_fFrontMass = ev->GetFloat(1);
m_fMass = m_fFrontMass + m_fBackMass;
}
/*
====================
Vehicle::SetBackMass
====================
*/
void Vehicle::SetBackMass(Event *ev)
{
m_fBackMass = ev->GetFloat(1);
m_fMass = m_fFrontMass + m_fBackMass;
}
/*
====================
Vehicle::SetTread
====================
*/
void Vehicle::SetTread(Event *ev)
{
m_fTread = ev->GetFloat(1);
}
/*
====================
Vehicle::SetTireRadius
====================
*/
void Vehicle::SetTireRadius(Event *ev)
{
m_fTireRadius = ev->GetFloat(1);
}
/*
====================
Vehicle::SetRollingResistance
====================
*/
void Vehicle::SetRollingResistance(Event *ev)
{
m_fRollingResistance = ev->GetFloat(1);
}
/*
====================
Vehicle::SetDrag
====================
*/
void Vehicle::SetDrag(Event *ev)
{
m_fDrag = ev->GetFloat(1);
}
/*
====================
Vehicle::BouncyCoef
====================
*/
void Vehicle::BouncyCoef(Event *ev)
{
m_fBouncyCoef = ev->GetFloat(1);
}
/*
====================
Vehicle::SpringyCoef
====================
*/
void Vehicle::SpringyCoef(Event *ev)
{
m_fSpringyCoef = ev->GetFloat(1);
}
/*
====================
Vehicle::YawMinMax
====================
*/
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::RollMinMax
====================
*/
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::ZMinMax
====================
*/
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::SetAnimationSet
====================
*/
void Vehicle::SetAnimationSet(Event *ev)
{
m_sAnimationSet = ev->GetString(1);
}
/*
====================
Vehicle::SetSoundSet
====================
*/
void Vehicle::SetSoundSet(Event *ev)
{
m_sSoundSet = ev->GetString(1);
}
/*
====================
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::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::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::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 < 0;
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 += angledist(SHORT2ANGLE(ucmd->angles[1]) - driver.ent->client->cmd_angles[1]) * 8;
return true;
}
/*
====================
Vehicle::PathDrive
====================
*/
qboolean Vehicle::PathDrive(usercmd_t *ucmd)
{
if (!pathDrivable) {
return false;
}
if (m_bAutoPilot) {
if (ucmd->forwardmove > 0) {
//
// forward speed
//
if (m_bBounceBackwards) {
m_fIdealSpeed = 0;
if (!currentspeed) {
m_bBounceBackwards = false;
}
} else {
m_fIdealSpeed = m_fMaxSpeed;
}
} else if (ucmd->forwardmove < 0) {
//
// backward speed
//
if (m_bBounceBackwards) {
m_fIdealSpeed = -m_fMaxSpeed;
} else {
m_fIdealSpeed = 0;
if (!currentspeed) {
m_bBounceBackwards = true;
}
}
} else {
//
// stopped
//
m_fIdealSpeed = 0;
}
}
return true;
}
/*
====================
Vehicle::AutoPilot
====================
*/
void Vehicle::AutoPilot(void)
{
Vector vAngles;
Vector vDeltaAngles, vDelta, vDeltaSave;
float *vTmp;
vec3_t vPrev, vCur, vTotal;
float fTotal, fCoef;
Vector vWishPosition, vAlternateWishPosition;
float fDistToCurPos, fCurPathPosition;
if (!m_pCurPath || m_pCurPath->m_iPoints == 0) {
m_bAutoPilot = false;
return;
}
if (g_showvehiclepath && g_showvehiclepath->integer) {
int iFlags;
float fBS;
Vector vTmp1;
Vector vTmp2;
Vector vBS;
iFlags = 0;
fBS = sin(level.time) * 16;
vBS = Vector(1, 1, 1) * fBS;
for (int i = 0; i < m_pCurPath->m_iPoints; i++) {
float fZ = 0;
Vector vColor(0, 1, 1);
vTmp = m_pCurPath->GetByNode(i, &iFlags);
vTmp1 = vTmp + 1;
fZ = 0;
if (iFlags & 1) {
vColor = Vector(1, 1, 1);
fZ += 1;
G_DebugString(vTmp1 + Vector(0, 0, fZ * 32), sin(level.time) + 3, 1, 1, 0, "START_STOPPING");
}
if (iFlags & 2) {
vColor = Vector(0, 1, 1);
fZ += 1;
G_DebugString(vTmp1 + Vector(0, 0, fZ * 32), sin(level.time) + 3, 0, 1, 0, "START_SKIDDING");
}
if (iFlags & 4) {
fZ += 1;
G_DebugString(vTmp1 + Vector(0, 0, fZ * 32), sin(level.time) + 3, 0, 0, 1, "STOP_SKIDDING");
}
if (i == m_iCurNode) {
G_DebugBBox(vTmp1, vBS * -1, vBS, 1, 0, 0, 1);
} else {
G_DebugBBox(vTmp1, vBS * -1, vBS, vColor[0], vColor[1], vColor[2], 1);
}
vTmp = m_pCurPath->GetByNode(i + 1, NULL);
vTmp2 = vTmp + 1;
G_DebugLine(vTmp1, vTmp2, 0, 1, 0, 1);
}
}
do {
if (m_iCurNode > 0) {
int iFlags;
vTmp = m_pCurPath->GetByNode(m_iCurNode, NULL);
VectorCopy(vTmp + 1, vCur);
if (g_showvehiclemovedebug->integer) {
G_DebugString(vCur, 3, 1, 1, 1, "%f", vTmp[0]);
}
vTmp = m_pCurPath->GetByNode(m_iCurNode - 1, NULL);
VectorCopy(vTmp + 1, vPrev);
if (g_showvehiclemovedebug->integer) {
G_DebugString(vPrev, 3, 1, 1, 1, "%f", vTmp[0]);
}
VectorSubtract(vCur, vPrev, vTotal);
fTotal = VectorLength(vTotal);
m_vIdealDir = vTotal;
VectorNormalize(m_vIdealDir);
angles.AngleVectorsLeft(&vDelta, NULL, NULL);
fCoef = ProjectLineOnPlane(vDelta, DotProduct(origin, vDelta), vPrev, vCur, NULL);
if (g_showvehiclemovedebug->integer) {
G_DebugBBox(vPrev, Vector(-32, -32, -32), Vector(32, 32, 32), 0, 1, 1, 1);
G_DebugBBox(vCur, Vector(-32, -32, -32), Vector(32, 32, 32), 1, 1, 0, 1);
G_DebugArrow(vCur, m_vIdealDir * -1, (1 - fCoef) * fTotal, 0, 1, 0, 1);
G_DebugArrow(vPrev, m_vIdealDir * 1, fCoef * fTotal, 0, 0, 1, 1);
}
vTmp = m_pCurPath->GetByNode(m_iCurNode - (1.0 - fCoef), NULL);
fCurPathPosition = vTmp[0];
// Added in 2.30
// Check if vehicle bounces backwards
if (m_bBounceBackwards) {
vTmp = m_pCurPath->Get(fCurPathPosition - m_fLookAhead, NULL);
} else {
vTmp = m_pCurPath->Get(fCurPathPosition + m_fLookAhead, NULL);
}
vWishPosition = vTmp + 1;
fDistToCurPos = Vector(origin[0] - vWishPosition[0], origin[1] - vWishPosition[1], 0).length();
// Added in 2.30
// Check if vehicle bounces backwards
if (fCoef > 1 && !m_bBounceBackwards) {
m_iCurNode++;
if (m_iCurNode >= m_pCurPath->m_iPoints) {
break;
}
m_pCurPath->GetByNode(m_iCurNode, &iFlags);
if (iFlags & 1) {
ProcessEvent(EV_Vehicle_StopAtEnd);
}
if (iFlags & 2) {
Event *event = new Event(EV_Vehicle_Skidding);
event->AddInteger(1);
ProcessEvent(event);
}
if (iFlags & 4) {
Event *event = new Event(EV_Vehicle_Skidding);
event->AddInteger(0);
ProcessEvent(event);
}
} else if (fCoef < 0 && m_bBounceBackwards) {
m_iCurNode--;
if (m_iCurNode < 1) {
m_iCurNode = 1;
m_bBounceBackwards = false;
currentspeed = -currentspeed;
moveimpulse = -moveimpulse;
}
}
cont:
if (m_pAlternatePath && m_pAlternatePath->m_iPoints) {
if (m_iAlternateNode > 1) {
vTmp = m_pAlternatePath->GetByNode(m_iAlternateNode, NULL);
VectorCopy(vTmp + 1, vCur);
vTmp = m_pAlternatePath->GetByNode(m_iAlternateNode - 1, NULL);
VectorCopy(vTmp + 1, vPrev);
VectorSubtract(vCur, vPrev, vTotal);
fTotal = VectorLength(vTotal);
m_vIdealDir = vTotal;
VectorNormalize(m_vIdealDir);
angles.AngleVectorsLeft(&vDelta, NULL, NULL);
fCoef = ProjectLineOnPlane(vDelta, DotProduct(origin, vDelta), vPrev, vCur, NULL);
vTmp = m_pCurPath->GetByNode(m_iAlternateNode - (1.0 - fCoef), NULL);
// 2.30: bounce backward
if (m_bBounceBackwards) {
vTmp = m_pCurPath->Get(vTmp[0] - m_fLookAhead, NULL);
} else {
vTmp = m_pCurPath->Get(vTmp[0] + m_fLookAhead, NULL);
}
vAlternateWishPosition = vTmp + 1;
if (fCoef > 1) {
m_iAlternateNode++;
} else if (fCoef < 0) {
// 2.30: backward
m_iAlternateNode--;
}
} else {
vTmp = m_pAlternatePath->GetByNode(m_iAlternateNode, NULL);
vAlternateWishPosition = vTmp + 1;
m_vIdealDir = origin - vWishPosition;
m_vIdealDir[2] = 0;
m_vIdealDir.normalize();
vDelta = vAlternateWishPosition - origin;
vDelta[2] = 0;
if (vDelta.length() <= m_fIdealDistance) {
m_iAlternateNode++;
}
}
}
vDelta = vWishPosition - origin;
if (g_showvehiclemovedebug->integer) {
G_DebugLine(vWishPosition, origin, 1, 0, 0, 1);
}
vDeltaSave = vDelta;
vDelta.normalize();
if (m_pAlternatePath && m_pAlternatePath->m_iPoints) {
trace_t trace;
trace = G_Trace(
origin, vec_zero, vec_zero, vWishPosition, this, edict->clipmask, qfalse, "Vehicle::AutoPilot"
);
if ((trace.fraction < 1 || trace.startsolid || trace.allsolid) && trace.ent
&& trace.entityNum != ENTITYNUM_WORLD) {
vDelta = vAlternateWishPosition - origin;
vDeltaSave = vDelta;
vDelta.normalize();
}
}
vDelta[2] = 0;
vDeltaSave[2] = 0;
if (fDistToCurPos > 1) {
// Added 2.30: checks if it doesn't bounce backwards
if (moveimpulse >= 0 && !m_bBounceBackwards) {
vectoangles(vDelta, vDeltaAngles);
turnimpulse = angledist(vDeltaAngles.y - angles.y);
} else {
Vector vVec = vDelta * -1;
vectoangles(vVec, vDeltaAngles);
if (IsSubclassOfVehicleTank()) {
turnimpulse = angledist(vDeltaAngles.y - angles.y);
} else {
turnimpulse = -angledist(vDeltaAngles.y - angles.y);
}
}
}
if (m_bStopEnabled) {
moveimpulse = (fCurPathPosition - m_fStopStartDistance) / (m_fStopEndDistance - m_fStopStartDistance);
moveimpulse *= moveimpulse;
moveimpulse *= moveimpulse;
moveimpulse *= moveimpulse;
moveimpulse *= moveimpulse;
moveimpulse = 1 - moveimpulse;
moveimpulse *= m_fStopStartSpeed;
if (fabs(moveimpulse) < 2) {
if (m_fIdealSpeed >= 0) {
moveimpulse = 2;
} else {
moveimpulse = -2;
}
}
// 2.30: check if it stays full speed when bouncing
} else if (m_bBounceStayFullSpeed) {
if (m_fIdealSpeed > fabs(moveimpulse)) {
moveimpulse -= m_fIdealAccel * level.frametime;
if (fabs(moveimpulse) > m_fIdealSpeed) {
moveimpulse = -m_fIdealSpeed;
}
}
} else if (moveimpulse < m_fIdealSpeed) {
moveimpulse += m_fIdealAccel * level.frametime;
if (moveimpulse > m_fIdealSpeed) {
moveimpulse = m_fIdealSpeed;
}
} else if (moveimpulse > m_fIdealSpeed) {
moveimpulse -= m_fIdealAccel * level.frametime;
if (moveimpulse < m_fIdealSpeed) {
moveimpulse = m_fIdealSpeed;
}
}
if (m_iNextPathStartNode >= 0 && m_pNextPath && m_pNextPath->m_iPoints
&& m_iCurNode > m_iNextPathStartNode + 2) {
cVehicleSpline *spline;
spline = m_pCurPath;
m_pCurPath = m_pNextPath;
m_pNextPath = spline;
m_iCurNode = 2;
m_pNextPath->Reset();
m_iNextPathStartNode = -1;
// notify scripts that the drive has finished
Unregister(STRING_DRIVE);
m_bStopEnabled = false;
m_bEnableSkidding = false;
}
return;
}
vTmp = m_pCurPath->GetByNode(m_iCurNode, NULL);
vWishPosition = vTmp + 1;
m_vIdealDir = origin - vWishPosition;
m_vIdealDir[2] = 0;
m_vIdealDir.normalize();
vDelta = vWishPosition - origin;
vDelta[2] = 0;
fDistToCurPos = 2;
if (vDelta.length() > m_fIdealDistance) {
goto cont;
}
fDistToCurPos = 0;
m_iCurNode++;
} while (m_iCurNode < m_pCurPath->m_iPoints);
if (m_bStopEnabled) {
moveimpulse = 0;
}
m_bAutoPilot = false;
m_bStopEnabled = false;
m_bEnableSkidding = false;
StopLoopSound();
// play the stop sound
Sound(m_sSoundSet + "snd_stop", CHAN_VOICE);
delete m_pCurPath;
m_pCurPath = NULL;
m_iCurNode = 0;
turnimpulse = 0;
//
// notify scripts that driving has stopped
//
Unregister(STRING_DRIVE);
}
/*
====================
Vehicle::EventDriveInternal
====================
*/
void Vehicle::EventDriveInternal(Event *ev, bool wait)
{
SimpleEntity *path;
SimpleEntity *alternate_path = NULL;
m_fIdealDistance = 100;
m_fLookAhead = 256;
m_fIdealAccel = 35;
m_fIdealSpeed = 250;
m_fMaxSpeed = 250; // Added in 2.30
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);
m_fMaxSpeed = m_fIdealSpeed; // Added in 2.30
case 1:
path = ev->GetSimpleEntity(1);
if (!path) {
ScriptError("Vehicle Given Drive Command with NULL path.");
return;
}
break;
default:
ScriptError("wrong number of arguments");
}
if (!m_pCurPath) {
m_pCurPath = new cVehicleSpline;
}
if (!m_pAlternatePath) {
m_pAlternatePath = new cVehicleSpline;
}
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::SetupPath
====================
*/
void Vehicle::SetupPath(cVehicleSpline *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()[0]) {
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 && i > 1) {
Com_Printf("^~^~^Warning: Vehicle Driving with a Path that contains 2 equal points\n");
continue;
}
fCurLength += vDelta.length();
vTmp[0] = fCurLength;
VectorCopy(ent->origin, vTmp + 1);
if (ent->IsSubclassOfVehiclePoint()) {
pPath->Add(vTmp, static_cast<VehiclePoint*>(ent)->spawnflags);
} else {
pPath->Add(vTmp, 0);
}
vLastOrigin = ent->origin;
if (ent == se && i > 1) {
break;
}
}
}
/*
====================
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_bAutoPilot = false;
m_bStopEnabled = false;
m_bIsSkidding = false;
moveimpulse = 0;
turnimpulse = 0;
velocity = vec_zero;
m_iCurNode = 0;
Unregister(STRING_DRIVE);
}
/*
====================
Vehicle::VehicleDestroyed
====================
*/
void Vehicle::VehicleDestroyed(Event *ev)
{
Entity *ent;
Entity *targetEnt;
Event *event;
const char *name;
VehicleBase *v;
Vector vDelta;
takedamage = DAMAGE_NO;
setSolidType(SOLID_NOT);
hideModel();
ent = ev->GetEntity(1);
if (driver.ent) {
EntityPtr driverPtr;
velocity = vec_zero;
driverPtr = driver.ent;
event = new Event(EV_Use);
event->AddEntity(driverPtr);
ProcessEvent(event);
vDelta = driverPtr->origin - origin;
vDelta[2] += 64;
vDelta.normalize();
// kill the driver
driverPtr->Damage(this, this, driverPtr->health * 2, origin, vDelta, vec_zero, 50, 0, MOD_VEHICLE);
}
if (flags & FL_DIE_EXPLODE) {
setSolidType(SOLID_NOT);
hideModel();
CreateExplosion(origin, edict->s.scale * 150, this, this, this, m_sExplosionModel);
}
if (flags & FL_DIE_GIBS) {
setSolidType(SOLID_NOT);
hideModel();
CreateGibs(this, -150, edict->s.scale, 3, NULL);
}
//
// remove all links
//
for (v = this; v->vlink; v = vlink) {
v->vlink->PostEvent(EV_Remove, EV_VEHICLE);
}
//
// remove all kill targets
//
name = KillTarget();
if (name && strcmp(name, "")) {
for (targetEnt = G_FindTarget(NULL, name); targetEnt; targetEnt = G_FindTarget(targetEnt, name)) {
targetEnt->PostEvent(EV_Remove, EV_VEHICLE);
}
}
//
// activate targets
//
name = Target();
if (name && strcmp(name, "")) {
for (targetEnt = G_FindTarget(NULL, name); targetEnt; targetEnt = G_FindTarget(targetEnt, name)) {
event = new Event(EV_Activate);
event->AddEntity(ent);
targetEnt->ProcessEvent(event);
}
}
PostEvent(EV_Remove, EV_VEHICLE);
}
/*
====================
Vehicle::DetachRemoteOwner
====================
*/
void Vehicle::DetachRemoteOwner()
{
VehicleTurretGun *vtg = static_cast<VehicleTurretGun *>(Turrets[0].ent.Pointer());
vtg->EndRemoteControl();
}
static float GetAngleBetweenVectors2D(const Vector& a, const Vector& b)
{
float value;
float angle;
value = atan2f(a.x, a.y);
angle = RAD2DEG(value - atan2f(b.x, b.y));
if (fabs(angle) <= 180) {
return angle;
} else if (angle < 0) {
return angle + 360;
} else {
return angle - 360;
}
}
/*
====================
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;
bool bHasExitPosition;
ent = ev->GetEntity(1);
bHasExitAngles = false;
bHasExitPosition = false;
if (locked) {
return;
}
// Added in 2.30
// Check use angle
if (!driver.ent && m_fMaxUseAngle && ent && Turrets[0].ent) {
Vector vForward;
Vector vDir;
AngleVectors(Turrets[0].ent->angles, vForward, NULL, NULL);
vDir = Turrets[0].ent->origin - ent->origin;
VectorNormalize(vDir);
if (fabs(GetAngleBetweenVectors2D(vForward, vDir)) > m_fMaxUseAngle) {
// Not usable
return;
}
}
if (ev->NumArgs() == 2) {
if (ev->IsVectorAt(2)) {
vExitPosition = ev->GetVector(2);
bHasExitPosition = true;
} else if (ev->IsEntityAt(2)) {
Entity *pEnt = ev->GetEntity(2);
bHasExitAngles = true;
vExitAngles = pEnt->angles;
vExitPosition = pEnt->origin;
bHasExitPosition = true;
} else if (ev->IsSimpleEntityAt(2)) {
SimpleEntity *pEnt = ev->GetSimpleEntity(2);
bHasExitAngles = true;
vExitAngles = pEnt->angles;
vExitPosition = pEnt->origin;
bHasExitPosition = true;
}
}
slot = FindDriverSlotByEntity(ent);
if (slot >= 0) {
DetachDriverSlot(slot, vec_zero, NULL);
if (IsSubclassOfVehicleTank()) {
// Added check to see if the turret is valid in OPM
if (Turrets[0].ent && Turrets[0].ent->IsSubclassOfVehicleTurretGun()) {
DetachRemoteOwner();
}
}
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->IsSubclassOfSentient()) {
Sentient *sent = static_cast<Sentient *>(ent);
TurretGun *sentTurret = sent->GetTurret();
if (sentTurret) {
slot = FindTurretSlotByEntity(sent->GetTurret());
if (slot >= 0) {
if (!bHasExitPosition) {
AttachTurretSlot(slot, sent, vec_zero, NULL);
} else if (!bHasExitAngles) {
AttachTurretSlot(slot, sent, vExitPosition, NULL);
} else {
AttachTurretSlot(slot, sent, vExitPosition, &vExitAngles);
}
sent->SetVehicle(NULL);
return;
}
}
}
// Check for passengers slots
for (slot = 0; slot < MAX_PASSENGERS; slot++) {
if (!(Passengers[slot].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[slot].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[slot].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) {
float length;
QueryDriverSlotPosition(slot, (float *)&pos);
dist = pos - ent->origin;
length = dist.length();
if (ent->IsSubclassOfPlayer()) {
length /= 2.0;
}
if (length < min_length) {
min_length = dist.length();
min_type = 2;
min_slot = slot;
}
}
if (g_gametype->integer == GT_SINGLE_PLAYER || min_type == 2) {
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::SetMoveInfo
====================
*/
void Vehicle::SetMoveInfo(vmove_t *vm)
{
memset(vm, 0, sizeof(vmove_t));
origin.copyTo(vs.origin);
vs.useGravity = false;
vs.entityNum = entnum;
vm->vs = &vs;
vm->frametime = level.frametime;
vm->tracemask = edict->clipmask;
mins.copyTo(vm->mins);
maxs.copyTo(vm->maxs);
vs.entityNum = edict->s.number;
vs.desired_dir[0] = velocity[0];
vs.desired_dir[1] = velocity[1];
vm->desired_speed = VectorNormalize2D(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)
{
Vector mins, maxs;
SetMoveInfo(vm);
mins = m_pCollisionEntity->mins;
maxs = m_pCollisionEntity->maxs;
mins -= Vector(24, 24, 0);
maxs += Vector(24, 24, 0);
mins.copyTo(vm->mins);
maxs.copyTo(vm->maxs);
}
/*
====================
Vehicle::GetCEMoveInfo
====================
*/
void Vehicle::GetCEMoveInfo(vmove_t *vm)
{
GetMoveInfo(vm);
}
/*
====================
Vehicle::SetViewAngles
====================
*/
void Vehicle::SetViewAngles(Vector newViewangles)
{
client->ps.delta_angles[0] = floor(ANGLE2SHORT(newViewangles.x));
client->ps.delta_angles[1] = floor(ANGLE2SHORT(newViewangles.y));
client->ps.delta_angles[2] = floor(ANGLE2SHORT(newViewangles.z));
v_angle = newViewangles;
AnglesToAxis(v_angle, orientation);
yaw_forward = orientation[0];
yaw_left = orientation[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::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: shrug
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);
vPush.copyTo(vm.vs->velocity);
vm.frametime = 1;
vs.desired_dir[0] = vPush[0];
vs.desired_dir[1] = vPush[1];
vm.desired_speed = VectorNormalize2D(vs.desired_dir);
if (g_showvehiclemovedebug->integer) {
G_DebugBBox(origin, vm.mins, vm.maxs, 1, 0, 0, 1);
G_DebugBBox(origin, vm.mins, vm.maxs, 0, 1, 0, 1);
}
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
|| m_pCollisionEntity && other->entity->groundentity->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);
newOrigin = vm.vs->origin;
if (bindmaster) {
newOrigin -= bindmaster->origin;
}
setLocalOrigin(newOrigin);
if (g_showvehiclemovedebug->integer) {
G_DebugBBox(origin, vm.mins, vm.maxs, 0, 0, 1, 1);
}
TouchStuff(&vm);
for (i = 0; i < iNumSkipped; i++) {
pSkippedEntities[i]->setSolidType(solidEntities[i]);
pSkippedEntities[i]->setContents(iContentsEntities[i]);
}
}
/*
====================
Vehicle::AnimMoveVehicle
====================
*/
void Vehicle::AnimMoveVehicle(void)
{
Vector vPosition;
Vector vAngles;
Entity *ent;
//
// velocity
//
vPosition = origin + frame_delta;
velocity = (vPosition - origin) * (1.0 / level.frametime);
setOrigin(vPosition);
//
// angular velocity
//
vAngles = angles;
vAngles[1] += angular_delta;
avelocity = (vAngles - angles) * (1.0 / level.frametime);
setAngles(vAngles);
for (ent = teamchain; ent; ent = ent->teamchain) {
ent->setLocalOrigin(ent->localorigin);
ent->setAngles(ent->localangles);
}
}
/*
====================
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;
float fSpeed;
bool bDoPush = true;
bool bDoGravity = false;
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;
Entity *chain;
Entity *driverEnt = NULL;
solid_t solidDriver = SOLID_NOT;
int contentsDriver = 0;
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();
if (velocity.length() > 0.5f) {
Vector vVel;
if (driver.ent && driver.ent->edict->solid != SOLID_NOT) {
//
// Added in OPM
// Make the driver nonsolid while moving.
// Scripts usually set the player to nonsolid.
// However some scripts still rely on the old 1.11 behavior
// where the player is set to nonsolid when driving some type of vehicles
solidDriver = driver.ent->edict->solid;
contentsDriver = driver.ent->getContents();
driver.ent->setSolidType(SOLID_NOT);
}
fSpeed = orientation[0] * velocity;
vecDelta = velocity * level.frametime;
vVel = velocity * level.frametime * 8;
vecDelta[2] = 0;
for (i = 0; i < 3 && bDoPush; i++) {
bool bContinue = false;
if (fSpeed > 0.0f) {
gp = m_sMoveGrid->GetGridPoint(2, i, 0);
} else {
gp = m_sMoveGrid->GetGridPoint(0, i, 0);
}
vecStart = origin + gp->origin;
if (real_velocity.length() > 0.5f) {
vecEnd = vecStart + vVel;
for (;;) {
tr = G_Trace(
vecEnd,
Vector(gp->vm.mins) - Vector(-32, -32, -32),
Vector(gp->vm.maxs) + Vector(32, 32, 32),
vecEnd,
this,
MASK_MOVEVEHICLE,
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->groundentity->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()
);
}
// Added in OPM
// Skip the entity if the sentient is dead.
// This is a workaround against sentients getting in path of vehicles
// and thus blocking them temporarily while dying.
// For example in e1l1, the first tank would get stuck, because sometimes
// there are too many actors moving in the path of the vehicle
if (!bHitPerson || !tr.ent->entity->IsDead()) {
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, gp->vm.mins, gp->vm.maxs, 1, 0, 0, 1);
}
vecStart.z += 64;
if (g_showvehiclemovedebug->integer) {
G_DebugBBox(vecStart, gp->vm.mins, gp->vm.maxs, 1, 0, 0, 1);
}
vecEnd = vecStart + vecDelta;
for (;;) {
tr = G_Trace(
vecStart,
Vector(gp->vm.mins),
Vector(gp->vm.maxs),
vecEnd,
this,
edict->clipmask,
false,
"Vehicle::MoveVehicle"
);
if (tr.fraction == 1.0f && !tr.allsolid && !tr.startsolid) {
bContinue = true;
break;
}
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->groundentity->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()
);
}
// Added in OPM
// (see the comment above)
if (!bHitPerson || !tr.ent->entity->IsDead()) {
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()
);
}
}
if (bContinue) {
continue;
}
if (flMoveFrac > tr.fraction) {
flMoveFrac = tr.fraction - 0.1f;
}
vecStart = tr.endpos;
vecEnd = vecStart;
vecEnd.z -= 64;
if (g_showvehiclemovedebug->integer) {
G_DebugBBox(tr.endpos, gp->vm.mins, gp->vm.maxs, 0, 1, 0, 1);
}
for (;;) {
tr = G_Trace(
vecStart,
Vector(gp->vm.mins),
Vector(gp->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) {
break;
}
tr.ent->entity->CheckGround();
if (!tr.ent->entity->groundentity
|| (tr.ent->entity->groundentity != edict
&& (!m_pCollisionEntity || tr.ent->entity->groundentity->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(MV2): %s : %s\n",
tr.ent->entity->getClassname(),
tr.ent->entity->TargetName().c_str()
);
}
// Added in OPM
// (see the comment above)
if (!bHitPerson || !tr.ent->entity->IsDead()) {
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()
);
}
}
if (g_showvehiclemovedebug->integer) {
G_DebugBBox(tr.endpos, gp->vm.mins, gp->vm.maxs, 0, 0, 1, 1);
}
if ((!tr.ent || !tr.ent->entity->IsSubclassOfProjectile()) && driver.ent
&& driver.ent->IsSubclassOfPlayer()) {
if (fSpeed > 0) {
if (i) {
if (i == 2 && turnimpulse >= 0.0f) {
turnimpulse -= level.frametime * 800;
}
} else if (turnimpulse <= 0) {
turnimpulse += level.frametime * 800;
}
} else {
if (i) {
if (i == 2 && turnimpulse <= 0.0f) {
turnimpulse += level.frametime * 800;
}
} else if (turnimpulse >= 0) {
turnimpulse -= level.frametime * 800;
}
}
}
if (flMoveFrac < 0.1f) {
bDoPush = false;
}
}
if (bDoPush) {
SlidePush(Vector(0, 0, 64));
SlidePush(vecDelta * flMoveFrac);
SlidePush(Vector(0, 0, -128));
bDoGravity = true;
if (!bHitPerson) {
velocity *= flMoveFrac;
}
} else if (!bHitPerson) {
velocity *= 0.5;
}
if (solidDriver != SOLID_NOT) {
driver.ent->setSolidType(solidDriver);
driver.ent->setContents(contentsDriver);
}
}
if (bDoGravity) {
velocity[2] -= sv_gravity->value * level.frametime;
tr = G_Trace(
origin,
mins,
maxs,
origin + Vector(0, 0, velocity[2] * level.frametime),
this,
edict->clipmask,
qtrue,
"Vehicle::MoveVehicle"
);
if (tr.fraction < 1) {
velocity[2] = 0;
}
setOrigin(tr.endpos);
}
for (i = 0; i < iNumSkippedEntities; i++) {
pSkippedEntities[i]->setSolidType(solidEntities[i]);
pSkippedEntities[i]->setContents(iContentsEntities[i]);
}
for (chain = teamchain; chain; chain = chain->teamchain) {
chain->setLocalOrigin(chain->localorigin);
chain->setAngles(chain->localangles);
}
}
/*
====================
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;
float dot;
Vector primal_angles = angles;
Vector primal_origin = origin;
Vector vTmp;
Vector vAddedAngles;
Vector n_angles;
orientation_t orient;
if (!g_vehicle->integer || (edict->s.renderfx & RF_DONTDRAW)) {
return;
}
if (m_pCollisionEntity) {
setSolidType(SOLID_NOT);
m_pCollisionEntity->Solid();
}
prev_velocity = velocity;
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();
if (m_bAnimMove) { // 2.0: use vehicle's animation to move
AnimMoveVehicle();
} else {
if (!m_bMovementLocked) {
FactorOutAnglesOffset();
FactorOutOriginOffset();
}
MoveVehicle();
}
SetSlotsSolid();
m_bThinkCalled = true;
if (m_bAnimMove) {
moveimpulse = velocity.length() * level.frametime;
turnimpulse = avelocity[1] * level.frametime;
} else if (m_bAutoPilot) {
AutoPilot();
} else {
if (!driver.ent || !driver.ent->IsSubclassOfPlayer()) {
acceleration = velocity;
if (acceleration.length() < 0.1f) {
velocity = vec_zero;
}
}
moveimpulse *= 0.825f;
turnimpulse *= 0.825f;
}
currentspeed = moveimpulse;
turnangle = turnangle * 0.25f + turnimpulse;
turnangle = Q_clamp_float(turnangle, -maxturnrate, maxturnrate);
if (level.inttime <= 1200) {
prev_origin = origin;
} else {
real_velocity = origin - prev_origin;
prev_origin = origin;
prev_acceleration = real_acceleration;
real_acceleration = real_velocity - prev_velocity;
prev_velocity = real_velocity;
acceleration = 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;
temp = orientation[0];
temp.z = 0;
drivespeed = velocity * temp;
drivespeed = Q_clamp_float(drivespeed, -speed, speed);
n_angles = temp * drivespeed;
velocity = n_angles;
velocity.z = drivespeed * jumpimpulse;
avelocity *= 0.05f;
if (steerinplace && drivespeed < 350) {
drivespeed = 350;
}
avelocity.y += turn * drivespeed;
angles += avelocity * level.frametime;
dot = acceleration * orientation[0];
UpdateSound();
i = orientation[0];
j = orientation[1];
k = orientation[2];
CalculateAnglesOffset(acceleration);
CalculateOriginOffset();
last_origin = origin;
vTmp = (angles - primal_angles) * level.frametime;
if (vTmp.x > 180 || vTmp.x < -180) {
vTmp.x = 0;
}
if (vTmp.y > 180 || vTmp.y < -180) {
vTmp.y = 0;
}
if (vTmp.z > 180 || vTmp.z < -180) {
vTmp.z = 0;
}
if (vTmp.x > -1 || vTmp.x < 1) {
vTmp.x = 0;
}
if (vTmp.y > -1 || vTmp.y < 1) {
vTmp.y = 0;
}
if (vTmp.z > -1 || vTmp.z < 1) {
vTmp.z = 0;
}
avelocity = vAddedAngles;
if (!m_bAnimMove && !m_bMovementLocked) {
FactorInOriginOffset();
FactorInAnglesOffset(&vAddedAngles);
}
n_angles = m_vOriginOffset + m_vOriginOffset2;
CalculateAnimationData(vAddedAngles, m_vOriginOffset + m_vOriginOffset2);
if (m_pCollisionEntity) {
Vector vaDelta;
SetSlotsNonSolid();
vaDelta[0] = angledist(angles[0] - m_pCollisionEntity->angles[0]);
vaDelta[1] = angledist(angles[1] - m_pCollisionEntity->angles[1]);
vaDelta[2] = angledist(angles[2] - m_pCollisionEntity->angles[2]);
m_pCollisionEntity->Solid();
G_PushMove(m_pCollisionEntity, origin - primal_origin, vaDelta);
G_TouchTriggers(m_pCollisionEntity);
SetSlotsSolid();
m_pCollisionEntity->setOrigin(origin);
m_pCollisionEntity->setAngles(angles);
}
SetupVehicleSoundEntities();
UpdateDriverSlot(0);
for (int slot = 0; slot < MAX_PASSENGERS; slot++) {
UpdatePassengerSlot(slot);
}
atmp = angles - primal_angles;
for (int slot = 0; slot < MAX_TURRETS; slot++) {
UpdateTurretSlot(slot);
}
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, 1, 0, 0, 1, true);
}
}
for (int slot = 0; slot < MAX_TURRETS; slot++) {
if (Turrets[slot].enter_boneindex >= 0) {
QueryTurretSlotPosition(slot, (float *)&temp);
G_DebugCircle(temp, 10, 0, 1, 0, 1, true);
}
}
if (driver.enter_boneindex >= 0) {
QueryDriverSlotPosition(0, (float *)&temp);
G_DebugCircle(temp, 10, 0, 0, 1, 1, true);
}
}
if (g_showvehicleslotpoints->integer) {
for (int slot = 0; slot < MAX_PASSENGERS; slot++) {
if (Passengers[slot].boneindex >= 0) {
GetTagPositionAndOrientation(Passengers[slot].boneindex, &orient);
G_DebugCircle(orient.origin, 10.0, 1.0f, 0.5f, 0.5f, 1.0f, true);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[0]) * 32, 1, 0, 0, 1);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[1]) * 32, 0, 1, 0, 1);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[2]) * 32, 0, 0, 1, 1);
}
}
for (int slot = 0; slot < MAX_TURRETS; slot++) {
if (Turrets[slot].boneindex >= 0) {
GetTagPositionAndOrientation(Turrets[slot].boneindex, &orient);
G_DebugCircle(orient.origin, 10.0, 0.5f, 1.0f, 1.0f, 1.0f, true);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[0]) * 32, 1, 0, 0, 1);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[1]) * 32, 0, 1, 0, 1);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[2]) * 32, 0, 0, 1, 1);
}
}
if (driver.boneindex >= 0) {
GetTagPositionAndOrientation(driver.boneindex, &orient);
G_DebugCircle(orient.origin, 10.0, 0.5f, 0.5f, 1.0f, 1.0f, true);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[0]) * 32, 1, 0, 0, 1);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[1]) * 32, 0, 1, 0, 1);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[2]) * 32, 0, 0, 1, 1);
}
}
if (g_showvehicletags->integer) {
int numtags;
int tagnum;
numtags = gi.TIKI_NumTags(edict->tiki);
for (tagnum = 0; tagnum < numtags; tagnum++) {
const char *name = gi.Tag_NameForNum(edict->tiki, tagnum);
if (!strncmp(name, "tag", 3)) {
GetTagPositionAndOrientation(tagnum, &orient);
G_DebugCircle(orient.origin, 10.0, 1, 1, 1, 1, true);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[0]) * 32, 1, 0, 0, 1);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[1]) * 32, 0, 1, 0, 1);
G_DebugLine(orient.origin, orient.origin + Vector(orient.axis[2]) * 32, 0, 0, 1, 1);
}
}
}
for (last = this; last->vlink; last = v) {
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.f) % 360);
v->setAngles(v->angles);
}
CheckWater();
WorldEffects();
if (m_pCollisionEntity) {
setSolidType(SOLID_NOT);
m_pCollisionEntity->Solid();
} else {
setSolidType(SOLID_BBOX);
setContents(CONTENTS_UNKNOWN2);
}
}
/*
====================
Vehicle::VehicleTouched
====================
*/
void Vehicle::VehicleTouched(Event *ev)
{
Entity *other;
float dot;
float speed;
Vector delta;
Vector dir;
Event *event;
other = ev->GetEntity(1);
if (other == driver.ent) {
return;
}
if (other == world) {
return;
}
delta = other->origin - last_origin;
delta.normalize();
dot = velocity * orientation[0];
if (dot > 0) {
if (delta * orientation[0] < 0) {
return;
}
} else {
if (delta * orientation[0] > 0) {
return;
}
}
if (!other->IsSubclassOfVehicleTurretGun()) {
speed = velocity.length();
if (speed > 10) {
Sound(m_sSoundSet + "vehicle_crash");
dir = delta * (1 / speed);
event = new Event(EV_Damage);
if (lastdriver.ent && lastdriver.ent->IsSubclassOfPlayer()) {
event->AddEntity(lastdriver.ent);
} else {
event->AddEntity(world);
}
event->AddFloat(speed);
event->AddEntity(this);
event->AddVector(origin);
event->AddVector(dir);
event->AddVector(vec_zero);
event->AddFloat(speed);
event->AddInteger(0);
event->AddInteger(MOD_VEHICLE);
event->AddInteger(-1);
other->PostEvent(event, 0);
}
}
}
void Vehicle::SetProjectileVulnerable(Event *ev)
{
if (ev->NumArgs() > 1) {
ScriptError("Too many arguments");
}
if (ev->NumArgs() > 0) {
m_iProjectileHitsRemaining = ev->GetInteger(1);
if (m_iProjectileHitsRemaining < 0) {
ScriptError("Negative arguments illegal");
}
} else {
m_iProjectileHitsRemaining = 0;
}
}
void Vehicle::DoProjectileVulnerability(Entity *pProjectile, Entity *pOwner, meansOfDeath_t meansOfDeath)
{
Event *event;
Vector delta;
if (m_iProjectileHitsRemaining > 1) {
m_iProjectileHitsRemaining--;
return;
}
event = new Event(EV_Damage);
delta = origin - pProjectile->origin;
event->AddEntity(pProjectile);
event->AddFloat(health * 2);
event->AddEntity(pOwner);
event->AddVector(origin);
event->AddVector(delta);
event->AddVector(vec_zero);
event->AddFloat(0);
event->AddInteger(0);
event->AddInteger(meansOfDeath);
event->AddInteger(-1);
PostEvent(event, 0);
}
/*
====================
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::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::UpdateVariables
====================
*/
void Vehicle::UpdateVariables(
Vector *acceleration, Vector *vpn, Vector *vup, Vector *vright, Vector *t_vpn, Vector *t_vup, Vector *t_vright
)
{}
/*
====================
Vehicle::TorqueLookup
====================
*/
float Vehicle::TorqueLookup(int rpm)
{
if (rpm < 5000) {
return 190;
} else if (rpm < 6000) {
return (float)(190 * (6000 - rpm)) / 1000.f;
} else {
return 0;
}
}
/*
====================
Vehicle::SpawnTurret
====================
*/
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);
pTurret->PostEvent(EV_TakeDamage, EV_POSTSPAWN);
UpdateTurretSlot(slot);
pTurret->ProcessPendingEvents();
}
/*
====================
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");
}
switch (ev->NumArgs()) {
case 3:
m_fLookAhead = ev->GetFloat(3);
case 2:
m_fIdealAccel = ev->GetFloat(2);
case 1:
m_fIdealSpeed = ev->GetFloat(1);
m_fMaxSpeed = m_fIdealSpeed;
break;
}
}
/*
====================
Vehicle::EventModel
====================
*/
void Vehicle::EventModel(Event *ev)
{
SetModelEvent(ev);
}
/*
====================
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::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 cVehicleSpline;
}
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::EventLockMovement
====================
*/
void Vehicle::EventLockMovement(Event *ev)
{
m_bMovementLocked = true;
}
/*
====================
Vehicle::EventUnlockMovement
====================
*/
void Vehicle::EventUnlockMovement(Event *ev)
{
m_bMovementLocked = false;
}
/*
====================
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::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_CORNERS; 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::TurnOnVehicleSoundEntities
====================
*/
void Vehicle::TurnOnVehicleSoundEntities(void)
{
for (int i = 0; i < MAX_CORNERS; 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_CORNERS; i++) {
if (!m_pVehicleSoundEntities[i]) {
m_pVehicleSoundEntities[i] = new VehicleSoundEntity(this);
}
m_pVehicleSoundEntities[i]->Stop();
}
}
/*
====================
Vehicle::RemoveVehicleSoundEntities
====================
*/
void Vehicle::RemoveVehicleSoundEntities(void)
{
for (int i = 0; i < MAX_CORNERS; i++) {
if (!m_pVehicleSoundEntities[i]) {
continue;
}
m_pVehicleSoundEntities[i]->PostEvent(EV_Remove, EV_VEHICLE);
}
}
/*
====================
Vehicle::KickSuspension
====================
*/
void Vehicle::KickSuspension(Vector vDirection, float fForce)
{
VectorNormalizeFast(vDirection);
m_fForwardForce += vDirection * orientation[1] * fForce;
m_fLeftForce += vDirection * orientation[0] * fForce;
}
/*
====================
Vehicle::EventDamage
====================
*/
void Vehicle::EventDamage(Event *ev)
{
Entity *pEnt;
Vector vDirection;
float fForce;
int i;
if (!IsDamagedBy(ev->GetEntity(3))) {
return;
}
pEnt = ev->GetEntity(1);
if (pEnt && pEnt == driver.ent) {
return;
}
Event *event = new Event(EV_Damage, ev->NumArgs());
vDirection = ev->GetVector(5);
fForce = ev->GetFloat(7);
KickSuspension(vDirection, fForce);
for (i = 1; i <= ev->NumArgs(); i++) {
if (i == 7) {
event->AddFloat(0);
} else {
event->AddValue(ev->GetValue(i));
}
}
if (driver.ent && driver.ent->IsSubclassOfPlayer()) {
Player *player = static_cast<Player *>(driver.ent.Pointer());
Vector dir = ev->GetVector(1);
float dir_yaw;
float camera_yaw;
if (player->camera) {
dir_yaw = dir.toYaw();
camera_yaw = player->camera->angles[1];
} else {
dir_yaw = dir.toYaw();
camera_yaw = player->GetVAngles()[1];
}
player->damage_yaw = AngleSubtract(camera_yaw, dir_yaw) + 180.5;
}
DamageEvent(event);
delete event;
}
/*
====================
Vehicle::FactorInOriginOffset
====================
*/
void Vehicle::FactorInOriginOffset(void)
{
origin += m_vOriginOffset;
setOrigin(origin);
}
/*
====================
Vehicle::FactorOutOriginOffset
====================
*/
void Vehicle::FactorOutOriginOffset(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;
Vector oldoffset;
oldoffset = m_vOriginOffset;
m_vOriginOffset += m_vOriginOffset2;
m_vOriginOffset2 = vec_zero;
for (index = 0; index < MAX_CORNERS; index++) {
if (m_bTireHit[index]) {
temp = m_vTireEnd[index];
vTireAvg += origin - temp;
iNum++;
} 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 += vec_zero;
FactorInSkidOrigin();
Vector vTmp = real_acceleration - prev_acceleration;
m_fDownForce = vTmp[2] * m_fZCoef;
m_fDownForce = Q_clamp_float(m_fDownForce, m_fZMin, m_fZMax);
m_fUpForce = -m_vOriginOffset[2] * m_fBouncyCoef + m_fUpForce;
m_fUpForce *= m_fSpringyCoef;
m_vOriginOffset[2] += (m_fDownForce + m_fUpForce) * level.frametime * 12;
if (m_vOriginOffset[2] < m_fZMin) {
m_vOriginOffset[2] = m_fZMin;
}
m_vOriginOffset -= m_vOriginOffset2;
if (!isfinite(m_vOriginOffset[0]) || !isfinite(m_vOriginOffset[1]) || !isfinite(m_vOriginOffset[2])) {
m_vOriginOffset = oldoffset;
}
}
/*
====================
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 && m_iLastTiresUpdate + 1000 > level.inttime) {
return;
}
m_iLastTiresUpdate = level.inttime;
vTmp.y = angles.y + 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 + Vector(0, 0, -400);
trace = G_Trace(start, t_mins, t_maxs, end, this, MASK_VEHICLE_TIRES, false, "Vehicle::PostThink Corners");
if (g_showvehiclemovedebug->integer) {
G_DebugLine(start, end, 1, 1, 1, 1);
G_DebugLine(start, trace.endpos, 1, 0, 0, 1);
}
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]->setContents(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 && m_iLastTiresUpdate != -1 && m_iLastTiresUpdate + 1000 > level.inttime) {
return;
}
angles.AngleVectorsLeft(&i, &j, NULL);
j = vec_zero - j;
m_vNormalSum = vec_zero;
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];
CrossProduct(vDist1, vDist2, vCross);
vCross.normalize();
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];
CrossProduct(vDist1, vDist2, vCross);
vCross.normalize();
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];
CrossProduct(vDist1, vDist2, vCross);
vCross.normalize();
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];
CrossProduct(vDist1, vDist2, vCross);
vCross.normalize();
m_vNormalSum += vCross;
m_iNumNormals++;
}
if (m_iNumNormals > 1) {
float x, z;
temp = m_vNormalSum / m_iNumNormals;
i = temp.CrossProduct(temp, j);
pitch = i;
x = pitch.toPitch();
temp = m_vNormalSum / m_iNumNormals;
pitch = temp.CrossProduct(temp, i);
z = pitch.toPitch();
angles[0] = x;
angles[2] = z;
}
}
/*
====================
Vehicle::UpdateBones
====================
*/
void Vehicle::UpdateBones(void)
{
float fNewTurnAngle = AngleNormalize180(turnangle - m_fSkidAngle);
fNewTurnAngle = Q_clamp_float(fNewTurnAngle, -maxturnrate, maxturnrate);
SetControllerAngles(0, Vector(0, fNewTurnAngle, 0));
SetControllerAngles(1, Vector(0, fNewTurnAngle, 0));
}
/*
====================
Vehicle::UpdateShaderOffset
====================
*/
void Vehicle::UpdateShaderOffset(void)
{
m_fShaderOffset -= real_velocity * orientation[0] / 4 * level.frametime;
edict->s.shader_time = m_fShaderOffset;
}
/*
====================
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);
}
if (drivable) {
driver.ent->avelocity = avelocity;
driver.ent->velocity = velocity;
driver.ent->setAngles(angles);
}
} 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::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);
}
Passengers[iSlot].ent->avelocity = avelocity;
Passengers[iSlot].ent->velocity = velocity;
if (!Passengers[iSlot].ent->IsSubclassOfActor() || !((Actor *)Passengers[iSlot].ent.Pointer())->m_Enemy) {
Vector newAngles;
MatrixToEulerAngles(orient.axis, newAngles);
Passengers[iSlot].ent->setAngles(newAngles);
}
} 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::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);
}
Turrets[iSlot].ent->avelocity = avelocity;
Turrets[iSlot].ent->velocity = velocity;
if (Turrets[iSlot].ent->IsSubclassOfVehicleTurretGun()) {
VehicleTurretGun *vtg = static_cast<VehicleTurretGun *>(Turrets[iSlot].ent.Pointer());
vtg->SetBaseOrientation(orient.axis, NULL);
}
} 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->IsSubclassOfVehicleTurretGun()) {
VehicleTurretGun *vtg = static_cast<VehicleTurretGun *>(Turrets[iSlot].ent.Pointer());
vtg->SetBaseOrientation(this->orientation, NULL);
}
}
}
/*
====================
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_iPoints, NULL);
m_bStopEnabled = true;
}
/*
====================
Vehicle::GetPathPosition
====================
*/
float Vehicle::GetPathPosition(cVehicleSpline *pPath, int iNode)
{
float *vTmp;
vec3_t vPrev;
vec3_t vCur;
vec3_t vTotal;
Vector vDelta;
float fTotal;
float fCoef;
vTmp = pPath->GetByNode(iNode, NULL);
VectorCopy(vTmp + 1, vCur);
if (g_showvehiclemovedebug->integer) {
G_DebugString(vCur, 3.0f, 1.0f, 1.0f, 1.0f, "%f", vTmp[0]);
}
vTmp = pPath->GetByNode(iNode - 1, NULL);
VectorCopy(vTmp + 1, vPrev);
if (g_showvehiclemovedebug->integer) {
G_DebugString(vPrev, 3.0f, 1.0f, 1.0f, 1.0f, "%f", vTmp[0]);
}
VectorSubtract(vCur, vPrev, vTotal);
fTotal = VectorLength(vTotal);
m_vIdealDir = vTotal;
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, -32, -32), Vector(32, 32, 32), 0, 1, 1, 1);
G_DebugBBox(vCur, Vector(-32, -32, -32), Vector(32, 32, 32), 1, 1, 0, 1);
G_DebugArrow(vCur, m_vIdealDir * -1.0, (1.0 - fCoef) * fTotal, 0, 1, 0, 1);
G_DebugArrow(vPrev, m_vIdealDir, fCoef * fTotal, 0, 0, 1, 1);
}
return *pPath->GetByNode(iNode - (1.0 - fCoef), NULL);
}
/*
====================
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::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::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::EventContinueSkidding
====================
*/
void Vehicle::EventContinueSkidding(Event *ev)
{
if (m_bEnableSkidding) {
if (HasAnim("skidding")) {
NewAnim("skidding", EV_Vehicle_ContinueSkidding, 7, 0.000001f);
} else {
assert(!"Vehicle without skidding animation.");
}
} else {
if (HasAnim("idle")) {
NewAnim("idle", 0, 7, 0.000001f);
} else {
assert(!"Vehicle without idle animation.");
}
}
}
/*
====================
Vehicle::FactorInAnglesOffset
====================
*/
void Vehicle::FactorInAnglesOffset(Vector *vAddedAngles)
{
(*vAddedAngles) = m_vAnglesOffset;
vAddedAngles->y += m_fSkidAngle;
}
/*
====================
Vehicle::FactorOutAnglesOffset
====================
*/
void Vehicle::FactorOutAnglesOffset(void) {}
/*
====================
Vehicle::CalculateAnglesOffset
====================
*/
void Vehicle::CalculateAnglesOffset(Vector acceleration)
{
if (level.inttime <= 1200) {
// leave some time before allowing to shake
// so all tanks spawn peacefully in the level
return;
}
m_fForwardForce += DotProduct(orientation[0], acceleration) * m_fYawCoef;
m_fBackForce = -m_vAnglesOffset[0] * m_fBouncyCoef + m_fBackForce;
m_fBackForce *= m_fSpringyCoef;
m_vAnglesOffset[0] += m_fForwardForce + m_fBackForce * 12.0 * level.frametime;
m_vAnglesOffset[0] = Q_clamp_float(m_vAnglesOffset[0], m_fYawMin, m_fYawMax);
m_fForwardForce = 0;
m_fLeftForce += acceleration * orientation[1] * m_fRollCoef;
m_fRightForce = -m_vAnglesOffset[2] * m_fBouncyCoef + m_fRightForce;
m_fRightForce *= m_fSpringyCoef;
m_vAnglesOffset[2] += 12.0 * (m_fLeftForce + m_fRightForce) * level.frametime;
m_vAnglesOffset[2] = Q_clamp_float(m_vAnglesOffset[2], m_fRollMin, m_fRollMax);
m_fLeftForce = 0;
}
/*
====================
Vehicle::CalculateAnimationData
====================
*/
void Vehicle::CalculateAnimationData(Vector vAngles, Vector vOrigin)
{
float fLeft = fEpsilon();
float fRight = fEpsilon();
float fForward = fEpsilon();
float fBack = fEpsilon();
float fLow = fEpsilon();
float fHigh = fEpsilon();
if (vAngles[0] < 0.0) {
fBack = vAngles[0] / m_fYawMin;
} else if (vAngles[0] > 0.0) {
fForward = vAngles[0] / 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;
}
if (!m_bAnimMove) {
NewAnim("idle", NULL, 0, 1.0);
NewAnim("lean_left", NULL, 3, fLeft);
NewAnim("lean_right", NULL, 4, fRight);
NewAnim("lean_forward", NULL, 1, fForward);
NewAnim("lean_back", NULL, 2, fBack);
NewAnim("high", NULL, 6, fLow);
NewAnim("low", NULL, 5, fHigh);
}
}
/*
====================
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);
}
void Vehicle::EventVehicleMoveAnim(Event *ev)
{
str anim_name;
anim_name = ev->GetString(1);
if (!HasAnim(anim_name)) {
return;
}
m_bAnimMove = true;
StopAnimating(0);
StopAnimating(3);
StopAnimating(4);
StopAnimating(1);
StopAnimating(2);
StopAnimating(6);
StopAnimating(5);
StopAnimating(8);
NewAnim(anim_name, EV_Vehicle_VehicleMoveAnimDone);
}
void Vehicle::EventVehicleMoveAnimDone(Event *ev)
{
Unregister(STRING_ANIMDONE);
moveimpulse = 0;
turnimpulse = 0;
m_bAnimMove = false;
}
void Vehicle::EventDamageSounds(Event *ev)
{
if (ev->NumArgs() == 1) {
m_bDamageSounds = ev->GetInteger(1);
} else {
m_bDamageSounds = true;
}
}
void Vehicle::EventRunSounds(Event *ev)
{
if (ev->NumArgs() == 1) {
m_bRunSounds = ev->GetInteger(1);
} else {
m_bRunSounds = true;
}
}
void Vehicle::Remove(Event *ev)
{
int i;
for (i = 0; i < MAX_TURRETS; i++) {
Entity *pTurret = Turrets[i].ent;
if (!pTurret) {
continue;
}
pTurret->PostEvent(EV_Remove, 0);
Turrets[i].ent = NULL;
}
Entity::Remove(ev);
}
/*
====================
Vehicle::IsDamagedBy
Returns whether or not the vehicle is damaged by the specified entity.
====================
*/
bool Vehicle::IsDamagedBy(Entity *ent)
{
int i;
if (FindDriverSlotByEntity(ent) != -1) {
return false;
}
if (FindPassengerSlotByEntity(ent) != -1) {
return false;
}
if (FindTurretSlotByEntity(ent) != -1) {
return false;
}
for (i = 0; i < MAX_TURRETS; i++) {
TurretGun *pTurret;
if (!Turrets[i].ent) {
continue;
}
pTurret = static_cast<TurretGun *>(Turrets[i].ent.Pointer());
if (pTurret->IsSubclassOfTurretGun() && pTurret->owner == ent) {
return false;
}
}
return true;
}
/*
====================
Vehicle::DriverAdded
====================
*/
void Vehicle::DriverAdded() {}
void Vehicle::EventSetMaxUseAngle(Event *ev)
{
m_fMaxUseAngle = ev->GetFloat(1);
}
void Vehicle::EventCanUse(Event *ev)
{
Entity *entity = ev->GetEntity(1);
if (driver.ent || !m_fMaxUseAngle || !Turrets[0].ent) {
ev->AddInteger(1);
return;
}
if (!entity) {
ev->AddInteger(0);
return;
}
Vector vForward;
Vector vDir;
AngleVectors(Turrets[0].ent->angles, vForward, NULL, NULL);
vDir = Turrets[0].ent->origin - entity->origin;
VectorNormalize(vDir);
if (fabs(GetAngleBetweenVectors2D(vForward, vDir)) > m_fMaxUseAngle) {
ev->AddInteger(false);
} else {
ev->AddInteger(true);
}
}
int Vehicle::GetProjectileHitsRemaining() const {
return m_iProjectileHitsRemaining;
}
/*
====================
Vehicle::Archive
====================
*/
void Vehicle::Archive(Archiver& arc)
{
VehicleBase::Archive(arc);
arc.ArchiveFloat(&maxturnrate);
arc.ArchiveFloat(&currentspeed);
arc.ArchiveFloat(&turnangle);
arc.ArchiveFloat(&turnimpulse);
arc.ArchiveFloat(&moveimpulse);
arc.ArchiveFloat(&jumpimpulse);
arc.ArchiveFloat(&speed);
arc.ArchiveFloat(&conesize);
arc.ArchiveFloat(&maxtracedist);
arc.ArchiveString(&weaponName);
arc.ArchiveString(&vehicleName);
arc.ArchiveVector(&last_origin);
arc.ArchiveVector(&seatangles);
arc.ArchiveVector(&seatoffset);
arc.ArchiveVector(&driveroffset);
arc.ArchiveVector(&Corners[0]);
arc.ArchiveVector(&Corners[1]);
arc.ArchiveVector(&Corners[2]);
arc.ArchiveVector(&Corners[3]);
arc.ArchiveBoolean(&drivable);
arc.ArchiveBoolean(&pathDrivable); // Added in 2.30
arc.ArchiveBoolean(&locked);
arc.ArchiveBoolean(&hasweapon);
arc.ArchiveBoolean(&showweapon);
arc.ArchiveBoolean(&steerinplace);
arc.ArchiveBoolean(&jumpable);
arc.ArchiveBoolean(&m_bMovementLocked);
arc.ArchiveBoolean(&m_bAnimMove); // Added in 2.0
arc.ArchiveBoolean(&m_bDamageSounds); // Added in 2.0
arc.ArchiveBoolean(&m_bRunSounds); // Added in 2.0
arc.ArchiveInteger(&m_iProjectileHitsRemaining); // Added in 2.30
driver.Archive(arc);
lastdriver.Archive(arc);
for (int i = 0; i < MAX_PASSENGERS; i++) {
Passengers[i].Archive(arc);
}
for (int i = 0; i < MAX_TURRETS; i++) {
Turrets[i].Archive(arc);
}
arc.ArchiveInteger(&numPassengers);
arc.ArchiveInteger(&numTurrets);
arc.ArchiveInteger(&moveresult);
arc.ArchiveInteger(&isBlocked);
arc.ArchiveInteger(&m_iFrameCtr);
arc.ArchiveInteger(&m_iGear);
arc.ArchiveInteger(&m_iRPM);
arc.ArchiveInteger(&m_iNextPathStartNode);
if (!arc.Saving()) {
m_iLastTiresUpdate = -1;
int tempInt;
arc.ArchiveInteger(&tempInt);
if (tempInt) {
m_pAlternatePath = new cVehicleSpline;
} else {
m_pAlternatePath = NULL;
}
} else {
int tempInt = m_pAlternatePath != NULL;
arc.ArchiveInteger(&tempInt);
}
if (m_pAlternatePath) {
m_pAlternatePath->Archive(arc);
}
arc.ArchiveInteger(&m_iAlternateNode);
if (!arc.Saving()) {
int tempInt;
arc.ArchiveInteger(&tempInt);
if (tempInt) {
m_pCurPath = new cVehicleSpline;
} else {
m_pCurPath = NULL;
}
} else {
int tempInt = m_pCurPath != NULL;
arc.ArchiveInteger(&tempInt);
}
if (m_pCurPath) {
m_pCurPath->Archive(arc);
}
arc.ArchiveInteger(&m_iCurNode);
if (!arc.Saving()) {
int tempInt;
arc.ArchiveInteger(&tempInt);
if (tempInt) {
m_pNextPath = new cVehicleSpline;
} else {
m_pNextPath = NULL;
}
} else {
int tempInt = m_pNextPath != NULL;
arc.ArchiveInteger(&tempInt);
}
if (m_pNextPath) {
m_pNextPath->Archive(arc);
}
arc.ArchiveFloat(&maxturnrate);
arc.ArchiveFloat(&currentspeed);
arc.ArchiveFloat(&turnangle);
arc.ArchiveFloat(&turnimpulse);
arc.ArchiveFloat(&moveimpulse);
arc.ArchiveFloat(&prev_moveimpulse);
arc.ArchiveFloat(&jumpimpulse);
arc.ArchiveFloat(&speed);
arc.ArchiveFloat(&conesize);
arc.ArchiveFloat(&maxtracedist);
arc.ArchiveFloat(&airspeed);
arc.ArchiveFloat(&m_fGearRatio[0]);
arc.ArchiveFloat(&m_fGearRatio[1]);
arc.ArchiveFloat(&m_fGearRatio[2]);
arc.ArchiveFloat(&m_fGearRatio[3]);
arc.ArchiveFloat(&m_fGearRatio[4]);
arc.ArchiveFloat(&m_fGearRatio[5]);
arc.ArchiveFloat(&m_fMass);
arc.ArchiveFloat(&m_fFrontMass);
arc.ArchiveFloat(&m_fBackMass);
arc.ArchiveFloat(&m_fWheelBase);
arc.ArchiveFloat(&m_fWheelFrontLoad);
arc.ArchiveFloat(&m_fWheelFrontInnerLoad);
arc.ArchiveFloat(&m_fWheelFrontOutterLoad);
arc.ArchiveFloat(&m_fWheelFrontDist);
arc.ArchiveFloat(&m_fWheelFrontSuspension);
arc.ArchiveFloat(&m_fWheelBackLoad);
arc.ArchiveFloat(&m_fWheelBackInnerLoad);
arc.ArchiveFloat(&m_fWheelBackOutterLoad);
arc.ArchiveFloat(&m_fWheelBackDist);
arc.ArchiveFloat(&m_fWheelBackSuspension);
arc.ArchiveFloat(&m_fCGHeight);
arc.ArchiveFloat(&m_fBankAngle);
arc.ArchiveFloat(&m_fTread);
arc.ArchiveFloat(&m_fTrackWidth);
arc.ArchiveFloat(&m_fTireFriction);
arc.ArchiveFloat(&m_fDrag);
arc.ArchiveFloat(&m_fTireRadius);
arc.ArchiveFloat(&m_fFrontBrakes);
arc.ArchiveFloat(&m_fBackBrakes);
arc.ArchiveFloat(&m_fRollingResistance);
arc.ArchiveFloat(&m_fTireRotationalSpeed);
arc.ArchiveFloat(&m_fFrontBrakingForce);
arc.ArchiveFloat(&m_fBackBrakingForce);
arc.ArchiveFloat(&m_fBrakingPerformance);
arc.ArchiveFloat(&m_fLastTurn);
arc.ArchiveFloat(&m_fTangForce);
arc.ArchiveFloat(&m_fInertia);
arc.ArchiveFloat(&m_fDifferentialRatio);
arc.ArchiveFloat(&m_fGearEfficiency);
arc.ArchiveFloat(&m_fMaxTraction);
arc.ArchiveFloat(&m_fTractionForce);
arc.ArchiveFloat(&m_fAccelerator);
arc.ArchiveFloat(&m_fTorque);
arc.ArchiveFloat(&m_fDownForce);
arc.ArchiveFloat(&m_fUpForce);
arc.ArchiveFloat(&m_fLeftForce);
arc.ArchiveFloat(&m_fRightForce);
arc.ArchiveFloat(&m_fForwardForce);
arc.ArchiveFloat(&m_fBackForce);
arc.ArchiveFloat(&m_fBouncyCoef);
arc.ArchiveFloat(&m_fSpringyCoef);
arc.ArchiveFloat(&m_fYawMin);
arc.ArchiveFloat(&m_fYawMax);
arc.ArchiveFloat(&m_fRollMin);
arc.ArchiveFloat(&m_fRollMax);
arc.ArchiveFloat(&m_fZMin);
arc.ArchiveFloat(&m_fZMax);
arc.ArchiveFloat(&m_fYawCoef);
arc.ArchiveFloat(&m_fRollCoef);
arc.ArchiveFloat(&m_fZCoef);
arc.ArchiveFloat(&m_fShaderOffset);
arc.ArchiveFloat(&m_fSoundMinSpeed);
arc.ArchiveFloat(&m_fSoundMinPitch);
arc.ArchiveFloat(&m_fSoundMaxSpeed);
arc.ArchiveFloat(&m_fSoundMaxPitch);
arc.ArchiveFloat(&m_fVolumeMinSpeed);
arc.ArchiveFloat(&m_fVolumeMinPitch);
arc.ArchiveFloat(&m_fVolumeMaxSpeed);
arc.ArchiveFloat(&m_fVolumeMaxPitch);
arc.ArchiveFloat(&m_fStopStartDistance);
arc.ArchiveFloat(&m_fStopStartSpeed);
arc.ArchiveFloat(&m_fStopEndDistance);
arc.ArchiveFloat(&m_fSkidAngle);
arc.ArchiveFloat(&m_fSkidLeftForce);
arc.ArchiveFloat(&m_fSkidRightForce);
arc.ArchiveString(&weaponName);
arc.ArchiveString(&m_sAnimationSet);
arc.ArchiveString(&m_sSoundSet);
arc.ArchiveString(&m_sExplosionModel);
arc.ArchiveVector(&last_origin);
arc.ArchiveVector(&seatangles);
arc.ArchiveVector(&driveroffset);
arc.ArchiveVector(&Corners[0]);
arc.ArchiveVector(&Corners[1]);
arc.ArchiveVector(&Corners[2]);
arc.ArchiveVector(&Corners[3]);
arc.ArchiveVector(&v_angle);
arc.ArchiveVector(&yaw_forward);
arc.ArchiveVector(&yaw_left);
arc.ArchiveVector(&ground_normal);
arc.ArchiveVector(&base_angles);
arc.ArchiveVector(&prev_velocity);
arc.ArchiveVector(&real_velocity);
arc.ArchiveVector(&prev_origin);
arc.ArchiveVector(&real_acceleration);
arc.ArchiveVector(&prev_acceleration);
arc.ArchiveVector(&m_vOldMins);
arc.ArchiveVector(&m_vOldMaxs);
arc.ArchiveVector(&m_vCG);
arc.ArchiveVector(&m_vAngularAcceleration);
arc.ArchiveVector(&m_vAngularVelocity);
arc.ArchiveVector(&m_vAngles);
arc.ArchiveVector(&m_vFrontNormal);
arc.ArchiveVector(&m_vBackNormal);
arc.ArchiveVector(&m_vFrontAngles);
arc.ArchiveVector(&m_vBackAngles);
arc.ArchiveVector(&m_vBaseNormal);
arc.ArchiveVector(&m_vBaseAngles);
arc.ArchiveVector(&m_vPrevNormal);
arc.ArchiveVector(&m_vResistance);
arc.ArchiveVector(&m_vWheelForce);
arc.ArchiveVector(&m_vTangForce);
arc.ArchiveVector(&m_vForce);
arc.ArchiveVector(&m_vAcceleration);
arc.ArchiveVector(&m_vOriginOffset);
arc.ArchiveVector(&m_vOriginOffset2);
arc.ArchiveVector(&m_vOriginCornerOffset);
arc.ArchiveVector(&m_vAnglesOffset);
arc.ArchiveVector(&m_vSaveAngles);
arc.ArchiveVector(&m_vSkidOrigin);
arc.ArchiveBoolean(&drivable);
arc.ArchiveBoolean(&locked);
arc.ArchiveBoolean(&hasweapon);
arc.ArchiveBoolean(&showweapon);
arc.ArchiveBoolean(&steerinplace);
arc.ArchiveBoolean(&jumpable);
arc.ArchiveBoolean(&m_bWheelSpinning);
arc.ArchiveBoolean(&m_bIsSkidding);
arc.ArchiveBoolean(&m_bIsBraking);
arc.ArchiveBoolean(&m_bBackSlipping);
arc.ArchiveBoolean(&m_bFrontSlipping);
arc.ArchiveBoolean(&m_bAutomatic);
arc.ArchiveBoolean(&m_bThinkCalled);
arc.ArchiveBoolean(&m_bRemoveOnDeath);
arc.ArchiveBoolean(&m_bStopEnabled);
arc.ArchiveBoolean(&m_bEnableSkidding);
arc.ArchiveVec3(vs.origin);
arc.ArchiveVec3(vs.velocity);
arc.ArchiveInteger(&vs.groundEntityNum);
arc.ArchiveBoolean(&vs.walking);
arc.ArchiveBoolean(&vs.groundPlane);
G_ArchiveTrace(arc, &vs.groundTrace);
arc.ArchiveInteger(&vs.entityNum);
arc.ArchiveVec2(vs.desired_dir);
arc.ArchiveBoolean(&vs.hit_obstacle);
arc.ArchiveVec3(vs.hit_origin);
arc.ArchiveVec3(vs.obstacle_normal);
arc.ArchiveBoolean(&vs.useGravity);
if (!arc.Saving()) {
m_sMoveGrid = new cMoveGrid(3, 3, 1);
}
m_sMoveGrid->Archive(arc);
arc.ArchiveFloat(&m_fIdealSpeed);
arc.ArchiveFloat(&m_fMaxSpeed); // Added in 2.30
arc.ArchiveBool(&m_bBounceBackwards); // Added in 2.30
arc.ArchiveVector(&m_vIdealPosition);
arc.ArchiveVector(&m_vIdealDir);
arc.ArchiveFloat(&m_fIdealAccel);
arc.ArchiveFloat(&m_fIdealDistance);
arc.ArchiveFloat(&m_fLookAhead);
arc.ArchiveBool(&m_bAutoPilot);
arc.ArchiveSafePointer(&m_pCollisionEntity);
ArchiveEnum(m_eSoundState, SOUND_STATE);
arc.ArchiveFloat(&m_fNextSoundState);
arc.ArchiveSafePointer(&m_pVehicleSoundEntities[0]);
arc.ArchiveSafePointer(&m_pVehicleSoundEntities[1]);
arc.ArchiveSafePointer(&m_pVehicleSoundEntities[2]);
arc.ArchiveSafePointer(&m_pVehicleSoundEntities[3]);
// Added in 2.30
arc.ArchiveFloat(&m_fMaxUseAngle);
arc.ArchiveBool(&m_bBounceStayFullSpeed);
}
/*
====================
Vehicle::GetSoundSet
====================
*/
str Vehicle::GetSoundSet(void)
{
return m_sSoundSet;
}
/*
====================
Vehicle::getName
====================
*/
const str& Vehicle::getName() const
{
return vehicleName;
}
/*
====================
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()
{
AddWaitTill(STRING_DEATH);
drivable = true;
setMoveType(MOVETYPE_VEHICLE);
flags |= FL_POSTTHINK | FL_THINK;
}
/*
====================
DrivableVehicle::Killed
====================
*/
void DrivableVehicle::Killed(Event *ev)
{
Entity *ent;
Entity *attacker;
Vector dir;
Event *event;
const char *name;
VehicleBase *last;
int i;
deadflag = DEAD_DEAD;
if (!m_bRemoveOnDeath) {
Unregister(STRING_DEATH);
return;
}
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 = static_cast<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);
}
//
// kill all passengers
//
for (i = 0; i < MAX_PASSENGERS; i++) {
if (Passengers[i].ent) {
Vector dir;
SentientPtr sent;
Event *event;
velocity = vec_zero;
sent = static_cast<Sentient *>(Passengers[i].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);
}
}
//
// remove all turrets
//
for (i = 0; i < MAX_TURRETS; i++) {
if (Turrets[i].ent) {
Turrets[i].ent->PostEvent(EV_Remove, EV_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, EV_VEHICLE);
last = last->vlink;
}
//
// kill the killtargets
//
name = KillTarget();
if (name && strcmp(name, "")) {
for (ent = G_FindTarget(NULL, name); ent; ent = G_FindTarget(ent, name)) {
ent->PostEvent(EV_Remove, EV_VEHICLE);
}
}
//
// fire targets
//
name = Target();
if (name && strcmp(name, "")) {
for (ent = G_FindTarget(NULL, name); ent; ent = G_FindTarget(ent, name)) {
event = new Event(EV_Activate);
event->AddEntity(attacker);
ent->ProcessEvent(event);
}
}
PostEvent(EV_Remove, EV_VEHICLE);
Unregister(STRING_DEATH);
}