
// Because only StaticShapes can be deployed, we deploy a tiny object with
// that is invisible and then use it's properties to properly
// place the forcefield.  Once it is placed a generator is placed between 
// the player and the field.  If the generator is destroyed, it will take
// down the field.

// ------------------------------------------------------------------------
// Packs
// ------------------------------------------------------------------------

datablock ShapeBaseImageData(ForceFieldDeployableImage)
{
   mass = 8.0;
   emap = true;

   shapeFile = "stackable1s.dts";
   item = ForceFieldDeployable;
   deployed = TempForceField;

   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(ForceFieldDeployable)
{
   className = Pack;
   catagory = "Deployables";
   shapeFile = "stackable1s.dts";

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

   image = ForceFieldDeployableImage;
   pickUpName = "a portable forcefield generator";

   computeCRC = true;
   emap = true;
};

function ForceFieldDeployableImage::onActivate(%data, %Player, %slot)
{
   %Velocity = %Player.getVelocity();
   if (VectorLen(%Velocity) > 0.1)
      return;
   
   ShapeBaseImageData::onActivate(%data, %Player, %slot);
}

// ------------------------------------------------------------------------
// Temporary Object
// ------------------------------------------------------------------------
datablock StaticShapeData(TempForceField)
{  
   className = Station;
   catagory = "Temp";
   shapeFile = "turret_muzzlepoint.dts";

   deployed = deployFieldBare;
};

// This function waits for the object to be placed and then calls the function
// to replace it'self with a forcefield array.
function TempForceField::onAdd(%data, %Object, %Position)
{
   schedule(250, 0, CreateForceField, %Object);
}

// ------------------------------------------------------------------------
// ForceField
// ------------------------------------------------------------------------

datablock ForceFieldBareData(deployFieldBare)
{
   className = "DeployedTurret";
   Category = "ForceField";

   fadeMS           = 800;
   baseTranslucency = 0.14;
   powerOffTranslucency = 0.0;
   teamPermiable    = true;
   otherPermiable   = false;
   color            = "0.28 0.89 0.31";
   powerOffColor    = "0.0 0.0 0.0";
   targetTypeTag    = 'Deployable ForceField'; 

   texture[0] = "skins/forcef1";
   texture[1] = "skins/forcef2";
   texture[2] = "skins/forcef3";
   texture[3] = "skins/forcef4";
   texture[4] = "skins/forcef5";

   framesPerSec = 10;
   numFrames = 5;
   scrollSpeed = 15;
   umapping = 1.0;
   vmapping = 0.15;
};

function deployFieldBare::damageObject(%Data, %Field, %SourceObject, %Position, %Amount, %DamageType)
{
   // Adjust damage based on type
   %DamageModifier = $shieldDamageScale[%damageType];
   if (%DamageModifier !$= "")
      %Amount *= %DamageModifier;

   // Load the Generators Energy Level
   %Generator = %Field.Generator;
   %Energy = %Generator.getEnergyLevel();

   %GeneratorData = %Generator.getDataBlock();

   %EnergyUsed = %Amount * %GeneratorData.energyPerDamagePoint;
   %NewEnergy = %Energy - %EnergyUsed;

   // if too much damage was taken, then apply the extra damage to the generator
   if (%NewEnergy <= 0)
   {
      %ExtraDamage = (%NewEnergy / %GeneratorData.energyPerDamagePoint) * -1;
      %NewEnergy = 0;
   } else {
      %ExtraDamage = 0;
   }
   %Generator.setEnergyLevel(%NewEnergy);
   
   // If there was extra damage, apply it to the generator itself
   if (%ExtraDamage > 0)
      %GeneratorData.damageObject(%Generator, %SourceObject, %Position, %ExtraDamage, %DamageType);

   // Check for an overload of too much damage in too short a period of time for it to handle.
//   %Generator.recentDamage += %EnergyUsed;
//   if (%Generator.recentDamageCheck $= "")
//      %Generator.recentDamageCheck = %Generator.schedule(500, recentDamageCheck);
}

// Turns on or off a forcefield.
function deployFieldBare::onTrigger(%this, %TriggerId, %On)
{
   // Check to see if the player is allowed through
   if (TriggerCheck(%this, %TriggerId, true) == false)
      return;

   // Increment/Decrement the number of Players holding the Door open
   if (%On == true) {
      %this.triggerCount++;
   } else {
      if (%this.triggerCount > 0)
         %this.triggerCount--;
   }

   // If someone/noone was holding it open, then open/close it.
   if (%this.triggerCount > 0) {
      %this.open();
   } else {
      %this.close();
   }
}


// If power is restored to the Forcefield (The generator was fixed)
// then reinstate the forcefield.
function deployFieldBare::gainPower(%data, %ForceField)
{
   if (%ForceField.CycleSchedule !$= "")
      return;

   Parent::gainPower(%data, %ForceField);
   %ForceField.close();

   // Activate the field's physical zone, if it has one
   if (%ForceField.PZone !$= "")
   {
      %ForceField.PZone.activate();
      killAllPlayersWithinZone(%data, %ForceField, %ForceField.team);
   }
}

// If power is cut to the Forcefield (The generator was damaged)
// then cut the forcefield.
function deployFieldBare::losePower(%data, %ForceField)
{
   Parent::losePower(%data, %ForceField);
   %ForceField.open();

   // Deactivate the field's physical zone, if it has one.
   if (%ForceField.PZone !$= "")
      %ForceField.PZone.deactivate();
}


// Causes the forcefield to open for a few seconds.
function ForceFieldCycle(%ForceField)
{
   %Random = (getRandom() * 2) + 2.5;

   ForceFieldDown(%ForceField);

   %ForceField.CycleSchedule = schedule(%Random * 1000, 0, ForceFieldUp, %ForceField);
}

// Opens the forcefield
function ForceFieldDown(%ForceField)
{
   %ForceField.open();

   if (%ForceField.PZone !$= "")
      %ForceField.PZone.deactivate();
}

// Closes the forcefield back
function ForceFieldUp(%ForceField)
{
   // Clear the restore schedule marker
   if (%ForceField.CycleSchedule !$= "")
      %ForceField.CycleSchedule = "";

   // If the ForceField Generator is down, don't raise it
   %Generator = %ForceField.Generator;
   if (%Generator.getDamageLevel() > %Generator.getDataBlock().disabledLevel)
      return;

   %ForceField.close();

   if (%ForceField.PZone !$= "")
   {
      %ForceField.PZone.activate();
      killAllPlayersWithinZone(%Data, %ForceField, %ForceField.Team);
   }
}

// ------------------------------------------------------------------------
// Generator
// ------------------------------------------------------------------------

datablock ItemData(ForceFieldGeneratorColor)
{
   shapeFile = "ammo_disc.dts";

   sticky = true;
   mass = 1;
   friction = 1.0;

   haslightradius = true;
   lightType = "PulsingLight";
   lightColor  = "0.28 0.89 0.31";
   lightTime = "4500";
   lightRadius = "2.5";

   computeCRC = true;
   emap = true;
};

datablock StaticShapeData(ForceFieldGeneratorDeployed) : StaticShapeDamageProfile
{
   className = Generator;
   shapeFile = "stackable1s.dts";
   image = ForceFieldDeployable;

   maxDamage = 0.8;
   destroyedLevel = 0.8;
   disabledLevel = 0.5;

   energyPerDamagePoint = 50;
   maxEnergy = 300;
   rechargeRate = 0.08;
   damagePerSecond = 150;
   providesPower = true;
   noDefendBonus = true;

   engType = "FFGenerator";

   Name = "Deployable Forcefield Generator";
   item = ForceFieldDeployable;

   deployedObject = true;

   cmdCategory = "DSupport";
   cmdIcon = CMDSensorIcon;
   cmdMiniIconName = "commander/MiniIcons/com_deploypulsesensor";
   targetNameTag = 'FF Generator';
   targetTypeTag = 'Portable FF Generator';

   explosion = DeployablesExplosion;
   dynamicType = $TypeMasks::SensorObjectType;
   debrisShapeName = "debris_generic_small.dts";
   debris = DeployableDebris;
   heatSignature = 0;
};

function ForceFieldGeneratorDeployed::onDestroyed(%this, %Generator, %prevState)
{
   // Remove the Field a second later
   %ForceField = %Generator.ForceField;
   %ForceField.schedule(900, delete);

   // Allow it to be repurchased
   $TeamDeployedCount[%Generator.team, ForceFieldDeployable]--;

   // Remove the Physical Zone immediately.
   %PZone = %ForceField.PZone;
   %PZone.Delete();

   %Trigger = %Generator.hacktrigger;
   %Trigger.delete();

   // Schedule the Deletion of the generator
   %Generator.schedule(450, delete);
}

function ForceFieldGeneratorDeployed::onRemove(%this, %Generator)
{
   // Remove the Field a second later
   %ForceField = %Generator.ForceField;
   %ForceField.delete();

   // Remove the Color and Effects Object
   %Generator.ExtraObject.delete();

   // Remove the Physical Zone immediately.
   %PZone = %ForceField.PZone;
   %PZone.Delete();

   %Trigger = %Generator.hacktrigger;
   %Trigger.delete();
}

function ForceFieldGeneratorDeployed::recentDamageCheck(%Data, %Generator)
{
   // If disabled, we don't try any further
   if (%Generator.disabled == true)
      return(0);

   %Data = %Generator.getDataBlock();
   %RecentDamage = %Generator.recentDamage;
   %Allowed = %Data.damagePerSecond;

   // If the shielding is being overloaded by too much damage all at once, then cause damage to
   // the shield generator itself in the form of feedback.  Start will a little smoke and eventually
   // it can catch on fire or explode.
   if (%RecentDamage > %Allowed)
   {
      %Damage = (%RecentDamage - %Allowed) / %Data.maxEnergy;
      %Generator.recentDamage = %RecentDamage - %Allowed;

      %Generator.damageObject(%this, 0, "0 0 0", %Damage, 0);

      if (%Generator.getDamageLevel() >= %Data.disabledLevel)
      {
         // If disabled, we catch it on fire and explode it.
         %Position = posFromTransform(%Generator.getTransform());
         AreaFire(%Position);
         %Generator.disabled = true;
         schedule(1000, 0, AreaSmoke, %Position);
         schedule(2000, 0, AreaSmoke, %Position);
         %Generator.schedule(2800, setDamageState, Destroyed);
      } else {
         AreaSmoke(%Position);
         %Generator.recentDamageCheck = %Data.schedule(1000, recentDamageCheck);
      }         
   } else {
      %Generator.recentDamage = 0;
   }
}

// ------------------------------------------------------------------------
// Associated Functions
// ------------------------------------------------------------------------

// This function makes a forcefield with the same properties as the 
// temporary marker and then removes that marker.
function CreateForceField(%BasedOn)
{
   %Team = %BasedOn.team;
   %SourceObject = %BasedOn.owner.player;

   %Transform = %BasedOn.getTransform();
   %Rotation = rotFromTransform(%Transform);
   %PlacementPosition = posFromTransform(%Transform);
   %PositionXY = getWords(%PlacementPosition, 0, 1);
   %PositionZ = getWord(%PlacementPosition, 2) - 0.5;
   %Position = %PositionXY @ " " @ %PositionZ;

   // The rotation and position need to be adjusted so that the forcefield is
   // perpendicular to the player that placed it and centered in front of them.
   %NewRotation = getWords(%Rotation, 0, 2) @ " " @ getWord(%Rotation, 3) + 1.57;

   %PlayerTransform = %SourceObject.getTransform();
   %PlayerPosition = posFromTransform(%PlayerTransform);
   %PlayerRotation = rotFromTransform(%PlayerTransform);
   %PlayerVector = VectorNormalize(getWords(%SourceObject.getEyeVector(), 0, 1) @ " 0");

   %RotatedVector = VectorScale(VectorCross(%PlayerVector, "0 0 1"), 5);

   // Scan to determine the dimensions of the forcefield.
   %ScaleThick = 0.2;
   %ScaleThick2 = 2.2;
   %ScaleWidth = 11.0;
   %ScaleHieght = 6.5;

   %SearchMask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType;

   // Scan from the NewPosition to up to see where the ceiling is.
   %VerticalVector = VectorScale("0 0 1", %ScaleHieght);
   %SearchPosition = getWords(%PlacementPosition, 0, 1) @ " " @ (getWord(%PlacementPosition, 2) + 0.1);
   %SearchEnd = VectorAdd(%SearchPosition, %VerticalVector);
   %Target = ContainerRayCast(%SearchPosition, %SearchEnd, %SearchMask);

   if (%Target !$= "0")
   {
      %TargetPosition = getWords(%Target, 1, 3);
      %ScaleHieght = VectorDist(%PlacementPosition, %TargetPosition);
   }

   // Scan either side by half the %ScaleWidth and see where the wall is.
   // We search to one side then the other from the center of the forcefield
   %SearchPosition = getWords(%PlacementPosition, 0, 1) @ " " @ getWord(%PlacementPosition, 2) + (%ScaleHieght / 2);

   %SearchEnd = VectorAdd(%SearchPosition, %RotatedVector);
   %Target = ContainerRayCast(%SearchPosition, %SearchEnd, %SearchMask);

   if (%Target !$= "0")
   {
      %TargetPosition = getWords(%Target, 1, 3);
      %ScaleWidth1 = VectorDist(%SearchPosition, %TargetPosition);
   } else {
      %ScaleWidth1 = (%ScaleWidth / 2);
   }

   %OtherRotatedVector = VectorScale(VectorNormalize(%RotatedVector), -5.5);
   %SearchEnd = VectorAdd(%SearchPosition, %OtherRotatedVector);
   %Target = ContainerRayCast(%SearchPosition, %SearchEnd, %SearchMask);

   if (%Target !$= "0")
   {
      %TargetPosition = getWords(%Target, 1, 3);
      %ScaleWidth2 = VectorDist(%SearchPosition, %TargetPosition);
   } else {
      %ScaleWidth2 = (%ScaleWidth / 2);
   }

   %ScaleWidth = %ScaleWidth1 + %ScaleWidth2;

   if (getWord(%Rotation, 2) > 0.98)
      %RotatedVector = VectorScale(VectorCross(%PlayerVector, "0 0 1"), -1 * (%ScaleWidth2) - 0.5);
   else
      %RotatedVector = VectorScale(VectorCross(%PlayerVector, "0 0 1"), %ScaleWidth1 + 0.5);

   %NewPosition = VectorAdd(%Position, %RotatedVector);
   %NewPosition2 = VectorAdd(%NewPosition, %PlayerVector);
   %NewTransform = %NewPosition @ " " @ %NewRotation;
   %NewTransform2 = %NewPosition2 @ " " @ %NewRotation;

   // Since the ForceField goes down into it's surroundings to seal things off, we do that now.
   %ScaleHieght += 1.0;
   %ScaleWidth += 1.0;

   %Scale = %ScaleThick @ " " @ %ScaleWidth @ " " @ %ScaleHieght;
   %Scale2 = %ScaleThick2 @ " " @ %ScaleWidth @ " " @ %ScaleHieght;

   // Increment the ForceField group and then make the forcefield and place it in it's group.
   $ForceFieldNo++;

   %ForceField = new ForceFieldBare()
   {
      dataBlock = "deployFieldBare";
      scale = %scale;
      team = %team;
   };

   %ForceFieldGroup = nameToID("MissionCleanup/Deployables/ForceField" @ $ForceFieldNo);
   if (%ForceFieldGroup <= 0)
   {
      %DeployableGroup = nameToID("MissionCleanup/Deployables");
      if (%DeployableGroup <= 0)
      {
         %DeployableGroup = new SimGroup("Deployables");
         MissionCleanup.add(%DeployableGroup);
      }

      %ForceFieldGroup = new SimGroup("ForceField" @ $ForceFieldNo);
      %DeployableGroup.add(%ForceFieldGroup);
   }
   %ForceFieldGroup.add(%ForceField);

   %ForceField.sourceObject = %SourceObject;
   %ForceField.team = %Team;
   %ForceField.setTransform(%NewTransform);
   %ForceField.rotatedVector = VectorNormalize(%RotatedVector);
   %ForceField.forwardVector = VectorNormalize(%PlayerVector);

   // Place the generator object and link it to the field
   %Generator = new StaticShape()
   {
      dataBlock = "ForceFieldGeneratorDeployed";
      position = %PlayerPosition;
      team = %team;
      owner = %BasedOn.owner;
   };                
   %ForceFieldGroup.add(%Generator);

   %Generator.ForceField = %ForceField;
   %ForceField.Generator = %Generator;

   %Generator.setTransform(%PlayerTransform);
   %Generator.setRechargeRate(%Generator.getDataBlock().rechargeRate);

   %Generator.BaseFFCenter[1] = posFromTransform(%Transform);

   // Remove the marker
   %BasedOn.delete();

   // Make the New Physical Zone
   %PZone = new PhysicalZone()
   {
      position = %ForceField.position;
      rotation = %ForceField.rotation;
      scale    = %ForceField.scale;
      polyhedron = "0.000000 1.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
      velocityMod  = 0.7;
      appliedForce = "0 0 0";
      team = %team;
   };

   %pzGroup = nameToID("MissionCleanup/PZones");
   if(%pzGroup <= 0)
   {
      %pzGroup = new SimGroup("PZones");
      MissionCleanup.add(%pzGroup);
   }
   %pzGroup.add(%PZone);
 
   %PZone.ffield = %ForceField;
   %ForceField.PZone = %PZone;

   // Make the color and effects object
   %PlayerPositionXY = getWords(%PlayerPosition, 0, 1);
   %PlayerPositionZ = getWords(%PlayerPosition, 2) + 0.36;
   %PlayerPosition2 = %PlayerPositionXY @ " " @ %PlayerPositionZ;

   %ExtraObject = new Item()
   {
      dataBlock = ForceFieldGeneratorColor;
      position = %PlayerPosition2;
   };
   MissionCleanUp.Add(%ExtraObject);
   %Generator.ExtraObject = %ExtraObject;

   // Make the hacking trigger
   %HackTrigger = new Trigger()
   {
      dataBlock = HackersForceFieldTrigger;
      polyhedron = "0.0 1.0 0.0 1.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 1.0";
      scale    = %Scale2;
      team = %team;
   };             
   MissionCleanup.add(%HackTrigger);

   %HackTrigger.setTransform(%NewTransform2);

   %HackTrigger.ForceField = %ForceField;
   %HackTrigger.Generator = %Generator;
   %ForceField.hacktrigger = %HackTrigger;
   %Generator.hacktrigger = %HackTrigger;

   %ForceFieldGroup.powerInit(1);

   %ForceFieldData = %ForceField.getDatablock();
   %ForceFieldData.gainPower(%ForceField);

   %ForceField.target = createTarget(%ForceField, "", "", "", %ForceFieldData.targetTypeTag, %Team, 0);
   setTargetSensorGroup(%ForceField.getTarget(), %Team);

   %Generator.target = createTarget(%Generator, "", "", "", %Generator.getDataBlock().targetTypeTag, %Team, 0);
   setTargetSensorGroup(%Generator.getTarget(), %Team);

   schedule(2500, 0, ForceFieldClearCheck, %ForceField);

   // Precalculate the various base transforms
   %Generator.getDataBlock().CalculateBasePositions(%Generator);
}

function ForceFieldGeneratorDeployed::onDisabled(%Data, %Generator, %PreviousState)
{
   %Generator.decPowerCount();
   Parent::onDisabled(%Data, %Generator, %PreviousState);
}

function ForceFieldGeneratorDeployed::onEnabled(%data, %Generator, %PreviousState)
{
   %Generator.incPowerCount();
   Parent::onEnabled(%Data, %Generator, %PreviousState);

   // Restart the power from scratch
   %Generator.setEnergyLevel(0);
}  

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

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

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

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

   %Object.ForceField.PZone.delete();
   %Object.ForceField.delete();
   %Object.hacktrigger.delete();
   %Object.delete();

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

   %NewItem.schedulePop();
}

