
//**************************************************************
// THUNDERSWORD BOMBER
//**************************************************************
//**************************************************************
// SOUNDS
//**************************************************************

datablock AudioProfile(BomberBombSound)
{
   filename    = "fx/vehicles/bomber_bomb_projectile.wav";
   description = AudioClose3d;
   preload = true;
};

datablock AudioProfile(BomberFlyerEngineSound)
{
   filename    = "fx/vehicles/bomber_engine.wav";
   description = AudioDefaultLooping3d;
   preload = true;
};

datablock AudioProfile(BomberFlyerThrustSound)
{
   filename    = "fx/vehicles/bomber_boost.wav";
   description = AudioDefaultLooping3d;
   preload = true;
};

datablock AudioProfile(FusionExpSound)
// Sound played when mortar impacts on target
{
   filename    = "fx/powered/turret_mortar_explode.wav";
   description = "AudioBIGExplosion3d";
   preload = true;
};

datablock AudioProfile(BomberTurretFireSound)
{
   filename    = "fx/vehicles/bomber_turret_fire.wav";
   description = AudioClose3d;
   preload = true;
};

datablock AudioProfile(BomberTurretActivateSound)
{
   filename    = "fx/vehicles/bomber_turret_activate.wav";
   description = AudioClose3d;
   preload = true;
};

datablock AudioProfile(BomberTurretReloadSound)
{
   filename    = "fx/vehicles/bomber_turret_reload.wav";
   description = AudioClose3d;
   preload = true;
};

datablock AudioProfile(BomberTurretIdleSound)
{
   filename    = "fx/misc/diagnostic_on.wav";
   description = ClosestLooping3d;
   preload = true;
};

datablock AudioProfile(BomberTurretDryFireSound)
{
   filename    = "fx/vehicles/bomber_turret_dryfire.wav";
   description = AudioClose3d;
   preload = true;
};
   
datablock AudioProfile(BomberBombReloadSound)
{
   filename    = "fx/vehicles/bomber_bomb_reload.wav";
   description = AudioClose3d;
   preload = true;
};

datablock AudioProfile(BomberBombProjectileSound)
{
   filename    = "fx/vehicles/bomber_bomb_projectile.wav";
   description = AudioDefaultLooping3d;
   preload = true;
};

datablock AudioProfile(BomberBombDryFireSound)
{
   filename    = "fx/vehicles/bomber_bomb_dryfire.wav";
   description = AudioClose3d;
   preload = true;
};

datablock AudioProfile(BomberBombFireSound)
{
   filename    = "fx/vehicles/bomber_bomb_reload.wav";
   description = AudioClose3d;
   preload = true;
};

datablock AudioProfile(BomberBombIdleSound)
{
   filename    = "fx/misc/diagnostic_on.wav";
   description = ClosestLooping3d;
   preload = true;
};

//**************************************************************
// VEHICLE CHARACTERISTICS
//**************************************************************

datablock FlyingVehicleData(BomberFlyer) : BomberDamageProfile
{
   spawnOffset = "0 0 2";

   catagory = "Vehicles";
   shapeFile = "vehicle_air_bomber.dts";
   multipassenger = true;
   computeCRC = true;

   weaponNode = 1;

   renderWhenDestroyed = false;

   debrisBase = 8;
   debrisVariance = 8;
   debrisShapeName = "vehicle_air_bomber_debris.dts";
//   debris = ShapeDebris;
   debrisEjectionScale = 1.6;

   drag    = 0.2;
   density = 1.0;

   mountPose[0] = sitting;
   mountPose[1] = sitting;
   numMountPoints = 3;  

   isProtectedMountPoint[0] = true;
   isProtectedMountPoint[1] = true;
   isProtectedMountPoint[2] = true;

   cameraDefaultFov = 90.0;
   cameraMinFov = 5.0;
   cameraMaxFov = 120.0;
   cameraMaxDist = 22;
   cameraOffset = 5;
   cameraLag = 1.0;

   explosion = LargeAirVehicleExplosion;
   explosionDamage = 2.0;
   explosionRadius = 28.0;
   explosionImpulse = 3500;

   maxDamage = 2.80;     // Total health
   destroyedLevel = 2.80;   // Damage textures show up at this health level

   isShielded = true;
   energyPerDamagePoint = 120;

   maxEnergy = 400;      // Afterburner and any energy weapon pool
   minDrag = 60;           // Linear Drag (eventually slows you down when not thrusting...constant drag)

   rotationalDrag = 1800;        // Angular Drag (dampens the drift after you stop moving the mouse...also tumble drag)
   rechargeRate = 0.7;

   // Auto stabilize speed
   maxAutoSpeed = 18;       // Autostabilizer kicks in when less than this speed. (meters/second)
   autoAngularForce = 1200;      // Angular stabilizer force (this force levels you out when autostabilizer kicks in)
   autoLinearForce = 300;        // Linear stabilzer force (this slows you down when autostabilizer kicks in)
   autoInputDamping = 0.95;      // Dampen control input so you don't whack out at very slow speeds
   
   // Maneuvering
   maxSteeringAngle = 4.8;    // Max radiens you can rotate the wheel. Smaller number is more maneuverable.
   horizontalSurfaceForce = 5;   // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning)
   verticalSurfaceForce = 8;     // Vertical center "wing" (controls side slip. lower numbers make MORE slide.)
   maneuveringForce = 4700;      // Horizontal jets (W,S,D,A key thrust)

   steeringForce = 1100;         // Steering jets (force applied when you move the mouse)
   steeringRollForce = 300;      // Steering jets (how much you heel over when you turn)

   rollForce = 8;                // Auto-roll (self-correction to right you after you roll/invert)
   hoverHeight = 5;        // Height off the ground at rest

   createHoverHeight = 3;  // Height off the ground when created
   maxForwardSpeed = 1000;

   // Turbo Jet
   jetForce = 3000;      // Afterburner thrust (this is in addition to normal thrust)
   minJetEnergy = 44.0;     // Afterburner can't be used if below this threshhold.
   jetEnergyDrain = 2.8;       // Energy use of the afterburners (low number is less drain...can be fractional)
   vertThrustMultiple = 2.2;

   dustEmitter = LargeVehicleLiftoffDustEmitter;
   triggerDustHeight = 4.0;
   dustHeight = 2.0;

   ELFDrain  = 2.2;

   damageEmitter[0] = LightDamageSmoke;
   damageEmitter[1] = HeavyDamageSmoke;
   damageEmitter[2] = DamageBubbles;
   damageEmitterOffset[0] = "3.0 -3.0 0.0 ";
   damageEmitterOffset[1] = "-3.0 -3.0 0.0 ";
   damageLevelTolerance[0] = 0.3;
   damageLevelTolerance[1] = 0.7;
   numDmgEmitterAreas = 2;

   // Rigid body
   mass = 275;        // Mass of the vehicle
   bodyFriction = 0;     // Don't mess with this.
   bodyRestitution = 0.5;   // When you hit the ground, how much you rebound. (between 0 and 1)
   minRollSpeed = 0;     // Don't mess with this.
   softImpactSpeed = 20;       // Sound hooks. This is the soft hit.
   hardImpactSpeed = 25;    // Sound hooks. This is the hard hit.

   // Ground Impact Damage (uses DamageType::Ground)
   minImpactSpeed = 20;      // If hit ground at speed above this then it's an impact. Meters/second
   speedDamageScale = 0.060;

   // Object Impact Damage (uses DamageType::Impact)
   collDamageThresholdVel = 25;
   collDamageMultiplier   = 0.020;

   //
   minTrailSpeed = 15;      // The speed your contrail shows up at.
   trailEmitter = ContrailEmitter;
   forwardJetEmitter = FlyerJetEmitter;
   downJetEmitter = FlyerJetEmitter;

   //
   jetSound = BomberFlyerThrustSound;
   engineSound = BomberFlyerEngineSound;
   softImpactSound = SoftImpactSound;
   hardImpactSound = HardImpactSound;
   //wheelImpactSound = WheelImpactSound;

   //
   softSplashSoundVelocity = 15.0; 
   mediumSplashSoundVelocity = 20.0;   
   hardSplashSoundVelocity = 30.0;   
   exitSplashSoundVelocity = 10.0;
   
   exitingWater      = VehicleExitWaterHardSound;
   impactWaterEasy   = VehicleImpactWaterSoftSound;
   impactWaterMedium = VehicleImpactWaterMediumSound;
   impactWaterHard   = VehicleImpactWaterHardSound;
   waterWakeSound    = VehicleWakeHardSplashSound; 
  
   minMountDist = 4;

   splashEmitter[0] = VehicleFoamDropletsEmitter;
   splashEmitter[1] = VehicleFoamEmitter;

   shieldImpact = VehicleShieldImpact;

   cmdCategory = "Tactical";
   cmdIcon = CMDFlyingBomberIcon;
   cmdMiniIconName = "commander/MiniIcons/com_bomber_grey";

   targetNameTag = 'Thundersword';
   targetTypeTag = 'Bomber';
   sensorData = VehiclePulseSensor;

   checkRadius = 7.1895;
   observeParameters = "1 10 10";
   shieldEffectScale = "0.75 0.975 0.375";
   showPilotInfo = 1;
};

