
//--------------------------------------------------------------------------
// Shockwave
//--------------------------------------------------------------------------
datablock ShockwaveData(TurretShockwave)
{
   width = 6.0;
   numSegments = 20;
   numVertSegments = 2;
   velocity = 8;
   acceleration = 20.0;
   lifetimeMS = 1500;
   height = 1.0;
   verticalCurve = 0.5;

   mapToTerrain = false;
   renderBottom = true;

   texture[0] = "special/shockwave4";
   texture[1] = "special/gradient";
   texWrap = 6.0;

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

   colors[0] = "0.8 0.8 0.8 1.00";
   colors[1] = "0.8 0.5 0.2 0.20";
   colors[2] = "1.0 0.5 0.5 0.0";
};

//--------------------------------------------------------------------------
// Explosion
//--------------------------------------------------------------------------
datablock ExplosionData(TurretExplosion)
{
   explosionShape = "effect_plasma_explosion.dts";
   soundProfile = ShapeExplosionSound;
   faceViewer = true;
   shockwave = TurretShockwave;
};

datablock ExplosionData(SmallTurretExplosion)
{
   soundProfile = "DeployablesExplosionSound";
   faceViewer = true;

   explosionShape = "effect_plasma_explosion.dts";
   sizes[0] = "0.3 0.3 0.3";
   sizes[1] = "0.3 0.3 0.3";
   times[0] = 0;
   times[1] = 1;
};


//--------------------------------------------------------------------------
// Turret Debris
//--------------------------------------------------------------------------
datablock DebrisData (TurretDebris)
{
   explodeOnMaxBounce = false;

   elasticity = 0.20;
   friction = 0.5;

   lifetime = 17.0;
   lifetimeVariance = 0.0;

   minSpinSpeed = 60;
   maxSpinSpeed = 600;

   numBounces = 10;
   bounceVariance = 0;

   staticOnMaxBounce = true;

   useRadiusMass = true;
   baseRadius = 0.4;

   velocity = 9.0;
   velocityVariance = 4.5;
};             

datablock DebrisData (TurretDebrisSmall)
{
   explodeOnMaxBounce = false;

   elasticity = 0.20;
   friction = 0.5;

   lifetime = 17.0;
   lifetimeVariance = 0.0;

   minSpinSpeed = 60;
   maxSpinSpeed = 600;

   numBounces = 10;
   bounceVariance = 0;

   staticOnMaxBounce = true;

   useRadiusMass = true;
   baseRadius = 0.2;

   velocity = 5.0;
   velocityVariance = 2.5;
};             


//--------------------------------------------------------------------------
// Turret base class functionality.  Barrels are in scripts/weapons/*.cs
// 
// 
//--------------------------------------------------------------------------

function TurretData::create(%Block)
{
   %Scale = %Block.Scale;
   if (%Scale $= "")
      %Scale = "1 1 1";

   %Turret = new Turret()
   {
      dataBlock = %Block;
      scale = %Scale;
   };

   return %Turret;
}

datablock SensorData(TurretBaseSensorObj)
{
   detects = true;
   detectsUsingLOS = true;
   detectsPassiveJammed = false;
   detectsActiveJammed = false;
   detectsCloaked = false;
   detectionPings = true;
   detectRadius = 120;
};

datablock TurretData(TurretBaseLarge) : TurretDamageProfile
{
   className      = TurretBase;
   catagory       = "Turrets";
   shapeFile      = "turret_base_large.dts";
   preload        = true;

   Name = "a Turret Base Emplacement";

   mass           = 100.0;

   maxDamage      = 2.25;
   destroyedLevel = 2.25;
   disabledLevel  = 1.5;

   explosion      = TurretExplosion;
   expDmgRadius = 15.0;
   expDamage = 0.7;
   expImpulse = 2000.0;

   repairRate = 0;
   emap = true;
   
   thetaMin = 15;
   thetaMax = 140;

   isShielded           = true;
   energyPerDamagePoint = 64;

   maxEnergy = 220;
   rechargeRate = 0.22;

   humSound = SensorHumSound;
   pausePowerThread = true;

   canControl = true;

   cmdCategory = "Tactical";
   cmdIcon = CMDTurretIcon;
   cmdMiniIconName = "commander/MiniIcons/com_turretbase_grey";

   targetNameTag = 'Base';
   targetTypeTag = 'Turret';

   sensorData = TurretBaseSensorObj;
   sensorRadius = TurretBaseSensorObj.detectRadius;
   sensorColor = "0 212 45";

   firstPersonOnly = true;

   debrisShapeName = "debris_generic.dts";
   debris = TurretDebris;

   TurretBase = true;
};