function ForceFieldClearCheck(%ForceField)
{
   // If the forcefield no longer exists, just stop here
   if (isObject(%ForceField) == false)
      return;

   %DeployablesGroup = nameToID("MissionCleanup/Deployables");
   if (%DeployablesGroup $= "")
      %DeployablesGroup = 0;

   %Position = %ForceField.Generator.BaseFFCenter[1];

   InitContainerRadiusSearch(%Position, 12, $TypeMasks::VehicleObjectType |
                                            $TypeMasks::StationObjectType |
                                            $TypeMasks::GeneratorObjectType |
                                            $TypeMasks::SensorObjectType |
                                            $TypeMasks::TurretObjectType |
                                            $TypeMasks::StaticObjectType |
                                            $TypeMasks::MoveableObjectType |
                                            $TypeMasks::DamagableItemObjectType);

   %numTargets = 0;
   while ((%TargetObject = containerSearchNext()) != 0)
   {
      %Group = %TargetObject.getGroup();

      if (%TargetObject.getDataBlock().deployedObject == true || %Group == %DeployablesGroup)
      {
         if (%TargetObject.isInForceField() == %ForceField)
         {
            if (%TargetObject.team == %ForceField.Generator.owner.team)
            {
               %Targets[%numTargets] = %TargetObject;
               %numTargets++;
            }
         }
      }
   }

   // If there are targets in the array, now apply the damage, impulses, and effects per thier distance
   for (%i = 0; %i < %numTargets; %i++)
   {
      %Object = %Targets[%i];
      if (%Object.owner == %ForceField.Generator.owner)
      {
         messageClient(%ForceField.Generator.owner, "", "Your forcefield has overpowered one of your deployables and destroyed it.");
      } else {
         messageClient(%Object.owner, "", "Your deployable has been overpowered by a friendly forcefield generator.");
         messageClient(%ForceField.Generator.owner, "", "Your forcefield has overpowered a friendly deployable and destroyed it.");
      }

      %Object.getDataBlock().damageObject(%Object, %ForceField.Generator.owner.player, "0 0 0", 100, $DamageType::Electricity, "0 0 0");
   }
}