//**************************************************************
// WEAPONS
//**************************************************************

//-------------------------------------
// BOMBER BELLY TURRET GUN (projectile)
//-------------------------------------

datablock ShockwaveData(BomberFusionShockwave)
{
   width = 0.5;
   numSegments = 13;
   numVertSegments = 1;
   velocity = 0.5;
   acceleration = 2.0;
   lifetimeMS = 900;
   height = 0.1;
   verticalCurve = 0.5;

   mapToTerrain = false;
   renderBottom = false;
   orientToNormal = true;

   texture[0] = "special/shockwave5";
   texture[1] = "special/gradient";
   texWrap = 3.0;

   times[0] = 0.0;
   times[1] = 0.5;
   times[2] = 1.0;

   colors[0] = "0.6 0.6 1.0 1.0";
   colors[1] = "0.6 0.3 1.0 0.5";
   colors[2] = "0.0 0.0 1.0 0.0";
};

datablock ParticleData(BomberFusionExplosionParticle1)
{
   dragCoefficient      = 2;
   gravityCoefficient   = 0.0;
   inheritedVelFactor   = 0.2;
   constantAcceleration = -0.0;
   lifetimeMS           = 600;
   lifetimeVarianceMS   = 000;
   textureName          = "special/crescent4";
   colors[0] = "0.6 0.6 1.0 1.0";
   colors[1] = "0.6 0.3 1.0 1.0";
   colors[2] = "0.0 0.0 1.0 0.0";
   sizes[0]      = 0.25;
   sizes[1]      = 0.5;
   sizes[2]      = 1.0;
   times[0]      = 0.0;
   times[1]      = 0.5;
   times[2]      = 1.0;
};

datablock ParticleEmitterData(BomberFusionExplosionEmitter)
{
   ejectionPeriodMS = 7;
   periodVarianceMS = 0;
   ejectionVelocity = 2;
   velocityVariance = 1.5;
   ejectionOffset   = 0.0;
   thetaMin         = 80;
   thetaMax         = 90;
   phiReferenceVel  = 0;
   phiVariance      = 360;
   overrideAdvances = false;
   orientParticles  = true;
   lifetimeMS       = 200;
   particles = "BomberFusionExplosionParticle1";
};

datablock ExplosionData(BomberFusionBoltExplosion)
{
   soundProfile   = blasterExpSound;
   shockwave      = BomberFusionShockwave;
   emitter[0]     = BomberFusionExplosionEmitter;
};


datablock LinearFlareProjectileData(BomberFusionBolt)
{
   projectileShapeName = "";
   directDamage        = 0.35;
   directDamageType    = $DamageType::Energy;
   hasDamageRadius     = false;
   explosion           = BomberFusionBoltExplosion;
   sound               = BlasterProjectileSound;

   dryVelocity       = 200.0;
   wetVelocity       = 200.0;
   velInheritFactor  = 1.0;
   fizzleTimeMS      = 2000;
   lifetimeMS        = 3000;
   explodeOnDeath    = false;
   reflectOnWaterImpactAngle = 0.0;
   explodeOnWaterImpact      = true;
   deflectionOnWaterImpact   = 0.0;
   fizzleUnderwaterMS        = -1;

   activateDelayMS = 100;

   numFlares         = 0;
   size              = 0.15;
   flareColor        = "0.7 0.3 1.0";
   flareModTexture   = "flaremod";
   flareBaseTexture  = "flarebase";
};

//-------------------------------------
// BOMBER BELLY TURRET CHARACTERISTICS
//-------------------------------------

datablock TurretData(BomberTurret) : TurretDamageProfile
{
   className      = VehicleTurret;
   catagory       = "Turrets";
   shapeFile      = "turret_belly_base.dts";
   preload        = true;

   mass           = 1.0;  // Not really relevant
   repairRate     = 0;
   maxDamage      = BomberFlyer.maxDamage;
   destroyedLevel = BomberFlyer.destroyedLevel;

   thetaMin = 90;
   thetaMax = 180;
   
   inheritEnergyFromMount = true;
   firstPersonOnly = true;
   useEyePoint = true;
   numWeapons = 3;

   reloadSound = BomberBombReloadSound;

   targetNameTag = 'Thundersword Belly';
   targetTypeTag = 'Turret';
};

datablock TurretImageData(BomberTurretBarrelPair)
{
   shapeFile      = "turret_belly_barrell.dts";
   mountPoint = 0;

   projectile = BomberFusionBolt;
   projectileType = LinearFlareProjectile;

   usesEnergy = true;
   useMountEnergy = true;
   fireEnergy = 6.25;
   minEnergy = 25.0;

   // Turret parameters
   activationMS      = 1000;
   deactivateDelayMS = 1500;
   thinkTimeMS       = 200;
   degPerSecTheta    = 360;
   degPerSecPhi      = 360;
   
   attackRadius      = 75;

   // State transitions
   stateName[0]                = "Activate";
   stateTransitionOnTimeout[0] = "WaitFire";
   stateTimeoutValue[0]        = 0.5;
   stateSequence[0]            = "Activate";
   stateSound[0]           = BomberTurretActivateSound;

   stateName[1] = "WaitFire";
   stateTransitionOnTriggerDown[1] = "InitFire";
   stateTransitionOnNoAmmo[1]      = "NoAmmo";
//   stateSound[1]                   = BomberTurretIdleSound;

   stateName[2] = "InitFire";
   stateWaitForTimeout[2]        = false;
   stateTransitionOnTimeout[2]   = "Fire";
 
   stateName[3]                = "Fire";
   stateTransitionOnTimeout[3] = "Reload";
   stateTimeoutValue[3]        = 0.35;
   stateFire[3]                = true;
   stateRecoil[3]              = LightRecoil;
   stateAllowImageChange[3]    = false;
   stateSequence[3]            = "Fire";
   stateScript[3]              = "onFire";
   stateSound[3]               = BomberTurretFireSound;

   stateName[4]                  = "Reload";
   stateTimeoutValue[4]          = 0.05;
   stateAllowImageChange[4]      = false;
   stateSequence[4]              = "Reload";
   stateTransitionOnTimeout[4]   = "WaitFire";
   stateTransitionOnNotLoaded[4] = "NoAmmo";
//   stateSound[4]                 = BomberTurretReloadSound;

   stateName[5]                    = "NoAmmo";
   stateTransitionOnAmmo[5]        = "Reload";
   stateSequence[5]                = "NoAmmo";
   stateTransitionOnTriggerDown[5] = "DryFire";

   stateName[6]                = "DryFire";
   stateSound[6]               = BomberTurretDryFireSound;
   stateTimeoutValue[6]        = 1.5;
   stateTransitionOnTimeout[6] = "NoAmmo";
   
};

