
// This is the Grappling Core for the Havoc Mod.  It is like a grappling
// hook that is able to attach itself to anything, but without the cable.
// The cable caused intermitten crashs and so was removed and what you are
// left with is what you see today.  The handheld unit is attracted to the
// hook, pulling you towards it.  The Grappling Core should not be used
// to make heavy offense that much more mobile and so the real power does
// not happen until the player has an energy pack.
//
// The handheld unit comes out when you press the beacon key when you are
// equiped with the Grappling Core Special.  You fire the core by pulling
// the trigger and the hook is shot out in an arch and will attach itself
// to anything that is hits.  There is a small explosion where it hits so
// that you can see where is has fastened onto.  If it hits a static shape,
// structure, or terrain it uses a different subroutine than if it hits
// a moveable object.  If the distance between the player and the hook/object
// begins to increase than an extra pull is provided to help catch up.
//
// There is also subtle animation of the hook being shot off.
//
//--------------------------------------------------------------------------
// The Moving Hook
//--------------------------------------------------------------------------
datablock ItemData(GHook)
{
   explosion = DeployablesExplosion;

   shapeFile = "turret_belly_barrelr.dts";
   mass = 0.8;
   elasticity = 0.0;
   friction = 1.0;
   pickupRadius = 1;
   maxDamage = 1000.0;

   sticky = true;
   emap = true;

   heatsignature = 0;
};

// Function called when the Hook hits something
function GHook::onStickyCollision(%DataBlock, %Hook)
{
   echo("Hook has struck a surface");

   if (isObject(%Hook) == false)
      return;

   %Position = %Hook.getLastStickyPos();
   %Normal = %Hook.getLastStickyNormal();
   
   %intAngle = getTerrainAngle(%Normal);
   %rotAxis = vectorNormalize(vectorCross(%Normal, "0 0 1"));

   if (getWord(%Normal, 0) == 1 || getWord(%Normal, 0) == -1)
   {
      %rotAxis = vectorNormalize(vectorCross(%Normal, "0 1 0"));
   }

   %Rotation = %rotAxis @ " " @ %intAngle;

   %Player = %Hook.sourceObject;

   if (%Player.ThrowHook !$= "")
   {
      cancel(%Player.ThrowHook);
      %Player.ThrowHook = "";
   }

   %Player.GrapplingHookFasten(%Position, %Rotation, %Hook);
}

function GHook::onCollision(%DataBlock, %Hook, %Object)
{
   if (isObject(%Hook) == false)
      return;

   %Hook.setDamagestate(Destroyed);
   %Hook.schedule(150, delete);

   %Player = %Hook.sourceObject;

   if (%Player.ThrowHook !$= "")
   {
      cancel(%Player.ThrowHook);
      %Player.ThrowHook = "";
   }

   %Player.GrapplingHookObjectFasten(%Object);
}

function GHook::damageObject(%this, %damageObj, %projectile, %amount, %damageType)
{
   // Don't do a thing let the other code handle it
}

//--------------------------------------------------------------------------
// The Anchored Hook
//--------------------------------------------------------------------------
datablock ItemData(AHook) : StaticShapeDamageProfile
{
   shapeFile = "turret_belly_barrelr.dts";
   explosion = DeployablesExplosion;
   maxDamage = 0.2;
   disabledLevel = 0.2;
   destroyedLevel = 0.2;

   sticky = true;
   mass = 0;
   friction = 1.0;

   heatsignature = 0;
};

// Allow Players to shoot another Player's Grappling Hook Anchor in
// order to disconnect it.
function AHook::damageObjectdamageObject(%data, %Hook, %projectile, %amount, %damageType)
{
   %Hook.damaged += %amount;

   if (%Hook.damaged >= %data.maxDamage)
   {
      %Player = %Hook.sourceObject;

      messageClient(%Player.client, 'MsgRepairPackOn', "Attached Core Destroy, recoil aborted.");

      %Player.GrapplingHookUnFire();
   }
}

