------------------------------------------------------------------------------------------------------------
Warp Gun Tutorial
------------------------------------------------------------------------------------------------------------
This tutorial shows you how to make a teleportation gun, that I call a Warp Gun. Only lights can use it, works just like a laser rifle, except it warps you right above whatever it hits. For balancing purposes, it uses both energy and ammo, and can't be used around the enemy flag.
Step #1
// ------------------------------------------
// inventoryHud.cs
// ------------------------------------------
1a)
$InvWeapon[6] = "Warp Gun"; //I prefer to put it right after laser rifle and bump the others down.

$NameToInv["Warp Gun"] = "WarpGun";


1b) Add this to buyFavorites and buyDeployableFavorites

         case WarpGun:
            %client.player.setInventory( WarpGunAmmo, 400 );


Step #2
// ------------------------------------------
// weapons.cs
// ------------------------------------------

$WeaponsHudData[11, bitmapName]   = "gui/hud_targetlaser";
$WeaponsHudData[11, itemDataName] = "WarpGun";
$WeaponsHudData[11, ammoDataName] = "WarpGunAmmo";

$WeaponsHudCount = 12;

exec("scripts/weapons/WarpGun.cs");


Step #3
// ------------------------------------------
// inventory.cs
// ------------------------------------------

3a) Add this with all the other 'case' in the function

function ShapeBase::hasAmmo( %this, %weapon )
{
   switch$ ( %weapon )
   {
      case WarpGun:
         return( %this.getInventory( WarpGunAmmo ) > 0 );
   }
}


3b) Add this with all the other cleared items

function ShapeBase::clearInventory(%this)
{
   %this.setInventory(WarpGun, 0);
   %this.setInventory(WarpGunAmmo, 0);
}


3c)

function serverCmdGiveAll(%client)
{
   if($TestCheats)
   {
      %player.setInventory(WarpGun, 1);
      %player.setInventory(WarpGunAmmo, 999);
   }
}


Step #4
// ------------------------------------------
// player.cs
// ------------------------------------------
4a) Add to player armor datablocks. Probably only want to give it to lights.

max [WarpGun] = 1;
max [WarpGunAmmo] = 5;


4b) Add these to function armor::applyConcussion

      if( %weaps[11] = %player.getInventory("WarpGun") > 0 ) %numWeapons++;

and

      case 12:
         %player.use("WarpGun");


Step #5
// ------------------------------------------
// hud.cs
// ------------------------------------------
Ignore this step if you use any server side reticle workarounds.

function clientCmdSetWeaponsHudActive(%slot)
{
   weaponsHud.setActiveWeapon(%slot);
   switch$($WeaponNames[%slot])
   {
      case "WarpGun":
         reticleHud.setBitmap("gui/hud_ret_targlaser");
         reticleFrameHud.setVisible(false);
   }
}


Step #6
// ------------------------------------------
// Ammopack.cs
// ------------------------------------------
Add ammo to ammopack, to increase with it.

$ammoItem[7] = WarpGunAmmo;
$numAmmoItems = 8;

datablock ItemData(AmmoPack)
{
   max[WarpGunAmmo] = 5;
}


Step #7
// ------------------------------------------
// OptionsDlg.cs
// ------------------------------------------
$RemapName[$RemapCount] = "Warp Gun";
$RemapCmd[$RemapCount] = "useWarpGun";
$RemapCount++;


Step #8
// ------------------------------------------
// ControlDefaults.cs
// ------------------------------------------

function useWarpGun( %val )
{
   if ( %val )
     use( WarpGun );
}


Step #9
// ------------------------------------------
// WarpGun.cs
// ------------------------------------------
Make a new file called WarpGun.cs in weapons directory. Add all this into it.


//--------------------------------------------------------------------------
// Warp Gun
//
//
//--------------------------------------------------------------------------

datablock EffectProfile(WarpGunSwitchEffect)
{
   effectname = "weapons/sniper_activate";
   minDistance = 2.5;
};

datablock EffectProfile(WarpGunFireEffect)
{
   effectname = "weapons/sniper_fire";
   minDistance = 4.0;
};

datablock EffectProfile(WarpGunFireWetEffect)
{
   effectname = "weapons/sniper_underwater";
   minDistance = 4.0;
};

datablock AudioProfile(WarpGunSwitchSound)
{
   filename    = "fx/weapons/sniper_activate.wav";
   description = AudioClosest3d;
   preload = true;
   effect = WarpGunSwitchEffect;
};

datablock AudioProfile(WarpGunFireSound)
{
   filename    = "fx/weapons/sniper_fire.wav";
   description = AudioClose3d;
   preload = true;
   effect = WarpGunFireEffect;
};

datablock AudioProfile(WarpGunFireWetSound)
{
   filename    = "fx/weapons/sniper_underwater.wav";
   description = AudioClose3d;
   preload = true;
   effect = WarpGunFireWetEffect;
};

datablock AudioProfile(WarpGunDryFireSound)
{
   filename    = "fx/weapons/chaingun_dryfire.wav";
   description = AudioClose3d;
   preload = true;
};