datablock ShapeBaseImageData(DeployedTurretBaseImage)
{
   mass = 20.0;
   emap = true;

   shapeFile = "stackable3s.dts";
   item = DeployableTurretBase;
   deployed = DeployedTurretBase;

   mountPoint = 1;
   offset = "0 -0.2 -0.32";

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

   stateName[1] = "Activate";
   stateScript[1] = "onActivate";
   stateTransitionOnTriggerUp[1] = "Idle";

   maxDepSlope = 30;
};

datablock ItemData(DeployableTurretBase)
{
   className = Pack;
   catagory = "Deployables";
   shapeFile = "stackable3s.dts";

   mass = 1.0;
   elasticity = 0.2;
   friction = 0.6;
   pickupRadius = 1;
   rotate = false;

   image = DeployedTurretBaseImage;
   pickUpName = "a deployable turret base";

   computeCRC = true;
   emap = true;
};

datablock SensorData(DeployedTurretBaseSensor)
{
   detects = true;
   detectsUsingLOS = true;
   detectsPassiveJammed = false;
   detectsActiveJammed = false;
   detectsCloaked = false;
   detectionPings = true;
   detectRadius = 100;
};

datablock TurretData(DeployedTurretBase) : TurretBaseLarge
{
   className      = DeployedTurret;
   catagory       = "Turrets";
   shapeFile      = "turret_base_large.dts";
   preload        = true;

   Name = "a Deployable Turret Base";

   mass           = 100.0;
   scale          = "0.9 0.9 0.9";

   maxDamage      = 1.75;
   destroyedLevel = 1.75;
   disabledLevel  = 1.25;

   explosion      = TurretExplosion;
   expDmgRadius = 12.0;
   expDamage = 0.55;
   expImpulse = 1800.0;

   KeepPlayerRotation = true;

   item = DeployableTurretBase;
   image = DeployedTurretBaseImage;

   deployedObject = true;

   repairRate = 0;
   emap = true;
   
   thetaMin = 15;
   thetaMax = 140;

   isShielded           = true;
   energyPerDamagePoint = 76;

   maxEnergy = 180;
   rechargeRate = 0.20;

   humSound = SensorHumSound;
   pausePowerThread = true;
 
   canControl = true;

   cmdCategory = "Tactical";
   cmdIcon = CMDTurretIcon;
   cmdMiniIconName = "commander/MiniIcons/com_turretbase_grey";

   targetNameTag = 'Base';
   targetTypeTag = 'Turret';

   sensorData = DeployedTurretBaseSensor;
   sensorRadius = DeployedTurretBaseSensor.detectRadius;
   sensorColor = "0 212 45";

   firstPersonOnly = true;

   debrisShapeName = "debris_generic.dts";
   debris = TurretDebris;

   TurretBase = true;
};

function DeployedTurretBase::onAdd(%Data, %Turret, %Position)
{
   Parent::onAdd(%Data, %Turret, %Position);

   %Turret.setSelfPowered();

   if (%Turret.Owner !$= "")
   {
      %Position = posFromTransform(%Turret.getTransform());
      %Rotation = rotFromTransform(%Turret.Owner.player.getTransform());

      %Turret.setTransform(%Position @ " " @ %Rotation);
   }

   %Turret.inWater = CheckinWater(%Turret);
}

function DeployedTurretBase::onDestroyed(%data, %Turret, %prevState)
{
   // Allow it to be repurchased
   $TeamDeployedCount[%Turret.team, DeployableTurretBase]--;

   // Schedule the Deletion of the generator
   %Turret.schedule(150, delete);

   // Make the effects of smoking after destroyed.
   %Position = posFromTransform(%Object.getTransform());

   AreaSmoke(%Position);
   schedule(1000, 0, AreaSmoke, %Position);
}

