Legacy:KShip
Well this is basically my code for a space ship, thanks a lot to Daid for lending me his replication code (which turned out to come from KCar but i thank him non the less, great guy he is :D)
I currently use a different, more client oriented replication system, but this one here is a lot better, i havent incorporated my ship to it yet because i have some other problems that come with it that i have to solve, but since the "core" ship here is simple, ill use the better replication for you guys :)
(Btw i deal with all the entering/leaving of the vehicle in a parent class, this is really irrelevant to physics and is easy to do, so i left it out just so there will be less and easier to read code)
var float Roll, CThrust, TempThrust, OldThrust;
// Throttle and Steering are already declared in KVehicle, so all we need is roll, CThrust, TempThrust, OldThrust will be explained soon
// BTW note that i use Throttle for Pitch alteration because it is already bound by other vehicles to the forward/backward key
// ok i have also incorporated a system that allows for various thrust managments,
//if you simply want to move forward full speed only while you press the the thrust button, you would use TempThrust.
//when you release the thrust button you though you should always set TempThrust to -1
// if you want to have a constant speed, assign it to CThrust, so every time you release the thrust button (the one that changes TempThrust)
// you would automatically start going at that speed (good for stuff like dogifghting and matching speeds)
// and lastly i added an afterburner effect, ill include it here for you guys to see although it isnt hard to do.
// also what i did with it was that when you afterburn and you run out of energy, while you still hold the afterburn key
// you would keep going at your max possible speed, and once you release it you would start going at your constant wanted speed (CThrust)
// Thats why i have 2 vars, bAfterburn is for after burning (holding key and having enough E), bBurn is for only holding the afterburn key with no regard to its depletion
var bool bAfterburn, bBurn; // note that you should set both of those to true when you want to afterburn
var() float ForwardTrust, TurnRate, AfterBurnThrust; // This is basically how fast our ship turns/moves
var float CurRoll, CurSteering, CurThrottle, UnitAccRate; // This is used for acceleration
// CThrust is used for ships that can assign a specific throttle value, like 75% of max speed or whatever...
// UnitAccRate is how much each value increases per second
var vector ExtraForce, ExtraTorque; // Used for Karama forces pileup
// Ship replication vars and functions, thanks daid!
struct StructShipState
{
var KRBVec ChassisPosition;
var Quat ChassisQuaternion;
var KRBVec ChassisLinVel;
var KRBVec ChassisAngVel;
var float ServerSteering;
var float ServerThrottle;
var float ServerRoll;
var bool ServerbAfterburn;
var bool ServerbBurn;
var bool bNewState; // Set to true whenever a new state is received and should be processed
};
var KRigidBodyState ChassisState;
var StructShipState ShipState; // This is replicated to the ship, and processed to update all the parts.
var bool bNewShipState; // Indicated there is new data processed, and chassis RBState should be updated.
var float NextNetUpdateTime; // Next time we should force an update of vehicles state.
var() float MaxNetUpdateInterval;
var int AVar;//Just for replication, else the ShipState doesn't get replicated
Replication
{
unreliable if(Role == ROLE_Authority)
ShipState, AVar;
}
simulated event VehicleStateReceived()
{
if(!ShipState.bNewState)
return;
// Get root chassis info
ChassisState.Position = ShipState.ChassisPosition;
ChassisState.Quaternion = ShipState.ChassisQuaternion;
ChassisState.LinVel = ShipState.ChassisLinVel;
ChassisState.AngVel = ShipState.ChassisAngVel;
// Update control inputs
Steering = ShipState.ServerSteering;
Throttle = ShipState.ServerThrottle;
Roll = ShipState.ServerRoll;
// Afterburner
bAfterburn = ShipState.ServerbAfterburn;
bBurn = ShipState.ServerbBurn;
// Update flags
ShipState.bNewState = false;
bNewShipState = true;
}
simulated event bool KUpdateState(out KRigidBodyState newState)
{
// This should never get called on the server - but just in case!
if(Role == ROLE_Authority || !bNewShipState)
return false;
// Apply received data as new position of ship chassis.
newState = ChassisState;
bNewShipState = false;
return true;
}
function PackState()
{
local vector chassisPos, chassisLinVel, chassisAngVel;
local vector oldPos, oldLinVel;
local KRigidBodyState ChassisState;
// Get chassis state.
KGetRigidBodyState(ChassisState);
chassisPos = KRBVecToVector(ChassisState.Position);
chassisLinVel = KRBVecToVector(ChassisState.LinVel);
chassisAngVel = KRBVecToVector(ChassisState.AngVel);
// Last position we sent
oldPos = KRBVectoVector(ShipState.ChassisPosition);
oldLinVel = KRBVectoVector(ShipState.ChassisLinVel);
// See if state has changed enough, or enough time has passed, that we
// should send out another update by updating the state struct.
if( !KIsAwake() )
{
return; // Never send updates if physics is at rest
}
if( VSize(oldPos - chassisPos) > 5 ||
VSize(oldLinVel - chassisLinVel) > 1 ||
Abs(ShipState.ServerThrottle - Throttle) > 0.1 ||
Abs(ShipState.ServerSteering - Steering) > 0.1 ||
Abs(ShipState.ServerRoll - Roll) > 0.1 ||
bAfterburn != ShipState.ServerbAfterburn ||
bBurn != ShipState.ServerbBurn ||
Level.TimeSeconds > NextNetUpdateTime )
{
NextNetUpdateTime = Level.TimeSeconds + MaxNetUpdateInterval;
}
else
{
return;
}
ShipState.ChassisPosition = ChassisState.Position;
ShipState.ChassisQuaternion = ChassisState.Quaternion;
ShipState.ChassisLinVel = ChassisState.LinVel;
ShipState.ChassisAngVel = ChassisState.AngVel;
// Player Input
ShipState.ServerSteering = Steering;
ShipState.ServerThrottle = Throttle;
ShipState.ServerRoll = Roll;
// AfterBurner
ShipState.ServerbAfterburn = bAfterburn;
ShipState.ServerbBurn = bBurn;
// This flag lets the client know this data is new.
ShipState.bNewState = true;
//Make sure ShipState gets replicated
AVar++;
if (AVar > 10)
AVar=0;
}
simulated function Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
if(!KIsAwake() && Controller!=None)
KWake();
if(Role == ROLE_Authority && Level.NetMode != NM_StandAlone)
PackState();
UpdateAcceleration(DeltaTime);
UpdateExtraForce(DeltaTime);
}
simulated function UpdateAcceleration(float Delta)
{
CurSteering = CurSteering + Steering * UnitAccRate * Delta;
CurThrottle = CurThrottle + Throttle * UnitAccRate * Delta;
CurRoll = CurRoll + Roll * UnitAccRate * Delta;
if(Steering==0)
CurSteering=0;
if(Throttle==0)
CurThrottle=0;
if(Roll==0)
CurRoll=0;
if(Abs(CurSteering)>Abs(Steering))
CurSteering=Steering;
if(Abs(CurThrottle)>Abs(Throttle))
CurThrottle=Throttle;
if(Abs(CurRoll)>Abs(Roll))
CurRoll=Roll;
if(bAfterBurn)
{
ReduceAfterBurnEnergy(); // just made this up, put it here incase you have something like this
if(GetAfterburnEnergy()<=0) // Another fictional function...
{
bAfterBurn=False;
}
else
{
CThrust=AfterBurnThrust;
}
}
if(bBurn && !bAfterBurn && CThrust!=1)
{
CThrust=1;
}
else if(!bBurn && !bAfterBurn && CThrust!=OldThrust)
{
CThrust=OldThrust;
}
if(TempThrust!=-1 && !bAfterBurn)
{
if(Abs(CurThrust-TempThrust)<0.01)
CurThrust=TempThrust;
if(CurThrust>TempThrust)
CurThrust = CurThrust - UnitAccRate * Delta / 4;
// I made linear acceleration 4 times slower than rotation, you can do whatever you want though
else if(CurThrust<TempThrust)
CurThrust = CurThrust + UnitAccRate * Delta / 4;
}
else if(CurThrust!=CThrust && !bAfterBurn)
{
if(Abs(CurThrust-CThrust)<0.01)
CurThrust=CThrust;
else if(CurThrust>CThrust)
CurThrust = CurThrust - UnitAccRate * Delta / 4;
else if(CurThrust<CThrust)
CurThrust = CurThrust + UnitAccRate * Delta / 4;
}
else if(CurThrust!=CThrust && bAfterBurn)
{
if(Abs(CurThrust-CThrust)<0.01)
CurThrust=CThrust;
else if(CurThrust>CThrust)
CurThrust = CurThrust - UnitAccRate * Delta / 2.2; // Acceleration with afterburner a lot faster
else if(CurThrust<CThrust)
CurThrust = CurThrust + UnitAccRate * Delta / 2.2;
}
}
simulated function UpdateExtraForce(float Delta)
{
local vector worldForward, worldDown, worldLeft;
worldForward = vect(1, 0, 0) >> Rotation;
worldDown = vect(0, 0, -1) >> Rotation;
worldLeft = vect(0, -1, 0) >> Rotation;
ExtraForce = ExtraForce + worldForward * ForwardThrust * Delta * CurThrust; // Speed
ExtraTorque = ExtraTorque + worldDown * TurnRate * Delta * CurSteering; // Yaw
ExtraTorque = ExtraTorque + worldLeft * TurnRate * Delta * CurThrottle; // Pitch
ExtraTorque = ExtraTorque + worldForward * TurnRate * Delta * -CurRoll; // Roll
}
simulated event KApplyForce(out vector Force, out vector Torque)
{
// This actually does the applying of the piled up force
Force = ExtraForce;
Torque = ExtraTorque;
ExtraForce = vect(0,0,0);
ExtraTorque = vect(0,0,0);
}
DefaultProperties
{
// Create Karma collision Params for ourselves, you can change whatever you want here
Begin Object Class=KarmaParamsRBFull Name=KParams0
KLinearDamping=2.000;
KAngularDamping=2.000;
KStartEnabled=True
bHighDetailOnly=False
bClientOnly=False
bKDoubleTickRate=False
KFriction=1.600000
KActorGravScale = 0.0;
KMass=2;
Name="KParams0"
End Object
KParams=KarmaParamsRBFull'<MyPackage>.<MyShipClass>.KParams0'
// And just some other variables:
TurnRate=8
ForwardThrust=5000
UnitAccRate=2.0
AfterBurnThrust=1.20 // basically means that afterburner goes at 120% of regular maximum speed
}
and that should be it, i hope i didnt miss anything...
Spark: KParams=KarmaParamsRBFull'<MyPackage>.<MyShipClass>.KParams0' <– Is this supposed to be a trap? ;) Took me a while to figure out that my ship didn't fly because this wasn't set to KParams0 and thus wasn't enabled at all. :)
Sir_Brizz: Zep, did you end up getting your Quaternion working for the indicator? I have a few ideas that work if you want to know what they are.
ProjectX: I'm a n00b to coding, but is it possible to take this code, and change it to create a "hovering vehicles" class, that basicly, wont go a certan height above ground, but, from high jumps, etc., go lower to the ground? I also saw that there was 3 types of thrust, is it possible to bind a key that will cycle through the flying types (for more precise flying, eg. for dog matches and realism)?
NickR: Does anyone have a working example of a flying vehicle?
Foxpaw: I do, but my mod is still quite a ways from release. The code above allegedly is working code for a flying vehicle, though I've never tested it.
LiKeMiKeS: I've tried many times compiling this KShip script and have ended up with an error for the ShipState 'struct' & 'var' references obscuring each other and haven't been able to figure out a clean fix so it will compile - without creating another unneeded var instance or reference. I'm still learning the nuances of uscript, so it's difficult to debug atm.
Brold9999: I tried the above code long ago and it didn't seem to work. I have gotten a lot of questions on my mod, UT Space Battle, as to how I implemented the vehicles. I did not use a Karma or PhysX based approach but that is what most of the people who have asked me about this seem to be looking for. I have taken the code above and hammered away at it until it works. I ripped out many unnecessary sections (such as afterburner) which only cluttered the salient concepts of implementing a 6DOF vehicle. The new version should compile and work out-of-the-box and has (mostly) working netcode. Some tweaks are still necessary to make a vehicle suitable for a mod but this one does the basics that seem to trip most people up when they try to implement a 6DOF vehicle. KShip is the base 6DOF vehicle, FlyingDog is a butchered Bulldog which is based on KShip. Code follows:
class KShip extends KVehicle;
// Used for Karma forces pileup
// Add to these variables to apply force and torque to the ship.
var vector extraForce, extraTorque;
// Amount to increment/decrement shipSteering.roll for every click of the mouse wheel.
var float rollUnit;
// Desired rotation torque is reduced each tick. This factor controls how rapidly old inputs decay.
var float turnDecayTime;
// Amount of rotation torque to apply for a given amount of mouse movement.
var float turnSensitivity;
// Maximum amount of rotation torque that can be accumulated.
var float maximumTurnRate;
// Maximum linear thrust the ship can apply.
var float maximumThrust;
// Desired torque on the ship - relative to it's current rotation.
var rotator shipSteering;
// Desired linear thrust to apply to the ship - not relative to it's current rotation.
var vector shipThrust;
// Ship replication vars and functions, thanks daid!
struct StructShipState
{
var KRBVec ChassisPosition;
var Quat ChassisQuaternion;
var KRBVec ChassisLinVel;
var KRBVec ChassisAngVel;
var rotator serverSteering;
var vector serverThrust;
var bool bNewState; // Set to true whenever a new state is received and should be processed
};
var KRigidBodyState ChassisState;
var StructShipState ShipState; // This is replicated to the ship, and processed to update all the parts.
var bool bNewShipState; // Indicated there is new data processed, and chassis RBState should be updated.
var float NextNetUpdateTime; // Next time we should force an update of vehicles state.
var() float MaxNetUpdateInterval;
var int AVar;//Just for replication, else the ShipState doesn't get replicated
replication
{
unreliable if(Role == ROLE_Authority)
ShipState, AVar;
}
simulated event VehicleStateReceived()
{
if(!ShipState.bNewState)
return;
// Get root chassis info
ChassisState.Position = ShipState.ChassisPosition;
ChassisState.Quaternion = ShipState.ChassisQuaternion;
ChassisState.LinVel = ShipState.ChassisLinVel;
ChassisState.AngVel = ShipState.ChassisAngVel;
// Update control inputs
shipSteering = ShipState.serverSteering;
shipThrust = ShipState.serverThrust;
// Update flags
ShipState.bNewState = false;
bNewShipState = true;
}
simulated event bool KUpdateState(out KRigidBodyState newState)
{
// This should never get called on the server - but just in case!
if(Role == ROLE_Authority || !bNewShipState)
return false;
// Apply received data as new position of ship chassis.
newState = ChassisState;
bNewShipState = false;
return true;
}
function PackState()
{
local vector chassisPos, chassisLinVel, chassisAngVel;
local vector oldPos, oldLinVel;
local KRigidBodyState localChassisState;
// Get chassis state.
KGetRigidBodyState(localChassisState);
chassisPos = KRBVecToVector(localChassisState.Position);
chassisLinVel = KRBVecToVector(localChassisState.LinVel);
chassisAngVel = KRBVecToVector(localChassisState.AngVel);
// Last position we sent
oldPos = KRBVectoVector(ShipState.ChassisPosition);
oldLinVel = KRBVectoVector(ShipState.ChassisLinVel);
// See if state has changed enough, or enough time has passed, that we
// should send out another update by updating the state struct.
if( !KIsAwake() )
{
return; // Never send updates if physics is at rest
}
if( VSize(oldPos - chassisPos) > 5 ||
VSize(oldLinVel - chassisLinVel) > 1 ||
abs(shipState.serverSteering.yaw - shipSteering.yaw) > 0.1 ||
abs(shipState.serverSteering.pitch - shipSteering.pitch) > 0.1 ||
abs(shipState.serverSteering.roll - shipSteering.roll) > 0.1 ||
VSize(shipState.serverThrust - shipThrust) > 1 ||
Level.TimeSeconds > NextNetUpdateTime )
{
NextNetUpdateTime = Level.TimeSeconds + MaxNetUpdateInterval;
}
else
{
return;
}
ShipState.ChassisPosition = localChassisState.Position;
ShipState.ChassisQuaternion = localChassisState.Quaternion;
ShipState.ChassisLinVel = localChassisState.LinVel;
ShipState.ChassisAngVel = localChassisState.AngVel;
shipState.serverSteering = shipSteering;
shipState.serverThrust = shipThrust;
// This flag lets the client know this data is new.
ShipState.bNewState = true;
//Make sure ShipState gets replicated
AVar++;
if (AVar > 10)
AVar=0;
}
simulated function setInitialState() {
super.setInitialState();
// don't disable my tick!
enable('tick');
}
simulated event drivingStatusChanged() {
super.drivingStatusChanged();
// don't disable my tick!
enable('tick');
}
function KDriverEnter(Pawn P) {
super.KDriverEnter(p);
if (PlayerController(controller) != none)
controller.gotoState(landMovementState);
}
simulated function prevWeapon() {
shipSteering.roll = fclamp(shipSteering.roll - (rollUnit * turnSensitivity), -maximumTurnRate, maximumTurnRate);
}
simulated function nextWeapon() {
shipSteering.roll = fclamp(shipSteering.roll + (rollUnit * turnSensitivity), -maximumTurnRate, maximumTurnRate);
}
simulated function updateRocketAcceleration(float deltaTime, float yawChange, float pitchChange) {
if (deltaTime >= turnDecayTime)
shipSteering = rot(0,0,0);
else
shipSteering *= (turnDecayTime-deltaTime) / turnDecayTime;
// / 6000 because the aForward, etc. are 6000 by default when the appropriate button is pressed.
shipThrust = (((vect(1,0,0) * PlayerController(Controller).aForward) + (vect(0,1,0) * PlayerController(Controller).aStrafe) + (vect(0,0,1) * PlayerController(Controller).aUp)) / 6000) >> rotation;
if (vsize(shipThrust) > 1)
shipThrust = normal(shipThrust);
shipSteering.yaw = fclamp(shipSteering.yaw - (yawChange * turnSensitivity), -maximumTurnRate, maximumTurnRate);
shipSteering.pitch = fclamp(shipSteering.pitch + (pitchChange * turnSensitivity), -maximumTurnRate, maximumTurnRate);
}
simulated function tick(float delta)
{ super.tick(delta);
if (controller == none) {
shipThrust = vect(0,0,0);
shipSteering = rot(0,0,0);
}
if(!KIsAwake() && controller != none)
KWake();
if(Role == ROLE_Authority && Level.NetMode != NM_StandAlone)
PackState();
updateExtraForce(delta);
}
simulated function updateExtraForce(float delta)
{
local vector worldForward, worldDown, worldLeft;
worldForward = vect(1, 0, 0) >> Rotation;
worldDown = vect(0, 0, -1) >> Rotation;
worldLeft = vect(0, -1, 0) >> Rotation;
ExtraForce = ExtraForce + shipThrust * maximumThrust * delta; // Speed
ExtraTorque = ExtraTorque + worldDown * shipSteering.yaw * delta; // Yaw
ExtraTorque = ExtraTorque - worldLeft * -shipSteering.pitch * delta; // Pitch
ExtraTorque = ExtraTorque + worldForward * -shipSteering.roll * delta; // Roll
}
simulated event KApplyForce(out vector Force, out vector Torque)
{
// This actually does the applying of the piled up force
Force = ExtraForce;
Torque = ExtraTorque;
ExtraForce = vect(0,0,0);
ExtraTorque = vect(0,0,0);
}
DefaultProperties
{
landMovementState=PlayerSpaceFlying
rollUnit=1024
turnDecayTime=0.5
turnSensitivity=0.5
maximumTurnRate=5000
maximumThrust=2500
}
class FlyingDog extends KShip placeable CacheExempt;
// triggers used to get into the FlyingDog
var const vector FrontTriggerOffset;
var SVehicleTrigger FLTrigger, FRTrigger;
// Maximum speed at which you can get in the vehicle.
var (FlyingDog) float TriggerSpeedThresh;
var bool TriggerState; // true for on, false for off.
// Destroyed Buggy
var (FlyingDog) class<Actor> DestroyedEffect;
var (FlyingDog) sound DestroyedSound;
// Weapon
var float FireCountdown;
var (FlyingDog) float FireInterval;
var (FlyingDog) vector weaponFireOffset;
simulated function PostNetBeginPlay()
{
local vector RotX, RotY, RotZ;
Super.PostNetBeginPlay();
GetAxes(Rotation,RotX,RotY,RotZ);
// Only have triggers on server
if(Level.NetMode != NM_Client)
{
// Create triggers for gettting into the FlyingDog
FLTrigger = spawn(class'SVehicleTrigger', self,, Location + FrontTriggerOffset.X * RotX + FrontTriggerOffset.Y * RotY + FrontTriggerOffset.Z * RotZ);
FLTrigger.SetBase(self);
FLTrigger.SetCollision(true, false, false);
FRTrigger = spawn(class'SVehicleTrigger', self,, Location + FrontTriggerOffset.X * RotX - FrontTriggerOffset.Y * RotY + FrontTriggerOffset.Z * RotZ);
FRTrigger.SetBase(self);
FRTrigger.SetCollision(true, false, false);
TriggerState = true;
}
// If this is not 'authority' version - don't destroy it if there is a problem.
// The network should sort things out.
if(Role != ROLE_Authority) {
KarmaParams(KParams).bDestroyOnSimError = False;
}
}
simulated event Destroyed()
{
// Clean up random stuff attached to the car
if(Level.NetMode != NM_Client)
{
FLTrigger.Destroy();
FRTrigger.Destroy();
}
Super.Destroyed();
// Trigger destroyed sound and effect
if(Level.NetMode != NM_DedicatedServer)
{
spawn(DestroyedEffect, self, , Location );
PlaySound(DestroyedSound);
}
}
function KDriverEnter(Pawn p)
{
Super.KDriverEnter(p);
p.bHidden = True;
ReceiveLocalizedMessage(class'Vehicles.BulldogMessage', 1);
}
function bool KDriverLeave(bool bForceLeave)
{
local Pawn OldDriver;
OldDriver = Driver;
// If we succesfully got out of the car, make driver visible again.
if( Super.KDriverLeave(bForceLeave) )
{
OldDriver.bHidden = false;
AmbientSound = None;
return true;
}
else
return false;
}
function fireWeapons(bool bWasAltFire)
{
local vector FireLocation;
local PlayerController PC;
// Client can't do firing
if(Role != ROLE_Authority)
return;
FireLocation = Location + (weaponFireOffset >> Rotation);
while(FireCountdown <= 0)
{
if(!bWasAltFire)
{
spawn(class'PROJ_TurretSkaarjPlasma', self, , FireLocation, rotation);
// Play firing noise
PlaySound(Sound'ONSVehicleSounds-S.Laser02', SLOT_None,,,,, false);
PC = PlayerController(Controller);
if (PC != None && PC.bEnableWeaponForceFeedback)
PC.ClientPlayForceFeedback("RocketLauncherFire");
}
else
{
spawn(class'PROJ_SpaceFighter_Rocket', self, , FireLocation, rotator(vector(rotation) + Vrand() * 0.05));
// Play firing noise
PlaySound(Sound'AssaultSounds.HnShipFire01', SLOT_None,,,,, false);
PC = PlayerController(Controller);
if (PC != None && PC.bEnableWeaponForceFeedback)
PC.ClientPlayForceFeedback("RocketLauncherFire");
}
FireCountdown += FireInterval;
}
}
// Fire a rocket (if we've had time to reload!)
function VehicleFire(bool bWasAltFire)
{
Super.VehicleFire(bWasAltFire);
if(FireCountdown < 0)
{
FireCountdown = 0;
fireWeapons(bWasAltFire);
}
}
simulated function Tick(float Delta)
{
Local float VMag;
Super.Tick(Delta);
// Weapons (run on server and replicated to client)
if(Role == ROLE_Authority)
{
// Countdown to next shot
FireCountdown -= Delta;
// This is for sustained barrages.
// Primary fire takes priority
if(bVehicleIsFiring)
fireWeapons(false);
else if(bVehicleIsAltFiring)
fireWeapons(true);
}
// Dont have triggers on network clients.
if(Level.NetMode != NM_Client)
{
// If vehicle is moving, disable collision for trigger.
VMag = VSize(Velocity);
if(VMag < TriggerSpeedThresh && TriggerState == false)
{
FLTrigger.SetCollision(true, false, false);
FRTrigger.SetCollision(true, false, false);
TriggerState = true;
}
else if(VMag > TriggerSpeedThresh && TriggerState == true)
{
FLTrigger.SetCollision(false, false, false);
FRTrigger.SetCollision(false, false, false);
TriggerState = false;
}
}
}
// Really simple at the moment!
function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation,
Vector momentum, class<DamageType> damageType)
{
// Avoid damage healing the car!
if(Damage < 0)
return;
if(damageType == class'DamTypeSuperShockBeam')
Health -= 100; // Instagib doesn't work on vehicles
else
Health -= 0.5 * Damage; // Weapons do less damage
// The vehicle is dead!
if(Health <= 0)
{
if ( Controller != None )
{
if( Controller.bIsPlayer )
{
ClientKDriverLeave(PlayerController(Controller)); // Just to reset HUD etc.
Controller.PawnDied(self); // This should unpossess the controller and let the player respawn
}
else
Controller.Destroy();
}
Destroy(); // Destroy the vehicle itself (see Destroyed below)
}
//KAddImpulse(momentum, hitlocation);
}
// AI Related code
function Actor GetBestEntry(Pawn P)
{
if ( VSize(P.Location - FLTrigger.Location) < VSize(P.Location - FRTrigger.Location) )
return FLTrigger;
return FRTrigger;
}
defaultproperties
{
DrawType=DT_StaticMesh
StaticMesh=StaticMesh'BulldogMeshes.Simple.S_Chassis'
Begin Object Class=KarmaParamsRBFull Name=KParams0
KActorGravScale=0
KInertiaTensor(0)=20
KInertiaTensor(1)=0
KInertiaTensor(2)=0
KInertiaTensor(3)=30
KInertiaTensor(4)=0
KInertiaTensor(5)=48
KCOMOffset=(X=0.8,Y=0.0,Z=-0.7)
KStartEnabled=True
KFriction=1.6
KLinearDamping=1
KAngularDamping=10
bKNonSphericalInertia=False
bHighDetailOnly=False
bClientOnly=False
bKDoubleTickRate=True
Name="KParams0"
End Object
KParams=KarmaParams'KParams0'
DrawScale=0.4
drawScale3D=(x=-1,y=1,z=1)
DestroyedEffect=class'XEffects.RocketExplosion'
DestroyedSound=sound'WeaponSounds.P1RocketLauncherAltFire'
FrontTriggerOffset=(X=0,Y=165,Z=10)
TriggerSpeedThresh=40
// Weaponry
FireInterval=0.1
weaponFireOffset=(X=0,Y=0,Z=80)
// Driver positions
ExitPositions(0)=(X=0,Y=200,Z=100)
ExitPositions(1)=(X=0,Y=-200,Z=100)
ExitPositions(2)=(X=350,Y=0,Z=100)
ExitPositions(3)=(X=-350,Y=0,Z=100)
DrivePos=(X=-165,Y=0,Z=-100)
Health=800
HealthMax=800
SoundRadius=255
}