datablock AudioProfile(WarpGunProjectileSound)
{
   filename    = "fx/weapons/sniper_miss.wav";
   description = AudioClose3d;
   preload = true;
};

datablock ItemData(WarpGunAmmo)
{
   className = Ammo;
   catagory = "Ammo";
   shapeFile = "ammo_chaingun.dts";
   mass = 1;
   elasticity = 0.2;
   friction = 0.6;
   pickupRadius = 2;
	pickUpName = "some warp gun ammo";

   computeCRC = true;

};

//--------------------------------------
// Rifle and item...
//--------------------------------------
datablock ItemData(WarpGun)
{
   className    = Weapon;
   catagory     = "Spawn Items";
   shapeFile    = "weapon_sniper.dts";
   image        = WarpGunImage;
   mass         = 1;
   elasticity   = 0.2;
   friction     = 0.6;
   pickupRadius = 2;
	pickUpName = "a warp gun";

   computeCRC = true;

};

datablock ShapeBaseImageData(WarpGunImage)
{
	className = WeaponImage;
   shapeFile = "weapon_sniper.dts";
   item = WarpGun;
   ammo 	 = WarpGunAmmo;
   showname = "Warp Gun";
	armThread = looksn;

	usesEnergy = true;
	minEnergy = 59;

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

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

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

   stateName[3]                     = "Fire";
   stateTransitionOnTimeout[3]      = "Reload";
   stateTimeoutValue[3]             = 0.5;
   stateFire[3]                     = true;
   stateAllowImageChange[3]         = false;
   stateSequence[3]                 = "Fire";
   stateScript[3]                   = "onFire";

   stateName[4]                     = "Reload";
   stateTransitionOnTimeout[4]      = "Ready";
   stateTimeoutValue[4]             = 3.0;
   stateAllowImageChange[4]         = false;

   stateName[5]                     = "CheckWet";
   stateTransitionOnWet[5]          = "DryFire";
   stateTransitionOnNotWet[5]       = "Fire";

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

   stateName[7]                     = "DryFire";
   stateSound[7]                    = WarpGunDryFireSound;
   stateTimeoutValue[7]             = 0.5;
   stateTransitionOnTimeout[7]      = "Ready";
};

function WarpGunImage::onFire(%data,%obj,%slot)
{
   %range = 600; //Max range someone can warp to.
   %rangeFromFlag = 150; //Meters from enemy Flag must be to work. Prevents Flag Capping with WarpGun
   %radius = 0.5;
   %rot = getWords(%obj.getTransform(), 3, 6);
   %muzzlePos = %obj.getMuzzlePoint(%slot);
   %muzzleVec = %obj.getMuzzleVector(%slot);

   %endPos    = VectorAdd(%muzzlePos, VectorScale(%muzzleVec, %range));

   %damageMasks = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType |
                  $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType |
                  $TypeMasks::SensorObjectType | $TypeMasks::TurretObjectType |
                  $TypeMasks::InteriorObjectType;

   %hit = ContainerRayCast(%muzzlePos, %endPos, %damageMasks | $TypeMasks::TerrainObjectType, %obj);
   %x2 = getWord(%hit, 1);
   %y2 = getWord(%hit, 2);
   %z2 = getWord(%hit, 3);
   if ((%hit) && (%z2 > 15))
   {
      %count = 0;
      %endpos = getWords(%hit.getWorldBoxCenter(), 0, 2);
      while (%count < 5)
      {
         %z2 += 2;
         %check1 = %x2 SPC %y2 SPC %z2;
         InitContainerRadiusSearch(%check1, %radius, %damageMasks);
         %checkok = 1;
         %targetObject = containerSearchNext();
         if (%targetObject)
            %count++;
         else
         {
            %checkok = 0;
            %count = 5;
         }
      }
      if (%checkok == 0)
      {
         if (%obj.team == 1)
            %team1 = 2;
         else
            %team1 = 1;
         %enemyFlag = $TeamFlag[%team1];
         if (isObject(%enemyFlag))
         {
            %dist = VectorDist(posfromTransform(%enemyFlag.getTransform()), %check1);
            %dist2 = VectorDist(posfromTransform(%enemyFlag.getTransform()), %obj.getMuzzlePoint(%slot));
            if ((%dist < %rangeFromFlag) || (%dist2 < %rangeFromFlag) || (%enemyFlag.carrier == %obj))
            {
               %checkok = 1;
               %checkok2 = 1;
               messageClient(%obj.client, 'msgBlocked', '\c1Interference from Flag.');
            }
         }
      }
      if (%checkok == 0)
      {
         if (%obj.getInventory( %data.ammo ) > 0)
         {
            %obj.setEnergyLevel(0);
            %newtrans = %check1 SPC %rot;
            %obj.setWhiteout(0.3);
            %obj.setVelocity("0 0 0");
            %obj.setTransform(%newtrans);
            %obj.decInventory(%data.ammo,1);
            %obj.playAudio(0, StationInventoryActivateSound);
         }
      }
      else if(%checkok2 != 1)
         messageClient(%obj.client, 'msgBlocked', '\c1The Target is Blocked.');
   }
   else
      messageClient(%obj.client, 'msgNothing', '\c1Nothing to lock onto.');
}