function DeployedTurretBase::ItemSwap(%Data, %Object)
{
   %Transform = %Object.getTransform();
   %Position = posFromTransform(%Transform);

   %OldBarrel = %Object.getMountedImage(0).getName().item;

   if (%OldBarrel !$= "")
   {
      %Item = new Item()
      {
         dataBlock = %OldBarrel;
         position = %Position;
      };
      MissionCleanup.add(%Item);

      %Item.applyImpulse(%Position, "0 0 15");
   }

   %Item = %Object.getDataBlock().item;

   %NewItem = new Item()
   {
      dataBlock = %Item;
      position = %Position;
   };
   MissionCleanup.add(%NewItem);

   $TeamDeployedCount[%Object.team, %Item]--;

   %Object.delete();

   %NewItem.applyImpulse(%Position, "0 0 10");
   %NewItem.schedulePop();
}

function TurretData::onGainPowerEnabled(%data, %obj)
{
   Parent::onGainPowerEnabled(%data, %obj);
   setTargetSensorData(%obj.target, %data.sensorData);
}

function TurretData::onLosePowerDisabled(%data, %obj)
{
   // Must kick players out of turret

   Parent::onLosePowerDisabled(%data, %obj);
   %obj.clearTarget();
   setTargetSensorData(%obj.target, 0);
}

// This is the target selection code, it will not allow a mortar turret to fire
// at air vehicles (those with a forwardJetEmitter = "FlyerJetEmitter").  It also
// checks to see if Line of Sight is blocked and then it won't try to kill them
// since it can no longer see them.
function TurretData::selectTarget(%this, %Turret)
{
   %TurretTarget = %Turret.getTarget();
   if(%TurretTarget == -1)
      return(0);

   // if the turret isn't on a team, don't fire at anyone
   if(getTargetSensorGroup(%TurretTarget) == 0)
   {
      %Turret.clearTarget();
      return(0);
   }

   // stop firing if turret is disabled or if it needs power and isn't powered
   if((!%Turret.isPowered()) && (!%Turret.needsNoPower))
   {
      %Turret.clearTarget();
      return(0);
   }

   %TargetSearchMask = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType;
   %BarrelPosition = %Turret.getMuzzlePoint(0);

   %SearchRadius = %Turret.getMountedImage(0).attackRadius;

   InitContainerRadiusSearch(%BarrelPosition,
                             %SearchRadius,
                             %TargetSearchMask);

   while ((%PotentialTarget = ContainerSearchNext()) !$= "0")
   {
      %Target = %PotentialTarget.getTarget();
      if (%Turret.isValidTarget(%PotentialTarget) && (getTargetSensorGroup(%TurretTarget) != getTargetSensorGroup(%Target)))
      {
         // If valid and not on the same team as the turret, then we check further
         %IgnoreVehicle = %Turret.getMountedImage(0).IgnoreVehicle;
         if (%IgnoreVehicle == true && (%PotentialTarget.getType() & $TypeMasks::VehicleObjectType || %PotentialTarget.getObjectMount() > 0))
             continue;

         // If the target is a plane and the turret should ignore it, then do so
         %IgnoreFlier = %Turret.getMountedImage(0).getDataBlock().IgnoreFlier;
         %Emitter = %PotentialTarget.getDataBlock().forwardJetEmitter;
         if (%IgnoreFlier == true && (%Emitter $= FlyerJetEmitter || %PotentialTarget.getObjectMount() > 0))
             continue;

         // Check if there is anything obstructing the Turret's view.
         %Coverage = calcExplosionCoverage(%BarrelPosition, %Target, $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType);
         if (%Coverage <= 0.1)
            continue;

         // Since everything is ok, open fire!
         %Turret.setTargetObject(%PotentialTarget);
         return(0);
      }
   }
}