datablock TurretImageData(BomberTurretBarrel) : BomberTurretBarrelPair 
{
   shapeFile      = "turret_belly_barrelr.dts";
   mountPoint = 1;
   stateScript[2]           = "onTriggerDown";
   stateScript[4]           = "onTriggerUp";
};

datablock TurretImageData(AIAimingTurretBarrel) 
{
   shapeFile = "turret_muzzlepoint.dts";
   mountPoint = 3;

   projectile = BomberFusionBolt;

   // Turret parameters
   activationMS      = 1000;
   deactivateDelayMS = 1500;
   thinkTimeMS       = 200;
   degPerSecTheta    = 500;
   degPerSecPhi      = 800;
   
   attackRadius      = 75;
};

datablock StaticShapeData(DropBombs)
{
   catagory       = "Turrets";
   shapeFile      = "bombers_eye.dts";
   maxDamage     = 1.0;
   disabledLevel  = 0.6;
   destroyedLevel = 0.8;
};

datablock ShapeBaseImageData(BomberBombImage)
{
   className = WeaponImage;
   shapeFile = "turret_muzzlepoint.dts";

   offset = "2 -4 0";
   mountPoint = 10;

   usesEnergy = true;
   useMountEnergy = true;

   fireEnergy = 1;
   minEnergy = 1;

   stateName[0] = "Activate";
   stateTransitionOnTimeout[0] = "WaitFire";
   stateTimeoutValue[0] = 0.5;
   stateSequence[0] = "Activate";

   stateName[1] = "ActivateReady";
   stateTransitionOnLoaded[1] = "WaitFire";

   stateName[2] = "WaitFire";
   stateTransitionOnNoAmmo[2] = "NoAmmo";
   stateTransitionOnTriggerDown[2] = "InitFire";
      
   stateName[3] = "InitFire";
   stateWaitForTimeout[3]        = false;
   stateTransitionOnTimeout[3]   = "Fire";
   
   stateName[4] = "Fire";
   stateTransitionOnTimeout[4] = "Reload";
   stateTimeoutValue[4] = 0.35;
   stateFire[4] = true;
   stateScript[4] = "onFire";

   stateName[5] = "Reload";
   stateTransitionOnNoAmmo[5] = "NoAmmo";
   stateTransitionOnTimeout[5] = "WaitFire";
   stateTimeoutValue[5] = 0.5;
   stateAllowImageChange[5] = false;
   stateSequence[5] = "Reload";
   
   stateName[6] = "NoAmmo";
   stateTransitionOnAmmo[6] = "Reload";
   stateSequence[6] = "NoAmmo";
   stateTransitionOnTriggerDown[6] = "DryFire";

   stateName[7]       = "DryFire";
   stateSound[7]      = BomberBombDryFireSound;
   stateTimeoutValue[7]        = 1.5;
   stateTransitionOnTimeout[7] = "NoAmmo";
   
};

//**************************************************************
// WEAPONS SPECIAL EFFECTS
//**************************************************************

//-------------------------------------
// BOMBER BELLY TURRET GUN (explosion)
//-------------------------------------

datablock ParticleData(FusionExplosionParticle)
{
   dragCoefficient      = 2;
   gravityCoefficient   = 0.2;
   inheritedVelFactor   = 0.2;
   constantAcceleration = 0.0;
   lifetimeMS           = 750;
   lifetimeVarianceMS   = 150;
   textureName          = "particleTest";
   colors[0]     = "0.56 0.36 0.26 1.0";
   colors[1]     = "0.56 0.36 0.26 0.0";
   sizes[0]      = 1;
   sizes[1]      = 2;
};

datablock ParticleEmitterData(FusionExplosionEmitter)
{
   ejectionPeriodMS = 7;
   periodVarianceMS = 0;
   ejectionVelocity = 12;
   velocityVariance = 1.75;
   ejectionOffset   = 0.0;
   thetaMin         = 0;
   thetaMax         = 60;
   phiReferenceVel  = 0;
   phiVariance      = 360;
   overrideAdvances = false;
   particles = "FusionExplosionParticle";
};

datablock ExplosionData(FusionBoltExplosion)
{
   explosionShape = "effect_plasma_explosion.dts";
   soundProfile   = FusionExpSound;
   particleEmitter = FusionExplosionEmitter;
   particleDensity = 250;
   particleRadius = 1.25;
   faceViewer = true;
};
                                    
//--------------------------------------------------------------------------
// BOMBER TARGETING LASER
//--------------------------------------------------------------------------

datablock AudioProfile(BomberTargetingSwitchSound)
{
   filename    = "fx/weapons/generic_switch.wav";
   description = AudioClosest3d;
   preload = true;
};

datablock AudioProfile(BomberTargetingPaintSound)
{
   filename    = "fx/weapons/targetinglaser_paint.wav";
   description = CloseLooping3d;
   preload = true;
};

//--------------------------------------
// BOMBER TARGETING PROJECTILE
//--------------------------------------
datablock TargetProjectileData(BomberTargeter)
{
   directDamage         = 0.0;
   hasDamageRadius      = false;
   indirectDamage       = 0.0;
   damageRadius         = 0.0;
   velInheritFactor     = 1.0;

   maxRifleRange        = 1000;
   beamColor            = "0.1 1.0 0.1";
                        
   startBeamWidth       = 0.20;
   pulseBeamWidth       = 0.15;
   beamFlareAngle       = 3.0;
   minFlareSize         = 0.0;
   maxFlareSize         = 400.0;
   pulseSpeed           = 6.0;
   pulseLength          = 0.150;

   textureName[0]       = "special/nonlingradient";
   textureName[1]       = "special/flare";
   textureName[2]       = "special/pulse";
   textureName[3]      	= "special/expFlare";
   beacon               = true;
   beaconType           = vehicle;
};

//-------------------------------------
// BOMBER TARGETING CHARACTERISTICS
//-------------------------------------
datablock ShapeBaseImageData(BomberTargetingImage)
{
   className                           = WeaponImage;

   shapeFile                           = "turret_muzzlepoint.dts";
   offset                              = "0 -0.04 -0.01";
   mountPoint                          = 2;

   projectile                          = BomberTargeter;
   projectileType                      = TargetProjectile;
   deleteLastProjectile                = true;

   usesEnergy                          = true;
   minEnergy                           = 3;

   stateName[0]                        = "Activate";
   stateSequence[0]                    = "Activate";
   stateSound[0]                       = BomberTargetingSwitchSound;
   stateTimeoutValue[0]                = 0.5;
   stateTransitionOnTimeout[0]         = "ActivateReady";

   stateName[1]                        = "ActivateReady";
   stateTransitionOnAmmo[1]            = "Ready";
   stateTransitionOnNoAmmo[1]          = "NoAmmo";

   stateName[2]                        = "Ready";
   stateTransitionOnNoAmmo[2]          = "NoAmmo";
   stateTransitionOnTriggerDown[2]     = "Fire";

   stateName[3]                        = "Fire";
   stateEnergyDrain[3]                 = 3;
   stateFire[3]                        = true;
   stateAllowImageChange[3]            = false;
   stateScript[3]                      = "onFire";
   stateTransitionOnTriggerUp[3]       = "Deconstruction";
   stateTransitionOnNoAmmo[3]          = "Deconstruction";
   stateSound[3]                       = BomberTargetingPaintSound;

   stateName[4]                        = "NoAmmo";
   stateTransitionOnAmmo[4]            = "Ready";

   stateName[5]                        = "Deconstruction";
   stateTransitionOnTimeout[5]         = "ActivateReady";
   stateTimeoutValue[5]                = 0.05;
};