function RepackForcefield(%ForceField)
{
   %ForceField.Generator.RepackObject(%ForceField.Generator.owner);
}

// This function precalculates the 5 possible orientations of the forcefields and
// records them for use later in an array attached to the generator.
function ForceFieldGeneratorDeployed::CalculateBasePositions(%data, %Generator)
{
   echo("Calculating Base Positions for Generator: " @ %Generator);

   %ForceField = %Generator.ForceField;

   %GeneratorTransform = %Generator.getTransform();
   %GeneratorPosition = posFromTransform(%GeneratorTransform);

   %BaseTransform = %ForceField.getTransform();
   %BasePosition = posFromTransform(%BaseTransform);
   %BaseRotation = rotFromTransform(%BaseTransform);

   %BaseRotationX = getWord(%Rotation, 0);
   %BaseRotationY = getWord(%Rotation, 1);
   %BaseRotationZ = getWord(%Rotation, 2);
   %BaseRotationA = getWord(%Rotation, 3);

   // Record the original position as 1
   %Generator.BaseFFTransform[1] = %BaseTransform;
   %Generator.BaseFFScale[1] = "0.2 11 6.5";

   %Generator.CurrentFFTransform = 1;
   %Generator.CurrentUpDown = 0;
   %Generator.CurrentLeftRight = 0;

   // Record the position on the other side of the generator the same distance as 2
   %VectorBetween = VectorScale(%ForceField.forwardVector, -5.8);
   %BasePosition2 = VectorAdd(%BasePosition, %VectorBetween);

   %Generator.BaseFFTransform[2] = %BasePosition2 @ " " @ %BaseRotation;
   %Generator.BaseFFScale[2] = "0.2 11 6.5";
   %Generator.BaseFFCenter[2] = VectorAdd(%Generator.BaseFFCenter[1], %VectorBetween);
   
   // Record the position in front of the generator, but layed down as 3
   %BasePosition3 = VectorAdd(%BasePosition, VectorScale(%ForceField.forwardVector, 5));
   %Generator.BaseFFTransform[3] = %BasePosition3 @ " " @ %BaseRotation;
   %Generator.BaseFFScale[3] = "6.5 11 0.2";
   %Generator.BaseFFCenter[3] = VectorAdd(%Generator.BaseFFCenter[1], VectorScale(%ForceField.forwardVector, 5));

   // Record the position behind of the generator, but layed down as 4
   %Generator.BaseFFTransform[4] = %BasePosition2 @ " " @ %BaseRotation;
   %Generator.BaseFFScale[4] = "6.5 11 0.2";
   %Generator.BaseFFCenter[3] = %Generator.BaseFFCenter[2];

   // Record the position above the Generator as 5
   %BaseOffset4 = VectorScale(VectorNormalize(%VectorBetween), -3);
   %BasePosition4 = VectorAdd(%GeneratorPosition, %BaseOffset4);
   %BasePosition4 = VectorAdd(%BasePosition, "0 0 5");
   %Generator.BaseFFTransform[5] = %BasePosition4 @ " " @ %BaseRotation;
   %Generator.BaseFFScale[5] = "6.5 11 0.2";
   %Generator.BaseFFCenter[5] = VectorAdd(%Generator.BaseFFCenter[1], "0 0 5");
}

