// This is a pack that can not be bought, it must be placed by the map maker.
// It can be worn by light and medium armors and when activated it will 
// push the wearer in the general direction they are moving, push some upward
// thrust to keep them aloft.

$JetPackForce = 3800;

datablock ParticleData(HumanArmorJetParticle)
{
   dragCoefficient      = 0.0;
   gravityCoefficient   = 0;

   inheritedVelFactor   = 0.2;
   constantAcceleration = 0.0;

   lifetimeMS           = 75;
   lifetimeVarianceMS   = 5;

   textureName          = "particleTest";
   colors[0]            = "0.32 0.47 0.47 1.0";
   colors[1]            = "0.32 0.47 0.47 0";

   sizes[0]             = 0.40;
   sizes[1]             = 0.15;
};

datablock ParticleEmitterData(HumanArmorJetEmitter)
{
   ejectionPeriodMS = 3;
   periodVarianceMS = 0;

   ejectionVelocity = 4;
   velocityVariance = 2.9;
   ejectionOffset   = 0.0;

   thetaMin         = 0;
   thetaMax         = 5;

   phiReferenceVel  = 0;
   phiVariance      = 360;

   overrideAdvances = false;
   particles        = "HumanArmorJetParticle";
};

datablock ItemData(JetPack)
{
   className = Pack;
   catagory = "Packs";
   shapeFile = "pack_barrel_aa.dts";

   mass = 12.0;
   elasticity = 0.2;
   friction = 0.6;
   pickupRadius = 1;
   rotate = true;

   image = JetPackImage;
   pickUpName = "a personal jetpack";

   computeCRC = true;
   emap = true;
};

// This shape is just for the effect of having small wings
datablock ShapeBaseImageData(JetPackWingsImage)
{
   shapeFile = "deploy_ammo.dts";

   mountPoint = 1;
   offset = "0 0 -0.8";
};

// This is the main pack that controls the Controls and Wings
// This is what is ejected and what ties everything together.
datablock ShapeBaseImageData(JetPackImage)
{
   shapeFile = "pack_barrel_aa.dts";
   item = JetPack;

   mountPoint = 1;
   offset = "0 0 0";
   rotation = "0 100 0 90";

   emap = true;
   mass = 20;

   usesEnergy = true;
   minEnergy = 3;

   stateName[0] = "Idle";
   stateTransitionOnTriggerDown[0] = "Activate";
	
   stateName[1] = "Activate";
   stateScript[1] = "onActivate";
   stateTransitionOnTriggerUp[1] = "Deactivate";
   stateTransitionOnNoAmmo[1] = "Deactivate";

   stateName[2] = "Deactivate";
   stateScript[2] = "onDeactivate";
   stateTransitionOnTimeout[2] = "Idle";
};

function JetPackImage::onActivate(%data, %Player, %slot)
{
   // Record the Last Weapon the Player had out
   %Player.lastWeapon = %Player.getMountedImage($WeaponSlot).getName();
   %Player.unmountImage($WeaponSlot);

   // Spread the Wings and put the helmet on
   %Player.mountImage(JetPackWingsImage, 6);

   // Put the Controls Stick in you hand
   %Player.mountImage(JetPackControlsImage, $WeaponSlot);

   if (%Player.inv[JetPackFuel] <= 0)
      %Message = "Wings formed, but no fuel remaining... ~wfx/misc/misc.error.wav";
   else
       %Message = "Wings formed...   Engine Ready...";

   messageClient(%Player.client, '', %Message);
}

function JetPackImage::onDeactivate(%data, %Player, %slot)
{
   if (%Player.JetPackCheck !$= "")
   {
      Cancel(%Player.JetPackCheck);
      %Player.JetPackCheck = "";
   }

   // Remove the Controls and Wings
   %Player.dontRespond = true;
   %Player.unMountImage(6);
   %Player.unMountImage(0);
   %Player.mountImage(%Player.lastWeapon, 0);

   %Message = "Deactivating wings...   Storing Rocket Pack Controls...";
   messageClient(%Player.client, '', %Message);
}

// This gets any fuel that is part of the pack
function JetPackImage::onPickup(%this, %Pack, %Player, %Amount)
{
   if (%Pack.sensors $= "")
      %Pack.sensors = 100;

   if (%Pack.sensors > 0)
   {
      // Note the Fuel from the pack
      %Player.inv[JetPackFuel] += %pack.sensors;
      if (%Player.inv[JetPackFuel] > 100)
         %Player.inv[JetPackFuel] = 100;
   }
}

