2060 lines
55 KiB
Ucode
2060 lines
55 KiB
Ucode
//=============================================================================
|
|
// Vehicle
|
|
// this abstract base class contains gameplay and AI code for vehicles
|
|
//=============================================================================
|
|
|
|
class Vehicle extends Pawn
|
|
native
|
|
nativereplication
|
|
abstract;
|
|
|
|
var globalconfig bool bVehicleShadows; // Global config option for vehicle shadows
|
|
|
|
var bool bDriving; // Vehicle is being driven
|
|
var bool bOldDriving;
|
|
var bool bDrawVehicleShadow; // Vehicle Shadow
|
|
var (Vehicle) bool bDrawDriverInTP; // Whether to draw the driver when in 3rd person mode.
|
|
var (Vehicle) bool bDriverCollideActors; // if driver is drawn in vehicle, is Driver.bCollideActors true ?
|
|
var (Vehicle) bool bRelativeExitPos; // relative vector to vehicle?
|
|
|
|
var bool bDefensive; // should be used by defenders
|
|
var bool bAutoTurret; // controlled by AI if no player controlling (FIXME Move to subclass)
|
|
var bool bRemoteControlled; // destroying vehicle won't kill player
|
|
var bool bEjectDriver; // If vehicle is destroyed, don't kill and eject driver(s)
|
|
var bool bTurnInPlace; // whether vehicle can turn in place
|
|
var bool bFollowLookDir; // used by AI to know that controller's rotation determines vehicle rotation
|
|
var bool bNonHumanControl; // Cannot be controlled by humans
|
|
var bool bStalled; // Vehicle is stalled (can't apply acceleration)
|
|
var bool bVehicleDestroyed; // Vehicle has been destroyed (no more need to simulate special vehicle physics)
|
|
var bool bShowDamageOverlay; // Vehicle should display the normal pawn damage overlay when hit by a weapon
|
|
var bool bDropDetail; // Vehicle should reduce its detail level
|
|
var bool bNoFriendlyFire; // FriendlyFire disabled for this vehicle
|
|
var bool bCanHover; // Actor can hover above water
|
|
var bool bCanDoTrickJumps; // AI hint
|
|
|
|
// Cameras
|
|
var (Vehicle) bool bDrawMeshInFP; // Whether to draw the vehicle mesh when in 1st person mode.
|
|
var (Vehicle) bool bZeroPCRotOnEntry; // If true, set camera rotation to zero on entering vehicle. If false, set it to the vehicle rotation.
|
|
var bool bPCRelativeFPRotation; // In 1st person, PlayerController rotation is relative to vehicle rotation
|
|
|
|
var bool bWeaponisFiring;
|
|
var bool bWeaponisAltFiring;
|
|
|
|
var bool bTeamLocked; // Team defines which players are allowed to enter the vehicle
|
|
var bool bEnterringUnlocks; // Vehicle is unlocked when a player enterred it..
|
|
var bool bCanFlip;
|
|
var bool bAllowViewChange;
|
|
var bool bAllowWeaponToss; //if the driver dies, will he toss his weapon?
|
|
|
|
var(Vehicle) bool bHUDTrackVehicle; // If true, Vehicle will tracked on HUD. (For Objectives in Assault)
|
|
var bool bHasRadar;
|
|
var bool bHasHandbrake; // hint for AI
|
|
var bool bScriptedRise; // hint for AI
|
|
var bool bKeyVehicle; // hint for AI
|
|
var bool bSeparateTurretFocus; // hint for AI (for tank type turreted vehicles)
|
|
|
|
var() bool bHighScoreKill; // vehicle is considered important, and awards 5 points upon destruction.
|
|
|
|
var bool bAdjustDriversHead; // rotate driver's head depending on view rotation
|
|
var bool bEnemyLockedOn;
|
|
var config bool bDesiredBehindView;
|
|
|
|
var bool bHideRemoteDriver; // If Set to true, the remote controlling driver will be hidden
|
|
var bool bShowChargingBar;
|
|
var bool bDriverHoldsFlag;
|
|
var bool bCanCarryFlag;
|
|
|
|
var bool bSpawnProtected; // Cannot be destroyed by a player before its been possessed.
|
|
|
|
var() bool bFPNoZFromCameraPitch; // Ignore any vehicle-space Z due to FPCamViewOffset (so looking up and down doesn't change camera Z rel to vehicle)
|
|
|
|
var byte StuckCount; // used by AI
|
|
var() byte Team;
|
|
var byte OldTeam, PrevTeam; // OldTeam is used for replication purposes, PrevTeam is the team of the previous driver.
|
|
|
|
var Rotator PlayerEnterredRotation; // Original rotation when player enterred vehicle
|
|
var float EjectMomentum;
|
|
|
|
var class<Controller> AutoTurretControllerClass;
|
|
|
|
// generic controls (set by controller, used by concrete derived classes)
|
|
var (Vehicle) float Steering; // between -1 and 1
|
|
var (Vehicle) float Throttle; // between -1 and 1
|
|
var (Vehicle) float Rise; // between -1 and 1
|
|
var int DriverViewPitch; // The driver's view pitch
|
|
var int DriverViewYaw; // The driver's view yaw
|
|
|
|
var float ThrottleTime; // last time at which throttle was 0 (used by AI)
|
|
var float StuckTime;
|
|
var float VehicleMovingTime; // used by AI C++
|
|
|
|
var (Vehicle) vector DrivePos; // Position (rel to vehicle) to put player while driving.
|
|
var (Vehicle) rotator DriveRot; // Rotation (rel to vehicle) to put driver while driving.
|
|
var (Vehicle) name DriveAnim; // Animation to play while driving.
|
|
|
|
//Info for EntryPositions
|
|
var (Vehicle) array<vector> ExitPositions; // Positions (rel to vehicle) to try putting the player when exiting.
|
|
var (Vehicle) vector EntryPosition; // Offset for the entry trigger
|
|
var (Vehicle) float EntryRadius; // Radius for the entry trigger
|
|
|
|
var (Vehicle) vector FPCamPos; // Position of camera when driving first person.
|
|
var (Vehicle) vector FPCamViewOffset; // Offset in reference frame of camera.
|
|
|
|
//clientside settings
|
|
var config float TPCamDistance;
|
|
// force feedback
|
|
var string CenterSpringForce;
|
|
var int CenterSpringRangePitch;
|
|
var int CenterSpringRangeRoll;
|
|
|
|
var (Vehicle) vector TPCamLookat; // Look at location in vehicle space
|
|
var (Vehicle) vector TPCamWorldOffset; // Applied in world space after vehicle transform.
|
|
var float DesiredTPCamDistance, LastCameraCalcTime, CameraSpeed; //for smoothly interpolating TPCamDistance to new value
|
|
var (Vehicle) Range TPCamDistRange;
|
|
|
|
var (Vehicle) int MaxViewYaw; // Maximum amount you can look left and right
|
|
var (Vehicle) int MaxViewPitch; // Maximum amount you can look up and down
|
|
|
|
var Pawn Driver; // Can be None if Controller spawns right away with vehicle
|
|
var SVehicleFactory ParentFactory;
|
|
|
|
// FX
|
|
var String TransEffects[2]; // Spawning effects
|
|
var ShadowProjector VehicleShadow; // Shadow projector
|
|
var float ShadowMaxTraceDist;
|
|
var float ShadowCullDistance;
|
|
|
|
var float MomentumMult; //damage momentum multiplied by this value before being applied to vehicle
|
|
var float DriverDamageMult; //damage to the driver is multiplied by this value
|
|
|
|
// Missle warning
|
|
var String LockOnClassString;
|
|
var float LastLockWarningTime;
|
|
var float LockWarningInterval;
|
|
|
|
var Vehicle NextVehicle;
|
|
var localized string VehiclePositionString;
|
|
var localized cache string VehicleNameString;
|
|
var localized cache string VehicleDescription;
|
|
|
|
var Texture TeamBeaconTexture, NoEntryTexture;
|
|
var Material TeamBeaconBorderMaterial;
|
|
|
|
var AIMarker myMarker; // used for stationary turrets
|
|
|
|
//VEHICULAR MANSLAUGHTER
|
|
var float MinRunOverSpeed; //speed must be greater than this for running into someone to do damage
|
|
var class<DamageType> RanOverDamageType, CrushedDamageType;
|
|
var sound RanOverSound;
|
|
var name StolenAnnouncement;
|
|
var sound StolenSound;
|
|
|
|
var float LinkHealMult; // If > 0, Link Gun secondary heals an amount equal to its damage times this
|
|
|
|
var float OldSteering;
|
|
var float VehicleLostTime, TeamUseTime, PlayerStartTime;
|
|
var float MaxDesireability;
|
|
var const float AIMoveCheckTime;
|
|
var float ObjectiveGetOutDist; //if AI controlled and bot needs to trigger an objective not triggerable by vehicles, it will try to get out this far away
|
|
|
|
var name FlagBone;
|
|
var vector FlagOffset;
|
|
var rotator FlagRotation;
|
|
|
|
var float WheelsScale;
|
|
|
|
// HORN
|
|
var array<sound> HornSounds;
|
|
var float LastHornTime;
|
|
|
|
// BULLET HITS
|
|
var() array<sound> BulletSounds;
|
|
|
|
// WATER DAMAGE
|
|
var() float WaterDamage;
|
|
var class<DamageType> VehicleDrowningDamType;
|
|
|
|
// HUD OVERLAY
|
|
var class<Actor> HUDOverlayClass;
|
|
var Actor HUDOverlay;
|
|
var() vector HUDOverlayOffset;
|
|
var() float HUDOverlayFOV;
|
|
|
|
// SPAWN OVERLAY MATERIAL
|
|
var() Material SpawnOverlay[2];
|
|
|
|
|
|
struct native SVehicleIcon
|
|
{
|
|
var Material Material;
|
|
var float X, Y, SizeX, SizeY;
|
|
var bool bIsGreyScale;
|
|
};
|
|
|
|
var SVehicleIcon VehicleIcon;
|
|
|
|
// if _RO_
|
|
// Interpolated throttle
|
|
var float ThrottleAmount; //Client side only. Used by the PlayerController to smoothly interpolate the throttle rather than having it be all or nothing
|
|
var bool bKeepDriverAuxCollision; // Keep the auxilary collision turned on for the driver of this vehicle - used only for ROPawn drivers (or ROPawn subclasses)
|
|
|
|
cpptext
|
|
{
|
|
virtual void PreNetReceive();
|
|
virtual void PostNetReceive();
|
|
void performPhysics(FLOAT DeltaSeconds);
|
|
virtual UBOOL moveToward(const FVector &Dest, AActor *GoalActor);
|
|
virtual void rotateToward(AActor *Focus, FVector FocalPoint);
|
|
virtual void PostRender(FLevelSceneNode* SceneNode, FRenderInterface* RI);
|
|
virtual UBOOL IsStuck();
|
|
INT* GetOptimizedRepList(BYTE* Recent, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map, UActorChannel* Channel);
|
|
INT AddMyMarker(AActor *S);
|
|
void SteerVehicle(FVector Direction);
|
|
UBOOL IsNetRelevantFor( APlayerController* RealViewer, AActor* Viewer, FVector SrcLocation );
|
|
virtual FVector GetDestination(AController *C);
|
|
virtual void setMoveTimer(FVector MoveDir);
|
|
virtual UBOOL Tick(FLOAT DeltaTime, enum ELevelTick TickType);
|
|
virtual UBOOL AdjustFlight(FLOAT ZDiff, UBOOL bFlyingDown, FLOAT Distance, AActor* GoalActor);
|
|
}
|
|
|
|
replication
|
|
{
|
|
reliable if ( Role==ROLE_Authority )
|
|
ClientKDriverEnter, ClientKDriverLeave, FixPCRotation, ClientClearController;
|
|
|
|
unreliable if( bNetDirty && Role==ROLE_Authority )
|
|
bDriving, bTeamLocked, Driver, Team, bVehicleDestroyed, WheelsScale;
|
|
|
|
unreliable if( bNetInitial && Role==ROLE_Authority )
|
|
bHUDTrackVehicle;
|
|
|
|
reliable if ( Role < ROLE_Authority )
|
|
VehicleFire, VehicleCeaseFire;
|
|
}
|
|
|
|
function PreBeginPlay()
|
|
{
|
|
if ( !Level.Game.bAllowVehicles && !bDeleteMe )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
|
|
Super.PreBeginPlay();
|
|
}
|
|
|
|
// if _RO_
|
|
// Ammo interface
|
|
|
|
// Implemented in sublclasses. Returns true if this vehicle's ammo was successfully resupplied
|
|
function bool ResupplyAmmo(){return false;}
|
|
function EnteredResupply();
|
|
function LeftResupply();
|
|
// end _RO_
|
|
|
|
function PlayerChangedTeam()
|
|
{
|
|
if ( Driver != None )
|
|
Driver.KilledBy(Driver);
|
|
else
|
|
Super.PlayerChangedTeam();
|
|
}
|
|
|
|
simulated function SetBaseEyeheight()
|
|
{
|
|
BaseEyeheight = Default.BaseEyeheight;
|
|
Eyeheight = BaseEyeheight;
|
|
}
|
|
|
|
simulated function string GetVehiclePositionString()
|
|
{
|
|
return VehiclePositionString;
|
|
}
|
|
|
|
function Suicide()
|
|
{
|
|
if ( Driver != None )
|
|
Driver.KilledBy(Driver);
|
|
else
|
|
KilledBy(self);
|
|
}
|
|
|
|
function bool CheatWalk()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function bool CheatGhost()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function bool CheatFly()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
simulated function PostBeginPlay()
|
|
{
|
|
local controller NewController;
|
|
|
|
super.PostBeginPlay();
|
|
|
|
if ( bDeleteMe )
|
|
return;
|
|
|
|
// if _RO_
|
|
/*
|
|
// end if _RO_
|
|
// Glue a shadow projector on
|
|
VehicleShadow = Spawn(class'ShadowProjector', self, '', Location);
|
|
VehicleShadow.ShadowActor = Self;
|
|
VehicleShadow.bBlobShadow = false;
|
|
VehicleShadow.LightDirection = Normal(vect(1,1,6));
|
|
VehicleShadow.LightDistance = 1200;
|
|
VehicleShadow.MaxTraceDistance = ShadowMaxTraceDist;
|
|
VehicleShadow.CullDistance = ShadowCullDistance;
|
|
VehicleShadow.InitShadow();
|
|
// if _RO_
|
|
*/
|
|
UpdateShadow();
|
|
// end if _RO_
|
|
|
|
if ( Role == Role_Authority )
|
|
{
|
|
if ( bAutoTurret && (Controller == None) && (AutoTurretControllerClass != None) )
|
|
{
|
|
NewController = spawn(AutoTurretControllerClass);
|
|
if ( NewController != None )
|
|
NewController.Possess(self);
|
|
}
|
|
if ( !bAutoTurret && !bNonHumanControl && IndependentVehicle() )
|
|
Level.Game.RegisterVehicle(self);
|
|
}
|
|
|
|
OldTeam = Team;
|
|
PrevTeam = Team;
|
|
}
|
|
|
|
// if _RO_
|
|
simulated function UpdateShadow()
|
|
{
|
|
if ( bVehicleShadows && bDrawVehicleShadow && (Level.NetMode != NM_DedicatedServer) )
|
|
{
|
|
if (VehicleShadow != none)
|
|
return;
|
|
|
|
// Glue a shadow projector on
|
|
VehicleShadow = Spawn(class'ShadowProjector', self, '', Location);
|
|
VehicleShadow.ShadowActor = Self;
|
|
VehicleShadow.bBlobShadow = false;
|
|
VehicleShadow.LightDirection = Normal(vect(1,1,6));
|
|
VehicleShadow.LightDistance = 1200;
|
|
VehicleShadow.MaxTraceDistance = ShadowMaxTraceDist;
|
|
VehicleShadow.CullDistance = ShadowCullDistance;
|
|
VehicleShadow.InitShadow();
|
|
}
|
|
else if (VehicleShadow != none && Level.NetMode != NM_DedicatedServer)
|
|
{
|
|
VehicleShadow.Destroy();
|
|
VehicleShadow = none;
|
|
}
|
|
}
|
|
// end if _RO_
|
|
|
|
simulated event SetInitialState()
|
|
{
|
|
Super.SetInitialState();
|
|
|
|
Disable('Tick');
|
|
}
|
|
|
|
function bool StronglyRecommended(Actor S, int TeamIndex, Actor Objective)
|
|
{
|
|
return bKeyVehicle;
|
|
}
|
|
|
|
//return a value indicating how useful this vehicle is to bots
|
|
function float BotDesireability(Actor S, int TeamIndex, Actor Objective)
|
|
{
|
|
local bool bSameTeam;
|
|
local PlayerController P;
|
|
|
|
bSameTeam = ( GetTeamNum() == TeamIndex );
|
|
if ( bSameTeam )
|
|
{
|
|
if ( Level.TimeSeconds < TeamUseTime )
|
|
return 0;
|
|
if ( !bKeyVehicle && (Level.TimeSeconds < PlayerStartTime) )
|
|
{
|
|
P = Level.GetLocalPlayerController();
|
|
if ( (P == None) || ((P.Pawn != None) && (Vehicle(P.Pawn) == None)) )
|
|
return 0;
|
|
}
|
|
}
|
|
if ( !bKeyVehicle && !bStationary && (Level.TimeSeconds < VehicleLostTime) )
|
|
return 0;
|
|
else if (Health <= 0 || Occupied() || (bTeamLocked && !bSameTeam))
|
|
return 0;
|
|
|
|
if (bKeyVehicle)
|
|
return 100;
|
|
|
|
return ((MaxDesireability * 0.5) + (MaxDesireability * 0.5 * (float(Health) / HealthMax)));
|
|
}
|
|
|
|
simulated function Destroyed()
|
|
{
|
|
local Vehicle V, Prev;
|
|
|
|
if ( ParentFactory != None )
|
|
ParentFactory.VehicleDestroyed( Self ); // Notify parent factory of death
|
|
|
|
if ( VehicleShadow != None )
|
|
VehicleShadow.Destroy(); // Destroy shadow projector
|
|
|
|
if ( bAutoTurret && (Controller != None) && ClassIsChildOf(Controller.Class, AutoTurretControllerClass) && !Controller.bDeleteMe )
|
|
{
|
|
Controller.Destroy();
|
|
Controller = None;
|
|
}
|
|
|
|
if ( Driver != None )
|
|
Destroyed_HandleDriver();
|
|
|
|
if ( Level.Game != None )
|
|
{
|
|
if ( Level.Game.VehicleList == Self )
|
|
Level.Game.VehicleList = NextVehicle;
|
|
else
|
|
{
|
|
Prev = Level.Game.VehicleList;
|
|
if ( Prev != None )
|
|
for ( V=Level.Game.VehicleList.NextVehicle; V!=None; V=V.NextVehicle )
|
|
{
|
|
if ( V == self )
|
|
{
|
|
Prev.NextVehicle = NextVehicle;
|
|
break;
|
|
}
|
|
else
|
|
Prev = V;
|
|
}
|
|
}
|
|
}
|
|
|
|
super.Destroyed();
|
|
}
|
|
|
|
simulated function Destroyed_HandleDriver()
|
|
{
|
|
local Pawn OldDriver;
|
|
|
|
Driver.LastRenderTime = LastRenderTime;
|
|
if ( Role == ROLE_Authority )
|
|
{
|
|
// if Driver wasn't visible in vehicle, destroy it
|
|
if ( Driver != None && !bRemoteControlled && !bEjectDriver && !bDrawDriverInTP && Driver.Health > 0 )
|
|
{
|
|
OldDriver = Driver;
|
|
Driver = None;
|
|
OldDriver.DrivenVehicle = None;
|
|
if ( !OldDriver.bDeleteMe )
|
|
OldDriver.Destroy();
|
|
}
|
|
else if ( !bRemoteControlled && !bEjectDriver )
|
|
{
|
|
// otherwise spawn dead karma body
|
|
if (!bDrawDriverInTP && PlaceExitingDriver())
|
|
{
|
|
Driver.StopDriving(self);
|
|
Driver.DrivenVehicle = self;
|
|
}
|
|
Driver.TearOffMomentum = Velocity * 0.25;
|
|
Driver.Died(Controller, class'DamRanOver', Driver.Location);
|
|
}
|
|
}
|
|
else if ( Driver.DrivenVehicle == self )
|
|
Driver.StopDriving(self);
|
|
}
|
|
|
|
simulated function vector GetCameraLocationStart()
|
|
{
|
|
return Location;
|
|
}
|
|
|
|
simulated function SpecialCalcBehindView(PlayerController PC, out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
|
|
{
|
|
local vector CamLookAt, HitLocation, HitNormal, OffsetVector;
|
|
local Actor HitActor;
|
|
local vector x, y, z;
|
|
|
|
if (DesiredTPCamDistance < TPCamDistance)
|
|
TPCamDistance = FMax(DesiredTPCamDistance, TPCamDistance - CameraSpeed * (Level.TimeSeconds - LastCameraCalcTime));
|
|
else if (DesiredTPCamDistance > TPCamDistance)
|
|
TPCamDistance = FMin(DesiredTPCamDistance, TPCamDistance + CameraSpeed * (Level.TimeSeconds - LastCameraCalcTime));
|
|
|
|
GetAxes(PC.Rotation, x, y, z);
|
|
ViewActor = self;
|
|
CamLookAt = GetCameraLocationStart() + (TPCamLookat >> Rotation) + TPCamWorldOffset;
|
|
|
|
OffsetVector = vect(0, 0, 0);
|
|
OffsetVector.X = -1.0 * TPCamDistance;
|
|
|
|
CameraLocation = CamLookAt + (OffsetVector >> PC.Rotation);
|
|
|
|
HitActor = Trace(HitLocation, HitNormal, CameraLocation, CamLookAt, true, vect(40, 40, 40));
|
|
if ( HitActor != None
|
|
&& (HitActor.bWorldGeometry || HitActor == GetVehicleBase() || Trace(HitLocation, HitNormal, CameraLocation, CamLookAt, false, vect(40, 40, 40)) != None) )
|
|
CameraLocation = HitLocation;
|
|
|
|
CameraRotation = Normalize(PC.Rotation + PC.ShakeRot);
|
|
CameraLocation = CameraLocation + PC.ShakeOffset.X * x + PC.ShakeOffset.Y * y + PC.ShakeOffset.Z * z;
|
|
}
|
|
|
|
simulated function SpecialCalcFirstPersonView(PlayerController PC, out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
|
|
{
|
|
local quat CarQuat, LookQuat, ResultQuat;
|
|
local vector VehicleZ, CamViewOffsetWorld, x, y, z;
|
|
local float CamViewOffsetZAmount;
|
|
|
|
GetAxes(PC.Rotation, x, y, z);
|
|
ViewActor = self;
|
|
|
|
if (bPCRelativeFPRotation)
|
|
{
|
|
CarQuat = QuatFromRotator(Rotation);
|
|
CameraRotation = Normalize(PC.Rotation);
|
|
LookQuat = QuatFromRotator(CameraRotation);
|
|
ResultQuat = QuatProduct(LookQuat, CarQuat);
|
|
CameraRotation = QuatToRotator(ResultQuat);
|
|
}
|
|
else
|
|
CameraRotation = PC.Rotation;
|
|
|
|
// Camera position is locked to car
|
|
CamViewOffsetWorld = FPCamViewOffset >> CameraRotation;
|
|
CameraLocation = GetCameraLocationStart() + (FPCamPos >> Rotation) + CamViewOffsetWorld;
|
|
|
|
if(bFPNoZFromCameraPitch)
|
|
{
|
|
VehicleZ = vect(0,0,1) >> Rotation;
|
|
CamViewOffsetZAmount = CamViewOffsetWorld Dot VehicleZ;
|
|
CameraLocation -= CamViewOffsetZAmount * VehicleZ;
|
|
}
|
|
|
|
CameraRotation = Normalize(CameraRotation + PC.ShakeRot);
|
|
CameraLocation = CameraLocation + PC.ShakeOffset.X * x + PC.ShakeOffset.Y * y + PC.ShakeOffset.Z * z;
|
|
}
|
|
|
|
// Special calc-view for vehicles
|
|
simulated function bool SpecialCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
|
|
{
|
|
local PlayerController pc;
|
|
|
|
pc = PlayerController(Controller);
|
|
|
|
// Only do this mode we have a playercontroller
|
|
if( (pc == None) || (pc.Viewtarget != self) )
|
|
return false;
|
|
|
|
if( pc.bBehindView )
|
|
SpecialCalcBehindView(PC,ViewActor,CameraLocation,CameraRotation);
|
|
else
|
|
SpecialCalcFirstPersonView(PC,ViewActor,CameraLocation,CameraRotation);
|
|
|
|
LastCameraCalcTime = Level.TimeSeconds;
|
|
|
|
return true;
|
|
}
|
|
|
|
simulated function bool SpectatorSpecialCalcView(PlayerController Viewer, out Actor ViewActor, out vector CameraLocation, out rotator CameraRotation)
|
|
{
|
|
local rotator DummyRotation;
|
|
|
|
if (Viewer.ViewTarget != self)
|
|
return false;
|
|
|
|
if (Viewer.bBehindView)
|
|
{
|
|
DesiredTPCamDistance = (TPCamDistRange.Max - TPCamDistRange.Min) * (Viewer.CameraDist / Viewer.CameraDistRange.Max) + TPCamDistRange.Min;
|
|
SpecialCalcBehindView(Viewer, ViewActor, CameraLocation, CameraRotation);
|
|
}
|
|
else
|
|
SpecialCalcFirstPersonView(Viewer, ViewActor, CameraLocation, DummyRotation); //use rotation set by playercontroller
|
|
|
|
LastCameraCalcTime = Level.TimeSeconds;
|
|
|
|
Viewer.SetLocation(CameraLocation);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Events called on driver entering/leaving vehicle
|
|
|
|
function Vehicle FindEntryVehicle(Pawn P)
|
|
{
|
|
if ( AIController(P.Controller) != None )
|
|
return self;
|
|
if (VSize(P.Location - (Location + (EntryPosition >> Rotation))) < EntryRadius)
|
|
return self;
|
|
return None;
|
|
}
|
|
|
|
// The pawn Driver has tried to take control of this vehicle
|
|
function bool TryToDrive(Pawn P)
|
|
{
|
|
if ( P.bIsCrouched || bNonHumanControl || (P.Controller == None) || (Driver != None) || (P.DrivenVehicle != None) || !P.Controller.bIsPlayer
|
|
|| P.IsA('Vehicle') || Health <= 0 )
|
|
return false;
|
|
|
|
if( !Level.Game.CanEnterVehicle(self, P) )
|
|
return false;
|
|
|
|
// Check vehicle Locking....
|
|
if ( !bTeamLocked || P.GetTeamNum() == Team )
|
|
{
|
|
if ( bEnterringUnlocks && bTeamLocked )
|
|
bTeamLocked = false;
|
|
|
|
KDriverEnter( P );
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
VehicleLocked( P );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
event VehicleLocked( Pawn P ); // Pawn tried to enter vehicle, but it's locked!!
|
|
|
|
function PossessedBy(Controller C)
|
|
{
|
|
local PlayerController PC;
|
|
|
|
if ( bAutoTurret && (Controller != None) && ClassIsChildOf(Controller.Class, AutoTurretControllerClass) && !Controller.bDeleteMe )
|
|
{
|
|
Controller.Destroy();
|
|
Controller = None;
|
|
}
|
|
|
|
super.PossessedBy( C );
|
|
|
|
// Stole another team's vehicle, so set Team to new owner's team
|
|
if ( C.GetTeamNum() != Team )
|
|
{
|
|
//add stat tracking event/variable here?
|
|
if ( Team != 255 && PlayerController(C) != None )
|
|
{
|
|
if( StolenAnnouncement != '' )
|
|
PlayerController(C).PlayRewardAnnouncement(StolenAnnouncement, 1);
|
|
|
|
if( StolenSound != None )
|
|
PlaySound( StolenSound,, 2.5*TransientSoundVolume,, 400);
|
|
}
|
|
|
|
if ( C.GetTeamNum() != 255 )
|
|
SetTeamNum( C.GetTeamNum() );
|
|
}
|
|
|
|
NetPriority = 3;
|
|
NetUpdateFrequency = 100;
|
|
ThrottleTime = Level.TimeSeconds;
|
|
bSpawnProtected = false;
|
|
|
|
PC = PlayerController(C);
|
|
if ( PC != None )
|
|
ClientKDriverEnter( PC );
|
|
|
|
if ( ParentFactory != None && ( !bAutoTurret || !ClassIsChildOf(C.Class, AutoTurretControllerClass) ) )
|
|
ParentFactory.VehiclePossessed( Self ); // Notify parent factory
|
|
}
|
|
|
|
function UnPossessed()
|
|
{
|
|
local PlayerController PC;
|
|
local Controller NewController;
|
|
local bool bWasPlayer;
|
|
|
|
StopWeaponFiring();
|
|
PC = PlayerController(Controller);
|
|
|
|
|
|
|
|
if ( PC != None )
|
|
{
|
|
bWasPlayer = true;
|
|
ClientKDriverLeave(PC);
|
|
if (bPCRelativeFPRotation && !PC.bBehindView)
|
|
FixPCRotation(PC);
|
|
}
|
|
else
|
|
ClientClearController();
|
|
|
|
NetPriority = Default.NetPriority; // restore original netpriority changed when possessing
|
|
NetUpdateTime = Level.TimeSeconds - 1;
|
|
NetUpdateFrequency = 8;
|
|
|
|
super.UnPossessed();
|
|
|
|
if ( ParentFactory != None && ( !bAutoTurret || (Controller == None) || !ClassIsChildOf(Controller.Class, AutoTurretControllerClass) ) )
|
|
ParentFactory.VehicleUnPossessed( Self ); // Notify parent of UnPossessed()
|
|
|
|
if ( Health > 0 && !bDeleteMe )
|
|
{
|
|
if ( bWasPlayer && bAutoTurret && (AutoTurretControllerClass != None) )
|
|
{
|
|
Controller = None;
|
|
NewController = spawn(AutoTurretControllerClass);
|
|
if ( NewController != None )
|
|
NewController.Possess( Self );
|
|
}
|
|
}
|
|
}
|
|
|
|
function KDriverEnter(Pawn P)
|
|
{
|
|
local Controller C;
|
|
|
|
bDriving = True;
|
|
StuckCount = 0;
|
|
|
|
// We don't have pre-defined exit positions here, so we use the original player location as an exit point
|
|
if ( !bRelativeExitPos )
|
|
{
|
|
PlayerEnterredRotation = P.Rotation;
|
|
ExitPositions[0] = P.Location + Vect(0,0,16);
|
|
}
|
|
|
|
// Set pawns current controller to control the vehicle pawn instead
|
|
C = P.Controller;
|
|
if ( !bCanCarryFlag && (C.PlayerReplicationInfo.HasFlag != None) )
|
|
P.DropFlag();
|
|
|
|
Driver = P;
|
|
Driver.StartDriving( Self );
|
|
|
|
// Disconnect PlayerController from Driver and connect to SVehicle.
|
|
C.bVehicleTransition = true; // to keep Bots from doing Restart()
|
|
C.Unpossess();
|
|
Driver.SetOwner( Self ); // This keeps the driver relevant.
|
|
C.Possess( Self );
|
|
C.bVehicleTransition = false;
|
|
|
|
DrivingStatusChanged();
|
|
|
|
if ( PlayerController(C) != None )
|
|
VehicleLostTime = 0;
|
|
|
|
AttachFlag(PlayerReplicationInfo.HasFlag);
|
|
|
|
Level.Game.DriverEnteredVehicle(self, P);
|
|
}
|
|
|
|
function AttachFlag(Actor FlagActor)
|
|
{
|
|
if ( !bDriverHoldsFlag && (FlagActor != None) )
|
|
{
|
|
AttachToBone(FlagActor,FlagBone);
|
|
FlagActor.SetRelativeRotation(FlagRotation);
|
|
FlagActor.SetRelativeLocation(FlagOffset);
|
|
}
|
|
}
|
|
|
|
simulated event SetWheelsScale(float NewScale)
|
|
{
|
|
WheelsScale = NewScale;
|
|
}
|
|
|
|
// Called from the PlayerController when player wants to get out.
|
|
event bool KDriverLeave( bool bForceLeave )
|
|
{
|
|
local Controller C;
|
|
local PlayerController PC;
|
|
local bool havePlaced;
|
|
|
|
if( !bForceLeave && !Level.Game.CanLeaveVehicle(self, Driver) )
|
|
return false;
|
|
|
|
if ( (PlayerReplicationInfo != None) && (PlayerReplicationInfo.HasFlag != None) )
|
|
Driver.HoldFlag(PlayerReplicationInfo.HasFlag);
|
|
|
|
// Do nothing if we're not being driven
|
|
if (Controller == None )
|
|
return false;
|
|
|
|
// Before we can exit, we need to find a place to put the driver.
|
|
// Iterate over array of possible exit locations.
|
|
|
|
if ( (Driver != None) && (!bRemoteControlled || bHideRemoteDriver) )
|
|
{
|
|
Driver.bHardAttach = false;
|
|
Driver.bCollideWorld = true;
|
|
Driver.SetCollision(true, true);
|
|
havePlaced = PlaceExitingDriver();
|
|
|
|
// If we could not find a place to put the driver, leave driver inside as before.
|
|
if (!havePlaced && !bForceLeave )
|
|
{
|
|
Driver.bHardAttach = true;
|
|
Driver.bCollideWorld = false;
|
|
Driver.SetCollision(false, false);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bDriving = False;
|
|
|
|
// Reconnect Controller to Driver.
|
|
C = Controller;
|
|
if (C.RouteGoal == self)
|
|
C.RouteGoal = None;
|
|
if (C.MoveTarget == self)
|
|
C.MoveTarget = None;
|
|
C.bVehicleTransition = true;
|
|
Controller.UnPossess();
|
|
|
|
if ( (Driver != None) && (Driver.Health > 0) )
|
|
{
|
|
Driver.SetOwner( C );
|
|
C.Possess( Driver );
|
|
|
|
PC = PlayerController(C);
|
|
if ( PC != None )
|
|
PC.ClientSetViewTarget( Driver ); // Set playercontroller to view the person that got out
|
|
|
|
Driver.StopDriving( Self );
|
|
}
|
|
C.bVehicleTransition = false;
|
|
|
|
if ( C == Controller ) // If controller didn't change, clear it...
|
|
Controller = None;
|
|
|
|
Level.Game.DriverLeftVehicle(self, Driver);
|
|
|
|
// Car now has no driver
|
|
Driver = None;
|
|
|
|
DriverLeft();
|
|
|
|
// Put brakes on before you get out :)
|
|
Throttle = 0;
|
|
Steering = 0;
|
|
Rise = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
// DriverLeft() called by KDriverLeave()
|
|
function DriverLeft()
|
|
{
|
|
DrivingStatusChanged();
|
|
}
|
|
|
|
simulated event UpdateTiltForceFeedback()
|
|
{
|
|
local rotator SpringCenter;
|
|
local float pitch, roll;
|
|
local PlayerController PC;
|
|
|
|
PC = PlayerController(Controller);
|
|
if ( PC != None )
|
|
{
|
|
SpringCenter = rotation;
|
|
pitch = Clamp(SpringCenter.Pitch, -CenterSpringRangePitch, CenterSpringRangePitch);
|
|
roll = Clamp(SpringCenter.Roll, -CenterSpringRangeRoll, CenterSpringRangeRoll);
|
|
pitch /= CenterSpringRangePitch;
|
|
roll /= CenterSpringRangeRoll;
|
|
PC.ChangeSpringFeedbackEffect(CenterSpringForce, roll, pitch);
|
|
}
|
|
}
|
|
|
|
simulated function ClientKDriverEnter(PlayerController PC)
|
|
{
|
|
// PC.bFreeCamera = true;
|
|
|
|
// Set rotation of camera when getting into vehicle based on bZeroPCRotOnEntry
|
|
if ( bZeroPCRotOnEntry )
|
|
PC.SetRotation( rot(0, 0, 0) );
|
|
|
|
//set starting camera distance to local player's preferences
|
|
TPCamDistance = default.TPCamDistance;
|
|
DesiredTPCamDistance = TPCamDistance;
|
|
|
|
if (!PC.bBehindView)
|
|
ActivateOverlay(True);
|
|
|
|
if ( PC.bEnableGUIForceFeedback
|
|
&& PC.bForceFeedbackSupported
|
|
&& (Viewport(PC.Player) != None) )
|
|
{
|
|
if ( (CenterSpringRangePitch > 0) && (CenterSpringRangeRoll > 0) )
|
|
UpdateTiltForceFeedback();
|
|
PC.ClientPlayForceFeedback(CenterSpringForce);
|
|
}
|
|
|
|
if (Driver!=None)
|
|
{
|
|
Driver.AmbientSound=none;
|
|
if (Driver.Weapon!=None)
|
|
Driver.Weapon.AmbientSound=none;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
simulated function ClientClearController()
|
|
{
|
|
ActivateOverlay(False);
|
|
}
|
|
|
|
simulated function ClientKDriverLeave(PlayerController PC)
|
|
{
|
|
// PC.bFreeCamera = false;
|
|
|
|
// Stop messing with bOwnerNoSee
|
|
if ( Driver != None )
|
|
Driver.bOwnerNoSee = Driver.default.bOwnerNoSee;
|
|
|
|
if (PC.bEnableGUIForceFeedback)
|
|
PC.StopForceFeedback(CenterSpringForce);
|
|
|
|
bWeaponisFiring = False;
|
|
bWeaponisAltFiring = False;
|
|
|
|
ActivateOverlay(False);
|
|
}
|
|
|
|
simulated function ActivateOverlay(bool bActive)
|
|
{
|
|
if (bActive)
|
|
{
|
|
if (HUDOverlayClass != None && HUDOverlay == None)
|
|
HUDOverlay = spawn(HUDOverlayClass);
|
|
}
|
|
else if (HUDOverlay != None)
|
|
HUDOverlay.Destroy();
|
|
}
|
|
|
|
//seperate replicated function called from UnPossessed() to make PC rotation no longer relative to vehicle
|
|
//needed because PC.bBehindView will get screwed around with as a result of unpossessing vehicle and repossessing Driver
|
|
simulated function FixPCRotation(PlayerController PC)
|
|
{
|
|
PC.SetRotation(rotator(vector(PC.Rotation) >> Rotation));
|
|
}
|
|
|
|
simulated function AttachDriver(Pawn P)
|
|
{
|
|
local vector AttachPos;
|
|
|
|
P.bHardAttach = true;
|
|
AttachPos = Location + (DrivePos >> Rotation);
|
|
P.SetLocation( AttachPos );
|
|
P.SetPhysics( PHYS_None );
|
|
P.SetBase( Self );
|
|
P.SetRelativeRotation( DriveRot );
|
|
}
|
|
|
|
simulated function DetachDriver(Pawn P) {}
|
|
|
|
function bool PlaceExitingDriver()
|
|
{
|
|
local int i, j;
|
|
local vector tryPlace, Extent, HitLocation, HitNormal, ZOffset, RandomSphereLoc;
|
|
local float BestDir, NewDir;
|
|
|
|
if ( Driver == None )
|
|
return false;
|
|
Extent = Driver.default.CollisionRadius * vect(1,1,0);
|
|
Extent.Z = Driver.default.CollisionHeight;
|
|
ZOffset = Driver.default.CollisionHeight * vect(0,0,1);
|
|
|
|
//avoid running driver over by placing in direction perpendicular to velocity
|
|
if ( VSize(Velocity) > 100 )
|
|
{
|
|
tryPlace = Normal(Velocity cross vect(0,0,1)) * (CollisionRadius + Driver.default.CollisionRadius ) * 1.25 ;
|
|
if ( (Controller != None) && (Controller.DirectionHint != vect(0,0,0)) )
|
|
{
|
|
if ( (tryPlace dot Controller.DirectionHint) < 0 )
|
|
tryPlace *= -1;
|
|
}
|
|
else if ( FRand() < 0.5 )
|
|
tryPlace *= -1; //randomly prefer other side
|
|
if ( (Trace(HitLocation, HitNormal, Location + tryPlace + ZOffset, Location + ZOffset, false, Extent) == None && Driver.SetLocation(Location + tryPlace + ZOffset))
|
|
|| (Trace(HitLocation, HitNormal, Location - tryPlace + ZOffset, Location + ZOffset, false, Extent) == None && Driver.SetLocation(Location - tryPlace + ZOffset)) )
|
|
return true;
|
|
}
|
|
|
|
if ( (Controller != None) && (Controller.DirectionHint != vect(0,0,0)) )
|
|
{
|
|
// first try best position
|
|
tryPlace = Location;
|
|
BestDir = 0;
|
|
for( i=0; i<ExitPositions.Length; i++)
|
|
{
|
|
NewDir = Normal(ExitPositions[i] - Location) Dot Controller.DirectionHint;
|
|
if ( NewDir > BestDir )
|
|
{
|
|
BestDir = NewDir;
|
|
tryPlace = ExitPositions[i];
|
|
}
|
|
}
|
|
Controller.DirectionHint = vect(0,0,0);
|
|
if ( tryPlace != Location )
|
|
{
|
|
if ( bRelativeExitPos )
|
|
{
|
|
if ( ExitPositions[0].Z != 0 )
|
|
ZOffset = Vect(0,0,1) * ExitPositions[0].Z;
|
|
else
|
|
ZOffset = Driver.default.CollisionHeight * vect(0,0,2);
|
|
|
|
tryPlace = Location + ( (tryPlace-ZOffset) >> Rotation) + ZOffset;
|
|
|
|
// First, do a line check (stops us passing through things on exit).
|
|
if ( (Trace(HitLocation, HitNormal, tryPlace, Location + ZOffset, false, Extent) == None)
|
|
&& Driver.SetLocation(tryPlace) )
|
|
return true;
|
|
}
|
|
else if ( Driver.SetLocation(tryPlace) )
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if ( !bRelativeExitPos )
|
|
{
|
|
for( i=0; i<ExitPositions.Length; i++)
|
|
{
|
|
tryPlace = ExitPositions[i];
|
|
|
|
if ( Driver.SetLocation(tryPlace) )
|
|
return true;
|
|
else
|
|
{
|
|
for (j=0; j<10; j++) // try random positions in a sphere...
|
|
{
|
|
RandomSphereLoc = VRand()*200* FMax(FRand(),0.5);
|
|
RandomSphereLoc.Z = Extent.Z * FRand();
|
|
|
|
// First, do a line check (stops us passing through things on exit).
|
|
if ( Trace(HitLocation, HitNormal, tryPlace+RandomSphereLoc, tryPlace, false, Extent) == None )
|
|
{
|
|
if ( Driver.SetLocation(tryPlace+RandomSphereLoc) )
|
|
return true;
|
|
}
|
|
else if ( Driver.SetLocation(HitLocation) )
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
for( i=0; i<ExitPositions.Length; i++)
|
|
{
|
|
if ( ExitPositions[0].Z != 0 )
|
|
ZOffset = Vect(0,0,1) * ExitPositions[0].Z;
|
|
else
|
|
ZOffset = Driver.default.CollisionHeight * vect(0,0,2);
|
|
|
|
tryPlace = Location + ( (ExitPositions[i]-ZOffset) >> Rotation) + ZOffset;
|
|
|
|
// First, do a line check (stops us passing through things on exit).
|
|
if ( Trace(HitLocation, HitNormal, tryPlace, Location + ZOffset, false, Extent) != None )
|
|
continue;
|
|
|
|
// Then see if we can place the player there.
|
|
if ( !Driver.SetLocation(tryPlace) )
|
|
continue;
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function Pawn CheckForHeadShot(Vector loc, Vector ray, float AdditionalScale)
|
|
{
|
|
return None;
|
|
}
|
|
|
|
function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation,
|
|
Vector momentum, class<DamageType> damageType, optional int HitIndex)
|
|
{
|
|
local int ActualDamage;
|
|
local Controller Killer;
|
|
|
|
// Spawn Protection: Cannot be destroyed by a player until possessed
|
|
if ( bSpawnProtected && instigatedBy != None && instigatedBy != Self )
|
|
return;
|
|
|
|
NetUpdateTime = Level.TimeSeconds - 1; // force quick net update
|
|
|
|
if (DamageType != None)
|
|
{
|
|
if ((instigatedBy == None || instigatedBy.Controller == None) && DamageType.default.bDelayedDamage && DelayedDamageInstigatorController != None)
|
|
instigatedBy = DelayedDamageInstigatorController.Pawn;
|
|
|
|
Damage *= DamageType.default.VehicleDamageScaling;
|
|
momentum *= DamageType.default.VehicleMomentumScaling * MomentumMult;
|
|
|
|
if (bShowDamageOverlay && DamageType.default.DamageOverlayMaterial != None && Damage > 0 )
|
|
SetOverlayMaterial( DamageType.default.DamageOverlayMaterial, DamageType.default.DamageOverlayTime, false );
|
|
}
|
|
|
|
if (bRemoteControlled && Driver!=None)
|
|
{
|
|
ActualDamage = Damage;
|
|
if (Weapon != None)
|
|
Weapon.AdjustPlayerDamage(ActualDamage, InstigatedBy, HitLocation, Momentum, DamageType );
|
|
if (InstigatedBy != None && InstigatedBy.HasUDamage())
|
|
ActualDamage *= 2;
|
|
|
|
ActualDamage = Level.Game.ReduceDamage(ActualDamage, self, instigatedBy, HitLocation, Momentum, DamageType);
|
|
|
|
if (Health - ActualDamage <= 0)
|
|
KDriverLeave(false);
|
|
}
|
|
|
|
if ( Physics != PHYS_Karma )
|
|
{
|
|
super.TakeDamage(Damage,InstigatedBy,HitLocation,Momentum,DamageType);
|
|
return;
|
|
}
|
|
|
|
if (Weapon != None)
|
|
Weapon.AdjustPlayerDamage(Damage, InstigatedBy, HitLocation, Momentum, DamageType );
|
|
if (InstigatedBy != None && InstigatedBy.HasUDamage())
|
|
Damage *= 2;
|
|
ActualDamage = Level.Game.ReduceDamage(Damage, self, instigatedBy, HitLocation, Momentum, DamageType);
|
|
Health -= ActualDamage;
|
|
|
|
PlayHit(actualDamage, InstigatedBy, hitLocation, damageType, Momentum);
|
|
// The vehicle is dead!
|
|
if ( Health <= 0 )
|
|
{
|
|
|
|
if ( Driver!=None && (bEjectDriver || bRemoteControlled) )
|
|
{
|
|
if ( bEjectDriver )
|
|
EjectDriver();
|
|
else
|
|
KDriverLeave( false );
|
|
}
|
|
|
|
// pawn died
|
|
if ( instigatedBy != None )
|
|
Killer = instigatedBy.GetKillerController();
|
|
if ( Killer == None && (DamageType != None) && DamageType.Default.bDelayedDamage )
|
|
Killer = DelayedDamageInstigatorController;
|
|
Died(Killer, damageType, HitLocation);
|
|
}
|
|
else if ( Controller != None )
|
|
Controller.NotifyTakeHit(instigatedBy, HitLocation, actualDamage, DamageType, Momentum);
|
|
|
|
MakeNoise(1.0);
|
|
|
|
if ( !bDeleteMe )
|
|
{
|
|
if ( Location.Z > Level.StallZ )
|
|
Momentum.Z = FMin(Momentum.Z, 0);
|
|
KAddImpulse(Momentum, hitlocation);
|
|
}
|
|
}
|
|
|
|
function bool HealDamage(int Amount, Controller Healer, class<DamageType> DamageType)
|
|
{
|
|
if ( PlayerController(Healer) != None )
|
|
PlayerStartTime = Level.TimeSeconds + 3;
|
|
if (Health <= 0 || Health >= HealthMax || Amount <= 0 || Healer == None || !TeamLink(Healer.GetTeamNum()))
|
|
return false;
|
|
|
|
Health = Min(Health + (Amount * LinkHealMult), HealthMax);
|
|
NetUpdateTime = Level.TimeSeconds - 1;
|
|
return true;
|
|
}
|
|
|
|
//determine if radius damage that hit the vehicle should damage the driver
|
|
function DriverRadiusDamage(float DamageAmount, float DamageRadius, Controller EventInstigator, class<DamageType> DamageType, float Momentum, vector HitLocation)
|
|
{
|
|
local float damageScale, dist;
|
|
local vector dir;
|
|
|
|
//if driver has collision, whatever is causing the radius damage will hit the driver by itself
|
|
if (EventInstigator == None || Driver == None || Driver.bCollideActors || bRemoteControlled)
|
|
return;
|
|
|
|
dir = Driver.Location - HitLocation;
|
|
dist = FMax(1, VSize(dir));
|
|
dir = dir/dist;
|
|
damageScale = 1 - FMax(0,(dist - Driver.CollisionRadius)/DamageRadius);
|
|
if (damageScale <= 0)
|
|
return;
|
|
|
|
Driver.SetDelayedDamageInstigatorController(EventInstigator);
|
|
Driver.TakeDamage( damageScale * DamageAmount, EventInstigator.Pawn, Driver.Location - 0.5 * (Driver.CollisionHeight + Driver.CollisionRadius) * dir,
|
|
damageScale * Momentum * dir, DamageType );
|
|
}
|
|
|
|
|
|
function DriverDied()
|
|
{
|
|
local Controller C;
|
|
|
|
Level.Game.DiscardInventory( Driver );
|
|
if (PlayerReplicationInfo != None && PlayerReplicationInfo.HasFlag != None)
|
|
PlayerReplicationInfo.HasFlag.Drop(0.5 * Velocity);
|
|
|
|
if ( Driver == None )
|
|
return;
|
|
C = Controller;
|
|
Driver.StopDriving( Self );
|
|
Driver.Controller = C;
|
|
Driver.DrivenVehicle = self; //for in game stats, so it knows pawn was killed inside a vehicle
|
|
|
|
if ( Controller == None )
|
|
return;
|
|
|
|
if ( PlayerController(Controller) != None )
|
|
{
|
|
Controller.SetLocation(Location);
|
|
PlayerController(Controller).SetViewTarget( Driver );
|
|
PlayerController(Controller).ClientSetViewTarget( Driver );
|
|
}
|
|
|
|
Controller.Unpossess();
|
|
if ( Controller == C )
|
|
Controller = None;
|
|
C.Pawn = Driver;
|
|
|
|
Level.Game.DriverLeftVehicle(self, Driver);
|
|
|
|
// Car now has no driver
|
|
Driver = None;
|
|
bDriving = false;
|
|
|
|
// Put brakes on before you get out :)
|
|
Throttle = 0;
|
|
Steering = 0;
|
|
Rise = 0;
|
|
}
|
|
|
|
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
|
{
|
|
local PlayerController PC;
|
|
local Controller C;
|
|
|
|
if ( bDeleteMe || Level.bLevelChange )
|
|
return; // already destroyed, or level is being cleaned up
|
|
|
|
if ( Physics != PHYS_Karma )
|
|
{
|
|
super.Died(Killer, damageType, HitLocation);
|
|
return;
|
|
}
|
|
|
|
if ( Level.Game.PreventDeath(self, Killer, damageType, HitLocation) )
|
|
{
|
|
Health = max(Health, 1); //mutator should set this higher
|
|
return;
|
|
}
|
|
Health = Min(0, Health);
|
|
|
|
if ( Controller != None )
|
|
{
|
|
C = Controller;
|
|
C.WasKilledBy(Killer);
|
|
Level.Game.Killed(Killer, C, self, damageType);
|
|
if( C.bIsPlayer )
|
|
{
|
|
PC = PlayerController(C);
|
|
if ( PC != None )
|
|
ClientKDriverLeave(PC); // Just to reset HUD etc.
|
|
else
|
|
ClientClearController();
|
|
if ( (bRemoteControlled || bEjectDriver) && (Driver != None) && (Driver.Health > 0) )
|
|
{
|
|
C.Unpossess();
|
|
C.Possess(Driver);
|
|
|
|
if ( bEjectDriver )
|
|
EjectDriver();
|
|
|
|
Driver = None;
|
|
}
|
|
else
|
|
C.PawnDied(self);
|
|
}
|
|
|
|
if ( !C.bIsPlayer && !C.bDeleteMe )
|
|
C.Destroy();
|
|
}
|
|
else
|
|
Level.Game.Killed(Killer, Controller(Owner), self, damageType);
|
|
|
|
if ( Killer != None )
|
|
TriggerEvent(Event, self, Killer.Pawn);
|
|
else
|
|
TriggerEvent(Event, self, None);
|
|
|
|
if ( IsHumanControlled() )
|
|
PlayerController(Controller).ForceDeathUpdate();
|
|
|
|
if ( !bDeleteMe )
|
|
Destroy(); // Destroy the vehicle itself (see Destroyed)
|
|
}
|
|
|
|
function AdjustDriverDamage(out int Damage, Pawn InstigatedBy, Vector HitLocation, out Vector Momentum, class<DamageType> DamageType)
|
|
{
|
|
if ( InGodMode() )
|
|
Damage = 0;
|
|
else
|
|
Damage *= DriverDamageMult;
|
|
}
|
|
|
|
function EjectDriver()
|
|
{
|
|
local Pawn OldPawn;
|
|
local vector EjectVel;
|
|
|
|
OldPawn = Driver;
|
|
|
|
KDriverLeave( true );
|
|
|
|
if ( OldPawn == None )
|
|
return;
|
|
|
|
EjectVel = VRand();
|
|
EjectVel.Z = 0;
|
|
EjectVel = (Normal(EjectVel)*0.2 + Vect(0,0,1)) * EjectMomentum;
|
|
|
|
OldPawn.Velocity = EjectVel;
|
|
|
|
// Spawn Protection
|
|
OldPawn.SpawnTime = Level.TimeSeconds;
|
|
OldPawn.PlayTeleportEffect( false, false);
|
|
}
|
|
|
|
// Input
|
|
function UsedBy( Pawn user )
|
|
{
|
|
if ( Driver != None )
|
|
return;
|
|
|
|
// Enter vehicle code
|
|
TryToDrive( User );
|
|
}
|
|
|
|
function Fire( optional float F )
|
|
{
|
|
VehicleFire( false );
|
|
bWeaponIsFiring = true;
|
|
}
|
|
|
|
function AltFire( optional float F )
|
|
{
|
|
VehicleFire( true );
|
|
bWeaponIsAltFiring = true;
|
|
}
|
|
|
|
function ClientVehicleCeaseFire(bool bWasAltFire)
|
|
{
|
|
if (bWasAltFire)
|
|
bWeaponIsAltFiring = false;
|
|
else
|
|
bWeaponIsFiring = false;
|
|
|
|
VehicleCeaseFire(bWasAltFire);
|
|
}
|
|
|
|
// Do some server-side vehicle firing stuff
|
|
function VehicleFire(bool bWasAltFire)
|
|
{
|
|
if ( bWasAltFire )
|
|
bWeaponIsAltFiring = true;
|
|
else
|
|
bWeaponIsFiring = true;
|
|
}
|
|
|
|
function VehicleCeaseFire(bool bWasAltFire)
|
|
{
|
|
if ( bWasAltFire )
|
|
bWeaponIsAltFiring = false;
|
|
else
|
|
bWeaponIsFiring = false;
|
|
}
|
|
|
|
state UnDeployed
|
|
{
|
|
function VehicleFire(bool bWasAltFire)
|
|
{
|
|
Global.VehicleFire(bWasAltFire);
|
|
}
|
|
|
|
function VehicleCeaseFire(bool bWasAltFire)
|
|
{
|
|
Global.VehicleCeaseFire(bWasAltFire);
|
|
}
|
|
}
|
|
|
|
state Deployed
|
|
{
|
|
function VehicleFire(bool bWasAltFire)
|
|
{
|
|
Global.VehicleFire(bWasAltFire);
|
|
}
|
|
|
|
function VehicleCeaseFire(bool bWasAltFire)
|
|
{
|
|
Global.VehicleCeaseFire(bWasAltFire);
|
|
}
|
|
}
|
|
|
|
function bool StopWeaponFiring()
|
|
{
|
|
local bool bResult;
|
|
|
|
if ( bWeaponIsFiring )
|
|
{
|
|
ClientVehicleCeaseFire( false );
|
|
bWeaponIsFiring = false;
|
|
bResult = true;
|
|
}
|
|
if ( bWeaponIsAltFiring )
|
|
{
|
|
ClientVehicleCeaseFire( true );
|
|
bWeaponIsAltFiring = false;
|
|
bResult = true;
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
|
|
event UpdateEyeHeight( float DeltaTime )
|
|
{
|
|
local Controller C;
|
|
|
|
if ( Controller != None && Controller.IsA('PlayerController') )
|
|
Controller.AdjustView( DeltaTime );
|
|
|
|
for ( C=Level.ControllerList; C!=None; C=C.NextController )
|
|
if ( C.IsA('PlayerController') && (PlayerController(C).ViewTarget == self) )
|
|
return;
|
|
|
|
bUpdateEyeHeight =false;
|
|
Eyeheight = BaseEyeheight;
|
|
}
|
|
|
|
// Vehicles ignore 'face rotation'.
|
|
simulated function FaceRotation( rotator NewRotation, float DeltaTime ) {}
|
|
|
|
simulated event SetAnimAction(name NewAction)
|
|
{
|
|
if ( bDrawDriverInTP && (Driver != None) )
|
|
Driver.SetAnimAction(NewAction);
|
|
}
|
|
|
|
// Vehicles dont get telefragged.
|
|
event EncroachedBy(Actor Other) {}
|
|
|
|
// RanInto() called for encroaching actors which successfully moved the other actor out of the way
|
|
event RanInto(Actor Other)
|
|
{
|
|
local vector Momentum;
|
|
local float Speed;
|
|
|
|
if (Pawn(Other) == None || Vehicle(Other) != None || Other == Instigator || Other.Role != ROLE_Authority)
|
|
return;
|
|
|
|
Speed = VSize(Velocity);
|
|
if (Speed > MinRunOverSpeed)
|
|
{
|
|
Momentum = Velocity * 0.25 * Other.Mass;
|
|
|
|
if (Controller != None && Controller.SameTeamAs(Pawn(Other).Controller))
|
|
Momentum += Speed * 0.25 * Other.Mass * Normal(Velocity cross vect(0,0,1));
|
|
if (RanOverSound != None)
|
|
PlaySound(RanOverSound,,TransientSoundVolume*2.5);
|
|
|
|
Other.TakeDamage(int(Speed * 0.075), Instigator, Other.Location, Momentum, RanOverDamageType);
|
|
}
|
|
}
|
|
|
|
// This will get called if we couldn't move a pawn out of the way.
|
|
function bool EncroachingOn(Actor Other)
|
|
{
|
|
if ( Other == None || Other == Instigator || Other.Role != ROLE_Authority || (!Other.bCollideActors && !Other.bBlockActors)
|
|
|| VSize(Velocity) < 10 )
|
|
return false;
|
|
|
|
// If its a non-vehicle pawn, do lots of damage.
|
|
if( (Pawn(Other) != None) && (Vehicle(Other) == None) )
|
|
{
|
|
Other.TakeDamage(10000, Instigator, Other.Location, Velocity * Other.Mass, CrushedDamageType);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
simulated function bool FindValidTaunt( out name Sequence )
|
|
{
|
|
if ( !bDrawDriverInTP || (Driver == None) )
|
|
return false;
|
|
return Driver.FindValidTaunt(Sequence);
|
|
}
|
|
|
|
simulated function bool CheckTauntValid( name Sequence )
|
|
{
|
|
if ( !bDrawDriverInTP || (Driver == None) )
|
|
return false;
|
|
return Driver.CheckTauntValid(Sequence);
|
|
}
|
|
|
|
|
|
// AI code
|
|
function bool Occupied()
|
|
{
|
|
return ( Controller != None );
|
|
}
|
|
|
|
function float ReservationCostMultiplier()
|
|
{
|
|
return 1.0;
|
|
}
|
|
|
|
function float NewReservationCostMultiplier(Pawn P)
|
|
{
|
|
return ReservationCostMultiplier();
|
|
}
|
|
|
|
function bool ChangedReservation(Pawn P)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function bool SpokenFor(Controller C)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function SetReservation(controller C);
|
|
|
|
function Vehicle OpenPositionFor(Pawn P)
|
|
{
|
|
if ( Controller == None )
|
|
return self;
|
|
return None;
|
|
}
|
|
|
|
simulated function bool IndependentVehicle()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
function Actor GetBestEntry(Pawn P)
|
|
{
|
|
return self;
|
|
}
|
|
|
|
function Vehicle GetMoveTargetFor(Pawn P)
|
|
{
|
|
return self;
|
|
}
|
|
|
|
simulated event DrivingStatusChanged()
|
|
{
|
|
local PlayerController PC;
|
|
|
|
PC = Level.GetLocalPlayerController();
|
|
|
|
if (bDriving && PC != None && (PC.ViewTarget == None || !(PC.ViewTarget.IsJoinedTo(self))))
|
|
bDropDetail = (Level.bDropDetail || (Level.DetailMode == DM_Low));
|
|
else
|
|
bDropDetail = False;
|
|
|
|
if (bDriving)
|
|
Enable('Tick');
|
|
else
|
|
Disable('Tick');
|
|
}
|
|
|
|
// TakeWaterDamage() called every tick when WaterDamage>0 and PhysicsVolume.bWaterVolume=true
|
|
event TakeWaterDamage(float DeltaTime)
|
|
{
|
|
local vector HitLocation,HitNormal;
|
|
local actor EntryActor;
|
|
|
|
TakeDamage(WaterDamage * DeltaTime, Self, vect(0,0,0), vect(0,0,0), VehicleDrowningDamType);
|
|
|
|
if ( (Level.TimeSeconds - SplashTime > 0.3) && (PhysicsVolume.PawnEntryActor != None) && !Level.bDropDetail && (Level.DetailMode != DM_Low) && EffectIsRelevant(Location,false)
|
|
&& (VSize(Velocity) > 300) )
|
|
{
|
|
SplashTime = Level.TimeSeconds;
|
|
if ( !PhysicsVolume.TraceThisActor(HitLocation, HitNormal, Location - CollisionHeight*vect(0,0,1), Location + CollisionHeight*vect(0,0,1)) )
|
|
{
|
|
EntryActor = Spawn(PhysicsVolume.PawnEntryActor,self,,HitLocation,rot(16384,0,0));
|
|
}
|
|
}
|
|
}
|
|
|
|
// LockOnWarning() called every LockWarningInterval when bEnemyLockedOn is true (on server/standalone)
|
|
event LockOnWarning()
|
|
{
|
|
local class<LocalMessage> LockOnClass;
|
|
|
|
LockOnClass = class<LocalMessage>(DynamicLoadObject(LockOnClassString, class'class'));
|
|
PlayerController(Controller).ReceiveLocalizedMessage(LockOnClass, 12);
|
|
LastLockWarningTime = Level.TimeSeconds;
|
|
}
|
|
|
|
/* PointOfView()
|
|
called by controller when possessing this pawn
|
|
false = 1st person, true = 3rd person
|
|
*/
|
|
simulated function bool PointOfView()
|
|
{
|
|
if (!bAllowViewChange)
|
|
return true;
|
|
|
|
return default.bDesiredBehindView;
|
|
}
|
|
|
|
// Spawn FX
|
|
function PlayTeleportEffect( bool bOut, bool bSound)
|
|
{
|
|
local Actor A;
|
|
local class<Actor> TransEffect;
|
|
|
|
if ( (GetTeam() == None) || (GetTeam().TeamIndex == 0) )
|
|
TransEffect = class<Actor>(DynamicLoadObject(TransEffects[0], class'Class'));
|
|
else
|
|
TransEffect = class<Actor>(DynamicLoadObject(TransEffects[1], class'Class'));
|
|
|
|
if ( TransEffect != None )
|
|
A = Spawn(TransEffect,,,Location + CollisionHeight * vect(0,0,0.75));
|
|
|
|
// for fast moving vehicles, make the effect sticky
|
|
if ( A != None )
|
|
A.SetBase( Self );
|
|
|
|
super.PlayTeleportEffect( bOut, bSound );
|
|
}
|
|
|
|
simulated event PlayDying(class<DamageType> DamageType, vector HitLoc) {}
|
|
|
|
simulated function int GetTeamNum()
|
|
{
|
|
if ( Role == Role_Authority && Team == 255 && (Controller != None) )
|
|
SetTeamNum( Controller.GetTeamNum() );
|
|
|
|
return Team;
|
|
}
|
|
|
|
//Notify vehicle that an enemy has locked on to it
|
|
event NotifyEnemyLockedOn()
|
|
{
|
|
bEnemyLockedOn = true;
|
|
}
|
|
|
|
function IncomingMissile(Projectile P);
|
|
|
|
//Notify vehicle that an enemy has lost the lock
|
|
event NotifyEnemyLostLock()
|
|
{
|
|
bEnemyLockedOn = false;
|
|
}
|
|
|
|
/*
|
|
Team is changed when vehicle is possessed
|
|
and PrevTeam is restored when vehicle is unpossessed
|
|
*/
|
|
function SetTeamNum(byte T)
|
|
{
|
|
PrevTeam = Team;
|
|
Team = T;
|
|
|
|
if ( PrevTeam != Team )
|
|
TeamChanged();
|
|
}
|
|
|
|
simulated event TeamChanged() {}
|
|
|
|
simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
|
|
{
|
|
super.DisplayDebug(Canvas, YL, YPos);
|
|
|
|
Canvas.SetDrawColor(255,255,255);
|
|
Canvas.DrawText("Steering "$Steering$" throttle "$Throttle$" rise "$Rise);
|
|
|
|
if ( Driver != None )
|
|
{
|
|
YPos += YL;
|
|
YPos += YL;
|
|
Canvas.SetPos(0, YPos);
|
|
Canvas.SetDrawColor(0,0,255);
|
|
Canvas.DrawText("--- DRIVER");
|
|
Canvas.SetPos(4, YPos);
|
|
Driver.DisplayDebug( Canvas, YL, YPos );
|
|
}
|
|
}
|
|
|
|
function Actor ShootSpecial(Actor A)
|
|
{
|
|
local Controller OldController;
|
|
|
|
if ( !Controller.bCanDoSpecial || (Weapon == None) )
|
|
return None;
|
|
|
|
Controller = OldController;
|
|
if ( KDriverLeave(false) && (OldController.Pawn != None) )
|
|
{
|
|
OldController.Pawn.SetRotation(rotator(A.Location - OldController.Pawn.Location));
|
|
OldController.Focus = A;
|
|
OldController.FireWeaponAt(A);
|
|
}
|
|
return A;
|
|
}
|
|
|
|
//Vehicles stall when they go above the level's StallZ
|
|
simulated event Stalled();
|
|
simulated event UnStalled();
|
|
|
|
simulated function NextWeapon()
|
|
{
|
|
local PlayerController PC;
|
|
|
|
if ( Level.Pauser != None )
|
|
return;
|
|
|
|
PC = PlayerController(Controller);
|
|
if (PC == None)
|
|
return;
|
|
|
|
if (!PC.bBehindView)
|
|
{
|
|
PC.BehindView(true);
|
|
DesiredTPCamDistance = TPCamDistRange.Min;
|
|
TPCamDistance = DesiredTPCamDistance;
|
|
}
|
|
else
|
|
DesiredTPCamDistance = Min(DesiredTPCamDistance + 100, TPCamDistRange.Max);
|
|
|
|
default.TPCamDistance = DesiredTPCamDistance;
|
|
StaticSaveConfig();
|
|
}
|
|
|
|
simulated function PrevWeapon()
|
|
{
|
|
local PlayerController PC;
|
|
|
|
if ( Level.Pauser != None )
|
|
return;
|
|
|
|
PC = PlayerController(Controller);
|
|
if (PC == None || !PC.bBehindView)
|
|
return;
|
|
|
|
if (DesiredTPCamDistance ~= TPCamDistRange.Min)
|
|
PC.BehindView(false);
|
|
else
|
|
{
|
|
DesiredTPCamDistance = Max(DesiredTPCamDistance - 100, TPCamDistRange.Min);
|
|
default.TPCamDistance = DesiredTPCamDistance;
|
|
StaticSaveConfig();
|
|
}
|
|
}
|
|
|
|
function bool TeamLink(int TeamNum)
|
|
{
|
|
return (LinkHealMult > 0 && Team == TeamNum && Health > 0);
|
|
}
|
|
|
|
event bool NeedsFlip()
|
|
{
|
|
local vector worldUp, gravUp;
|
|
local float GravMag;
|
|
|
|
GravMag = VSize(PhysicsVolume.Gravity);
|
|
if( GravMag < 0.1 )
|
|
gravUp = vect(0,0,1);
|
|
else
|
|
gravUp = -1.0 * (PhysicsVolume.Gravity/GravMag);
|
|
|
|
worldUp = vect(0,0,1) >> Rotation;
|
|
if (worldUp Dot gravUp < -0.5)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function Flip(vector HitNormal, float ForceScale);
|
|
|
|
simulated function float ChargeBar();
|
|
|
|
simulated function ClientPlayForceFeedback( String EffectName )
|
|
{
|
|
local PlayerController PC;
|
|
|
|
PC = PlayerController(Controller);
|
|
//if ( PC != None && PC.bEnableVehicleForceFeedback )
|
|
if ( PC != None && PC.bEnableGUIForceFeedback )
|
|
{
|
|
PC.ClientPlayForceFeedback( EffectName );
|
|
}
|
|
}
|
|
|
|
simulated function StopForceFeedback( String EffectName )
|
|
{
|
|
local PlayerController PC;
|
|
|
|
PC = PlayerController(Controller);
|
|
//if ( PC != None && PC.bEnableVehicleForceFeedback )
|
|
if ( PC != None && PC.bEnableGUIForceFeedback )
|
|
{
|
|
PC.StopForceFeedback( EffectName );
|
|
}
|
|
}
|
|
|
|
function ServerPlayHorn(int HornIndex)
|
|
{
|
|
if( (Level.TimeSeconds - LastHornTime > 3.0) && (HornIndex >= 0) && (HornIndex < HornSounds.Length) )
|
|
{
|
|
if(HornSounds[HornIndex] != none )
|
|
PlaySound( HornSounds[HornIndex],, 3.5*TransientSoundVolume,, 800);
|
|
LastHornTime = Level.TimeSeconds;
|
|
}
|
|
}
|
|
|
|
simulated function int NumPassengers()
|
|
{
|
|
if ( Driver != None )
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
function Pawn GetInstigator()
|
|
{
|
|
return Self;
|
|
}
|
|
|
|
function AIController GetBotPassenger()
|
|
{
|
|
return AIController(Controller);
|
|
}
|
|
|
|
event bool IsVehicleEmpty()
|
|
{
|
|
return (Driver == None);
|
|
}
|
|
|
|
function bool HasOccupiedTurret()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function float AdjustedStrength()
|
|
{
|
|
if (bStationary && bDefensive)
|
|
return 1.0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static function StaticPrecache(LevelInfo L);
|
|
|
|
function int GetSpree()
|
|
{
|
|
if (Driver != None)
|
|
return Driver.GetSpree();
|
|
|
|
return 0;
|
|
}
|
|
|
|
function IncrementSpree()
|
|
{
|
|
if (Driver != None)
|
|
Driver.IncrementSpree();
|
|
}
|
|
|
|
simulated function POVChanged(PlayerController PC, bool bBehindViewChanged)
|
|
{
|
|
if (PC.bBehindView)
|
|
{
|
|
if (bBehindViewChanged && bPCRelativeFPRotation)
|
|
PC.SetRotation(rotator(vector(PC.Rotation) >> Rotation));
|
|
|
|
bOwnerNoSee = false;
|
|
|
|
if (Driver != None)
|
|
{
|
|
if (bDrawDriverInTP)
|
|
Driver.bOwnerNoSee = false;
|
|
else
|
|
Driver.bOwnerNoSee = true;
|
|
}
|
|
|
|
if (PC == Controller) // No overlays for spectators
|
|
ActivateOverlay(False);
|
|
}
|
|
else
|
|
{
|
|
if (bPCRelativeFPRotation)
|
|
PC.SetRotation(rotator(vector(PC.Rotation) << Rotation));
|
|
|
|
if (bDrawMeshInFP)
|
|
bOwnerNoSee = false;
|
|
else
|
|
bOwnerNoSee = true;
|
|
|
|
if (Driver != None)
|
|
{
|
|
Driver.bOwnerNoSee = true;
|
|
}
|
|
|
|
if (bDriving && PC == Controller) // No overlays for spectators
|
|
ActivateOverlay(True);
|
|
}
|
|
}
|
|
|
|
function PlayTakeHit(vector HitLocation, int Damage, class<DamageType> DamageType)
|
|
{
|
|
local int SoundNum;
|
|
|
|
if (IndependentVehicle() && DamageType.Default.bBulletHit && BulletSounds.Length > 0)
|
|
{
|
|
SoundNum = Rand(BulletSounds.Length);
|
|
|
|
if (Controller != None && Controller == Level.GetLocalPlayerController())
|
|
PlayOwnedSound(BulletSounds[SoundNum], SLOT_None, 2.0, False, 400);
|
|
else
|
|
PlayOwnedSound(BulletSounds[SoundNum], SLOT_None, 2.0, False, 100);
|
|
}
|
|
}
|
|
|
|
function array<Vehicle> GetTurrets();
|
|
|
|
function CheckSuperBerserk();
|
|
|
|
// Used to override locking actors. Returns who the aggressor really should lock on to
|
|
|
|
event bool VerifyLock(actor Aggressor, out actor NewTarget)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Returns any alternate target actor for this vehicle if it has one
|
|
|
|
simulated function actor AlternateTarget()
|
|
{
|
|
return None;
|
|
}
|
|
|
|
function ShouldTargetMissile(Projectile P)
|
|
{
|
|
local AIController C;
|
|
|
|
C = AIController(Controller);
|
|
if (C != None && C.Skill >= 5.0 && (C.Enemy == None || !C.LineOfSightTo(C.Enemy)) )
|
|
ShootMissile(P);
|
|
}
|
|
|
|
function ShootMissile(Projectile P)
|
|
{
|
|
Controller.Focus = P;
|
|
Controller.FireWeaponAt(P);
|
|
}
|
|
|
|
function bool ImportantVehicle()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function bool IsArtillery()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================
|
|
// defaultproperties
|
|
//=============================================================================
|
|
|
|
defaultproperties
|
|
{
|
|
bAdjustDriversHead=true
|
|
LandMovementState=PlayerDriving
|
|
bVehicleShadows=true
|
|
bDrawVehicleShadow=true
|
|
bDontPossess=true
|
|
bUseCompressedPosition=false
|
|
bCanPickupInventory=false
|
|
Team=255
|
|
OldTeam=255
|
|
PrevTeam=255
|
|
|
|
Steering=0
|
|
Throttle=0
|
|
|
|
EjectMomentum=1000
|
|
ExitPositions(0)=(X=0,Y=0,Z=0)
|
|
|
|
EntryPosition=(X=0,Y=0,Z=0)
|
|
EntryRadius=100.0
|
|
|
|
DrivePos=(X=0,Y=0,Z=0)
|
|
bZeroPCRotOnEntry=true
|
|
bRelativeExitPos=true
|
|
bPCRelativeFPRotation=true
|
|
|
|
TPCamLookat=(X=-100,Y=0,Z=100)
|
|
TPCamWorldOffset=(X=0,Y=0,Z=0)
|
|
TPCamDistance=600
|
|
TPCamDistRange=(Min=50,Max=1500)
|
|
CameraSpeed=500
|
|
bDesiredBehindView=true
|
|
|
|
MaxViewYaw=16000
|
|
MaxViewPitch=16000
|
|
|
|
TransEffects(0)="XEffects.TransEffectRed"
|
|
TransEffects(1)="XEffects.TransEffectBlue"
|
|
|
|
DriveAnim=Vehicle_Driving
|
|
CollisionRadius=+120.0
|
|
CollisionHeight=+50.0
|
|
SightRadius=+8000.0
|
|
ShadowMaxTraceDist=+350
|
|
ShadowCullDistance=1500.0
|
|
bForceSkelUpdate=true
|
|
bCanBeBaseForPawns=True
|
|
|
|
MomentumMult=4.0
|
|
DriverDamageMult=1.0
|
|
VehiclePositionString="in a vehicle"
|
|
VehicleNameString="Vehicle"
|
|
|
|
LockOnClassString="Onslaught.ONSOnslaughtMessage"
|
|
|
|
RanOverDamageType=class'DamRanOver'
|
|
CrushedDamageType=class'Crushed'
|
|
bAllowViewChange=true
|
|
LinkHealMult=0.35
|
|
VehicleLostTime=0.0
|
|
NetUpdateFrequency=4
|
|
NetPriority=1
|
|
|
|
CenterSpringForce="SpringONSHoverBike"
|
|
CenterSpringRangePitch=2000
|
|
CenterSpringRangeRoll=2000
|
|
|
|
bDriverHoldsFlag=true
|
|
bCanCarryFlag=true
|
|
|
|
WalkingPct=+1.0
|
|
CrouchedPct=+1.0
|
|
|
|
MaxDesireability=0.5
|
|
|
|
ObjectiveGetOutDist=1000.0
|
|
WaterDamage=0.0
|
|
LockWarningInterval=+1.5
|
|
}
|