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