function BomberTargetingImage::onFire(%Data,%Object,%Slot)
{
   %Vehicle = %Object.getObjectMount();
   if (%Vehicle.beacon !$= "")
   {
      %Vehicle.beacon.delete();
      %Vehicle.beacon = "";
   }

   %Projectile = Parent::onFire(%Data, %Object, %Slot);
   %Projectile.setTarget(%Object.team);
}

function BomberTargetingImage::deconstruct(%Data, %Object, %Slot)
{
   %Position = %Object.lastProjectile.getTargetPoint();
   %Vehicle = %Object.getObjectMount();
   
   if (%Vehicle.beacon !$= "")
      %Vehicle.beacon.delete();

   %Vehicle.beacon = new BeaconObject()
   {
      dataBlock = "BomberBeacon";
      beaconType = "vehicle";
      position = %Position;
   };

   %Vehicle.beacon.playThread($AmbientThread, "ambient");
   %Vehicle.beacon.team = %Vehicle.team;
   %Vehicle.beacon.sourceObject = %Vehicle;

   // give it a team target
   %Vehicle.beacon.setTarget(%Vehicle.team);                  
   BeaconGroup.add(%Vehicle.beacon);
   
   Parent::deconstruct(%Data, %Object, %Slot);
}

//-------------------------------------
// BOMBER BEACON
//-------------------------------------
datablock StaticShapeData(BomberBeacon)
{
   shapeFile = "turret_muzzlepoint.dts";
   targetNameTag = 'beacon';
   isInvincible = true;
   
   dynamicType = $TypeMasks::SensorObjectType;
};

// -------------------------------------------------------------------------------
// Standard Bomb
// -------------------------------------------------------------------------------
// Regular looking bomb that explodes on Contact with anything

datablock ItemData(BomberBomb)
{
   shapeFile = "bomb.dts";
   scale     = "1.0 1.0 1.0";

   mass = 0.95;
   elasticity = 0.0;
   friction = 1;
   drag = 0.3;

   sticky = true;

   hasDamageRadius     = true;
   indirectDamage      = 1.5;
   damageRadius        = 26;
   radiusDamageType    = $DamageType::Explosion;
   kickBackStrength    = 2750;

   explosion           = "VehicleBombExplosion";

   ReloadTime = 850;
   BombEnergy = 18;

   maxDamage = 0.3;
   emap = true;

   heatSignature = 1.0;
};

function BomberBomb::onAdd(%this, %Bomb) 
{
   Parent::onAdd(%this, %Bomb);

   // Altitude check
   %Bomb.AltitudeCheck = schedule(250, 0, CheckBombAltitude, %Bomb, 2.5);

   // We set a timing delay of 25 seconds that if it has not detonated
   // then we go ahead and detonate it now
   %Bomb.DetonationCheck = %Bomb.schedule(25000, setDamagedState, "Destroyed");
   %Bomb.setHeat(1);
}

function BomberBomb::onCollision(%Data, %Bomb, %ColidingObject)
{
   %Bomb.setDamageState(Destroyed);
}

function BomberBomb::onStickyCollision(%Data, %Bomb)
{
   %Bomb.setDamageState(Destroyed);
}

function BomberBomb::onDestroyed(%Data, %Bomb, %prevState)
{
   // If it was exploded this way already, ignore the second time
   if (%Bomb.detonated == true)
      return;

   // If it was not called by being set destroyed, do that now
   if (%Bomb.getDamageState() !$= "Destroyed")
      %Bomb.setDamageState(Destroyed);

   %Position = posFromTransform(%Bomb.getTransform());

   %Radius = %Data.damageRadius;
   %Damage = %Data.indirectDamage;
   %KickBack = %Data.kickBackStrength;
   %Owner = %Bomb.sourceObject;
   %DamageType = %Data.radiusDamageType;

   // Explosion Damage
   RadiusExplosion(%Bomb, %Position, %Radius, %Damage, %KickBack, %Owner, %DamageType);

   if (%Bomb.AltitudeCheck !$= "")
      cancel(%Bomb.AltitudeCheck);   

   %Bomb.detonated = true;
   %Bomb.schedule(750,"delete");
}

// -------------------------------------------------------------------------------
// Cluster Bomb
// -------------------------------------------------------------------------------
// Fat looking bomb that explodes with 6 bomblets, there is a pattern
// with a bit of randomness in it.  The bomblets are grenade launcher
// grenades and can bounce before they explode, being highly dangerous

datablock ItemData(ClusterBomb)
{
   shapeFile = "mortar_projectile.dts";
   scale     = "6 6 6";

   mass = 0.95;
   elasticity = 0.0;
   friction = 1;
   sticky = true;

   hasDamageRadius = false;
   explosion = "DeployablesExplosion";

   indirectDamage      = 0.40;
   damageRadius        = 15.0;
   radiusDamageType    = $DamageType::Explosion;
   kickBackStrength    = 1500;

   maxDamage = 0.3;
   emap = true;

   Altitude = 85;

   Bomblets = 8;
   BombletName = BasicGrenade;
   BombletType = GrenadeProjectile;

   ReloadTime = 1250;
      BombEnergy = 28;

   heatSignature = 1.0;
};

datablock ItemData(PrematureClusterBomb)
{
   shapeFile = "mortar_projectile.dts";
   scale     = "6 6 6";

   mass = 0.95;
   elasticity = 0.0;
   friction = 1;
   drag = 0.3;

   sticky = true;

   hasDamageRadius = true;
   indirectDamage      = 0.40;
   damageRadius        = 15.0;
   radiusDamageType    = $DamageType::Explosion;
   kickBackStrength    = 1500;

   explosion = "SatchelMainExplosion";

   maxDamage = 1000;
   emap = true;
};

function ClusterBomb::onAdd(%this, %Bomb) 
{
   Parent::onAdd(%this, %Bomb);

   // We tell it to arm itself 2 seconds after being dropped so that
   // it can be dropped into or just above water and function fine.
   %Bomb.schedule(2000, "Armed", true);

   // We start the bomb checking it's altitude to seperation 2.5 seconds
   // from being deployed
   %Altitude = %this.Altitude;
   %Bomb.AltitudeCheck = schedule(250, 0, CheckBombAltitude, %Bomb, %Altitude);

   // We set a timing delay of 25 seconds that if it has not detonated
   // then we go ahead and detonate it now, seperating it into it's
   // parts
   %Bomb.DetonationCheck = %Bomb.schedule(24750, "seperate", true);
   %Bomb.DetonationCheck = %Bomb.schedule(25000, setDamagedState, "Destroyed");

   %Bomb.setHeat(1);
}

// This function controls what happens when the bomb does not explode
// above it's target and instead hits something unexpectedly.  It forms
// some of the bomblets flings them randomly out in different
// directions and the remaining explode where they hit.  The explosion
// is calculated and then carried out and then the bomblets are spawned.
function ClusterBomb::onCollision(%Data, %Bomb, %ColidingObject)
{
   %Bomb.setDamageState(Destroyed);
}

function ClusterBomb::onStickyCollision(%Data, %Bomb)
{
   %Bomb.setDamageState(Destroyed);
}

function ClusterBomb::onDestroyed(%Data, %Bomb, %prevState)
{
   // If it was exploded this way already, ignore the second time
   if (%Bomb.detonated == true)
      return;

   // If it was not called by being set destroyed, do that now
   if (%Bomb.getDamageState() !$= "Destroyed")
      %Bomb.setDamageState(Destroyed);

   %Position = posFromTransform(%Bomb.getTransform());

   if (%Bomb.Seperate == true)
   {
       %Bomblets = %Data.Bomblets;
       %Pattern = true;
   } else {
      // Spawn between 0 and 1 less than total number of bomblets
      %Bomblets = mFloor(getRandom() * %Data.Bomblets);
      %Pattern = false;

      // Scale the Explosion based on remaining bomblets that detonated at once
      %DetonatedBomblets = %Data.Bomblets - %Bomblets - 1;
      %Radius = %Data.damageRadius + (%DetonatedBomblets * 3);
      %Damage = %Data.indirectDamage + (%DetonatedBomblets * 0.2);
      %KickBack = %Data.kickBackStrength + (%DetonatedBomblets * 650);

      %Owner = %Bomb.sourceObject;
      %DamageType = %Data.radiusDamageType;

      // Explosion Damage
      RadiusExplosion(%Bomb, %Position, %Radius, %Damage, %KickBack, %Owner, %DamageType);
   }

   MakeBomblets(%Bomb, %Position, %Bomblets, %Pattern);

   if (%Bomb.AltitudeCheck !$= "")
      cancel(%Bomb.AltitudeCheck);   

   %Bomb.detonated = true;
   %Bomb.schedule(750,"delete");
}