// This function readjusts the size of the field based on it's surroundings each time the
// field is moved to prevent it from pushing through improperly.  This is called after an
// orientation change or 1.5 seconds after the last move (to decrease unneccessary processes).
function ForceFieldGeneratorDeployed::ForceFieldAdjust(%data, %Generator)
{
   if (%Generator.CurrentFFTransform < 3)
      %Data.VForceFieldAdjust(%Generator);
   else
      %Data.HForceFieldAdjust(%Generator);

   %ForceField = %Generator.ForceField;
   KillAllPlayersWithinZone(%Data, %ForceField, %ForceField.Team);
}

// This function works for Vertical Forcefields and adjusts the size of them to prevent
// pentration.
function ForceFieldGeneratorDeployed::VForceFieldAdjust(%data, %Generator)
{
   %ForceField = %Generator.ForceField;

   echo("Vertical ForceField Adjustment");

   // One last Check to prevent improper scaling situations.
   if (%Generator.CurrentFFTransform > 2)
      return;

   echo("Starting Scaling");

   %Transform = %ForceField.getTransform();
   %Rotation = rotFromTransform(%Transform);

   %Position = %Generator.BaseFFCenter[%Generator.CurrentFFTransform];
   %PositionXY = getWords(%PlacementPosition, 0, 1);
   %PositionZ = getWord(%PlacementPosition, 2);
   %FinalPosition = %PositionXY @ " " @ %PositionZ - 0.5;

   // The rotation and position need to be adjusted so that the forcefield is
   // perpendicular to the player that placed it and centered in front of them.
   // Load these now.
   %PlayerVector = %ForceField.forwardVector;
   %RotatedVector = %ForceField.rotatedVector;

   // Scan to determine the dimensions of the forcefield.
   %ScaleThick = 0.2;
   %ScaleThick2 = 2.2;
   %ScaleWidth = 11.0;
   %ScaleHieght = 6.5;

   %SearchMask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType;

   // Scan from the NewPosition to up to see where the ceiling is.
   %VerticalVector = VectorScale("0 0 1", %ScaleHieght);
   %SearchPosition = %PositionXY @ " " @ %PositionZ + 0.1;
   %SearchEnd = VectorAdd(%SearchPosition, %VerticalVector);
   %Target = ContainerRayCast(%SearchPosition, %SearchEnd, %SearchMask);

   echo("Ceiling Search Result: " @ %Target);

   // If something is in the way, scale for the obstruction.
   if (%Target !$= "0")
   {
      %TargetPosition = getWords(%Target, 1, 3);
      %ScaleHieght = VectorDist(%Position, %TargetPosition);
   }

   // Scan either side by half the %ScaleWidth and see where the wall is.
   // We search to one side then the other from the center of the forcefield
   %SearchPosition = %PositionXY @ " " @ %PositionZ + (%ScaleHieght / 2);

   %SearchEnd = VectorAdd(%SearchPosition, %RotatedVector);
   %Target = ContainerRayCast(%SearchPosition, %SearchEnd, %SearchMask);

   // If something is in the way, scale for the obstruction.
   if (%Target !$= "0")
   {
      %TargetPosition = getWords(%Target, 1, 3);
      %ScaleWidth1 = VectorDist(%SearchPosition, %TargetPosition);
   } else {
      %ScaleWidth1 = (%ScaleWidth / 2);
   }

   echo("Wall 1 Search Result: " @ %Target);

   %OtherRotatedVector = VectorScale(VectorNormalize(%RotatedVector), -5.5);
   %SearchEnd = VectorAdd(%SearchPosition, %OtherRotatedVector);
   %Target = ContainerRayCast(%SearchPosition, %SearchEnd, %SearchMask);

   if (%Target !$= "0")
   {
      %TargetPosition = getWords(%Target, 1, 3);
      %ScaleWidth2 = VectorDist(%SearchPosition, %TargetPosition);
   } else {
      %ScaleWidth2 = (%ScaleWidth / 2);
   }

   echo("Wall 2 Search Result: " @ %Target);

   %ScaleWidth = %ScaleWidth1 + %ScaleWidth2;

   %NewPosition = VectorAdd(%FinalPosition, %RotatedVector);
   %NewPosition2 = VectorAdd(%NewPosition, %PlayerVector);
   %NewTransform = %NewPosition @ " " @ %NewRotation;
   %NewTransform2 = %NewPosition2 @ " " @ %NewRotation;

   // Since the ForceField goes down into it's surroundings to seal things off, we do that now.
   %ScaleHieght += 1.0;
   %ScaleWidth += 1.0;

   %Scale = %ScaleThick @ " " @ %ScaleWidth @ " " @ %ScaleHieght;
   %Scale2 = %ScaleThick2 @ " " @ %ScaleWidth @ " " @ %ScaleHieght;

   %ForceField.setScale(%Scale);
   %ForceField.setTransform(%NewTransform);

   %PZone = %ForceField.PZone;
   %PZone.setScale(%Scale);
   %PZone.setTransform(%NewTransform);

   %Trigger = %ForceField.HackTrigger;
   %Trigger.setScale(%Scale2);
   %Trigger.setTransform(%NewTransform2);
}

