/* =========================================================================== 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(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(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(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(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(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(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(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(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(¤tspeed); 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(¤tspeed); 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(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(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); }