// If the Bomb hits water and is armed, then we seperate the bombs
function ClusterBomb::onEnterLiquid(%data, %Bomb, %coverage, %type)
{
   %Bomb.InWater = true;

   // If the Bomb lands in lava, just delete it.
   switch(%type)
   {
      case 4:
         //Lava
         %Bomb.schedule(550, delete);
         return;

      case 5:
         //Hot Lava
         %Bomb.schedule(550, delete);
         return;

      case 6:    
         //Crusty Lava
         %Bomb.schedule(550, delete);
         return;
   }

   if (%Bomb.Armed == true)
      %Bomb.setDamageState(Destroyed);
   else
      %Bomb.setHeat(0);
}

// -------------------------------------------------------------------------------
// Depth Charge
// -------------------------------------------------------------------------------
// This is a somewhat long tube that when dropped will explode on contact with anything.
// The explosion is very small unless it is in the water.  Once it enters the water
// it will explode 20-30 meters below the surface

datablock AudioProfile(DepthChargeSound)
{
   filename    = "fx/weapons/mortar_explode_UW.wav";
   description = LoudestAudio3d;
   preload = true;
   effect = MortarExplosionEffect;
};

datablock ExplosionData(DepthChargeEffect)
{
   soundProfile = DepthChargeSound;

   shockwave = UnderwaterMortarShockwave;
   shockwaveOnTerrain = true;

   subExplosion[0] = UnderwaterMortarSubExplosion1;
   subExplosion[1] = UnderwaterMortarSubExplosion2;
   subExplosion[2] = UnderwaterMortarSubExplosion3;

   emitter[0] = MortarExplosionBubbleEmitter;
   emitter[1] = UnderwaterExplosionSparksEmitter;
   
   shakeCamera = true;
   camShakeFreq = "24 26 22";
   camShakeAmp = "45 55 45";
   camShakeDuration = 2.25;
   camShakeRadius = 35.0;
};

datablock ItemData(DepthCharge)
{
   shapeFile = "stackable1m.dts";
   scale     = "0.7 1.0 1.0";

   mass = 0.95;
   elasticity = 0.0;
   friction = 1;
   drag = 0.3;

   sticky = true;

   hasDamageRadius = true;
   explosion = DepthChargeEffect;

   indirectDamage      = 1.20;
   damageRadius        = 26.0;
   radiusDamageType    = $DamageType::Explosion;
   kickBackStrength    = 7500;

   depth = 25;

   maxDamage = 0.3;
   emap = true;

   ReloadTime = 1000;
      BombEnergy = 28;

   heatSignature = 1.0;
};

datablock ItemData(PrematureDepthCharge)
{
   shapeFile = "stackable2m.dts";
   scale     = "0.7 1.0 1.0";

   mass = 0.95;
   elasticity = 0.0;
   friction = 1;
   sticky = true;

   hasDamageRadius = true;
   indirectDamage      = 0.30;
   damageRadius        = 12.0;
   radiusDamageType    = $DamageType::Explosion;
   kickBackStrength    = 1200;

   explosion = "SatchelMainExplosion";

   maxDamage = 1000;
   emap = true;
};

// This function controls what happens when the bomb does not explode
// above it's target and instead hits something unexpectedly.  It forms
// some of the bomblets flings them randomly out in different
// directions and the remaining explode where they hit.  The explosion
// is calculated and then carried out and then the bomblets are spawned.

function DepthCharge::onAdd(%this, %Bomb) 
{
   Parent::onAdd(%this, %Bomb);

   // We set a timing delay of 20 seconds that if it has not detonated
   // then we go ahead and detonate it now
   %Bomb.DetonationCheck = %Bomb.schedule(24750, "seperate", true);
   %Bomb.DetonationCheck = %Bomb.schedule(25000, setDamagedState, "Destroyed");

   %Bomb.setHeat(1);
}

function DepthCharge::onCollision(%Data, %Bomb, %ColidingObject)
{
   %Bomb.setDamageState(Destroyed);
}

function DepthCharge::onStickyCollision(%Data, %Bomb)
{
   %Bomb.setDamageState(Destroyed);
}

function DepthCharge::onDestroyed(%Data, %Bomb, %prevState)
{
   // If it was exploded this way already, ignore the second time
   if (%Bomb.detonated == true)
      return;

   // If it was not called by being set destroyed, do that now
   if (%Bomb.getDamageState() !$= "Destroyed")
      %Bomb.setDamageState(Destroyed);

   %Position = posFromTransform(%Bomb.getTransform()); 

   if (%Bomb.InWater == true)
   {
      %Radius = %Data.damageRadius;
      %Damage = %Data.indirectDamage;
      %KickBack = %Data.kickBackStrength;
      %Owner = %Bomb.sourceObject;
      %DamageType = %Data.radiusDamageType;

      RadiusExplosion(%Bomb, %Position, %Radius, %Damage, %KickBack, %Owner, %DamageType);
   } else {
      %Rotation = rotFromTransform(%Bomb.getTransform());

      %Item = new Item()
      {
         dataBlock = PrematureDepthCharge;
         position = %Position;
         rotation = %Rotation;
         sourceObject = %Bomb.sourceObject;
      };
      MissionCleanUp.Add(%Item);

      schedule(650, 0, detonateGrenade, %Item);
   }
   if (%Bomb.DepthCheck !$= "")
      cancel(%Bomb.AltitudeCheck);   

   %Bomb.detonated = true;
   %Bomb.schedule(600,"delete");
}

// If the Bomb hits water then we note that and start the depth checking
function DepthCharge::onEnterLiquid(%data, %Bomb, %coverage, %type)
{
   %Bomb.InWater = true;

   // If the Bomb lands in lava, just delete it.
   switch(%type)
   {
      case 4:
         //Lava
         %Bomb.schedule(550, delete);
         return;

      case 5:
         //Hot Lava
         %Bomb.schedule(550, delete);
         return;

      case 6:    
         //Crusty Lava
         %Bomb.schedule(550, delete);
         return;
   }

   // Slow the Depth Charge by 60%
   %Velocity = %Bomb.getVelocity();
   %Bomb.setVelocity(VectorScale(%Velocity, 0.4));

   // Note the Water Line and figure out how far down we should detonate
   %Position = posFromTransform(%Bomb.getTransform());
   %Random = (getRandom() * 5) + 20;
   %Depth = getWord(%Position, 2) - %Random;

   // Start the Depth Checking
   %Bomb.DepthCheck = schedule(250, 0, CheckBombDepth, %Bomb, %Depth);

   %Bomb.setHeat(0);
}

// -------------------------------------------------------------------------------
// Incendiary Bomb
// -------------------------------------------------------------------------------
// Fat looking bomb that explodes with 5 bomblets, there is a pattern.  The bomblets
// are incendiary grenades and can bounce before they explode, being highly dangerous.

datablock ItemData(IncendiaryBomb)
{
   shapeFile = "grenade_projectile.dts";
   scale     = "6.0 6.0 6.0";

   mass = 0.95;
   elasticity = 0.0;
   friction = 1;
   drag = 0.3;

   sticky = true;

   hasDamageRadius = false;
   explosion = "DeployablesExplosion";

   indirectDamage      = 0.22;
   damageRadius        = 12.0;
   radiusDamageType    = $DamageType::Fire;
   kickBackStrength    = 800;

   BurnStrength = 0.05;
   BurnDuration = 10;

   maxDamage = 0.3;
   emap = true;

   Altitude = 60;

   Bomblets = 6;
   BombletName = IncendiaryGrenadeThrown;
   BombletType = Item;

   ReloadTime = 1250;
      BombEnergy = 28;

   heatSignature = 1.0;
};