function TurretData::replaceCallback(%this, %turret, %engineer)
{
   // First, is the player in range?
   %pos         = %turret.getWorldBoxCenter();
   %engineerPos = %engineer.getWorldBoxCenter();
   %len = VectorLen(VectorSub(%pos, %engineerPos));
   
   if (%len < 8)
   {
      // This is a valid replacement.  First, let's see if the engineer
      //  still has the correct pack in place...
      if (%engineer.getMountedImage($BackPackSlot) != 0)
      {
         %barrel = %engineer.getMountedImage($BackPackSlot).turretBarrel;
         if (%barrel !$= "")
         {
            // if there was a barrel there before, get rid of it
            %turret.unmountImage(0);
            // remove the turret barrel pack
            %engineer.setInventory(%engineer.getMountedImage($BackPackSlot).item, 0);
            // mount new barrel on base
            %turret.mountImage(%barrel, 0, false);
         }
         else
         {
            // Player doesn't have the correct pack on...
         }
      }
      else
      {
         // Player doesn't have any pack on...
      }
   }
   else
   {
      // Player is too far away to replace the barrel...
   }
}

function TurretData::onDestroyed(%this, %turret, %prevState)
{
   if( isObject( %turret.lastProjectile ) )
      %turret.lastProjectile.delete();
         
   Parent::onDestroyed(%this, %turret, %prevState);

   // Make the effects of smoking after destroyed.
   %Position = posFromTransform(%turret.getTransform());

   AreaSmoke(%Position);
   schedule(1000, 0, AreaSmoke, %Position);
}

function TurretData::onCollision(%Data, %Turret, %Player)
{
   // If a friendly, ignore them
   if (%Turret.team == %Player.team)
      return;

   // If not a player, ignore it
   %ClassName = %Player.getDataBlock().className;
   if (%ClassName !$= "Armor")
      return;

   %Player.TurretShock = %Player.schedule(2000, TurretShock, %Turret);
}

datablock ItemData(TurretShockwaveItem)
{
   shapeFile = "turret_muzzlepoint.dts";
   mass = 0.1;

   elasticity = 0;
   friction = 1;
   pickupRadius = 0;

   maxDamage = 100;

   ApplyConcussion = true;

   explosion = ConcussionGrenadeExplosion;

   hasDamageRadius   = true;
   damageRadius      = 5.5;
   indirectDamage    = 0.15;
   radiusDamageType  = $DamageType::Electricity;
   kickBackStrength  = 1250;

   computeCRC = true;
};