//--------------------------------------------------------------------------
// The Item and Image
//--------------------------------------------------------------------------
datablock ItemData(GrapplingHook)
{
   className    = Special;
   catagory     = "Spawn Items";
   shapeFile    = "weapon_elf.dts";
   image        = GrapplingHookImage;
   mass         = 3;
   elasticity   = 0.2;
   friction     = 0.6;
   pickupRadius = 1;
   pickUpName = "a Grappling Core";
   emap = true;

   heatsignature = 0;
};

datablock ShapeBaseImageData(GrapplingHookImage)
{
   className = WeaponImage;
   shapeFile = "pack_deploy_inventory.dts";
   item = GrapplingHook;
   mountPoint = $WeaponSlot;
   offset = "0 0.55 0";
   emap = true;
   usesEnergy = true;
   minEnergy = 1;

   anchor = AHook;
   hook = GHook;

   stateName[0] = "Idle";
   stateTransitionOnTriggerDown[0] = "Activate";

   stateName[1] = "Activate";
   stateScript[1] = "onActivate";
   stateSequence[1] = "fire";
   stateTransitionOnTriggerUp[1] = "Deactivate";

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

datablock ShapeBaseImageData(GrapplingHookHookImage)
{
   shapeFile = "turret_belly_barrelr.dts";
   mountPoint = $WeaponSlot;
   offset = "0 0.65 0";
   emap = true;
};

function GrapplingHookImage::onDeactivate(%this, %Player, %Slot)
{
   messageClient(%Player.client, 'MsgRepairPackOn', "Grappling Core Deactivated.");

   %Player.GrapplingHookUnFire();
   %Player.mountImage(GrapplingHookHookImage, 1);
}

function GrapplingHookImage::onUnmount(%data, %Player, %node)
{
   messageClient(%Player.client, 'MsgRepairPackOn', "Storing Grappling Core.");

   %Player.GrapplingHookUnFire();
   %Player.UnMountImage(1);
}

function GrapplingHookImage::onActivate(%this, %Player, %Slot)
{
   if (%Player.Hook !$= "")
   {
      %Player.Hook = "";
      %Player.Hook.setDamageState(Destroyed);
   }

   if (%Player.HookReady == false)
      return(0);

   // Take energy for firing the hook
   %Energy = %Player.getEnergyLevel();
   %Player.setEnergyLevel(%Energy - 10);

   messageClient(%Player.client, 'MsgRepairPackOn', '\c2Firing Grappling Core.');

   %Player.UnMountImage(1);

   // Search for an object in player's LOS to determine distance
   %Vector = VectorNormalize(%Player.getMuzzleVector($WeaponSlot));
   %Range = VectorScale(%Vector, 120);
   %Transform = %Player.getMuzzlePoint($WeaponSlot);
   %Position = posFromTransform(%Transform);
   %End = VectorAdd(%Position, %Range);
   %Target = ContainerRayCast(%Position, %End, $EverythingMask);

   if(%Target $= "0")
      %Force = 120;
   else
   {
      %Transform = %Target.getTransform();
      %Position = posFromTransform(%Transform);
      %Vector = vectorSub(%Position, %PlayerPosition);
      %Force = vectorLen(%Vector);
   }

   %Item = new Item()
   {
      dataBlock = GHook;
      rotation = "0 0 1 1.57";
      sourceObject = %Player;
   };
   MissionCleanup.add(%Item);

   %Item.schedule(3500, delete);

   %Player.throwObject(%Item, %Force);
   %Player.Hook = %Item;
}

function Player::GrapplingHookFasten(%Player, %Position, %Rotation, %Hook)
{
   // If there is a throw in effect, cancel it.
   if (%Player.ThrowHook !$= "")
   {
      cancel(%Player.ThrowHook);
      %Player.ThrowHook = "";
   }   

   // Set the hook as having been fired
   %Player.HookReady = false;


   %Hook.setDamageState(Destroyed);

   messageClient(%Player.client, 'MsgTurretMount', "Grappling Core has fastened onto a surface.");

   // Make the new hook
   %Item = new Item()
   {
      dataBlock = AHook;
      sourceObject = %Player;
   };
   MissionCleanUp.add(%Item);
   %Player.Hook = %Item;

   %Item.setTransform(%Position @ " " @ %Rotation);

   // Figure out the pull and energy consumption
   %MassFactor = (%Player.getDataBlock().mass + %Player.ExtraMass) / 55;
   %Player.GrapplingPullForce = 740 * %MassFactor;

   if (%Player.hasEnergyPack == true)
      %Player.GrapplingPullForce *= 1.5;

   %EnergyTaken = 6.0;
   %EnergyTaken *= %MassFactor;
   %Player.GrapplingEnergyUsed = %EnergyTaken;
   %Player.LastDistance = 10000;
 
   %Player.HookPull = %Player.schedule(650, GrapplingHookPullCheck, %Position);
}

// This is run to simulate the pulling of the cable as it is retracted into
// the electromechanical grappling hook
function Player::GrapplingHookPullCheck(%Player, %HookPosition)
{
   if (%Player.HookReady == true)
      return;

   // Check to make sure there is enough power to continue pulling
   %Energy = %Player.getEnergyLevel();
   if (%Energy < 8)
   {
      messageClient(%Player.client, 'MsgTurretMount', "Not enough power to continue core recoil.");
      %Player.GrapplingHookUnFire();
      return;
   }

   // Take energy for using the core
   %Player.setEnergyLevel(%Energy - %Player.GrapplingEnergyUsed);

   // Determine the Vector Between where the player is and where they should be
   %Transform = %Player.getTransform();
   %PlayerPosition = posFromTransform(%Transform);
   %Vector = vectorSub(%HookPosition, %PlayerPosition);

   // Do a check to see if we are moving further away and attempt to compensate
   // This is a springback effect of the "rope" becoming tight.
   %PullModifier = 1;
   %Distance = VectorLen(%Vector);
   if (%Distance > %Player.LastDistance)
      %PullModifier /= (%Player.LastDistance / %Distance);

   %Player.LastDistance = %Distance;

   // We normalize and then force a linear pull
   %Normal = VectorNormalize(%Vector);
   %Vector = VectorScale(%Normal, %Player.GrapplingPullForce * %PullModifier);

   // Cause the pulling
   %Player.applyImpulse(%PlayerPosition, %Vector);

   %Player.HookPull = %Player.schedule(500, GrapplingHookPullCheck, %HookPosition);
}

// This function returns the hook to the player and resets the grappling hook to ready
function Player::GrapplingHookUnFire(%Player)
{
   // If there is a hook, remove it.
   if (%Player.Hook !$= "")
   {
      %Player.Hook.setDamageState(Destroyed);
      %Player.Hook = "";
   }

   // If there is a throw in effect, cancel it.
   if (%Player.ThrowHook !$= "")
   {
      cancel(%Player.ThrowHook);
      %Player.ThrowHook = "";
   }

   // If there is a pull in effect, cancel it.
   if (%Player.HookPull !$= "")
   {
      cancel(%Player.HookPull);
      %Player.HookPull = "";
   }

   %Player.schedule(850, ReloadHook);
}

function Player::ReloadHook(%Player)
{
   %Player.HookReady = true;
}

function Player::GrapplingHook(%Player)
{
   // Don't do a thing if the player is piloting a vehicle
   if(%Player.isPilot() == true)
      return;

   // Move the Players arm down to thier waist
   %Player.setArmThread(look);

   // Get what the player already has in thier hands
   %OldWeapon = %Player.getMountedImage($WeaponSlot).getName();

   // If the grappling hook is not in the hand, then put it there
   if(%OldWeapon !$= "GrapplingHookImage")
   {
      messageClient(%Player.client, 'MsgRepairPackOn', '\c2Grappling Core Activated.');

      %Player.HookReady = true;

      // unmount any weapon the player may have been holding
      if(%Player.getMountedImage($WeaponSlot))
         %Player.unmountImage($WeaponSlot);

      // make sure player's arm thread is "look"
      %Player.setArmThread(look);

      // mount the Grappling Hook
      commandToClient(%obj.client, 'setRepairReticle');
      %Player.mountImage(GrapplingHookImage, $WeaponSlot);
      %Player.mountImage(GrapplingHookHookImage, 1);

      // If the player wants to see help, bottom print it for them.
      if (%Client.ShowHelp == true)
      {
         if (%Client.HelpSchedule !$= "")
            Cancel(%Client.HelpSchedule);

         %Message = $HelpDesc[GrapplingHookImage];
         %Lines = $HelpLines[GrapplingHookImage];
         if (%Message $= "")
            return;

         bottomPrint(%Client, %Message, 10, %Lines);
      
         %Client.HelpSchedule = schedule(%Lines * 6000, %Client, clearBottomPrint, %Client);
      }

   }

   commandToClient(%Player.client, 'setRepairReticle');
}

function Player::GrapplingHookObjectFasten(%Player, %Object)
{
   // If there is a throw in effect, cancel it.
   if (%Player.ThrowHook !$= "")
   {
      cancel(%Player.ThrowHook);
      %Player.ThrowHook = "";
   }   

   // Set the hook as having been fired
   %Player.HookReady = false;

   %Hook.setDamageState(Destroyed);

   messageClient(%Player.client, 'MsgTurretMount', "Grappling Core has fastened onto an object.");

   // Figure out the pull and energy consumption
   %MassFactor = (%Player.getDataBlock().mass + %Player.ExtraMass)/ 55;
   %Player.GrapplingPullForce = 740 * %MassFactor;

   if (%Player.hasEnergyPack == true)
      %Player.GrapplingPullForce *= 1.65;

   %EnergyTaken = 6.0;
   %EnergyTaken *= %MassFactor;
   %Player.GrapplingEnergyUsed = %EnergyTaken;
   %Player.LastDistance = 10000;

   %Player.HookPull = %Player.schedule(650, GrapplingHookObjectPullCheck, %Object);
}

// This is run to simulate the pulling of the cable as it is retracted into
// the electromechanical grappling hook
function Player::GrapplingHookObjectPullCheck(%Player, %Object)
{
   if (%Player.HookReady == true)
      return;

   // Check to make sure there is enough power to continue pulling
   %Energy = %Player.getEnergyLevel();
   if (%Energy < 8)
   {
      messageClient(%Player.client, 'MsgTurretMount', "Not enough power to continue core recoil.");
      %Player.GrapplingHookUnFire();
      return;
   }

   // Take energy for using the core
   %Player.setEnergyLevel(%Energy - %Player.GrapplingEnergyUsed);

   %HookPosition = %Object.getWorldBoxCenter();

   // Determine the Vector Between where the player is and where they should be
   %Transform = %Player.getTransform();
   %PlayerPosition = posFromTransform(%Transform);
   %Vector = vectorSub(%HookPosition, %PlayerPosition);

   // Do a check to see if we are moving further away and attempt to compensate
   // This is a springback effect of the "rope" becoming tight.
   %PullModifier = 1;
   %Distance = VectorLen(%Vector);
   if (%Distance > %Player.LastDistance)
      %PullModifier /= (%Player.LastDistance / %Distance);

   %Player.LastDistance = %Distance;

   // We normalize and then force a linear pull
   %Normal = VectorNormalize(%Vector);
   %Vector = VectorScale(%Normal, %Player.GrapplingPullForce * %PullModifier);

   // Cause the pulling
   %Player.applyImpulse(%PlayerPosition, %Vector);

   %Player.HookPull = %Player.schedule(500, GrapplingHookObjectPullCheck, %Object);
}