// This leaves any fuel with the pack
function JetPackImage::onThrow(%this, %Pack, %Player)
{
   %Player.throwSensorPack = true;

   %Pack.sensors = %Player.inv[JetPackFuel];
   %Player.inv[JetPackFuel] = 0;
   %Player.client.updateSensorPackText(%Player.inv[JetPackFuel]);

   // do the normal ItemData::onThrow stuff -- sound and schedule deletion
   serverPlay3D(ItemThrowSound, %Player.getTransform());
   %Pack.schedulePop();
}

// This function can punish the player for getting rid of the pack early by burning
// them some.  All the player must do is turn the pack off, but they can choose to use
// it as a missile or whatever.
function JetPackImage::onUnmount(%Data, %Player, %Node)
{
   // If it is not running and not started, no biggie drop it
   if (%Player.JetPackCheck $= "" && %Player.cantJet == true)
      return;

   echo("Deleting Used Pack");

   %JetPack = %Player.ThrownItem;
//   if (%Player.JetPackCheck $= "" && %Player.cantJet != true)
//   {
//      %JetPack.delete();
      return;
//   }

   %Player.unMountImage(6);

   if (%Player.getMountedImage(0).getName() $= "JetPackControlsImage")
      %Player.unMountImage(0);

   echo("Throwing Running Pack");

   // Stop any further jetting
   cancel(%Player.JetPackCheck);
   %Player.JetPackCheck = "";

   // Player takes lots of damage from tossing the pack early   
   %Transform = %Player.getTransform();
   %Position = posFromTransform(%Transform);

   %Damage = 0.4 * (%Player.JetPackForce / $JetPackForce);
   %BurnStrength = 0.04 * (%Player.JetPackForce / $JetPackForce);
   %BurnTime = 10 * (%Player.JetPackForce / $JetPackForce);

   %Player.getDataBlock().damageObject(%Player, %Player, %Position, %Damage, $DamageType::Fire);
   %Player.Burn(%BurnTime, %BurnStrength, %Player);

   %Velocity = VectorScale(%Player.getVelocity(), 6);
   %JetPack = %Player.ThrownItem;
   %JetPack.ApplyImpulse(%Position, %Velocity);

   %Random = getRandom();
   if (%Random < 0.4)
   {
      // Send the pack pretty much straight ahead and have it become a projectile that will
      // explode on contact with just about anything.

   } else {
      echo("Sending Pack Skywards");
 
      // Send the Old Jet Pack Flying
      %JetPack.schedule(2800, startFade, 1000, 0, true);

      echo("Jet Pack: " @ %JetPack);

      schedule(800, 0, LooseJetPack, %JetPack);
      schedule(1600, 0, LooseJetPack, %JetPack);
      schedule(2400, 0, LooseJetPack, %JetPack);
      schedule(3200, 0, LooseJetPack, %JetPack);
      %JetPack.schedule(4000, delete);
   }

   %Player.schedule(4200, "canJet", "");
}

datablock ShapeBaseImageData(JetPackControlsImage)
{
   className = WeaponImage;
   shapeFile = "weapon_targeting.dts";

   mountPoint = $WeaponSlot;
   offset = "0 0.1 0.15";
   rotation = "1 0 0 90";

   ammo = JetPackFuel;
   emap = true;

   usesEnergy = true;
   minEnergy = 3;

   mass = 0;

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

   stateName[1]                    = "Ready";
   stateTransitionOnNoAmmo[1]      = "NoAmmo";
   stateTransitionOnTriggerDown[1] = "Start";
   stateSound[1]                   = DiscSwitchSound;

   stateName[2]                    = "Start";
   stateScript[2]                  = "onFire";
   stateTransitionOnTimeout[2]     = "Running";
   stateTimeoutValue[2]            = 0.2;
   stateAllowImageChange[2]        = false;

   stateName[3]                    = "Running";
   stateTransitionOnNoAmmo[3]      = "Stop";
   stateTransitionOnTriggerUp[3]   = "Stop";
   stateAllowImageChange[3]        = false;
   stateTransitionOnTimeout[3]     = "Running";
   stateTimeoutValue[3]            = 0.5;
   stateSound[3]                   = MissileProjectileSound;

   stateName[4]                    = "Stop";
   stateTimeoutValue[4]            = 0.2;
   stateTransitionOnTimeout[4]     = "Ready";
   stateFire[4]                    = true;
   stateScript[4]                  = "offFire";
   stateSound[4]                   = plasmaExpSound;
   stateAllowImageChange[4]        = false;

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

   stateName[6]                    = "DryFire";
   stateSound[6]                   = plasmaExpSound;
   stateTimeoutValue[6]            = 1.0;
   stateTransitionOnTimeout[6]     = "Ready";
};

function JetPackControlsImage::onUnmount(%Data, %Player, %Node)
{
   if (%Player.dontRespond == true)
   {
      %Player.dontRespond = "";
      return;
   }

   %Player.setImageTrigger($BackPackSlot, false);
   JetPackImage::onDeactivate(%data, %Player, %slot);
}