function Player::TurretShock(%Player, %Turret)
{
   // If either the Player or Turret is no more, then abort.
   if (isObject(%Player) == false || isObject(%Turret) == false)
      return;

   // If the turret is dead, then abort.
   if (%Turret.isDisabled() == true)
      return;

   %PlayerPosition = posFromTransform(%Player.getTransform());
   %TurretPosition = %Turret.getWorldBoxCenter();

   %Distance = VectorLen(VectorSub(%PlayerPosition, %TurretPosition));

   // If the player is no longer close to the turret, abort.
   if (%Distance > 2.6)
      return;

   %Item = new Item()
   {
      dataBlock = TurretShockwaveItem;
      position = %TurretPosition;
      sourceObject = %Turret;
   };
   MissionClean.Add(%Item);

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

function checkTurretMount(%data, %Player, %slot)
{
   // Disable further searching
   %Player.setImageTrigger($BackpackSlot, false);

   // search for a turret base in player's LOS
   %EyeVector = VectorNormalize(%Player.getEyeVector());
   %SearchRange = VectorScale(%EyeVector, 6.0);
   %PlayerEyePosition = posFromTransform(%Player.getEyeTransform());
   %SearchEnd = VectorAdd(%PlayerEyePosition, %SearchRange);
   %Turret = ContainerRayCast(%PlayerEyePosition, %SearchEnd, $TypeMasks::TurretObjectType);

   if(%Turret $= "0")
   {
      // I don't see any turret
      messageClient(%Player.client, 'MsgNoTurretBase', "\c2No turret within range.");
      return;
   }

   %Turret = getWord(%Turret, 0);

   // Can only mount small turret barrels on small turrets
   if(%Turret.getDataBlock().TurretBase != true)
   {
      messageClient(%Player.client, 'MsgNotTurretBase', "\c2Can only mount a large barrel on a MPB turret base or a turret emplacement.");
      return;
   }

   // Can only change out turrets on your team or universal (no idea why they would want to, but they can)
   if(%Turret.team != %Player.client.team && %Turret.team != 0)
   {
     messageClient(%Player.client, 'MsgTryEnemyTurretMount', "\c2Cannot mount a barrel on an enemy turret base!");
     return;
   }

   // Turret must be functional to trade out the barrel
   if(%Turret.getDamageState() !$= "Enabled")
   {
      messageClient(%Player.client, 'MsgBaseDestroyed', "\c2Turret base is disabled, cannot mount new barrel.");
      return;
   }

   // Everything is good, do the trade
   %NewBarrel = %Player.getMountedImage($BackPackSlot);
   %NewBarrelName = getWords(%NewBarrel.item.pickupName, 0, 2);

   %OldBarrel = %Turret.getMountedImage(0).getName().item;

   serverPlay3D(TurretPackActivateSound, %Turret.getTransform());
   %Turret.initiateBarrelSwap(%Player);

   %BarrelMessage = "Installing " @ %NewBarrelName;

   if (%OldBarrel !$= "")
   {
      %OldBarrelName = getWords(%OldBarrel.pickupName, 0, 2);
      %BarrelMessage = %BarrelMessage @ " in place of " @ %OldBarrelName;

      %Item = new Item()
      {
         dataBlock = %OldBarrel;
         position = %PlayerEyePosition;
      };
      MissionCleanup.add(%Item);

      %Player.pickUp(%Item, 1);
      %Item.schedule(500, delete);
   }

   %TurretBase = %Turret.getDataBlock().Name;
   %BarrelMessage = %BarrelMessage @ " in " @ %TurretBase @ ".";
   messageClient(%Player.client, 'MsgTurretMount', "\c2" @ %BarrelMessage @ ".");

   if (%Turret.getDataBlock().getName() $= "MobileTurretBase")
      %Turret.Vehicle.Barrel = %NewBarrel.turretBarrel;
}

function checkSmallTurretMount(%data, %Player, %slot)
{
   // Disable further searching
   %Player.setImageTrigger($BackpackSlot, false);

   // search for a turret base in player's LOS
   %EyeVector = VectorNormalize(%Player.getEyeVector());
   %SearchRange = VectorScale(%EyeVector, 5.0);
   %PlayerEyePosition = posFromTransform(%Player.getEyeTransform());
   %SearchEnd = VectorAdd(%PlayerEyePosition, %SearchRange);
   %Turret = ContainerRayCast(%PlayerEyePosition, %SearchEnd, $TypeMasks::TurretObjectType);

   if(%Turret $= "0")
   {
      // I don't see any turret
      messageClient(%Player.client, 'MsgNoTurretBase', "\c2No turret within range.");
      return;
   }

   %Turret = getWord(%Turret, 0);
   // Can only mount small turret barrels on small turrets
   if(%Turret.getDataBlock().SmallTurretBase != true)
   {
      messageClient(%Player.client, 'MsgNotTurretBase', "\c2Can only mount a small barrel on a spider clamp or land spike turret base.");
      return;
   }

   // Can only change out turrets on your team or universal (no idea why they would want to, but we let them)
   if(%Turret.team != %Player.client.team && %Turret.team != 0)
   {
     messageClient(%Player.client, 'MsgTryEnemyTurretMount', "\c2Cannot mount a small barrel on an enemy small turret base!");
     return;
   }

   // Turret must be functional to trade out the barrel
   if(%Turret.getDamageState() !$= "Enabled")
   {
      messageClient(%Player.client, 'MsgBaseDestroyed', "\c2Small Turret base is disabled, cannot mount new barrel.");
      return;
   }

   // Everything is good, do the trade
   %NewBarrel = %Player.getMountedImage($BackPackSlot);
   %NewBarrelName = getWords(%NewBarrel.item.pickUpName, 0, 2);

   $TeamDeployCount[%Player.team, %NewBarrel.item]++;

   %OldBarrel = %Turret.getMountedImage(0).getName().item;

   if (%OldBarrel !$= "")
      $TeamDeployCount[%Player.team, %OldBarrel]--;
   

   // If there was a special object around the turret, remove it now.
   %OldExtraObject = %Turret.getMountedImage(0).ExtraObject;
   if (%OldExtraObject !$= "")
      %OldExtraObject.delete();

   serverPlay3D(TurretPackActivateSound, %Turret.getTransform());

   // Take the Backpack off and put it in the turret
   %Turret.unMountImage(0);
   %Turret.mountImage(%NewBarrel.deployed, 0, false);

   // If there is a special object to place around the turret, do that now
   %ExtraObject = %NewBarrel.deployed.ExtraObject;
   if (%ExtraObject !$= "")
   {
      %Position = posFromTransform(%Turret.getTransform());
      %Offset = %ExtraObject.offset;
      %Position = VectorAdd(%Position, %Offset);

      %Item = new Item()
      {
         dataBlock = %ExtraObject;
         position = %Position;
      };
      MissionCleanup.add(%Item);
   }

   // Remove the old Pack from the Players Back and Inventory
   %Player.unmountImage(%Slot);
   %Player.decInventory(%NewBarrel.item, 1);  

   %BarrelMessage = "Installing " @ %NewBarrelName;

   if (%OldBarrel !$= "")
   {
      %OldBarrelName = getWords(%OldBarrel.pickupName, 0, 2);
      %BarrelMessage = %BarrelMessage @ " in place of " @ %OldBarrelName;

      // This is cheap, but it drops the old pack right on top of the player using
      // the ingame collision to pick it back up.
      %Item = new Item()
      {
         dataBlock = %OldBarrel;
         position = %PlayerEyePosition;
      };
      MissionCleanup.add(%Item);

      %Player.pickUp(%Item, 1);
      %Item.schedule(500, delete);
   }

   %Turret.NoRepack = true;
   schedule(2000, 0, ClearRepack, %Turret);

   %TurretBaseName = %Turret.getDataBlock().Name;

   %BarrelMessage = %BarrelMessage @ " in " @ %TurretBaseName @ ".";

   messageClient(%Player.client, 'MsgTurretMount', "\c2" @ %BarrelMessage @ ".");
}

function serverCmdChangeTurretMode(%Client, %Mode)
{
   if (%Client.player.getArmorSize() !$= "Engineer")
   {
      messageClient(%Client, "", "You are not uplinked with any friendly deployables.");
      return;
   }

   if (%Client.player.EngObject $= "")
   {
      messageClient(%Client, "", "You are not uplinked with any friendly deployables.");
      return;
   }

   if (isObject(%Client.player.EngObject) == false)
   {
      messageClient(%Client, "", "You are not uplinked with any friendly deployables.");
      return;
   }

   %Client.player.EngObject.ChangeTurretMode(%Client, %Mode);
}

// Execute all the Other Scripts
exec("scripts/turrets/sentryturret.cs");
exec("Scripts/turrets/indoorDeployableBarrel.cs");
exec("Scripts/turrets/outdoorDeployableBarrel.cs");
exec("Scripts/turrets/EMPBarrel.cs");
exec("Scripts/turrets/FlameBarrel.cs");
exec("Scripts/turrets/FusionBarrel.cs");
exec("Scripts/turrets/ImpulseBarrel.cs");
exec("Scripts/turrets/IonBarrel.cs");
exec("Scripts/turrets/LaserBarrel.cs");
exec("Scripts/turrets/TractorBarrel.cs");
exec("Scripts/turrets/AABarrelLarge.cs");
exec("Scripts/turrets/ElfBarrelLarge.cs");
exec("Scripts/turrets/MissileBarrelLarge.cs");
exec("Scripts/turrets/MortarBarrelLarge.cs");
exec("Scripts/turrets/PlasmaBarrelLarge.cs");

// Fix Dynamix's mistake
AABarrelLarge.item = AABarrelPack;
ELFBarrelLarge.item = ELFBarrelPack;
MissileBarrelLarge.item = MissileBarrelPack;
MortarBarrelLarge.item = MortarBarrelPack;
MortarBarrelLarge.IgnoreFlier = true;
PlasmaBarrelLarge.item = PlasmaBarrelPack;