
#include "MW4Headers.hpp"

#include "AI_Behavior.hpp"

#include "AI_Condition.hpp"
#include "AI_Action.hpp"
#include "Vehicle.hpp"
#include "Airplane.hpp"
#include "Mech.hpp"
#include "Dropship.hpp"



using namespace MW4AI;
using namespace MW4AI::Behaviors;
using namespace MW4AI::Conditions;
using namespace MW4AI::Actions;

const Stuff::Scalar threshold = 0.5f;
const Stuff::Scalar traffic_jam_avoidance_radius = 45.0f;
const Stuff::Scalar typical_fire_satisfaction_time = 7.5f;
const Stuff::Scalar max_damage_to_leave_jump_and_shoot = 40.0f;

// TODO: move this elsewhere ...

Behavior::Behavior()
	: m_Happy(true)
{
}

bool Behavior::ShouldStopImmediately() const
{
	return (false);
}

Behavior::~Behavior()
{
}

inline MoveType MoveToFaceUnlessNotFired(TacticInterface& i)
{
	if ((i.FiredWithin(4.0f) == false) ||
		(i.GetSelf().IsDerivedFrom(Mech::DefaultData) == false))
	{
		return (MOVE_FORWARD);
	}

	return (MOVE_TO_FACE_TARGET);
}

bool ShouldMove(TacticInterface& i)
{
	if (i.ShouldRun() == false)
	{
		return (false);
	}

	if (i.CanMove() == false)
	{
		return (false);
	}

	if ((i.MoveRequestPending() == true) &&
		(i.ShouldForceDestinationRecalc() == false))
	{
		return (false);
	}

	return (true);
}