datablock ItemData(PrematureIncendiaryBomb)
{
   shapeFile = "grenade_projectile.dts";
   scale     = "6.0 6.0 6.0";

   mass = 0.95;
   elasticity = 0.0;
   friction = 1;
   sticky = true;

   hasDamageRadius = true;
   indirectDamage      = 0.40;
   damageRadius        = 15.0;
   radiusDamageType    = $DamageType::Fire;
   kickBackStrength    = 1500;

   explosion = "IncendiaryGrenadeExplosion";

   maxDamage = 1000;
   emap = true;
};

function IncendiaryBomb::onAdd(%this, %Bomb) 
{
   Parent::onAdd(%this, %Bomb);

   // We start the bomb checking it's altitude to seperation 2.5 seconds
   // from being deployed
   %Altitude = %this.Altitude;
   %Bomb.AltitudeCheck = schedule(250, 0, CheckBombAltitude, %Bomb, %Altitude);

   // We set a timing delay of 20 seconds that if it has not detonated
   // then we go ahead and detonate it now, seperating it into it's
   // parts
   %Bomb.DetonationCheck = %Bomb.schedule(24750, "seperate", true);
   %Bomb.DetonationCheck = %Bomb.schedule(25000, setDamagedState, "Destroyed");

   %Bomb.setHeat(1);
}

// This function controls what happens when the bomb does not explode
// above it's target and instead hits something unexpectedly.  It forms
// some of the bomblets flings them randomly out in different
// directions and the remaining explode where they hit.  The explosion
// is calculated and then carried out and then the bomblets are spawned.
function IncendiaryBomb::onCollision(%Data, %Bomb, %ColidingObject)
{
   %Bomb.setDamageState(Destroyed);
}

function IncendiaryBomb::onStickyCollision(%Data, %Bomb)
{
   %Bomb.setDamageState(Destroyed);
}

// This is the generic detonate Incendiary Bomb code.  If certain flags are not
// set by the altitude control function the explosion is larger and very random
// This can happen by it hitting something or by it being shot.
function IncendiaryBomb::onDestroyed(%Data, %Bomb, %prevState)
{
   // If it was exploded this way already, ignore the second time
   if (%Bomb.detonated == true)
      return;

   // If it was not called by being set destroyed, do that now
   if (%Bomb.getDamageState() !$= "Destroyed")
      %Bomb.setDamageState(Destroyed);

   %Position = posFromTransform(%Bomb.getTransform());

   if (%Bomb.Seperate == true)
   {
       %Bomblets = %Data.Bomblets;
       %Pattern = true;
   } else {
      // Spawn between 0 and 1 less than total number of bomblets
      %Bomblets = mFloor(getRandom() * %Data.Bomblets);
      %Pattern = false;

      %Radius = %Data.damageRadius;
      %Damage = %Data.indirectDamage;
      %KickBack = %Data.kickBackStrength;
      %Owner = %Bomb.sourceObject;
      %DamageType = %Data.radiusDamageType;

      %DamageStrength = %Grenade.getDatablock().BurnStrength;
      %DamageStrength = %Grenade.getDatablock().BurnStrength;

      // Scale the Explosion based on remaining bomblets that detonated at once
      %DetonatedBomblets = %Data.Bomblets - %Bomblets - 2;
      if (%DetonatedBomblets > 0)
      {
         %Radius += (%DetonatedBomblets * 3);
         %Damage += (%DetonatedBomblets * 0.2);
         %KickBack += (%DetonatedBomblets * 650);

         // Fire Damage Scaled
         %DamageStrength += (%DetonatedBomblets * 0.015);
         %DamageDuration += (%DetonatedBomblets * 4);
      }

      // Explosion Damage
      RadiusExplosion(%Bomb, %Position, %Radius, %Damage, %KickBack, %Owner, %DamageType);

      IncendiaryCheck(%Position, %Radius, %DamageDuration, %DamageStrength, %Owner);
      schedule(750, 0, IncendiaryCheck, %Position, %Radius, %DamageDuration * 0.8, %DamageStrength * 0.8, %Owner);
      schedule(1500, 0, IncendiaryCheck, %Position, %Radius, %DamageDuration * 0.5, %DamageStrength * 0.5, %Owner);

      %Item = new Item()
      {
         dataBlock = PrematureIncendiaryBomb;
         position = %Position;
         rotation = %Rotation;
         sourceObject = %Bomb.sourceObject;
      };
      MissionCleanUp.Add(%Item);

      schedule(650, 0, detonateGrenade, %Item);
   }

   MakeBomblets(%Bomb, %Position, %Bomblets, %Pattern);

   if (%Bomb.AltitudeCheck !$= "")
      cancel(%Bomb.AltitudeCheck);   

   %Bomb.detonated = true;
   %Bomb.schedule(750,"delete");
}

// If the Bomb hits water and is armed, then we seperate the bombs which won't
// do anything anyways
function IncendiaryBomb::onEnterLiquid(%data, %Bomb, %coverage, %type)
{
   %Bomb.InWater = true;

   // If the Bomb lands in lava, just delete it.
   switch(%type)
   {
      case 4:
         //Lava
         %Bomb.schedule(550, delete);
         return;

      case 5:
         //Hot Lava
         %Bomb.schedule(550, delete);
         return;

      case 6:    
         //Crusty Lava
         %Bomb.schedule(550, delete);
         return;
   }

   %Bomb.setDamageState(Destroyed);   
}

// -------------------------------------------------------------------------------
// EMP Bomb
// -------------------------------------------------------------------------------
// Fat looking bomb that explodes with a massive EMP wave.  The bomb creates an
// EMP field in the area that lasts for 8 seconds

datablock ItemData(EMPBomb)
{
   shapeFile = "stackable4l.dts";
   scale     = "1.0 1.0 0.5";

   mass = 0.95;
   elasticity = 0.0;
   friction = 1;
   drag = 0.3;

   sticky = true;

   hasDamageRadius = false;
   explosion = EMPBlastExplosion;

   indirectDamage      = 0.05;
   damageRadius        = 25.0;
   radiusDamageType    = $DamageType::Electricity;
   kickBackStrength    = 1500;

   EMPDuration = 18.0;
   EMPStrength = 0.05;

   maxDamage = 0.3;
   emap = true;

   ReloadTime = 1250;
   BombEnergy = 28;

   Altitude = 10;

   heatSignature = 1.0;
};

// This function controls what happens when the bomb does not explode
// above it's target and instead hits something unexpectedly.  It forms
// some of the bomblets flings them randomly out in different
// directions and the remaining explode where they hit.  The explosion
// is calculated and then carried out and then the bomblets are spawned.

function EMPBomb::onAdd(%this, %Bomb) 
{
   Parent::onAdd(%this, %Bomb);

   // We tell it to arm itself 2 seconds after being dropped so that
   // it can be dropped into or just above water and function fine.
   %Bomb.schedule(2000, "Armed", true);

   // We start the bomb checking it's altitude to seperation 2.5 seconds
   // from being deployed
   %Altitude = %this.Altitude;
   %Bomb.AltitudeCheck = schedule(2500, 0, CheckBombAltitude, %Bomb, %Altitude);


   // We set a timing delay of 25 seconds that if it has not detonated
   // then we go ahead and detonate it now
   %Bomb.DetonationCheck = schedule(25000, 0, "setDamagedState", "Destroyed");

   %Bomb.setHeat(1);
}

function EMPBomb::onCollision(%Data, %Bomb, %ColidingObject)
{
   %Bomb.setDamageState(Destroyed);
}