// This function starts the thrust for the pack
function JetPackControlsImage::onFire(%Data, %Player, %Slot)
{
   if (%Player.inv[JetPackFuel] <= 0)
      return;

   %EnergyLevel = %Player.getEnergyLevel();
   if (%EnergyLevel < 15)
   {
      messageClient(%Player.client, '', 'Not enough energy to start the engine! ~wfx/misc/misc.error.wav');
      return;
   }
   %Player.setEnergyLevel(%EnergyLevel - 15);

   %Player.cantJet = true;

   %Player.JetPackForce = 2000;
   %Player.JetPackCheck = %Player.schedule(250, JetPackCheck);
}

// This function cuts the thrust for the pack
function JetPackControlsImage::offFire(%Data, %Player, %Slot)
{
   cancel(%Player.JetPackCheck);
   %Player.JetPackCheck = "";

   %Player.cantJet = "";
}

function Player::JetPackCheck(%Player)
{
   // Make the Player a hot target
   %Player.setHeat(1);

   // Get the Facing Vector
   %CurrentFacing = %Player.getMuzzleVector($WeaponsSlot);
   %CurrentFacing = VectorNormalize(%CurrentFacing);
   %FacingVector = vectorScale(%CurrentFacing, %Player.JetPackForce);

   // Add a vertical component
   if (%Player.JetPackForce < $JetPackForce)
      %ImpulseVector = vectorAdd(%FacingVector, vectorScale("0 0 1", %Player.JetPackForce));
   else
      %ImpulseVector = vectorAdd(%FacingVector, vectorScale("0 0 1", %Player.JetPackForce / 1.5));

   // Alter the Original Vector if we are approaching terminal velocity, there is a
   // 50% chance of some disturbance
   if (%Player.JetPackForce >= $JetPackForce)
   {
      %CauseDisturbance = true;

      %Disturbance = mFloor(getRandom() * 16);
      switch (%Disturbance)
      {
         // Left
         case 0 :
            %DisturbanceVector = VectorCross(%CurrentFacing, "0 0 1");

         // Right
         case 1 :
            %DisturbanceVector = VectorCross(%CurrentFacing, "0 0 -1");

         // Up
         case 2 :
            %DisturbanceVector = "0 0 1";

         // Down
         case 3 :
            %DisturbanceVector = "0 0 -1";

         // None
         default :
            %CauseDisturbance = false;
      }

      // If there is a disturbance, mess with the players flight vector
      if (%CauseDisturbance == true)
      {
         %ScaledDisturbance = VectorScale(%DisturbanceVector, 1400);
         %ImpulseVector = VectorAdd(%ImpulseVector, %ScaledDisturbance);
      }

   } else {
      // Increase the speed and add a governor
      %Player.JetPackForce *= 1.08;

      if (%Player.JetPackForce > $JetPackForce)
         %Player.JetPackForce = $JetPackForce;
   }

   // Apply the actual Thrust now
   %CurrentPosition = posFromTransform(%Player.getTransform());
   %Player.applyImpulse(%CurrentPosition, %ImpulseVector);
 
   // If the Jet Pack is out of Fuel, shut it down
   if (%Player.inv[JetPackFuel] <= 0)
   {
      %Player.JetPackCheck = "";
      messageClient(%Player.client, '', 'Rocket Pack is out of fuel! ~wfx/powered/turret_outdoor_fire.wav');

      // If the Player has the Inertial Dampner, then a delayed signal is sent to active the 
      // dampner in order to protect the player
      if (%Player.Special $= "InertialDampner")
         %Player.schedule(1850, InertialDampen);
   } else {
      // If the player is running low on fuel, then let them know
      if (%Player.inv[JetPackFuel] < 10 && %Player.LowFuelWarning $= "")
      {
         // Write the Actual Message
         messageClient(%Player.client, '', 'Running low on fuel!');

         // Play a warning tone
         messageClient(%Player.client, '', '~wfx/misc/misc.error.wav');
         %Player.client.schedule(1500, messageClient, '', '~wfx/misc/misc.error.wav');

         // Schedule the Reset of the warning so that the player does not get a barrage of messages.
         %Player.LowFuelWarning = %Player.schedule(6500, ResetFuelWarning);
      }

      %Player.JetPackCheck = %Player.schedule(500, JetPackCheck);
   }  
}

// This function sends the pack skywards so that it can vanish in peace.
function LooseJetPack(%JetPack)
{
   %Position = posFromTransform(%JetPack.getTransform());
   %JetPack.ApplyImpulse(%Position, "0 0 500");
}

function Player::ResetFuelWarning(%Player)
{
   %Player.LowFuelWarning = "";
}