bool CanJump(TacticInterface& i)
{
	if ((i.CanJump() == false) ||
		(EliteLevelBetween(i,UserConstants::min_evade_by_jumping,UserConstants::max_evade_by_jumping) < threshold))
	{
		return (false);
	}
	
	Stuff::Scalar normalized_elite_level = i.GetEliteLevel() / (Stuff::Scalar)100;

	Stuff::Scalar duration = (Stuff::Scalar)NormalizedInterpolation(normalized_elite_level,
																	UserConstants::Instance()->Get(UserConstants::jump_wait_time_at_0),
																	UserConstants::Instance()->Get(UserConstants::jump_wait_time_at_100));

	return (JumpedWithin(i,duration) < threshold);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

TryToFire::TryToFire(FireStyles::FireStyleID style, Stuff::Scalar multiplier)
	: m_FireStyle(style)
	, m_Multiplier(multiplier)
{
}

Fuzzy TryToFire::Execute(TacticInterface& i)
{
	AILOGFUNC("TryToFire::Execute");

	Fuzzy rv = 0;

	if ((i.ShouldRun() == true) &&
		(InsideWeaponsRadiusOfTarget(i,TacticInterface::SELF,TacticInterface::MAX,m_Multiplier) >= threshold))
	{
		Fire(i,m_FireStyle);
		rv = 1;
	}

	Track(i,TO_TARGET);
	LOG_AND_RETURN(rv);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

EvasiveManeuvers::EvasiveManeuvers()
	: m_LastForceDestinationRecalc(0)
{
}

Fuzzy EvasiveManeuvers::Execute(TacticInterface& i)
{
	AILOGFUNC("EvasiveManeuvers::Execute");

	if (And(AttackThrottleBetween(i,0,75),
			EliteLevelBetween(i,UserConstants::min_evade_by_throttle_override,UserConstants::max_evade_by_throttle_override)) >= threshold)
	{
		ThrottleOverride(i);
	}

	if ((Jumping(i) == true) &&
		(i.GetNormalizedJumpJetFuel() > 0.3f))
	{
		Jump(i);
		LOG_AND_RETURN(1);
	}

	bool fCanCrouch(i.CanCrouch());
	
	if (fCanCrouch == true)
	{
		fCanCrouch = (EliteLevelBetween(i,UserConstants::min_evade_by_crouching,UserConstants::max_evade_by_crouching) >= threshold);
	}

	bool fCanJump = CanJump(i);

	if (VehicleMoving(i) >= threshold)
	{
		if ((fCanJump == true) &&
			(i.GetNormalizedJumpJetFuel() > 0.8f))
		{
			Jump(i);
			LOG_AND_RETURN(1);
		}

		if ((m_LastForceDestinationRecalc == 0) ||
			(m_LastForceDestinationRecalc + 5.0f < gos_GetElapsedTime()))
		{
			m_LastForceDestinationRecalc = (Stuff::Scalar)gos_GetElapsedTime();
			ForceDestinationRecalc(i);
			LOG_AND_RETURN(1);
		}
	}
	else
	{
		if (VehicleCrouching(i) >= threshold)
		{
			if ((fCanCrouch == true) &&
				(OKtoCrouch(i) == true))
			{
				Crouch(i);
				LOG_AND_RETURN(1);
			}

			if ((fCanJump == true) &&
				(i.GetNormalizedJumpJetFuel() > 0.8f))
			{
				Jump(i);
				LOG_AND_RETURN(1);
			}
		}
	}

	LOG_AND_RETURN(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy EvasiveBehavior::Execute(TacticInterface& i)
{
	AILOGFUNC("EvasiveBehavior::Execute");

	if (EliteLevelBetween(i,UserConstants::min_evade_when_missiles_shot_at_me,UserConstants::max_evade_when_missiles_shot_at_me) >= threshold)
	{
		if (ProjectileApproaching(i,80.0f) >= threshold)
		{
			LOG_AND_RETURN(EvasiveManeuvers::Execute(i));
		}
	}

	if (EliteLevelBetween(i,UserConstants::min_evade_when_targeted,UserConstants::max_evade_when_targeted) >= threshold)
	{
		Stuff::Scalar normalized_elite_level = i.GetEliteLevel() / (Stuff::Scalar)100;

		Stuff::Scalar evade_focus_time = NormalizedInterpolation(normalized_elite_level,
																UserConstants::Instance()->Get(UserConstants::evade_focus_time_at_0),
																UserConstants::Instance()->Get(UserConstants::evade_focus_time_at_100));

		if (TargetedByEnemy(i,evade_focus_time) >= threshold)
		{
			LOG_AND_RETURN(EvasiveManeuvers::Execute(i));
		}
	}

	if (VehicleCrouching(i) >= threshold)
	{
		Crouch(i);
	}

	LOG_AND_RETURN(0);
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

JumpToAvoidCollision::JumpToAvoidCollision(Stuff::Scalar time, Stuff::Scalar radius)
	: m_Radius(radius)
	, m_Time(time)
{
}

Fuzzy JumpToAvoidCollision::Execute(TacticInterface& i)
{
	AILOGFUNC("JumpToAvoidCollision::Execute");

	if (CanJump(i) == true)
	{
		if (And(PlayerWillHitMe(i,m_Time,m_Radius),
				EliteLevelBetween(i,UserConstants::min_jump_if_rushed,UserConstants::max_jump_if_rushed)) >= threshold)
		{
			Jump(i);
			LOG_AND_RETURN(1);
		}
	}

	LOG_AND_RETURN(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

AvoidTrafficJams::AvoidTrafficJams()
	: m_LastTrafficJam(0)
{
}

Fuzzy AvoidTrafficJams::Execute(TacticInterface& i)
{
	AILOGFUNC("AvoidTrafficJams::Execute");

	if (ClusterFuckingSomeone(i) >= threshold)
	{
		m_LastTrafficJam = (Stuff::Scalar)gos_GetElapsedTime();
	}

	if (m_LastTrafficJam + 3.0f > gos_GetElapsedTime())
	{
		Disengage(i,traffic_jam_avoidance_radius);
		LOG_AND_RETURN(1);
	}

	LOG_AND_RETURN(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy DodgeIfLineOfFireBlocked::Execute(TacticInterface& i)
{
	AILOGFUNC("DodgeIfLineOfFireBlocked::Execute");

	if (HittingSomethingElse(i) >= threshold)
	{
		FindBetterLineOfFire(i);
		ThrottleOverride(i);
		LOG_AND_RETURN(1);
	}

	LOG_AND_RETURN(0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy DefensiveBehavior::Execute(TacticInterface& i)
{
	AILOGFUNC("DefensiveBehavior::Execute");

	if ((m_JumpToAvoidCollision.Execute(i) > 0) ||
		(EvasiveBehavior::Execute(i) > 0) ||
		(m_AvoidTrafficJams.Execute(i) > 0))
	{
		LOG_AND_RETURN(1);
	}
	
	LOG_AND_RETURN(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Fuzzy AggressiveBehavior::Execute(TacticInterface& i)
{
	AILOGFUNC("AggressiveBehavior::Execute");

	if ((DefensiveBehavior::Execute(i) > 0) ||
		(m_DodgeIfLineOfFireBlocked.Execute(i) > 0))
	{
		LOG_AND_RETURN(1);
	}
	
	LOG_AND_RETURN(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy StopAndFire::Execute(TacticInterface& i)
{
	AILOGFUNC("StopAndFire::Execute");

	m_Fire.Execute(i);

	if (Fired(i,typical_fire_satisfaction_time) >= threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (EvasiveBehavior::Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	if (InsidePercentileRangeOf(i,20) >= threshold)
	{
		StopIfMoving(i);

		LOG_AND_RETURN(1);
	}

	ApproachTarget(i,MoveToFaceUnlessNotFired(i));
	LOG_AND_RETURN(1);
}

bool StopAndFire::OKtoCrouch(TacticInterface& i) const
{
	return (InsidePercentileRangeOf(i,20) >= threshold);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy Ram::Execute(TacticInterface& i)
{
	AILOGFUNC("Ram::Execute");

	if (InsideRadiusOfTarget(i,300) > threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == true)
	{
		if (Rammed(i,6.0f) >= threshold)
		{
			GoToDistanceFromTarget(i,100.0f,MOVE_BACKWARD);
		}
		else
		{
			RamTarget(i);
		}
	}

	LOG_AND_RETURN(m_Fire.Execute(i));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Joust::Joust()
	: m_Mode(HEADING_IN)
{
}

Fuzzy Joust::Execute(TacticInterface& i)
{
	AILOGFUNC("Joust::Execute");

	m_Fire.Execute(i);

	if (Fired(i,typical_fire_satisfaction_time) >= threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (DefensiveBehavior::Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	const Scalar outer_radius = 100;

	GoToOtherSideOfTarget(i,outer_radius);

	if ((Not(InFrontOfTarget(i,true)) >= threshold) ||
		(FacingTarget(i) >= threshold))
	{
		ThrottleOverride(i);
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy Rush::Execute(TacticInterface& i)
{
	AILOGFUNC("Rush::Execute");

	m_Fire.Execute(i);

	if (Fired(i,typical_fire_satisfaction_time) >= threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (AggressiveBehavior::Execute(i) > 0)
	{
		SetHappy(true);
		LOG_AND_RETURN(1);
	}

	Circle(i,MoveToFaceUnlessNotFired(i),i.GetCombatRadiusForRange(5));

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

HitAndRun::HitAndRun()
	: m_Mode(HEADING_IN)
{
}

Fuzzy HitAndRun::Execute(TacticInterface& i)
{
	AILOGFUNC("HitAndRun::Execute");

	m_Fire.Execute(i);

	if (Fired(i,typical_fire_satisfaction_time) >= threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (AggressiveBehavior::Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	if (InsidePercentileRangeOf(i,20) >= threshold)
	{
		m_Mode = HEADING_OUT;
	}
	else
	{
		if (Not(InsidePercentileRangeOf(i,60)) >= threshold)
		{
			m_Mode = HEADING_IN;
		}
	}

	if (m_Mode == HEADING_OUT)
	{
		Dodge(i,i.GetCombatRadiusForRange(60),MoveToFaceUnlessNotFired(i));
		ThrottleOverride(i);
		SetHappy(true);
	}
	else
	{
		Verify(m_Mode == HEADING_IN);
		ApproachTarget(i,MoveToFaceUnlessNotFired(i));
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy Rear::Execute(TacticInterface& i)
{
	AILOGFUNC("Rear::Execute");

	m_Fire.Execute(i);

	if (Fired(i,typical_fire_satisfaction_time) >= threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (AggressiveBehavior::Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	if (Not(InsidePercentileRangeOf(i,90)) >= threshold)
	{
		GoToDistanceFromTarget(i,i.GetCombatRadiusForRange(90) * 0.9f,MoveToFaceUnlessNotFired(i));
	}
	else
	{
		if (InFrontOfTarget(i,true) >= threshold)
		{
			CircleToBehindTarget(i,80);
		}
		else
		{
			if (InsideRadiusOfTarget(i,traffic_jam_avoidance_radius + 5.0f) >= threshold)
			{
				StopIfMoving(i);
			}
			else
			{
				GoBehindTarget(i,traffic_jam_avoidance_radius + 2.0f);
			}
		}
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy Front::Execute(TacticInterface& i)
{
	AILOGFUNC("Front::Execute");

	m_Fire.Execute(i);

	if (Fired(i,typical_fire_satisfaction_time) >= threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (AggressiveBehavior::Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	if (Not(InsidePercentileRangeOf(i,90)) >= threshold)
	{
		GoToDistanceFromTarget(i,i.GetCombatRadiusForRange(90) * 0.9f,MoveToFaceUnlessNotFired(i));
	}
	else
	{
		if (InFrontOfTarget(i,true) < threshold)
		{
			CircleToFrontOfTarget(i,80);
		}
		else
		{
			if (InsideRadiusOfTarget(i,traffic_jam_avoidance_radius + 5.0f) >= threshold)
			{
				StopIfMoving(i);
			}
			else
			{
				GoInFrontOfTarget(i,traffic_jam_avoidance_radius + 2.0f);
			}
		}
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy CircleOfDeath::Execute(TacticInterface& i)
{
	AILOGFUNC("CircleOfDeath::Execute");

	m_Fire.Execute(i);

	if (Fired(i,typical_fire_satisfaction_time) >= threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (DefensiveBehavior::Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	if (Or(HittingSomethingElse(i),
		   Not(InsidePercentileRangeOf(i,80))) >= threshold)
	{
		GoToDistanceFromTarget(i,i.GetCombatRadiusForRange(80) * 0.9f,MoveToFaceUnlessNotFired(i));
	}
	else
	{
		if (InsidePercentileRangeOf(i,5) >= threshold)
		{
			ThrottleOverride(i);
			Dodge(i,100.0f,MoveToFaceUnlessNotFired(i));
		}
		else
		{
			Circle(i,MoveToFaceUnlessNotFired(i));
		}
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Retreat::Retreat()
	: m_Jump(2.0f,45.0f)
{
}

Fuzzy Retreat::Execute(TacticInterface& i)
{
	AILOGFUNC("Retreat::Execute");

	if (i.ShouldRun() == true)
	{
		Fire(i,FireStyles::FIRE_MAXIMUM);
	}

	Track(i,TO_NEAREST_ENEMY_OR_TARGET,true,false);

	if (i.ShotWithin(4.0f) == true)
	{
		SetHappy(false);
	}
	else
	{
		SetHappy(true);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	ThrottleOverride(i);

	if (EvasiveBehavior::Execute(i) >= threshold)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	if (m_AvoidTrafficJams.Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	Stuff::Scalar radius = i.WeaponRange(TacticInterface::TARGET,TacticInterface::MAX) * 1.2f;
	Min_Clamp(radius,200);

	if (InsideRadiusOfTarget(i,radius) >= threshold)
	{
		FleeTarget(i);
	}
	else
	{
		StopIfMoving(i);
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy BackUpAndFire::Execute(TacticInterface& i)
{
	AILOGFUNC("BackUpAndFire::Execute");

	m_Fire.Execute(i);

	if (Fired(i,typical_fire_satisfaction_time) >= threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (EvasiveBehavior::Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	if (m_AvoidTrafficJams.Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	if (Or(HittingSomethingElse(i),
		   Not(InsidePercentileRangeOf(i,45))) >= threshold)
	{
		GoToDistanceFromTarget(i,i.GetCombatRadiusForRange(45) * 0.9f,MOVE_FORWARD);
	}
	else
	{
		if (InsidePercentileRangeOf(i,0) >= threshold)
		{
			ThrottleOverride(i);
			Dodge(i,120.0f,MoveToFaceUnlessNotFired(i));
		}
		else
		{
			Circle(i,MoveToFaceUnlessNotFired(i));
		}
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy CircleHover::Execute(TacticInterface& i)
{
	AILOGFUNC("CircleHover::Execute");

	Stuff::Scalar distance = 200;
	distance += (10 * (i.GetObjectID() % 8));

	if (i.GetSelf().IsDerivedFrom(Dropship::DefaultData) == true)
	{
		Track(i,TO_TARGET);

		if (i.CanMove() == true)
		{
			CircleFloat(i,distance);
		}

		m_Fire.Execute(i);
	}
	else
	{
		if (i.CanMove() == true)
		{
			CircleFloat(i,distance);
			Track(i,TO_TARGET,false,true,true);
		}

		if (FacingTarget(i) >= threshold)
		{
			Stuff::UnitVector3D forward;
			i.GetSelf().GetLocalToWorld().GetLocalForwardInWorld(&forward);
			Stuff::Line3D line((Stuff::Point3D)i.GetSelf().GetLocalToWorld(),
							   forward,
							   20.0f);

			if (i.LineMightHitFriendlyUnits(line) == false)
			{
				m_Fire.Execute(i);
			}
		}
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Strafe::Strafe()
	: m_Mode(HEADING_IN)
{
}

Fuzzy Strafe::Execute(TacticInterface& i)
{
	AILOGFUNC("Strafe::Execute");

	const Stuff::Scalar bomb_drop_time = 5.0f;

	if ((i.ShouldRun() == true) &&
		(InsideRadiusOfTarget(i,20,bomb_drop_time) >= threshold))
	{
		Fire(i,FireStyles::FIRE_MAXIMUM);
	}

	if (InsideRadiusOfTarget(i,50) >= threshold)
	{
		m_Mode = HEADING_OUT;
	}
	else
	{
		if (Not(InsideRadiusOfTarget(i,600)) >= threshold)
		{
			m_Mode = HEADING_IN;
		}
	}

	// TODO: must also correct course every few seconds
	if (m_Mode == HEADING_OUT)
	{
		MoveForward(i,600);
	}
	else
	{
		Verify(m_Mode == HEADING_IN);
		ApproachTarget(i,bomb_drop_time);
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

JumpAndShoot::JumpAndShoot()
	: m_SeenByTarget(false)
	, m_JumpMode(WAITING)
	, m_HoverHeight(0)
{
}

bool JumpAndShoot::ShouldStopImmediately() const
{
	return (m_SeenByTarget);
}

Fuzzy JumpAndShoot::Execute(TacticInterface& i)
{
	AILOGFUNC("JumpAndShoot::Execute");

	if ((Not(IsHighOffGround(i)) >= threshold) &&
		(CanBeSeenByTarget(i) >= threshold))
	{
		m_SeenByTarget = true;
	}

	if (i.ShouldRun() == false)
	{
		Track(i,TO_TARGET);
		LOG_AND_RETURN(1);
	}

	if (i.DamageTakenSinceAttackOrder() > max_damage_to_leave_jump_and_shoot)
	{
		m_SeenByTarget = true;
	}

	if (InsideWeaponsRadiusOfTarget(i,TacticInterface::SELF,TacticInterface::MAX) >= threshold)
	{
		Fire(i,FireStyles::FIRE_MAXIMUM);
		Track(i,TO_TARGET);

		StopIfMoving(i);

		if (i.GetNormalizedJumpJetFuel() > 0.98f)
		{
			if (m_JumpMode != JUMPING)
			{
				m_JumpMode = JUMPING;

				const Stuff::Scalar min = UserConstants::Instance()->Get(UserConstants::jump_and_shoot_hover_height_min);
				const Stuff::Scalar max = UserConstants::Instance()->Get(UserConstants::jump_and_shoot_hover_height_max);

				m_HoverHeight = min + (Stuff::Random::GetFraction() * (max - min));
			}
		}
		else
		{
			if (i.GetNormalizedJumpJetFuel() < 0.05f)
			{
				m_JumpMode = WAITING;
			}
		}

		if (m_JumpMode == JUMPING)
		{
			JumpToHeight(i,m_HoverHeight);
		}
	}
	else
	{
		StopIfMoving(i);
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Snipe::Snipe()
	: m_Mode(HEADING_IN)
{
}

Fuzzy Snipe::Execute(TacticInterface& i)
{
	AILOGFUNC("Snipe::Execute");

	m_Fire.Execute(i);

	if (Fired(i,typical_fire_satisfaction_time) >= threshold)
	{
		SetHappy(true);
	}
	else
	{
		SetHappy(false);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (DefensiveBehavior::Execute(i) > 0)
	{
		SetHappy(false);
		LOG_AND_RETURN(1);
	}

	if (InsideRadiusOfTarget(i,130) >= threshold)
	{
		if (m_Mode != HEADING_OUT)
		{
			m_Mode = HEADING_OUT;
			StopIfMoving(i);
		}
	}
	else
	{
		if (Not(InsideRadiusOfTarget(i,280)) >= threshold)
		{
			m_Mode = HEADING_IN;
		}
	}

	if (m_Mode == HEADING_OUT)
	{
		FleeTarget(i);
		ThrottleOverride(i);
	}
	else
	{
		Verify(m_Mode == HEADING_IN);
		ApproachTarget(i,MOVE_FORWARD);
		SetHappy(true);
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy Defend::Execute(TacticInterface& i)
{
	AILOGFUNC("Defend::Execute");

	if (i.ShouldRun() == true)
	{
		Fire(i,FireStyles::FIRE_OPPORTUNITY);
	}

	Track(i,TO_NEAREST_ENEMY);

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (EvasiveBehavior::Execute(i) > 0)
	{
		LOG_AND_RETURN(1);
	}

	if (m_AvoidTrafficJams.Execute(i) > 0)
	{
		LOG_AND_RETURN(1);
	}

	CircleTargetNearEnemies(i,160.0f + ((i.GetSelf().objectID & 0x07) * 20.0f));

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy StandGround::Execute(TacticInterface& i)
{
	AILOGFUNC("StandGround::Execute");

	m_Fire.Execute(i);

	if (i.ShouldRun() == false)
	{
		LOG_AND_RETURN(1);
	}

	if (DefensiveBehavior::Execute(i) > 0)
	{
		LOG_AND_RETURN(1);
	}

	StopIfMoving(i);
	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy LocalPatrol::Execute(TacticInterface& i)
{
	AILOGFUNC("StandGround::Execute");

	m_Fire.Execute(i);

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (DefensiveBehavior::Execute(i) > 0)
	{
		LOG_AND_RETURN(1);
	}

	PatrolWithinRadiusOfAttackOrderPosition(i,50,100);
	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy Surrender::Execute(TacticInterface& i)
{
	AILOGFUNC("Surrender::Execute");

	if (i.ShouldRun() == false)
	{
		LOG_AND_RETURN(1);
	}

	i.SurrenderPose();

//	const Stuff::Scalar min_radius = 250;

	if (Not(InsidePercentileRangeOf(i,20)) >= threshold)
	{
		GoToDistanceFromTarget(i,i.GetCombatRadiusForRange(20) * 0.85f,MOVE_FORWARD);
		i.SetSpeed(0.25f);
	}
	else
	{
		if (i.Moving() == true)
		{
			i.Stop();
		}
		else
		{
			i.TurnTo(i.GetTarget().GetLineOfSight(),false);
		}
	}

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Ambush::Ambush()
	: m_Mode(RUSH_OUT)
	, m_SeenByTarget(false)
{
}

bool Ambush::ShouldStopImmediately() const
{
	return (m_SeenByTarget);
}

Fuzzy Ambush::Execute(TacticInterface& i)
{
	AILOGFUNC("Ambush::Execute");

	m_Fire.Execute(i);

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	if (Fired(i,4.0f) >= threshold)
	{
		m_Mode = RUSH_BACK;
	}
	else
	{
		if (i.Moving() == false)
		{
			if (CanBeSeenByTarget(i) >= threshold)
			{
				m_SeenByTarget = true;
			}

			m_Mode = RUSH_OUT;
		}
	}


	if (m_Mode == RUSH_OUT)
	{
		Stuff::Point3D dest(i.GetAttackOrderPosition());

		Stuff::UnitVector3D forward_vector;
		i.GetAttackOrderPosition().GetLocalForwardInWorld(&forward_vector);
		Stuff::Point3D forward(forward_vector);
		forward *= 50.0f;
		dest += forward;

		i.MoveTo(dest);
	}
	else
	{
		i.MoveTo((Stuff::Point3D)i.GetAttackOrderPosition(),false);
	}	

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DeathFromAbove::DeathFromAbove()
	: m_Mode(WAIT_FOR_FUEL)
	, m_DoneItOnce(false)
{
}

bool DeathFromAbove::ShouldStopImmediately() const
{
	return (m_DoneItOnce);
}

Fuzzy DeathFromAbove::Execute(TacticInterface& i)
{
	AILOGFUNC("DeathFromAbove::Execute");

	Verify(i.GetSelf_AsVehicle() != 0);

	const Stuff::Scalar max_speed = i.GetSelf_AsVehicle()->GetGameModel()->maxSpeed;
	const Stuff::Scalar standard_speed = 28.33f;
	const Stuff::Scalar jump_radius(45.0f * standard_speed / max_speed);

	Track(i,TO_TARGET);

	if (i.ShouldRun() == true)
	{
		Fire(i,FireStyles::FIRE_MAXIMUM);
	}

	if (ShouldMove(i) == false)
	{
		LOG_AND_RETURN(1);
	}

	const Stuff::Scalar outer_radius(150.0f * standard_speed / max_speed);
	const Stuff::Scalar fall_radius(11.0f * standard_speed / max_speed);

	switch (m_Mode)
	{
		case WAIT_FOR_FUEL:
		{
			if ((Not(InsideRadiusOfRamTarget(i,outer_radius * 0.9f)) > threshold) &&
				(i.GetNormalizedJumpJetFuel() > 0.95f))
			{
				m_Mode = RUSH_IN;
			}
			else
			{
				if (InsideRadiusOfTarget(i,outer_radius) > threshold)
				{
					GoToDistanceFromTarget(i,outer_radius,MoveToFaceUnlessNotFired(i));
				}
				else
				{
					Circle(i,MoveToFaceUnlessNotFired(i));
				}
			}

			break;
		}

		case RUSH_IN:
		{
			RamTarget(i);

			if (InsideRadiusOfRamTarget(i,jump_radius) > threshold)
			{
				if ((i.GetNormalizedJumpJetFuel() > 0.98f) &&
					(FacingTarget(i) > threshold))
				{
					m_Mode = JUMP;
				}
				else
				{
					m_Mode = WAIT_FOR_FUEL;
				}
			}

			break;
		}

		case JUMP:
		{
			JumpToHeight(i,10.0f);
			RamTarget(i);

			if ((i.GetNormalizedJumpJetFuel() < 0.33f) ||
				(InsideRadiusOfRamTarget(i,fall_radius) > threshold) ||
				(Rammed(i,1.0f) > threshold) ||
				(FacingTarget(i) < threshold))
			{
				m_Mode = WAIT_FOR_FUEL;
				m_DoneItOnce = true;
			}

			break;
		}
	}

	SetHappy(true);

	LOG_AND_RETURN(1);
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

HeliPopup::HeliPopup()
	: m_Mode(POP_UP)
	, m_SeenByTarget(false)
{
}

Fuzzy HeliPopup::Execute(TacticInterface& i)
{
	// TODO: move this code elsewhere; should not have airplane-specific code here
	if (i.GetSelf().IsDerivedFrom(Airplane::DefaultData) == false)
	{
		SetHappy(false);
		LOG_AND_RETURN(0);
	}

	Track(i,TO_TARGET,false,true,true);

	if (i.ShouldRun() == true)
	{
		if (FacingTarget(i) >= threshold)
		{
			Fire(i,FireStyles::FIRE_MAXIMUM);
		}
	}
	else
	{
		LOG_AND_RETURN(1);
	}

	Airplane* airplane = Cast_Object(Airplane*,&i.GetSelf());
	Check_Object(airplane);

	Stuff::Point3D pos(i.GetSelf().GetLocalToWorld());
	Stuff::Point3D start_pos(i.GetAttackOrderPosition());

	switch (m_Mode)
	{
		case POP_UP:
		{
			if (pos.y > start_pos.y + 50 - 1)
			{
				m_Mode = POP_DOWN;
			}
			else
			{
				airplane->Popup(start_pos.y + 50);
			}

			break;
		}

		case POP_DOWN:
		{
			if (pos.y < start_pos.y + 1)
			{
				if (CanBeSeenByTarget(i) >= threshold)
				{
					m_SeenByTarget = true;
				}
				else
				{
					m_Mode = POP_UP;
				}
			}
			else
			{
				airplane->Popdown(start_pos.y);
			}

			break;
		}
	}

	SetHappy(true);
	LOG_AND_RETURN(1);
}

bool HeliPopup::ShouldStopImmediately() const
{
	return (m_SeenByTarget);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy DiveBomb::Execute(TacticInterface& i)
{
	if (i.ShouldRun() == false)
	{
		LOG_AND_RETURN(1);
	}

	Fire(i,FireStyles::FIRE_MAXIMUM);

	if (ShouldMove(i) == true)
	{
		i.ContinuousFlyBy((Stuff::Point3D)i.GetTarget().GetLocalToWorld());
	}

	return (1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy FastCircle::Execute(TacticInterface& i)
{
	AILOGFUNC("FastCircle::Execute");

	m_Fire.Execute(i);

	if (m_JumpToAvoidCollision.Execute(i) > 0)
	{
		LOG_AND_RETURN(1);
	}

	if (EvasiveBehavior::Execute(i) > 0)
	{
		LOG_AND_RETURN(1);
	}

	if (InsideRadiusOfTarget(i,450) < threshold)
	{
		GoToDistanceFromTarget(i,300,MOVE_FORWARD);
		LOG_AND_RETURN(1);
	}

	FastCircleMoveType move_type(MOVE_RANDOM);

	if ((HittingSomethingElse(i) > threshold) ||
		(InsideRadiusOfTarget(i,300) < threshold))
	{
		move_type = MOVE_CLOSER;
	}
	else
	{
		if (InsideRadiusOfTarget(i,100) > threshold)
		{
			move_type = MOVE_CLOSER;
		}
	}

	DoFastCircle(i,move_type);

	LOG_AND_RETURN(1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Fuzzy Stare::Execute(TacticInterface& i)
{
	AILOGFUNC("Stare::Execute");

	Track(i,TO_TARGET,true,false);

	LOG_AND_RETURN(1);
}