function EMPBomb::onStickyCollision(%Data, %Bomb)
{
   %Bomb.setDamageState(Destroyed);
}

function EMPBomb::onDestroyed(%Data, %Bomb, %prevState)
{
   // If it was exploded this way already, ignore the second time
   if (%Bomb.detonated == true)
      return;

   // If it was not called by being set destroyed, do that now
   if (%Bomb.getDamageState() !$= "Destroyed")
      %Bomb.setDamageState(Destroyed);

   %Position = posFromTransform(%Bomb.getTransform());

   // Load the Explosion Information
   %Radius = %Data.damageRadius;
   %Damage = %Data.indirectDamage;
   %KickBack = %Data.kickBackStrength;
   %Owner = %Bomb.sourceObject;
   %DamageType = %Data.radiusDamageType;

   RadiusExplosion(%Bomb, %Position, %Radius, %Damage, %KickBack, %Owner, %DamageType);

   // Load the EMP Explosion Information
   %EMPStrength = %Data.EMPStrength;
   %EMPDuration = %Data.EMPDuration;

   // If the grenade is in the water, make it more potent
   if (%Bomb.InWater == true)
   {
      %EMPStrength *= 0.6;
      %EMPDuration *= 1.4;
   }

   // Do the EMP checks
   EMPingCheck(%Position, %Radius, %EMPDuration, %EMPStrength, %Owner);
   schedule(1000, 0, EMPingCheck, %Position, %Radius, %EMPDuration * 0.95, %EMPStrength * 1.1, %Owner);
   schedule(2000, 0, EMPingCheck, %Position, %Radius, %EMPDuration * 0.85, %EMPStrength * 1.2, %Owner);
   schedule(3000, 0, EMPingCheck, %Position, %Radius * 0.85, %EMPDuration * 0.7, %EMPStrength * 1.4, %Owner);
   schedule(4000, 0, EMPingCheck, %Position, %Radius * 0.6, %EMPDuration * 0.55, %EMPStrength * 1.7, %Owner);
   schedule(5000, 0, EMPingCheck, %Position, %Radius * 0.4, %EMPDuration * 0.4, %EMPStrength * 2, %Owner);
   schedule(6000, 0, EMPingCheck, %Position, %Radius * 0.15, %EMPDuration * 0.25, %EMPStrength * 2.5, %Owner);

   AreaEMP(%Position);
   schedule(1250, 0, AreaEMP, %Position);
   schedule(2500, 0, AreaEMP, %Position);
   schedule(3750, 0, AreaEMP, %Position);

   if (%Bomb.AltitudeCheck !$= "")
      cancel(%Bomb.AltitudeCheck);   

   %Bomb.detonated = true;
   %Bomb.schedule(750,"delete");
}

// If the Bomb hits water and is armed, then we seperate the bombs
function EMPBomb::onEnterLiquid(%data, %Bomb, %coverage, %type)
{
   %Bomb.InWater = true;

   // If the Bomb lands in lava, just delete it.
   switch(%type)
   {
      case 4:
         //Lava
         %Bomb.schedule(750, delete);
         return;

      case 5:
         //Hot Lava
         %Bomb.schedule(750, delete);
         return;

      case 6:    
         //Crusty Lava
         %Bomb.schedule(750, delete);
         return;
   }

   %Bomb.setHeat(0);
}

// -------------------------------------------------------------------------------
// Bomb Functions
// -------------------------------------------------------------------------------

function CheckBombAltitude(%Bomb, %Altitude)
{

   // If the Bomb is detonated, then we no longer try to do a thing
   if (%Bomb.detonated == true)
      return;

   // If it no longer exists, stop trying.
   if (isObject(%Bomb) == false)
      return;

   // Load the Position of the Bomb
   %Transform = %Bomb.getTransform();
   %Position = posFromTransform(%Transform);

   %PositionZ = getWord(%Position, 2);

   // Load the Velocity, Speed, and Direction
   %Velocity = %Bomb.getVelocity();
   %FallingVector = VectorNormalize(%Velocity);

   // Load the Seperate and/or Detonate Altitudes
   %BombData = %Bomb.getDataBlock();

   // If the Bomb is dropping faster per second then the altitude we will detonate at,
   // we check now.
   %SearchDistance = 1000;

   %SearchVector = VectorScale(%FallingVector, %SearchDistance);
   %EndPosition = VectorAdd(%Position, %SearchVector);

   %Target = ContainerRayCast(%Position, %EndPosition, $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType, %Bomb);

   // If nothing was found under the bomb, check again in a bit
   if (%Target $= "0")
   {
      %Bomb.AltitudeCheck = schedule(500, 0, CheckBombAltitude, %Bomb, %Altitude);

      return;
   }

   %TargetPosition = getWords(%Target, 1, 3);

   %Distance = VectorLen(VectorSub(%Position, %TargetPosition));

   // If the bomb is not yet at it's proper altitude, we check again in a little bit
   // the delay in checking is scaled to help prevent seperating/exploding too low.
   if (%Distance > %Altitude)
   {
       %Bomb.AltitudeCheck = schedule(500, 0, CheckBombAltitude, %Bomb, %Altitude);

       if (%Bomb.TAGBeacon !$= "")
       {
         %Success = BombDrift(%Bomb, %Bomb.TAGBeacon, %Bomb.TAGPosition, %TargetPosition);
         if (%Success == false)
            %Bomb.TAGBeacon = "";
       }

      return;
   }

   if (%Bomb.DetonationCheck !$= "")
      Cancel(%Bomb.DetonationCheck);

   %Bomb.Seperate = true;

   // Let the individual Explode Code Play out
   %Bomb.setDamageState(Destroyed);
}

// This makes Bomblets based on the main Bomb
function MakeBomblets(%Bomb, %Position, %Bomblets, %Pattern)
{
   // If no Bomblets were requested, go no further
   if (%Bomblets <= 0)
      return;

   %BombData = %Bomb.getDataBlock();
   %BombletName = %BombData.BombletName;
   %BombletType = %BombData.BombletType;

   %Velocity = VectorScale(%Bomb.getVelocity(), 0.5);
   
   if (%Pattern == true)
   {
      // If a carpet Pattern was requested, lay the bomblets out along it
      %i = 0;
      while (%i < %Bomblets)
      {
         // Figure out the bomblet spread
         switch (%i)
         {
            case 0 :
               // drift to one side
               %Variance = "1 0 0";

            case 1 :
               // drift to the other side
               %Variance = "-1 0 0";

            case 2 :
               // drift forward
               %Variance = "0 1 0";

            case 3 :
               // drift backwards
               %Variance = "0 -1 0";

            case 4 :
               // Straight along the same course
               %Variance = "0 0 0";

            default :
               // Just go about randomly
               %Random = getRandom();
               %VarianceX = %Random * 2 - %Random;
               %Random = getRandom();
               %VarianceY = %Random * 2 - %Random;
               %Variance = %VarianceX @ " " @ %VarianceY @ " 0";
          }

         %ScaledVariance = VectorScale(%Variance, 3);
         %SpreadVector = VectorAdd(%Velocity, %ScaledVariance);

         %Item = new (%BombletType)()
         {
            dataBlock = %BombletName;
            initialDirection = VectorNormalize(%SpreadVector);
            initialPosition  = %Position;
            position         = %Position;
            sourceObject     = %Bomb.sourceObject;
         };
         MissionCleanUp.Add(%Item);

         %ScaledVariance = VectorScale(%Variance, 5);
         %SpreadVector = VectorAdd(%Velocity, %ScaledVariance);
         %Item.applyImpulse(%Position, %SpreadVector);

         %i++;
      }
   } else {

      // If no pattern was requested, throw the bomblets to the sides randomly
      %i = 0;
      while (%i < %Bomblets)
      {
         // Just go about randomly
         %Random = getRandom();
         %VarianceX = %Random * 2 - %Random;
         %Random = getRandom();
         %VarianceY = %Random * 2 - %Random;
         %Variance = %VarianceX @ " " @ %VarianceY @ " 1";

         %ScaledVariance = VectorScale(%Variance, 500);
         %FinalVector = VectorNormalize(VectorAdd(%Velocity, %Variance));

         %Item = new (%BombletType)()
         {
            dataBlock = %BombletName;
            initialDirection = %FinalVector;
            initialPosition  = %Position;
            sourceObject     = %Bomb.sourceObject;
         };
         MissionCleanUp.Add(%Item);

         %i++;
      }
   }
}

