//---------------------------------------------------------------------------------------------
// TGB Turret behavior
// Put together by Shaderman
// This behavior uses code written by Tom Ogburn and James Ford
//---------------------------------------------------------------------------------------------

if (!isObject(TurretBehavior))
{
   %template = new BehaviorTemplate(TurretBehavior);
   
   %template.friendlyName = "Turret";
   %template.behaviorType = "AI";
   %template.description  = "Shoots automatically at a target";

   %template.addBehaviorField(target, "The target object to face and shoot at", object, "", t2dSceneObject);
   %template.addBehaviorField(angularSpeed, "The rotation speed", float, "45.0");
   %template.addBehaviorField(tickFreq, "The delay in ms between rotation updates", integer, "200");
   %template.addBehaviorField(range, "The activity range (rotate and shoot when the target is within this range). 0 means there's no limit.", integer, "20");

   %template.addBehaviorField(projectile, "The projectile to shoot", object, "", t2dSceneObject);
   %template.addBehaviorField(projectileLayer, "The projectile layer (0-31)", integer, "", "0");
   %template.addBehaviorField(projectileEff, "The projectile effect", object, "", t2dSceneObject);
   %template.addBehaviorField(speed, "The projectile speed", float, "10.0");
   %template.addBehaviorField(angle, "Objects within this angle will be shot", integer, "45");
   %template.addBehaviorField(numShots, "The number of shots fired in a row", integer, "3");
   %template.addBehaviorField(shotDelay, "The delay between shots fired in a row", integer, "3000");
   %template.addBehaviorField(reloadTime, "The reload time", integer, "5000");
}

function TurretBehavior::onLevelLoaded(%this, %scenegraph)
{
   if (!isObject(%this.target) || !%this.target.getVisible())
      return;

   %this.isReloaded = true;
   %this.rotationSchedule = %this.schedule(%this.tickFreq, "rotate");
}

function TurretBehavior::reload(%this)
{
   %this.isReloaded = true;
}

function TurretBehavior::rotate(%this)
{
   if (!isObject(%this.target) || !%this.target.getVisible())
   {
      %this.rotationSchedule = %this.schedule(%this.tickFreq, "rotate");
      return;
   }

   if(%this.range $= 0)
      %this.dist = %this.range;
   else
      %this.dist = t2dVectorDistance(%this.owner.getPosition(), %this.target.getPosition());

   if(%this.dist <= %this.range)
   {
      %rotateDir = shortestDirection(%this.owner.getRotation(), %this.target.getRotation());
      %directionVectorX = %this.target.getpositionX() - %this.owner.getpositionX();
      %directionVectorY = %this.target.getpositionY() - %this.owner.getpositionY();
      
      %angle = 180 - mRadtoDeg(maTan(%directionVectorX , %directionVectorY));
      
      %this.owner.rotateTo(%angle, %this.angularSpeed * %rotateDir, true, true, true, 2);
   }
   
   %this.rotationSchedule = %this.schedule(%this.tickFreq, "rotate");
}

function TurretBehavior::onRotationTarget(%this)
{
   cancel(%this.rotationSchedule);

   %this.shotsFired = 0;
   %this.schedule(%this.shotDelay, "shoot");
}

function TurretBehavior::shoot(%this)
{
   if(!%this.isReloaded)
   {
      %this.rotationSchedule = %this.schedule(%this.tickFreq, "rotate");
      return;
   }
   
   if(%this.shotsFired >= %this.numShots)
   {
      %this.shotsFired = 0;
      %this.isReloaded = false;
      %this.schedule(%this.reloadTime, "reload");
      %this.rotationSchedule = %this.schedule(%this.tickFreq, "rotate");

      return;
   } else {
      %origin = %this.owner.getPosition();
      %fvec = rotationToVector( %this.owner.getRotation() );
      %targetPos = %this.target.getPosition();
      
      %inRange = pointInSight( %origin, %fvec, %targetPos, %this.angle );
      if(!%inRange)
         return;

      %pro = %this.projectile.cloneWithBehaviors();
      %pro.position = %this.owner.getPosition();
      %pro.rotation = %this.owner.getRotation();
      %pro.scenegraph = %this.owner.getScenegraph();
      %pro.setLayer(%this.projectileLayer);
      
      if(isObject(%this.projectileEff))
      {
         %effect = %this.projectileEff.cloneWithBehaviors();
         %effect.scenegraph = %this.owner.getScenegraph();
         %effect.setLayer(%this.projectileLayer);
         %effect.mountToLinkpoint(%pro, 1, 0, true , true, true, true);
      }

      %force = VectorScale(%fvec, %this.speed);
      %pro.setConstantForce(getWord(%force, 0), getWord(%force, 1), false);
      
      %this.shotsFired++;
      %this.schedule(%this.shotDelay, "shoot");
   }
}

// This function was written by Tom Ogburn
// http://www.garagegames.com/community/forums/viewthread/37261
function shortestDirection(%currentAngle, %targetAngle)
{
	//Default to clockwise if equi-distant or if it is shorter
	%direction = 1;

	%angleDifference1 = mabs(%currentAngle - %targetAngle);
	%angleDifference2 = mabs((%currentAngle + 360) - %targetAngle);

	if (%angleDifference1 > %angleDifference2)
	{
		//Go counter-clockwise
		%direction = -1;
	}

	return %direction;
}

// The following code was written by James Ford
// http://www.garagegames.com/community/forums/viewthread/77232
//
// origin@ The position of the agent (t2dVector)
// fvec@ The forward vector of the agent (t2dVector)
// point@ The point to test (t2dVector)
// maxAngle@ The agent's max view angle in degrees (F32)
function pointInSight( %origin, %fvec, %point, %maxAngle )
{
   %toPoint = t2dVectorNormalise( t2dVectorSub( %point, %origin ) );
   %dot = t2dVectorDot( %toPoint, %fvec );
   %angle = mRadToDeg( mAcos( %dot ) );

   return %angle < %maxAngle;  
}

function rotationToVector( %rotation )
{
   // "0 -1" is the vector straight up in TGB eg. it corresponds to a rotation of zero, 
   // so rotate it by your objects rotation and you get your objects forward vector.

   %fvec = rotateVector( "0 -1", %rotation );
   return %fvec;
}

function rotateVector( %vector, %angle )
{
   %angle = mDegToRad(%angle);

   %x = getWord(%vector,0);
   %y = getWord(%vector,1);

   %nx = mCos(%angle) * %x - mSin(%angle) * %y;
   %ny = mSin(%angle) * %x + mCos(%angle) * %y;

   return %nx SPC %ny;
}