// This function adjusts the Horizontal Forcefields to prevent penetration.
function ForceFieldGeneratorDeployed::HForceFieldAdjust(%data, %Generator)
{
   %ForceField = %Generator.ForceField;

   // One last Check to prevent improper scaling situations.
   if (%Generator.CurrentFFTransform < 3)
      return;

   %Transform = %ForceField.getTransform();
   %Rotation = rotFromTransform(%Transform);

   %Position = posFromTransform(%Transform);
   %PositionX = getWord(%PlacementPosition, 0);
   %PositionY = getWord(%PlacementPosition, 1);
   %PositionZ = getWord(%PlacementPosition, 2);
   %FinalPosition = %PositionX @ " " @ %PositionY @ " " @ %PositionZ - 0.5;

   // The rotation and position need to be adjusted so that the forcefield is
   // perpendicular to the player that placed it and centered in front of them.
   // Load these now.
   %PlayerVector = %ForceField.forwardVector;
   %PlayerVectorX = getWord(%PlayerVector, 0);
   %PlayerVectorY = getWord(%PlayerVector, 1);
   %PlayerVectorZ = getWord(%PlayerVector, 2);
   %PlayerVector1 = VectorScale(%PlayerVector, 0.1);
   %PlayerVectorX1 = getWord(%PlayerVector1, 0);
   %PlayerVectorY1 = getWord(%PlayerVector1, 1);
   %PlayerVectorZ1 = getWord(%PlayerVector1, 2);

   %RotatedVector = %ForceField.rotatedVector;
   %RotatedVectorX = getWord(%RotatedVector, 0);
   %RotatedVectorY = getWord(%RotatedVector, 1);
   %RotatedVectorZ = getWord(%RotatedVector, 2);

   // Scan to determine the dimensions of the forcefield.
   %ScaleThick = 6.5;
   %ScaleWidth = 11.0;
   %ScaleHieght = 0.2;
   %ScaleHieght2 = 2.2;

   %SearchMask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType;

   // Scan from the NewPosition out to see where the wall is.
   %HorizontalVector = VectorScale(%PlayerVector, %ScaleThick);
   %SearchPosition = %PositionX + %PlayerVectorX1 @ " " @ %PositionY + %PlayerVectorY1 @ " " @%PositionZ;
   %SearchEnd = VectorAdd(%SearchPosition, %HorizontalVector);
   %Target = ContainerRayCast(%SearchPosition, %SearchEnd, %SearchMask);

   // If something is in the way, scale for the obstruction.
   if (%Target !$= "0")
   {
      %TargetPosition = getWords(%Target, 1, 3);
      %ScaleHieght = VectorDist(%Position, %TargetPosition);
   }

   // Scan either side by half the %ScaleWidth and see where the wall is.
   // We search to one side then the other from the center of the forcefield
   %SearchPosition = %PositionX @ " " @ %PositionY @ " " @ %PositionZ + (%ScaleHieght / 2);

   %SearchEnd = VectorAdd(%SearchPosition, %RotatedVector);
   %Target = ContainerRayCast(%SearchPosition, %SearchEnd, %SearchMask);

   // If something is in the way, scale for the obstruction.
   if (%Target !$= "0")
   {
      %TargetPosition = getWords(%Target, 1, 3);
      %ScaleWidth1 = VectorDist(%SearchPosition, %TargetPosition);
   } else {
      %ScaleWidth1 = (%ScaleWidth / 2);
   }

   %OtherRotatedVector = VectorScale(VectorNormalize(%RotatedVector), -5.5);
   %SearchEnd = VectorAdd(%SearchPosition, %OtherRotatedVector);
   %Target = ContainerRayCast(%SearchPosition, %SearchEnd, %SearchMask);

   if (%Target !$= "0")
   {
      %TargetPosition = getWords(%Target, 1, 3);
      %ScaleWidth2 = VectorDist(%SearchPosition, %TargetPosition);
   } else {
      %ScaleWidth2 = (%ScaleWidth / 2);
   }

   %ScaleWidth = %ScaleWidth1 + %ScaleWidth2;

   %NewPosition = VectorAdd(%FinalPosition, %RotatedVector);
   %NewPosition2 = VectorAdd(%NewPosition, %PlayerVector);
   %NewTransform = %NewPosition @ " " @ %NewRotation;
   %NewTransform2 = %NewPosition2 @ " " @ %NewRotation;

   // Since the ForceField goes down into it's surroundings to seal things off, we do that now.
   %ScaleHieght += 1.0;
   %ScaleWidth += 1.0;

   %Scale = %ScaleThick @ " " @ %ScaleWidth @ " " @ %ScaleHieght;
   %Scale2 = %ScaleThick2 @ " " @ %ScaleWidth @ " " @ %ScaleHieght;

   %ForceField.setScale(%Scale);
   %ForceField.setTransform(%NewTransform);

   %PZone = %ForceField.PZone;
   %PZone.setScale(%Scale);
   %PZone.setTransform(%NewTransform);

   %Trigger = %ForceField.HackTrigger;
   %Trigger.setScale(%Scale2);
   %Trigger.setTransform(%NewTransform2);
}