function CheckBombDepth(%Bomb, %Depth)
{
   // If the Bomb is detonated, then we no longer try to do a thing
   if (%Bomb.detonated == true)
      return;

   // Load the Position of the Bomb
   %Transform = %Bomb.getTransform();
   %Position = posFromTransform(%Transform);
   %PositionZ = getWord(%Position, 2);

   // If it has not sunk that low, then check again in a second
   if (%PositionZ > %Depth)
   {
      %Bomb.DepthCheck = schedule(500, 0, CheckBombDepth, %Bomb, %Depth);
      return;
   }

   %Bomb.setDamageState(Destroyed);
}

// Code to Control the generic dropping of bombs
function DropBomb(%Player, %Turret)
{
   // If the turret is turned off/reloading don't fire
   if (%Turret.NoFire == true)
      return;

   // Load the Bomb Type that the player has selected
   %BombNo = %Turret.selectedBomb;
   if (%BombNo $= "")
      %BombNo = 1;

   %BombType = $BombType[%BombNo];

   %Vehicle = %Turret.vehicleMounted;

   // Get the position and rotation for the drop
   %Transform = %Vehicle.getTransform();
   %Rotation = rotFromTransform(%Transform);
   %Rotation123 = getWords(%Rotation, 0, 2);
   %Rotation4 = getWord(%Rotation, 3) + 1.57;
   %Rotation = %Rotation123 @ " " @ %Rotation4;

   %Position = posFromTransform(%Transform);
   %PositionXY = getWords(%Position, 0, 1);
   %PositionZ = getWord(%Position, 2) - 4.5;
   %Position = %PositionXY @ " " @ %PositionZ;

   // Make the bomb below the bomber at the specified rotation
   %Bomb = new Item()
   {
      dataBlock = %BombType;
      position = %Position;
      rotation = %Rotation;
      sourceObject = %Player;
      scale = %BombType.scale;
   };
   MissionCleanUp.Add(%Bomb);

   // Push it at the same speed as the vehicle
   %Velocity = getWords(%Vehicle.getVelocity(), 0, 1) @ " -1";
   %Bomb.ApplyImpulse(%Position, %Velocity);

   // Play the sound of a dropping bomb
   %Turret.playAudio(0, BomberBombSound);

   // Attempt a Beacon Lock
   %Beacon = GetBeaconLock(%Position, %Velocity);
   if (%Beacon !$= "0")
   {
      %Bomb.TAGBeacon = %Beacon;
      %Bomb.TAGPosition = posFromTransform(%Beacon.getTransform());
   }      

   // Set the Turret as unable to fire and then schedule the reability to fire
   %Turret.NoFire = true;
   %ReloadTime = %BombType.ReloadTime;
   %EnergyRequired = %BombType.BombEnergy;
   %Turret.ReloadSchedule = schedule(%ReloadTime, 0, ResetTurret, %Turret, %Vehicle, %EnergyRequired);
}

// This function attempts to estimate where a bomb will hit by determining
// the Vehicle's Relative Altitude and then checking at the location for
// an obstruction between the vehicle and the location.  We loop through
// attempting to find a point where the vehicle and ground are both in view.

function GetBeaconLock(%Position, %VehicleVelocity)
{
   // Load the Masks for the Terrain/Building/Whatever search
   %Mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType |
           $TypeMasks::ForceFieldObjectType | $TypeMasks::StaticObjectType |

   // Attempt to find out where the ground is under us, upto 1000 meters.
   %EndPosition = VectorAdd(%Position, "0 0 -1000");

   %Target = ContainerRayCast(%Position, %EndPosition, %Mask, %Vehicle);

   // If no terrain is found, just quit here
   if (%Target $= "0")
      return("0");

   // Pull the Altitude out of the Position
   %PositionXY = getWords(%Position, 0, 1);
   %PositionZ = getWord(%Position, 2);

   // Based on the Location of the Terrain versus the Position of the Vehicle, we
   // estimate where the bombs will land.
   %TargetPosition = getWords(%Target, 1, 3);
   %Height = %PositionZ - getWord(%TargetPosition, 2);

   // We are too low, don't attempt TAG lock
   if (%Height < 30)
      return("0");

   // Load the Vehicle's Velocity without the Z component
   %Velocity = getWords(%VehicleVelocity, 0, 1) @ " 0";

   %FallTime = mSqrt((2.0 * %Height) / 9.81);

   %EstimatedPosition = VectorAdd(%Position, VectorScale(%Velocity, %FallTime));
   %TAGBeacon = GetNearestBeacon(%EstimatedPosition);

   return(%TAGBeacon);
}

// Check the Active TAG Beacons for one within 50 meters of where we think the
// bomb will land, then return the object
function GetNearestBeacon(%Position)
{
   %Position2D = getWords(%Position, 0, 1) @ " 0";

   %Distance = 10000;

   %i = 0;
   %Count = BeaconGroup.getCount();
   while (%i < %Count)
   {
      %TAGObject = BeaconGroup.getObject(%i);

      %TAGPosition = posFromTransform(%TAGObject.getTransform());
      %TAGPosition2D = getWords(%TAGPosition, 0, 1) @ " 0";

      %TAGDistance = VectorLen(VectorSub(%Position2D, %TAGPosition2D));
      if (%TAGDistance < %Distance)
      {
         %Distance = %TAGDistance;
         %FoundBeacon = %TAGObject;
      }

      %i++;
   }

   // If the TAG we found is less than 50 meters, we use that one
   if (%Distance < 50)
      return(%FoundBeacon);
   else
      return("0");
}

// This is used to drift the bomb towards the target
function BombDrift(%Bomb, %TAGBeacon, %TAGPosition, %LandingPosition)
{
   // If the bomb has exploded, stop drifting
   if (%Bomb.detonated == true)
      return(false);

   // If the Beacon or the Bomb is no longer active, stop drifting
   if (isObject(%TAGBeacon) != true || isObject(%Bomb) != true)
      return(false);

   %Position = posFromTransform(%Bomb.getTransform());
   %PositionZ = getWord(%Position, 2);
   
   %TAGPositionZ = getWord(%TAGPosition, 2);

   // If the TAG Marker is above the bomb, stop trying to fall towards it
   if (%PositionZ < %TAGPositionZ)
      return(false);

   // Figure out how high up we are relative to the angle of decent and estimate
   // time to impact.
   %FallingVector = VectorSub(%Position, %LandingPosition);
   %Height = VectorLen(%FallingVector);
   %FallTime = mSqrt((2.0 * %Height) / 9.81);

   // Determine the Distance between the TAG marker and the estimated landing position
   %LandingPosition2D = getWords(%LandingPosition, 0, 1) @ " 0";
   %TAGPosition2D = getWords(%TAGPosition, 0, 1) @ " 0";
   %VectorBetween = VectorSub(%LandingPosition2D, %TAGPosition2D);
   %Distance2D = VectorLen(%VectorBetween);
   %NormalBetween = VectorNormalize(%VectorBetween);

   // Check to see if we are so close that full drift is not needed
   if (%Distance2d < %FallTime * 10)
      %PushForce = -1 * (%Distance2D / %FallTime);
   else
      %PushForce = -5;

   %Velocity = %Bomb.getVelocity();
   %ImpulseVector = VectorAdd(%Velocity, VectorScale(%NormalBetween, %PushForce));

   %Bomb.ApplyImpulse(%Position, %ImpulseVector);
   return(true);
}