DTS/Scripting/Vehicles/Guide
From TDN
Introduction
This documentation aims at providing a complete vehicular support for the player and Torque's vehicles. As with any scripting there are many ways of doing one thing and this provides only one method of completing this task.
Requirements
It is advised that you have read the DTS guides to vehicles, including having a vehicle ready for use with all assets. A template vehicle is being put together for this example.
Due to the complexity of these functions, it is advised that you become very familiar with TorqueScript, especially as this section covers the player, camera, moveMaps, vehicle data and more.
Script
Each section is detailed to explain what is going on, a final compiled script can be found at the end of this document.
This script is run from ~/server/scripts/vehicles/vehicles.cs.
We are aiming to get a fully functioning vehicle system, to allow the player to mount the vehicle, dismount it, get damage during the vehicle. This currently doesn't add weapon support, although this can be expanded on by using the moveMaps to control the weapons on the vehicles. While these scripts only provide a basic template, they aim to give you a good understanding of what is happening with each function to get this gameplay section working.
General Purpose Functions
This section contains global functions used for all the vehicles. Typically this will entail adding and removing the vehicle from the WorldSpace.
VehicleData::onAdd
//**************************************************************
//* GENERAL PURPOSE FUNCTIONS
//**************************************************************
function VehicleData::onAdd(%this, %obj)
{
Parent::onAdd(%this, %obj);
%obj.setRechargeRate(%this.rechargeRate);
// set full energy
%obj.setEnergyLevel(%this.MaxEnergy);
if(%obj.disableMove)
%obj.immobilized = true;
if(%obj.deployed)
{
if($countDownStarted)
%data.schedule(($Host::WarmupTime * 1000) / 2, "vehicleDeploy", %obj, 0, 1);
else
{
$VehiclesDeploy[$NumVehiclesDeploy] = %obj;
$NumVehiclesDeploy++;
}
}
if(%obj.mountable || %obj.mountable $= "")
%this.isMountable(%obj, true);
else
%this.isMountable(%obj, false);
%obj.setSelfPowered();
// %this.canObserve = true;
}
VehicleData::onRemove
function VehicleData::onRemove(%this, %obj)
{
// if there are passengers/driver, kick them out
%this.deleteAllMounted(%obj);
for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++)
if (%obj.getMountNodeObject(%i)) {
%passenger = %obj.getMountNodeObject(%i);
%passenger.unmount();
}
vehicleListRemove(%obj.getDataBlock(), %obj);
if(%obj.lastPilot.lastVehicle == %obj)
%obj.lastPilot.lastVehicle = "";
Parent::onRemove(%this, %obj);
}
Creating Vehicles
HoverVehicleData::create
//**************************************************************
//* VEHICLE CREATE
//**************************************************************
function HoverVehicleData::create(%this, %team, %oldObj)
{
if(%oldObj $= "")
{
%obj = new HoverVehicle()
{
datablock = %this;
respawn = "0";
};
}
else
{
%obj = new HoverVehicle()
{
datablock = %this;
respawn = "0";
mountable = %oldObj.mountable;
disableMove = %oldObj.disableMove;
resetPos = %oldObj.resetPos;
respawnTime = %oldObj.respawnTime;
marker = %oldObj;
};
}
return(%obj);
}
WheeledVehicleData::create
$Vehicle::Wheeled::EnergyLevel = 60;
function WheeledVehicleData::create(%this, %obj, %oldObj)
{
if(%oldObj $= "")
{
%obj = new WheeledVehicle()
{
datablock = %this;
respawn = "0";
};
}
else
{
%obj = new WheeledVehicle()
{
datablock = %this;
respawn = "0";
mountable = %oldObj.mountable;
disableMove = %oldObj.disableMove;
resetPos = %oldObj.resetPos;
deployed = %oldObj.deployed;
respawnTime = %oldObj.respawnTime;
marker = %oldObj;
};
%obj.mountable = true;
%obj.setEnergyLevel($Vehicle::Wheeled::EnergyLevel);
}
return(%obj);
}
FlyingVehicleData::create
$Vehicle::Flying::EnergyLevel = 120
function FlyingVehicleData::create(%this, %obj, %oldObj)
{
if(%oldObj $= "")
{
%obj = new FlyingVehicle()
{
datablock = %this;
respawn = "0";
teamBought = %team;
team = %team;
};
}
else
{
%obj = new FlyingVehicle()
{
datablock = %this;
teamBought = %team;
team = %team;
mountable = %oldObj.mountable;
disableMove = %oldObj.disableMove;
resetPos = %oldObj.resetPos;
respawnTime = %oldObj.respawnTime;
marker = %oldObj;
};
%obj.mountable = true;
%obj.setEnergyLevel($Vehicle::Hover::EnergyLevel);
}
return(%obj);
}
Player Functions
This section explains code that is specific to the player's functions in mounting/dismounting through various ways. You can set the mounting and dismounting functions to act upon collision or key binds.
PlayerData::onCollision
1. Ignore collisions if this player is "Dead". i.e. a Corpse
Note - This will probably need to be removed for RPG style games where you pick up a corpse's inventory.
2. Call into the parent callback allowing it to do any work it should do first.
3. Handle collisions with vehicles. This is accomplished by checking whether a player has collided with a vehicle, then whether the vehicle is mountable. Then, the player is checked to see if it can mount vehicles, and finally whether it is mounted already.
function PlayerData::onCollision()
{
}
PlayerData::doVehicleMount
function PlayerData::doVehicleMount()
{
}
PlayerData::doDismount
function doPlayerDismount(%player, %obj, %forced)
{
%vel = %obj.getVelocity();
%speed = vectorLen(%vel);
// Check our speed. If we're still moving, we can't dismount.
if (%speed >= 1 || !%obj.isMounted())
{
// The vehicle is moving or the player is not mounted.
return;
}
// Find the position above dismount point.
%pos = getWords(%obj.getTransform(), 0, 2);
%oldPos = %pos;
%vec[0] = " 2 0 0";
%vec[1] = " 0 -2 0";
%vec[2] = " 0 2 0";
%vec[3] = "-2 0 0";
%vec[4] = " 4 0 0";
%impulseVec = "0 0 0";
%vec[0] = MatrixMulVector( %obj.getTransform(), %vec[0]);
// Make sure the point is valid
%pos = "0 0 0";
%numAttempts = 5;
%success = -1;
for (%i = 0; %i < %numAttempts; %i++) {
%pos = VectorAdd(%oldPos, VectorScale(%vec[%i], 3));
if (%obj.checkDismountPoint(%oldPos, %pos)) {
%success = %i;
%impulseVec = %vec[%i];
break;
}
}
if (%forced && %success == -1)
%pos = %oldPos;
%obj.unmount();
%obj.setControlObject(%obj);
%obj.mountVehicle = false;
// Schedule the function to set the mount flag, so that the player
// can mount another vehicle in the future.
%obj.schedule(4000, "MountVehicles", true);
// Position above dismount point
%obj.setTransform(%pos);
%obj.applyImpulse(%pos, VectorScale(%impulseVec, %obj.getDataBlock().mass));
%obj.setActionThread("run",true,true);
%obj.setArmThread("look");
// Command the client to display the correct movement map and
// activate the command menu.
CommandToClient(%obj.client, 'PopActionMap', $Vehicle::moveMaps[%obj.mSeat]);
CommandToClient(%obj.client, 'PushActionMap', moveMap);
CommandToClient(%obj.client,'activateCommandMenu');
}
PlayerData::onUnmount
Assume that the player is dismounting a shape that was until now the control object.
function PlayerData::onUnmount( %this, %Obj, %mount, %node )
{
%Obj.setControlObject( %Obj );
//Adds his weapon back
%obj.mountImage(%obj.lastWeapon, $WeaponSlot);
}
VehicleData::onPlayerMount
Player::onVehicleMount
function onPlayerMount(%player,%obj,%vehicle,%node)
{
CommandToClient(%obj.client, 'PopActionMap', moveMap);
CommandToClient(%obj.client, 'PushActionMap', $Vehicle::moveMaps[%node]);
CommandToClient(%obj.client,'HideCommandMenuServer');
%obj.setTransform(%vehicle.getDataBlock().mountPointTransform[%node]);
%obj.lastWeapon = %obj.getMountedImage($WeaponSlot);
%obj.unmountImage($WeaponSlot);
%obj.setActionThread(%vehicle.getDatablock().mountPose[%node],true,true);
// Are we driving this vehicle?
if (%node == 0) {
%obj.setControlObject(%vehicle);
}
Damage Functions
VehicleData::damage
//**************************************************************
//* DAMAGE FUNCTIONS
//**************************************************************
function VehicleData::damage(%this,%obj,%type,%vel,%damage,%damageType)
{
echo("Vehicle IS HIT");
%obj.applyDamage(%damage);
if(%obj.getDamageState() $= "Destroyed" )
{
%obj.blowUp();
}
//Parent::damage(%this, %obj);
}
VehicleData::onDamage
function VehicleData::onDamage(%this,%obj,%amount)
{
//called when damage level is changed
echo("AT DAMAGE TIME");
%damage = %obj.getDamageLevel();
//
echo("damage given by projectile " @ %amount);
echo("THIS dataBlock is " @ %this.getDataBlock().dataBlock);
echo("OBJ dataBlock is " @ %obj.dataBlock);//The vehicle
echo("getEnergyLevel is " @ %this.getEnergyLevel());
echo("getEnergyValue is " @ %this.getEnergyValue());
echo("getDamageState is " @ %obj.getDamageState());//enablebed
echo("getDamageLevel is " @ %obj.getDamageLevel());//the amount of damage taken
echo("MaxDamage is " @ %this.maxDamage);//amount of damage it can take
%healthLeft = %this.maxDamage - %damage;
echo("The Vehicles's health is " @ %healthLeft);
//if (%damage >= %this.destroyedLevel)
//GUI should show maxDamage - damageLevel
if(%damage >= %this.maxDamage)
{
if(%obj.getDamageState() !$= "Destroyed")
{
if(%obj.respawnTime !$= "")
%obj.marker.schedule = %obj.marker.data.schedule(%obj.respawnTime, "respawn", %obj.marker);
%obj.setDamageState(Destroyed);
}
}
else
{
if(%obj.getDamageState() !$= "Enabled")
%obj.setDamageState(Enabled);
}
}
VehicleData::onImpact
function VehicleData::onImpact(%data, %vehicleObject, %collidedObject, %vec, %vecLen)
{
if(%vecLen > %data.minImpactSpeed)
%data.damageObject(%vehicleObject, 0, VectorAdd(%vec, %vehicleObject.getPosition()),
%vecLen * %data.speedDamageScale, $DamageType::Ground);
// associated "crash" sounds
if(%vecLen > %vDataBlock.hardImpactSpeed)
%vehicleObject.playAudio(0, %vDataBlock.hardImpactSound);
else if(%vecLen > %vDataBlock.softImpactSpeed)
%vehicleObject.playAudio(0, %vDataBlock.softImpactSound);
}
VehicleData::damageObject
function VehicleData::damageObject(%data, %targetObject, %sourceObject,
%position, %amount, %damageType, %momVec, %theClient, %proj)
{
//{ echo("you are damaging an object");
// if(%proj !$= "")
// {
// if(%amount > 0 && %targetObject.lastDamageProj !$= %proj)
// {
// %targetObject.lastDamageProj = %proj;
// %targetObject.lastDamageAmount = %amount;
// }
// else if(%targetObject.lastDamageAmount < %amount)
// %amount = %amount - %targetObject.lastDamageAmount;
// else
// return;
// }
// check for team damage
// %sourceClient = %sourceObject ? %sourceObject.getOwnerClient() : 0;
// %targetTeam = getTargetSensorGroup(%targetObject.getTarget());
// if(%sourceClient)
// %sourceTeam = %sourceClient.getSensorGroup();
// else if(isObject(%sourceObject) && %sourceObject.getClassName() $= "Turret")
// %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget());
// else
// %sourceTeam = %sourceObject ? getTargetSensorGroup(%sourceObject.getTarget()) : -1;
// vehicles no longer obey team damage -JR
// if(!$teamDamage && (%targetTeam == %sourceTeam) && %targetObject.getDamagePercent() > 0.5)
// return;
//but we do want to track the destroyer
// if(%sourceObject)
// {
// %targetObject.lastDamagedBy = %sourceObject;
// %targetObject.lastDamageType = %damageType;
// }
// else
// %targetObject.lastDamagedBy = 0;
// Scale damage type & include shield calculations...
// if (%data.isShielded)
// %amount = %data.checkShields(%targetObject, %position, %amount, %damageType);
// %damageScale = %data.damageScale[%damageType];
// if(%damageScale !$= "")
// %amount *= %damageScale;
// if(%amount != 0)
// %targetObject.applyDamage(%amount);
// if(%targetObject.getDamageState() $= "Destroyed" )
// {
// if( %momVec !$= "")
// %targetObject.setMomentumVector(%momVec);
// }
}
VehicleData::onEnterLiquid
function VehicleData::onEnterLiquid(%data, %obj, %coverage, %type)
{
switch(%type)
{
case 0:
//Water
%obj.setHeat(0.0);
case 1:
//Ocean Water
%obj.setHeat(0.0);
case 2:
//River Water
%obj.setHeat(0.0);
case 3:
//Stagnant Water
%obj.setHeat(0.0);
case 4:
//Lava
%obj.liquidDamage(%data, $VehicleDamageLava, $DamageType::Lava);
case 5:
//Hot Lava
%obj.liquidDamage(%data, $VehicleDamageHotLava, $DamageType::Lava);
case 6:
//Crusty Lava
%obj.liquidDamage(%data, $VehicleDamageCrustyLava, $DamageType::Lava);
case 7:
//Quick Sand
}
}
VehicleData::onLeaveLiquid
function VehicleData::onLeaveLiquid(%data, %obj, %type)
{
switch(%type)
{
case 0:
//Water
%obj.setHeat(1.0);
case 1:
//Ocean Water
%obj.setHeat(1.0);
case 2:
//River Water
%obj.setHeat(1.0);
case 3:
//Stagnant Water
%obj.setHeat(1.0);
case 4:
//Lava
case 5:
//Hot Lava
case 6:
//Crusty Lava
case 7:
//Quick Sand
}
if(%obj.lDamageSchedule !$= "")
{
cancel(%obj.lDamageSchedule);
%obj.lDamageSchedule = "";
}
}
FlyingVehicle::liquidDamage
function FlyingVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType)
{
if(%obj.getDamageState() !$= "Destroyed")
{
%data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType);
%obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType);
passengerLiquidDamage(%obj, %damageAmount, %damageType);
}
else
%obj.lDamageSchedule = "";
}
WheeledVehicle::liquidDamage
function WheeledVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType)
{
if(%obj.getDamageState() !$= "Destroyed")
{
%data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType);
%obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType);
passengerLiquidDamage(%obj, %damageAmount, %damageType);
}
else
%obj.lDamageSchedule = "";
}
HoverVehicle::liquidDamage
function HoverVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType)
{
if(%obj.getDamageState() !$= "Destroyed")
{
%data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType);
%obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType);
passengerLiquidDamage(%obj, %damageAmount, %damageType);
}
else
%obj.lDamageSchedule = "";
}
PassengerLiquidDamage
function PassengerLiquidDamage(%obj, %damageAmount, %damageType)
{
for(%i = %num; %i < %obj.getDataBlock().numMountPoints; %i++)
if (%p = %obj.getMountNodeObject(%i))
%p.liquidDamage(%p.getDatablock(), $DamageLava, $DamageType::Lava);
}
VehicleData::onDestroyed
function VehicleData::onDestroyed(%data, %obj, %prevState)
{
if(%obj.lastDamagedBy)
{
%destroyer = %obj.lastDamagedBy;
game.vehicleDestroyed(%obj, %destroyer);
//error("vehicleDestroyed( "@ %obj @", "@ %destroyer @")");
}
radiusVehicleExplosion(%data, %obj);
if(%obj.turretObject)
if(%obj.turretObject.getControllingClient())
%obj.turretObject.getDataBlock().playerDismount(%obj.turretObject);
for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++)
{
if (%obj.getMountNodeObject(%i)) {
%flingee = %obj.getMountNodeObject(%i);
%flingee.getDataBlock().doDismount(%flingee, true);
%xVel = 250.0 - (getRandom() * 500.0);
%yVel = 250.0 - (getRandom() * 500.0);
%zVel = (getRandom() * 100.0) + 50.0;
%flingVel = %xVel @ " " @ %yVel @ " " @ %zVel;
%flingee.applyImpulse(%flingee.getTransform(), %flingVel);
echo("got player..." @ %flingee.getClassName());
%flingee.damage(0, %obj.getPosition(), 0.4, $DamageType::Crash);
}
}
%data.deleteAllMounted(%obj);
%obj.schedule(2000, "delete");
}
RadiusVehicleExplosion
function RadiusVehicleExplosion(%data, %vehicle)
{
// this is a modified version of RadiusExplosion() from projectiles.cs
%position = %vehicle.getPosition();
InitContainerRadiusSearch(%position, %data.explosionRadius, $TypeMasks::PlayerObjectType |
$TypeMasks::VehicleObjectType |
$TypeMasks::MoveableObjectType |
$TypeMasks::StaticShapeObjectType |
$TypeMasks::ForceFieldObjectType |
$TypeMasks::TurretObjectType |
$TypeMasks::ItemObjectType);
%numTargets = 0;
while ((%targetObject = containerSearchNext()) != 0)
{
if(%targetObject == %vehicle)
continue;
%dist = containerSearchCurrRadDamageDist();
if (%dist > %data.explosionRadius)
continue;
if (%targetObject.isMounted())
{
%mount = %targetObject.getObjectMount();
if(%mount == %vehicle)
continue;
%found = -1;
for (%i = 0; %i < %mount.getDataBlock().numMountPoints; %i++)
{
if (%mount.getMountNodeObject(%i) == %targetObject)
{
%found = %i;
break;
}
}
if (%found != -1)
{
if (%mount.getDataBlock().isProtectedMountPoint[%found] && (%mount != %vehicle))
continue;
}
}
%targets[%numTargets] = %targetObject;
%targetDists[%numTargets] = %dist;
%numTargets++;
}
for (%i = 0; %i < %numTargets; %i++)
{
%targetObject = %targets[%i];
%dist = %targetDists[%i];
%coverage = calcExplosionCoverage(%position, %targetObject,
($TypeMasks::InteriorObjectType |
$TypeMasks::TerrainObjectType |
$TypeMasks::ForceFieldObjectType));
if (%coverage == 0)
continue;
%amount = (1.0 - (%dist / %data.explosionRadius)) * %coverage * %data.explosionDamage;
%targetData = %targetObject.getDataBlock();
%momVec = "0 0 1";
if(%amount > 0)
%targetData.damageObject(%targetObject, %sourceObject, %position, %amount, $DamageType::Explosion, %momVec);
}
}
Controls
Movemaps can be defined for every mounted position as follows, however this is limited for vehicle-specific controls which might use weapons.
Place the copies of the following moveMaps in your ~/client/scripts/ folder.
General Vehicle Movemaps
// Define the move maps for each position. This may need // to be defined on a per vehicle basis in the future // once mounted weapons are implemented. $Vehicle::moveMaps[0] = "vehicleDriverMap"; $Vehicle::moveMaps[1] = "vehiclePassengerMap"; $Vehicle::moveMaps[2] = "vehiclePassengerMap"; $Vehicle::moveMaps[3] = "vehiclePassengerMap"; $Vehicle::moveMaps[4] = "vehiclePassengerMap"; $Vehicle::moveMaps[5] = "vehiclePassengerMap"; $Vehicle::moveMaps[6] = "vehiclePassengerMap"; $Vehicle::moveMaps[7] = "vehiclePassengerMap"; $Vehicle::moveMaps[8] = "vehiclePassengerMap";
Resources
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=3925
Final Script
// FROM TRIBES 2
// WATCH FOR PRODUCTION
// Notes:
// - respawning vehicles with turrets (bomber/tank) will not setup the turret properly
//Damage Rate for entering Liquid
$VehicleDamageLava = 0.0325;
$VehicleDamageHotLava = 0.0325;
$VehicleDamageCrustyLava = 0.0325;
$NumVehiclesDeploy = 0;
//**************************************************************
//* GENERAL PURPOSE FUNCTIONS
//**************************************************************
function VehicleData::onAdd(%data, %obj)
{
Parent::onAdd(%data, %obj);
if((%data.sensorData !$= "") && (%obj.getTarget() != -1))
setTargetSensorData(%obj.getTarget(), %data.sensorData);
%obj.setRechargeRate(%data.rechargeRate);
// set full energy
%obj.setEnergyLevel(%data.MaxEnergy);
if(%obj.disableMove)
%obj.immobilized = true;
if(%obj.deployed)
{
if($countDownStarted)
%data.schedule(($Host::WarmupTime * 1000) / 2, "vehicleDeploy", %obj, 0, 1);
else
{
$VehiclesDeploy[$NumVehiclesDeploy] = %obj;
$NumVehiclesDeploy++;
}
}
if(%obj.mountable || %obj.mountable $= "")
%data.isMountable(%obj, true);
else
%data.isMountable(%obj, false);
%obj.setSelfPowered();
// %data.canObserve = true;
}
function VehicleData::onRemove(%this, %obj)
{
// if there are passengers/driver, kick them out
%this.deleteAllMounted(%obj);
for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++)
if (%obj.getMountNodeObject(%i)) {
%passenger = %obj.getMountNodeObject(%i);
%passenger.unmount();
}
vehicleListRemove(%obj.getDataBlock(), %obj);
if(%obj.lastPilot.lastVehicle == %obj)
%obj.lastPilot.lastVehicle = "";
Parent::onRemove(%this, %obj);
}
}
//**************************************************************
//* DAMAGE FUNCTIONS
//**************************************************************
function VehicleData::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType, %momVec, %theClient, %proj)
{ echo("you are damaging an object");
// check for team damage
//%sourceClient = %sourceObject ? %sourceObject.getOwnerClient() : 0;
//%targetTeam = getTargetSensorGroup(%targetObject.getTarget());
// if(%sourceClient)
// %sourceTeam = %sourceClient.getSensorGroup();
// else if(isObject(%sourceObject) && %sourceObject.getClassName() $= "Turret")
// %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget());
//else
// %sourceTeam = %sourceObject ? getTargetSensorGroup(%sourceObject.getTarget()) : -1;
// vehicles no longer obey team damage -JR
// if(!$teamDamage && (%targetTeam == %sourceTeam) && %targetObject.getDamagePercent() > 0.5)
// return;
//but we do want to track the destroyer
//if(%sourceObject)
//{
// %targetObject.lastDamagedBy = %sourceObject;
// %targetObject.lastDamageType = %damageType;
//}
//else
// %targetObject.lastDamagedBy = 0;
// Scale damage type & include shield calculations...
if (%data.isShielded)
%amount = %data.checkShields(%targetObject, %position, %amount, %damageType);
%damageScale = %data.damageScale[%damageType];
if(%damageScale !$= "")
%amount *= %damageScale;
if(%amount != 0)
%targetObject.applyDamage(%amount);
if(%targetObject.getDamageState() $= "Destroyed" )
{
if( %momVec !$= "")
%targetObject.setMomentumVector(%momVec);
}
}
function VehicleData::damage(%this,%obj,%type,%vel,%damage,%damageType)
{
echo("Vehicle IS HIT");
%obj.applyDamage(%damage);
if(%obj.getDamageState() $= "Destroyed" )
{
%obj.blowUp();
}
//Parent::damage(%this, %obj);
}
function VehicleData::onDamage(%this,%obj,%amount)
{
//called when damage level is changed
echo("AT DAMAGE TIME");
%damage = %obj.getDamageLevel();
//
echo("damage given by projectile " @ %amount);
echo("THIS dataBlock is " @ %this.getDataBlock().dataBlock);
echo("OBJ dataBlock is " @ %obj.dataBlock);//The vehicle
echo("getEnergyLevel is " @ %this.getEnergyLevel());
echo("getEnergyValue is " @ %this.getEnergyValue());
echo("getDamageState is " @ %obj.getDamageState());//enablebed
echo("getDamageLevel is " @ %obj.getDamageLevel());//the amount of damage taken
echo("MaxDamage is " @ %this.maxDamage);//amount of damage it can take
%healthLeft = %this.maxDamage - %damage;
echo("The Vehicles's health is " @ %healthLeft);
//if (%damage >= %this.destroyedLevel)
//GUI should show maxDamage - damageLevel
if(%damage >= %this.maxDamage)
{
if(%obj.getDamageState() !$= "Destroyed")
{
if(%obj.respawnTime !$= "")
%obj.marker.schedule = %obj.marker.data.schedule(%obj.respawnTime, "respawn", %obj.marker);
%obj.setDamageState(Destroyed);
}
}
else
{
if(%obj.getDamageState() !$= "Enabled")
%obj.setDamageState(Enabled);
}
}
function VehicleData::onImpact(%data, %vehicleObject, %collidedObject, %vec, %vecLen)
{
if(%vecLen > %data.minImpactSpeed)
%data.damageObject(%vehicleObject, 0, VectorAdd(%vec, %vehicleObject.getPosition()),
%vecLen * %data.speedDamageScale, $DamageType::Ground);
// associated "crash" sounds
if(%vecLen > %vDataBlock.hardImpactSpeed)
%vehicleObject.playAudio(0, %vDataBlock.hardImpactSound);
else if(%vecLen > %vDataBlock.softImpactSpeed)
%vehicleObject.playAudio(0, %vDataBlock.softImpactSound);
}
function VehicleData::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType, %momVec, %theClient, %proj)
{
//{ echo("you are damaging an object");
// if(%proj !$= "")
// {
// if(%amount > 0 && %targetObject.lastDamageProj !$= %proj)
// {
// %targetObject.lastDamageProj = %proj;
// %targetObject.lastDamageAmount = %amount;
// }
// else if(%targetObject.lastDamageAmount < %amount)
// %amount = %amount - %targetObject.lastDamageAmount;
// else
// return;
// }
// check for team damage
// %sourceClient = %sourceObject ? %sourceObject.getOwnerClient() : 0;
// %targetTeam = getTargetSensorGroup(%targetObject.getTarget());
// if(%sourceClient)
// %sourceTeam = %sourceClient.getSensorGroup();
// else if(isObject(%sourceObject) && %sourceObject.getClassName() $= "Turret")
// %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget());
// else
// %sourceTeam = %sourceObject ? getTargetSensorGroup(%sourceObject.getTarget()) : -1;
// vehicles no longer obey team damage -JR
// if(!$teamDamage && (%targetTeam == %sourceTeam) && %targetObject.getDamagePercent() > 0.5)
// return;
//but we do want to track the destroyer
// if(%sourceObject)
// {
// %targetObject.lastDamagedBy = %sourceObject;
// %targetObject.lastDamageType = %damageType;
// }
// else
// %targetObject.lastDamagedBy = 0;
// Scale damage type & include shield calculations...
// if (%data.isShielded)
// %amount = %data.checkShields(%targetObject, %position, %amount, %damageType);
// %damageScale = %data.damageScale[%damageType];
// if(%damageScale !$= "")
// %amount *= %damageScale;
// if(%amount != 0)
// %targetObject.applyDamage(%amount);
// if(%targetObject.getDamageState() $= "Destroyed" )
// {
// if( %momVec !$= "")
// %targetObject.setMomentumVector(%momVec);
// }
}
//**************************************************************
//* PLAYER FUNCTIONS
//**************************************************************
//PLAYER: Dismount
function VehicleData::playerDismounted(%data, %obj, %player)
{
if( %player.client.observeCount > 0 )
resetObserveFollow( %player.client, true );
setTargetSensorGroup(%obj.getTarget(), %obj.team);
// if there is a turret, set its team as well.
if( %obj.turretObject > 0 )
setTargetSensorGroup(%obj.turretObject.getTarget(), %obj.team);
}
function VehicleData::onEnterLiquid(%data, %obj, %coverage, %type)
{
switch(%type)
{
case 0:
//Water
%obj.setHeat(0.0);
case 1:
//Ocean Water
%obj.setHeat(0.0);
case 2:
//River Water
%obj.setHeat(0.0);
case 3:
//Stagnant Water
%obj.setHeat(0.0);
case 4:
//Lava
%obj.liquidDamage(%data, $VehicleDamageLava, $DamageType::Lava);
case 5:
//Hot Lava
%obj.liquidDamage(%data, $VehicleDamageHotLava, $DamageType::Lava);
case 6:
//Crusty Lava
%obj.liquidDamage(%data, $VehicleDamageCrustyLava, $DamageType::Lava);
case 7:
//Quick Sand
}
}
function VehicleData::onLeaveLiquid(%data, %obj, %type)
{
switch(%type)
{
case 0:
//Water
%obj.setHeat(1.0);
case 1:
//Ocean Water
%obj.setHeat(1.0);
case 2:
//River Water
%obj.setHeat(1.0);
case 3:
//Stagnant Water
%obj.setHeat(1.0);
case 4:
//Lava
case 5:
//Hot Lava
case 6:
//Crusty Lava
case 7:
//Quick Sand
}
if(%obj.lDamageSchedule !$= "")
{
cancel(%obj.lDamageSchedule);
%obj.lDamageSchedule = "";
}
}
function FlyingVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType)
{
if(%obj.getDamageState() !$= "Destroyed")
{
%data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType);
%obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType);
passengerLiquidDamage(%obj, %damageAmount, %damageType);
}
else
%obj.lDamageSchedule = "";
}
function WheeledVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType)
{
if(%obj.getDamageState() !$= "Destroyed")
{
%data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType);
%obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType);
passengerLiquidDamage(%obj, %damageAmount, %damageType);
}
else
%obj.lDamageSchedule = "";
}
function HoverVehicle::liquidDamage(%obj, %data, %damageAmount, %damageType)
{
if(%obj.getDamageState() !$= "Destroyed")
{
%data.damageObject(%obj, 0, "0 0 0", %damageAmount, %damageType);
%obj.lDamageSchedule = %obj.schedule(50, "liquidDamage", %data, %damageAmount, %damageType);
passengerLiquidDamage(%obj, %damageAmount, %damageType);
}
else
%obj.lDamageSchedule = "";
}
function assengerLiquidDamage(%obj, %damageAmount, %damageType)
{
for(%i = %num; %i < %obj.getDataBlock().numMountPoints; %i++)
if (%p = %obj.getMountNodeObject(%i))
%p.liquidDamage(%p.getDatablock(), $DamageLava, $DamageType::Lava);
}
function VehicleData::onDestroyed(%data, %obj, %prevState)
{
if(%obj.lastDamagedBy)
{
%destroyer = %obj.lastDamagedBy;
game.vehicleDestroyed(%obj, %destroyer);
//error("vehicleDestroyed( "@ %obj @", "@ %destroyer @")");
}
radiusVehicleExplosion(%data, %obj);
if(%obj.turretObject)
if(%obj.turretObject.getControllingClient())
%obj.turretObject.getDataBlock().playerDismount(%obj.turretObject);
for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++)
{
if (%obj.getMountNodeObject(%i)) {
%flingee = %obj.getMountNodeObject(%i);
%flingee.getDataBlock().doDismount(%flingee, true);
%xVel = 250.0 - (getRandom() * 500.0);
%yVel = 250.0 - (getRandom() * 500.0);
%zVel = (getRandom() * 100.0) + 50.0;
%flingVel = %xVel @ " " @ %yVel @ " " @ %zVel;
%flingee.applyImpulse(%flingee.getTransform(), %flingVel);
echo("got player..." @ %flingee.getClassName());
%flingee.damage(0, %obj.getPosition(), 0.4, $DamageType::Crash);
}
}
%data.deleteAllMounted(%obj);
%obj.schedule(2000, "delete");
}
function radiusVehicleExplosion(%data, %vehicle)
{
// this is a modified version of RadiusExplosion() from projectiles.cs
%position = %vehicle.getPosition();
InitContainerRadiusSearch(%position, %data.explosionRadius, $TypeMasks::PlayerObjectType |
$TypeMasks::VehicleObjectType |
$TypeMasks::MoveableObjectType |
$TypeMasks::StaticShapeObjectType |
$TypeMasks::ForceFieldObjectType |
$TypeMasks::TurretObjectType |
$TypeMasks::ItemObjectType);
%numTargets = 0;
while ((%targetObject = containerSearchNext()) != 0)
{
if(%targetObject == %vehicle)
continue;
%dist = containerSearchCurrRadDamageDist();
if (%dist > %data.explosionRadius)
continue;
if (%targetObject.isMounted())
{
%mount = %targetObject.getObjectMount();
if(%mount == %vehicle)
continue;
%found = -1;
for (%i = 0; %i < %mount.getDataBlock().numMountPoints; %i++)
{
if (%mount.getMountNodeObject(%i) == %targetObject)
{
%found = %i;
break;
}
}
if (%found != -1)
{
if (%mount.getDataBlock().isProtectedMountPoint[%found] && (%mount != %vehicle))
continue;
}
}
%targets[%numTargets] = %targetObject;
%targetDists[%numTargets] = %dist;
%numTargets++;
}
for (%i = 0; %i < %numTargets; %i++)
{
%targetObject = %targets[%i];
%dist = %targetDists[%i];
%coverage = calcExplosionCoverage(%position, %targetObject,
($TypeMasks::InteriorObjectType |
$TypeMasks::TerrainObjectType |
$TypeMasks::ForceFieldObjectType));
if (%coverage == 0)
continue;
%amount = (1.0 - (%dist / %data.explosionRadius)) * %coverage * %data.explosionDamage;
%targetData = %targetObject.getDataBlock();
%momVec = "0 0 1";
if(%amount > 0)
%targetData.damageObject(%targetObject, %sourceObject, %position, %amount, $DamageType::Explosion, %momVec);
}
}
function VehicleData::deleteAllMounted()
{
// urm...
};
//**************************************************************
//* VEHICLE CREATION
//**************************************************************
function HoverVehicle::useCreateHeight()
{
//this function is declared to prevent console error msg spam...
}
function WheeledVehicle::useCreateHeight()
{
//this function is declared to prevent console error msg spam...
}
//**************************************************************
//* MULTI-CREW VEHICLE DELETION
//**************************************************************
//----------------------------
// THUNDERSWORD BOMBER
//----------------------------
function BomberFlyer::deleteAllMounted(%data, %obj)
{
if(isObject(%obj.beacon))
%obj.beacon.delete();
%turret = %obj.getMountNodeObject(10);
if(!%turret)
return;
%turret.altTrigger = 0;
%turret.fireTrigger = 0;
if(%client = %turret.getControllingClient())
{
commandToClient(%client, 'endBomberSight');
%client.player.setControlObject(%client.player);
%client.player.mountImage(%client.player.lastWeapon, $WeaponSlot);
%client.player.mountVehicle = false;
%client.player.bomber = false;
%client.player.isBomber = false;
}
%turret.delete();
}
//**************************************************************
//* WEAPON MOUNTING ON VEHICLES
//**************************************************************
//----------------------------
// SKY HAWK FLIER
//----------------------------
function buildPassengerString(%vehicle)
{
%passStr = "";
for(%i = 0; %i < %vehicle.getDatablock().numMountPoints; %i++)
{
if(%vehicle.getMountNodeObject(%i) > 0)
%passStr = %passStr @ "1 ";
else
%passStr = %passStr @ "0 ";
}
return %passStr;
}
function WheeledVehicle::deployVehicle(%obj, %data, %player)
{
if (!%data.vehicleDeploy(%obj, %player))
%obj.schedule(500, "deployVehicle", %data, %player);
}
//**************************************************************
//* VEHICLE INVENTORY MANAGEMENT
//**************************************************************
//--------------------------------------------------------------
// NUMBER OF PURCHASEABLE VEHICLES PER TEAM
//--------------------------------------------------------------
$VehicleRespawnTime = 15000;
$Vehiclemax[ScoutVehicle] = 4;
$VehicleMax[AssaultVehicle] = 3;
$VehicleMax[MobileBaseVehicle] = 1;
$VehicleMax[ScoutFlyer] = 4;
$VehicleMax[BomberFlyer] = 2;
$VehicleMax[HAPCFlyer] = 2;
function vehicleListRemove(%data, %obj)
{
%blockName = %data.getName();
for($i = 0; %i < $VehicleMax[%blockName]; %i++)
if($VehicleInField[%obj.team, %blockName, %i] == %obj)
{
$VehicleInField[%obj.team, %blockName, %i] = 0;
$VehicleTotalCount[%obj.team, %blockName]--;
break;
}
}
}
function vehicleListAdd(%blockName, %obj)
{
for($i = 0; %i < $VehicleMax[%blockName]; %i++)
{
if($VehicleInField[%obj.team, %blockName, %i] $= "" || $VehicleInField[%obj.team, %blockName, %i] == 0)
{
$VehicleInField[%obj.team, %blockName, %i] = %obj;
$VehicleTotalCount[%obj.team, %blockName]++;
break;
}
}
}
function clearVehicleCount(%team)
{
$VehicleTotalCount[%team, ScoutVehicle] = 0;
$VehicleTotalCount[%team, AssaultVehicle] = 0;
$VehicleTotalCount[%team, MobileBaseVehicle] = 0;
$VehicleTotalCount[%team, ScoutFlyer] = 0;
$VehicleTotalCount[%team, BomberFlyer] = 0;
$VehicleTotalCount[%team, HAPCFlyer] = 0;
}
//**************************************************************
//* VEHICLE HUD - SEAT - INDICATOR LIGHTS
//**************************************************************
function findEmptySeat(%vehicle, %vehicleblock)
{
for (%i = 0; %i < %vehicleblock.numMountPoints; %i++)
{
%node = %vehicle.getMountNodeObject(%i);
if (%node == 0)
{
return %i;
}
}
return -1;
}
function findAIEmptySeat(%vehicle)
{
%num = 0;
%node = -1;
for(%i = %num; %i < %vehicle.numMountPoints; %i++)
{
if (!%vehicle.getMountNodeObject(%i))
{
//cheap hack - for now, will mount the next available node regardless of where they collided
%node = %i;
break;
}
}
//return the empty seat
return %node;
}
function findFirstHeavyNode(%data)
{
for(%i = 0; %i < %data.numMountPoints; %i++)
if(%data.mountPose[%i] $= "")
return %i;
return %data.numMountPoints;
}
//**************************************************************
//* VEHICLE TIMEOUTS
//**************************************************************
function vehicleAbandonTimeOut(%vehicle)
{
if(%vehicle.getDatablock().cantAbandon $= "" && %vehicle.lastPilot $= "")
{
for(%i = 0; %i < %vehicle.getDatablock().numMountPoints; %i++)
if (%vehicle.getMountNodeObject(%i))
{
%passenger = %vehicle.getMountNodeObject(%i);
if(%passenger.lastVehicle !$= "")
schedule(15000, %passenger.lastVehicle,"vehicleAbandonTimeOut", %passenger.lastVehicle);
%passenger.lastVehicle = %vehicle;
%vehicle.lastPilot = %passenger;
return;
}
if(%vehicle.respawnTime !$= "")
%vehicle.marker.schedule = %vehicle.marker.data.schedule(%vehicle.respawnTime, "respawn", %vehicle.marker);
%vehicle.mountable = 0;
%vehicle.startFade(1000, 0, true);
%vehicle.schedule(1001, "delete");
}
}
}
//**************************************************************
//* VEHICLE CREATE
//**************************************************************
function HoverVehicleData::create(%block, %team, %oldObj)
{
if(%oldObj $= "")
{
%obj = new HoverVehicle()
{
dataBlock = %block;
respawn = "0";
teamBought = %team;
team = %team;
};
}
else
{
%obj = new HoverVehicle()
{
dataBlock = %data;
respawn = "0";
teamBought = %team;
team = %team;
mountable = %oldObj.mountable;
disableMove = %oldObj.disableMove;
resetPos = %oldObj.resetPos;
respawnTime = %oldObj.respawnTime;
marker = %oldObj;
};
}
return(%obj);
}
function WheeledVehicleData::create(%data, %team, %oldObj)
{
if(%oldObj $= "")
{
%obj = new WheeledVehicle()
{
dataBlock = %data;
respawn = "0";
teamBought = %team;
team = %team;
};
}
else
{
%obj = new WheeledVehicle()
{
dataBlock = %data;
respawn = "0";
teamBought = %team;
team = %team;
mountable = %oldObj.mountable;
disableMove = %oldObj.disableMove;
resetPos = %oldObj.resetPos;
deployed = %oldObj.deployed;
respawnTime = %oldObj.respawnTime;
marker = %oldObj;
};
}
return(%obj);
}
function FlyingVehicleData::create(%data, %team, %oldObj)
{
if(%oldObj $= "")
{
%obj = new FlyingVehicle()
{
dataBlock = %data;
respawn = "0";
teamBought = %team;
team = %team;
};
}
else
{
%obj = new FlyingVehicle()
{
dataBlock = %data;
teamBought = %team;
team = %team;
mountable = %oldObj.mountable;
disableMove = %oldObj.disableMove;
resetPos = %oldObj.resetPos;
respawnTime = %oldObj.respawnTime;
marker = %oldObj;
};
}
return(%obj);
}
function FlyingVehicleData::switchSidesSetPos(%data, %oldObj)
{
%team = %oldObj.curTeam == 1 ? 2 : 1;
%oldObj.curTeam = %team;
%obj = new FlyingVehicle()
{
dataBlock = %data;
teamBought = %team;
team = %team;
mountable = %oldObj.mountable;
disableMove = %oldObj.disableMove;
resetPos = %oldObj.resetPos;
respawnTime = %oldObj.respawnTime;
marker = %oldObj;
};
%obj.setTransform(%oldObj.getTransform());
return(%obj);
}
function WheeledVehicleData::switchSidesSetPos(%data, %oldObj)
{
%team = %oldObj.curTeam == 1 ? 2 : 1;
%oldObj.curTeam = %team;
%obj = new WheeledVehicle()
{
dataBlock = %data;
respawn = "0";
teamBought = %team;
team = %team;
mountable = %oldObj.mountable;
disableMove = %oldObj.disableMove;
resetPos = %oldObj.resetPos;
deployed = %oldObj.deployed;
respawnTime = %oldObj.respawnTime;
marker = %oldObj;
};
%obj.setTransform(%oldObj.getTransform());
return(%obj);
}
function HoverVehicleData::switchSidesSetPos(%data, %oldObj)
{
%team = %oldObj.curTeam == 1 ? 2 : 1;
%oldObj.curTeam = %team;
%obj = new HoverVehicle()
{
dataBlock = %data;
respawn = "0";
teamBought = %team;
team = %team;
mountable = %oldObj.mountable;
disableMove = %oldObj.disableMove;
resetPos = %oldObj.resetPos;
respawnTime = %oldObj.respawnTime;
marker = %oldObj;
};
%obj.setTransform(%oldObj.getTransform());
return(%obj);
}
function resetNonStaticObjPositions()
{
MissionGroup.setupPositionMarkers(false);
MissionCleanup.positionReset();
}
function next(%team)
{
ResetObjsPositions(%team);
}
function SimGroup::positionReset(%group)
{
for(%i = %group.getCount() - 1; %i >=0 ; %i--)
{
%obj = %group.getObject(%i);
if(%obj.resetPos && %obj.getName() !$= PosMarker)
%obj.delete();
else
%obj.positionReset();
}
for(%i = 0; %i < %group.getCount(); %i++)
{
%obj = %group.getObject(%i);
if(%obj.getName() $= PosMarker)
{
cancel(%obj.schedule);
%newObj = %obj.data.switchSidesSetPos(%obj);
MissionCleanup.add(%newObj);
setTargetSensorGroup(%newObj.target, %newObj.team);
}
else
%obj.positionReset();
}
}
function VehicleData::respawn(%data, %marker)
{
%mask = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::TurretObjectType;
InitContainerRadiusSearch(%marker.getWorldBoxCenter(), %data.checkRadius, %mask);
if(containerSearchNext() == 0)
{
%newObj = %data.create(%marker.curTeam, %marker);
%newObj.startFade(1000, 0, false);
%newObj.setTransform(%marker.getTransform());
setTargetSensorGroup(%newObj.target, %newObj.team);
MissionCleanup.add(%newObj);
}
else
{
%marker.schedule = %data.schedule(3000, "respawn", %marker);
}
}
function SimObject::positionReset(%group, %team)
{
//Used to avoid warnings
}
function Terraformer::positionReset(%group, %team)
{
//Used to avoid warnings
}
function SimGroup::setupPositionMarkers(%group, %create)
{
for(%i = %group.getCount() - 1; %i >= 0; %i--)
{
%obj = %group.getObject(%i);
if(%obj.resetPos || %obj.respawnTime !$= "")
{
if(%create)
{
%marker = %obj.getDataBlock().createPositionMarker(%obj);
MissionCleanup.add(%marker);
%obj.marker = %marker;
}
else
{
%obj.delete();
}
}
else
%obj.setupPositionMarkers(%create);
}
}
function SimObject::setupPositionMarkers(%group, %create)
{
//Used to avoid warnings
}
function VehicleData::createPositionMarker(%data, %obj)
{
%marker = new Trigger(PosMarker)
{
dataBlock = markerTrigger;
mountable = %obj.mountable;
disableMove = %obj.disableMove;
resetPos = %obj.resetPos;
data = %obj.getDataBlock().getName();
deployed = %obj.deployed;
curTeam = %obj.team;
respawnTime = %obj.respawnTime;
};
%marker.setTransform(%obj.getTransform());
return %marker;
}
function VehicleData::hasDismountOverrides(%data, %obj)
{
return false;
}



