#include "MW4Headers.hpp"

#include "AI_UserConstants.hpp"

using namespace MW4AI;

UserConstants* UserConstants::m_Instance = 0;

UserConstants::UserConstants()
	: m_RefCount(0)
	, m_Resource("ai\\ai.constants")
	, m_LastPage(0)
{
	Verify(m_Resource.DoesResourceExist());
	m_Resource.LoadData();
	m_NotationFile.Assimilate(new Stuff::NotationFile(&m_Resource));

	const std::string friendly_fire_building_cheat("Friendly Fire Building Cheat");
	const std::string mood_entropy("Mood Entropy");
	const std::string mood_multipliers("Mood Modifiers");
	const std::string missed_shot_modifiers("Missed Shot Modifiers");
	const std::string speed_definitions("Speed Definitions");
	const std::string tonnage_definitions("Tonnage Definitions");
	const std::string distance_definitions("Distance Definitions");
	const std::string to_hit_modifiers("To-Hit Modifiers");
	const std::string elite_levels("Elite Levels");
	const std::string evasion_characteristics("Evasion Characteristics");
	const std::string lancemates("Lancemates");
	const std::string firing("Firing");
	const std::string tactics("Tactics");
	const std::string tactic_selection("Tactic Selection");
	const std::string ai_slope ("AI Slope");
	const std::string ai_movecost ("AI Movement Costs");
	const std::string objective ("Objective");
	const std::string skills ("Skills");
	const std::string piloting ("Piloting");
	const std::string ejecting("Ejecting");
	const std::string difficulty ("Difficulty");
	const std::string collision ("Collision");
	const std::string joystick ("Joystick");
	const std::string salvage ("Salvage");
	const std::string mouse ("Mouse");

	{for (int i = ID_FIRST;
		  i <= ID_LAST;
		  ++i)
	{
		switch ((ID)i)
		{
#define ENTRY(itemname,blockname) case itemname: { LookUpValue(blockname,#itemname,(int)itemname); break; }

			ENTRY(entropy_regeneration_at_0,	mood_entropy);
			ENTRY(entropy_regeneration_at_100,	mood_entropy);

			ENTRY(building_ff_multiplier,	friendly_fire_building_cheat);

			ENTRY(shutdown,					mood_multipliers);
			ENTRY(internal_armor_breached,	mood_multipliers);
			ENTRY(fall_down,				mood_multipliers);
			ENTRY(first_arm_hit,			mood_multipliers);
			ENTRY(second_arm_hit,			mood_multipliers);
			ENTRY(first_leg_hit,			mood_multipliers);
			ENTRY(mood_squad_dead_unit_multiplier,	mood_multipliers);
			ENTRY(interval_multiplier_desperate,	mood_multipliers);
			ENTRY(interval_multiplier_defensive,	mood_multipliers);
			ENTRY(interval_multiplier_neutral,		mood_multipliers);
			ENTRY(interval_multiplier_aggressive,	mood_multipliers);
			ENTRY(interval_multiplier_brutal,		mood_multipliers);

			ENTRY(skew_at_0,				missed_shot_modifiers);
			ENTRY(skew_at_100,				missed_shot_modifiers);
			ENTRY(skew_inc_at_0,			missed_shot_modifiers);
			ENTRY(skew_inc_at_100,			missed_shot_modifiers);

			ENTRY(speed_stopped,			speed_definitions);
			ENTRY(speed_slow,				speed_definitions);
			ENTRY(speed_medium,				speed_definitions);
			ENTRY(speed_fast,				speed_definitions);
			ENTRY(speed_maximum,			speed_definitions);

			ENTRY(mech_tiny,				tonnage_definitions);
			ENTRY(mech_small,				tonnage_definitions);
			ENTRY(mech_medium,				tonnage_definitions);
			ENTRY(mech_large,				tonnage_definitions);
			ENTRY(mech_huge,				tonnage_definitions);

			ENTRY(distance_adjacent,		distance_definitions);
			ENTRY(distance_near,			distance_definitions);
			ENTRY(distance_medium,			distance_definitions);
			ENTRY(distance_far,				distance_definitions);
			ENTRY(distance_very_far,		distance_definitions);

			ENTRY(if_crouched,				to_hit_modifiers);
			ENTRY(if_tiny,					to_hit_modifiers);
			ENTRY(if_small,					to_hit_modifiers);
			ENTRY(if_medium_size,			to_hit_modifiers);
			ENTRY(if_large,					to_hit_modifiers);
			ENTRY(if_huge,					to_hit_modifiers);
			ENTRY(if_stopped,				to_hit_modifiers);
			ENTRY(if_slow,					to_hit_modifiers);
			ENTRY(if_medium_speed,			to_hit_modifiers);
			ENTRY(if_fast,					to_hit_modifiers);
			ENTRY(if_maximum,				to_hit_modifiers);
			ENTRY(if_shooter_stopped,		to_hit_modifiers);
			ENTRY(if_shooter_slow,			to_hit_modifiers);
			ENTRY(if_shooter_medium,		to_hit_modifiers);
			ENTRY(if_shooter_fast,			to_hit_modifiers);
			ENTRY(if_shooter_maximum,		to_hit_modifiers);
			ENTRY(if_adjacent,				to_hit_modifiers);
			ENTRY(if_near,					to_hit_modifiers);
			ENTRY(if_medium_range,			to_hit_modifiers);
			ENTRY(if_far,					to_hit_modifiers);
			ENTRY(if_very_far,				to_hit_modifiers);
			ENTRY(if_firing_with_one_arm,	to_hit_modifiers);
			ENTRY(if_turning,				to_hit_modifiers);
			ENTRY(if_bearing_90,			to_hit_modifiers);

			ENTRY(min_evade_when_missiles_shot_at_me,		elite_levels);
			ENTRY(max_evade_when_missiles_shot_at_me,		elite_levels);
			ENTRY(min_evade_when_targeted,		elite_levels);
			ENTRY(max_evade_when_targeted,		elite_levels);
			ENTRY(min_evade_by_jumping,		elite_levels);
			ENTRY(max_evade_by_jumping,		elite_levels);
			ENTRY(min_evade_by_throttle_override,		elite_levels);
			ENTRY(max_evade_by_throttle_override,		elite_levels);
			ENTRY(min_evade_by_crouching,				elite_levels);
			ENTRY(max_evade_by_crouching,				elite_levels);
			ENTRY(min_jump_if_rushed,					elite_levels);
			ENTRY(max_jump_if_rushed,					elite_levels);
			ENTRY(min_opportunity_fire,					elite_levels);
			ENTRY(max_opportunity_fire,					elite_levels);
			ENTRY(min_look_left_or_right,				elite_levels);
			ENTRY(max_look_left_or_right,				elite_levels);
			ENTRY(min_look_time_at_0,					elite_levels);
			ENTRY(min_look_time_at_100,					elite_levels);
			ENTRY(min_dont_use_limited_ammo_vs_non_mechs,		elite_levels);
			ENTRY(max_dont_use_limited_ammo_vs_non_mechs,		elite_levels);
			ENTRY(min_vulnerable_leg_hiding,					elite_levels);
			ENTRY(max_vulnerable_leg_hiding,					elite_levels);
			ENTRY(min_mood_vulnerable_leg_hiding,		elite_levels);
			ENTRY(max_mood_vulnerable_leg_hiding,		elite_levels);
			ENTRY(switch_to_random_component_picking,			elite_levels);
			ENTRY(switch_to_most_damaged_component_picking,		elite_levels);
			ENTRY(min_prefer_water,						elite_levels);
			ENTRY(max_prefer_water,						elite_levels);
			ENTRY(min_throttle_override,				elite_levels);
			ENTRY(torso_twist_multiplier_at_0,			elite_levels);
			ENTRY(torso_twist_use_full_multiplier,		elite_levels);

			ENTRY(jump_wait_time_at_0,		evasion_characteristics);
			ENTRY(jump_wait_time_at_100,	evasion_characteristics);
			ENTRY(evade_focus_time_at_0,	evasion_characteristics);
			ENTRY(evade_focus_time_at_100,	evasion_characteristics);

			ENTRY(lancemate_can_attack_radius,			lancemates);
			ENTRY(max_lancemate_distance_from_leader,	lancemates);

			ENTRY(max_defend_distance_from_target,	tactics);
			ENTRY(jump_and_shoot_hover_height_min,	tactics);
			ENTRY(jump_and_shoot_hover_height_max,	tactics);

			ENTRY(max_fire_cheat_angle,				firing);
			ENTRY(max_airplane_fire_cheat_angle,	firing);
			ENTRY(max_turret_fire_cheat_angle,		firing);

			ENTRY(min_tactic_time,					tactic_selection);
			ENTRY(min_elite_joust,					tactic_selection);
			ENTRY(max_elite_joust,					tactic_selection);
			ENTRY(min_mood_joust,					tactic_selection);
			ENTRY(max_mood_joust,					tactic_selection);
			ENTRY(min_elite_rush,					tactic_selection);
			ENTRY(max_elite_rush,					tactic_selection);
			ENTRY(min_mood_rush,					tactic_selection);
			ENTRY(max_mood_rush,					tactic_selection);
			ENTRY(min_elite_hitandrun,				tactic_selection);
			ENTRY(max_elite_hitandrun,				tactic_selection);
			ENTRY(min_mood_hitandrun,				tactic_selection);
			ENTRY(max_mood_hitandrun,				tactic_selection);
			ENTRY(min_elite_rear,					tactic_selection);
			ENTRY(max_elite_rear,					tactic_selection);
			ENTRY(min_mood_rear,					tactic_selection);
			ENTRY(max_mood_rear,					tactic_selection);
			ENTRY(min_elite_circle,					tactic_selection);
			ENTRY(max_elite_circle,					tactic_selection);
			ENTRY(min_mood_circle,					tactic_selection);
			ENTRY(max_mood_circle,					tactic_selection);
			ENTRY(min_elite_retreat,				tactic_selection);
			ENTRY(max_elite_retreat,				tactic_selection);
			ENTRY(min_mood_retreat,					tactic_selection);
			ENTRY(max_mood_retreat,					tactic_selection);
			ENTRY(min_elite_dfa,					tactic_selection);
			ENTRY(max_elite_dfa,					tactic_selection);
			ENTRY(min_mood_dfa,						tactic_selection);
			ENTRY(max_mood_dfa,						tactic_selection);

			ENTRY(leg_up_slope,				ai_slope);		
			ENTRY(leg_down_slope,			ai_slope);		
			ENTRY(track_up_slope,			ai_slope);		
			ENTRY(track_down_slope,		ai_slope);		
			ENTRY(wheel_up_slope,			ai_slope);		
			ENTRY(wheel_down_slope,		ai_slope);		
			ENTRY(hover_up_slope,			ai_slope);		
			ENTRY(hover_down_slope,		ai_slope);

			ENTRY(leg_dirt_cost,					ai_movecost);
			ENTRY(leg_rock_cost,					ai_movecost);
			ENTRY(leg_steel_cost,					ai_movecost);
			ENTRY(leg_blacktop_cost,			ai_movecost);
			ENTRY(leg_glass_cost,					ai_movecost);
			ENTRY(leg_brick_cost,					ai_movecost);
			ENTRY(leg_grass_cost,					ai_movecost);
			ENTRY(leg_wood_cost,					ai_movecost);
			ENTRY(leg_tree_cost,					ai_movecost);
			ENTRY(leg_swamp_cost,					ai_movecost);
			ENTRY(leg_concrete_cost,			ai_movecost);
			ENTRY(leg_rough_cost,					ai_movecost);
			ENTRY(leg_snow_cost,					ai_movecost);
			ENTRY(leg_underbrush_cost,		ai_movecost);
			ENTRY(leg_shallowwater_cost,	ai_movecost);
			ENTRY(leg_midwater_cost,			ai_movecost);
			ENTRY(leg_oceanicwater_cost,	ai_movecost);
			ENTRY(leg_desert_cost,				ai_movecost);
			ENTRY(leg_flatswamp_cost,			ai_movecost);
			ENTRY(leg_thickswamp_cost,		ai_movecost);
// MSL 5.03 Lava
			ENTRY(leg_crackedlava_cost,		ai_movecost);
			ENTRY(leg_openlava_cost,		ai_movecost);
			ENTRY(track_dirt_cost,				ai_movecost);
			ENTRY(track_rock_cost,				ai_movecost);
			ENTRY(track_steel_cost,				ai_movecost);
			ENTRY(track_blacktop_cost,		ai_movecost);
			ENTRY(track_glass_cost,				ai_movecost);
			ENTRY(track_brick_cost,				ai_movecost);
			ENTRY(track_grass_cost,				ai_movecost);
			ENTRY(track_wood_cost,				ai_movecost);
			ENTRY(track_tree_cost,				ai_movecost);
			ENTRY(track_swamp_cost,				ai_movecost);
			ENTRY(track_concrete_cost,		ai_movecost);
			ENTRY(track_rough_cost,				ai_movecost);
			ENTRY(track_snow_cost,				ai_movecost);
			ENTRY(track_underbrush_cost,	ai_movecost);
			ENTRY(track_shallowwater_cost,ai_movecost);
			ENTRY(track_midwater_cost,		ai_movecost);
			ENTRY(track_oceanicwater_cost,ai_movecost);
			ENTRY(track_desert_cost,			ai_movecost);
			ENTRY(track_flatswamp_cost,		ai_movecost);
			ENTRY(track_thickswamp_cost,	ai_movecost);
// MSL 5.03 Lava
			ENTRY(track_crackedlava_cost,	ai_movecost);
			ENTRY(track_openlava_cost,		ai_movecost);
			ENTRY(wheel_dirt_cost,				ai_movecost);
			ENTRY(wheel_rock_cost,				ai_movecost);
			ENTRY(wheel_steel_cost,				ai_movecost);
			ENTRY(wheel_blacktop_cost,		ai_movecost);
			ENTRY(wheel_glass_cost,				ai_movecost);
			ENTRY(wheel_brick_cost,				ai_movecost);
			ENTRY(wheel_grass_cost,				ai_movecost);
			ENTRY(wheel_wood_cost,				ai_movecost);
			ENTRY(wheel_tree_cost,				ai_movecost);
			ENTRY(wheel_swamp_cost,				ai_movecost);
			ENTRY(wheel_concrete_cost,		ai_movecost);
			ENTRY(wheel_rough_cost,				ai_movecost);
			ENTRY(wheel_snow_cost,				ai_movecost);
			ENTRY(wheel_underbrush_cost,	ai_movecost);
			ENTRY(wheel_shallowwater_cost,ai_movecost);
			ENTRY(wheel_midwater_cost,		ai_movecost);
			ENTRY(wheel_oceanicwater_cost,ai_movecost);
			ENTRY(wheel_desert_cost,			ai_movecost);
			ENTRY(wheel_flatswamp_cost,		ai_movecost);
			ENTRY(wheel_thickswamp_cost,	ai_movecost);
// MSL 5.03 Lava
			ENTRY(wheel_crackedlava_cost,	ai_movecost);
			ENTRY(wheel_openlava_cost,		ai_movecost);
			ENTRY(hover_dirt_cost,				ai_movecost);
			ENTRY(hover_rock_cost,				ai_movecost);
			ENTRY(hover_steel_cost,				ai_movecost);
			ENTRY(hover_blacktop_cost,		ai_movecost);
			ENTRY(hover_glass_cost,				ai_movecost);
			ENTRY(hover_brick_cost,				ai_movecost);
			ENTRY(hover_grass_cost,				ai_movecost);
			ENTRY(hover_wood_cost,				ai_movecost);
			ENTRY(hover_tree_cost,				ai_movecost);
			ENTRY(hover_swamp_cost,				ai_movecost);
			ENTRY(hover_concrete_cost,		ai_movecost);
			ENTRY(hover_rough_cost,				ai_movecost);
			ENTRY(hover_snow_cost,				ai_movecost);
			ENTRY(hover_underbrush_cost,	ai_movecost);
			ENTRY(hover_shallowwater_cost,ai_movecost);
			ENTRY(hover_midwater_cost,		ai_movecost);
			ENTRY(hover_oceanicwater_cost,ai_movecost);
			ENTRY(hover_desert_cost,			ai_movecost);
			ENTRY(hover_flatswamp_cost,		ai_movecost);
			ENTRY(hover_thickswamp_cost, 	ai_movecost);
// MSL 5.03 Lava
			ENTRY(hover_crackedlava_cost,	ai_movecost);
			ENTRY(hover_openlava_cost,		ai_movecost);

			ENTRY(objective_display_time, objective);

			ENTRY(default_pilot,					skills);
			ENTRY(default_gunnery,				skills);
			ENTRY(default_elite,					skills);	
			ENTRY(default_minheat,				skills);
			ENTRY(default_maxheat,				skills);
			ENTRY(default_sensor,					skills);
			ENTRY(default_blind,					skills);
			ENTRY(default_longrange,			skills);
			ENTRY(default_shortrange,			skills);
			ENTRY(default_addpilot,				skills);
			ENTRY(default_addgunnery,			skills);
			ENTRY(default_addelite,				skills);
			ENTRY(max_skill_add,					skills);
			ENTRY(max_fog_dist_to_use_blind_skill,	skills);

			ENTRY(damage_amount,					piloting);
			ENTRY(fall_damage_minus,				piloting);
			ENTRY(fall_splash_multiplier,			piloting);
			ENTRY(min_fall_timer,					piloting);
			ENTRY(damage_mod,							piloting);
			ENTRY(low_water_mod,					piloting);
			ENTRY(mid_water_mod,					piloting);
			ENTRY(concrete_mod,						piloting);
			ENTRY(gimped_mod,							piloting);
			ENTRY(double_damage_mod,			piloting);
			ENTRY(moving_fast_mod,				piloting);
			ENTRY(jump_recover_mod,				piloting);
			ENTRY(max_water_jump,					piloting);
			ENTRY(flying_turn_rate,					piloting);

			ENTRY(eject_chance_for_non_lancemate,		ejecting);
			ENTRY(eject_chance_for_lancemate,			ejecting);
			ENTRY(eject_chance_ok_when_ejected,			ejecting);
			ENTRY(eject_chance_injured_when_ejected,	ejecting);

			ENTRY(friend_easy_gunnery_mod,			difficulty);
			ENTRY(friend_medium_gunnery_mod,		difficulty);
			ENTRY(friend_hard_gunnery_mod,			difficulty);
			ENTRY(friend_impossible_gunnery_mod,	difficulty);
			ENTRY(enemy_easy_gunnery_mod,			difficulty);
			ENTRY(enemy_medium_gunnery_mod,			difficulty);
			ENTRY(enemy_hard_gunnery_mod,			difficulty);
			ENTRY(enemy_impossible_gunnery_mod,		difficulty);
			ENTRY(neutral_easy_gunnery_mod,			difficulty);
			ENTRY(neutral_medium_gunnery_mod,		difficulty);
			ENTRY(neutral_hard_gunnery_mod,			difficulty);
			ENTRY(neutral_impossible_gunnery_mod,	difficulty);

			ENTRY(collision_mech_light_damage,						collision);
			ENTRY(collision_mech_light_slowdown,				collision);
			ENTRY(collision_mech_light_mech_speed,				collision);
			ENTRY(collision_mech_light_building_speed,			collision);

			ENTRY(collision_mech_medium_tonage,	collision);
			ENTRY(collision_mech_medium_damage,	collision);
			ENTRY(collision_mech_medium_slowdown,	collision);
			ENTRY(collision_mech_medium_mech_speed,	collision);
			ENTRY(collision_mech_medium_building_speed,	collision);

			ENTRY(collision_mech_heavy_tonage,	collision);
			ENTRY(collision_mech_heavy_damage,	collision);
			ENTRY(collision_mech_heavy_slowdown,	collision);
			ENTRY(collision_mech_heavy_mech_speed,	collision);
			ENTRY(collision_mech_heavy_building_speed,	collision);

			ENTRY(collision_dfa_speed,					collision);
			ENTRY(collision_dfa_damage_given,			collision);
			ENTRY(collision_dfa_damage_taken,			collision);
			ENTRY(collision_tank_slowdown,				collision);

			ENTRY(throttle_center,								joystick);
			ENTRY(throttle_dead_low,									joystick);
			ENTRY(throttle_dead_high,									joystick);

			ENTRY(chance_salvage,									salvage);

			ENTRY(mouse_min_change,								mouse);
			ENTRY(mouse_interia_mult,             mouse);
			ENTRY(mouse_max_mouse,                mouse);
															
		default: { Verify(!"Should never get here."); }

#undef ENTRY
		}
	}}

	for (int i = ID_MOVE_COST_START;i <= ID_MOVE_COST_LAST;++i)
	{
		if (m_Value[i] <= -100)
			m_Value[i] = FLT_MAX;
	}
	
}

UserConstants::~UserConstants()
{
}

Stuff::Scalar UserConstants::Get(ID id) const
{
	Verify(id >= ID_FIRST);
	Verify(id <= ID_LAST);

	return (m_Value[(int)id]);
}

void UserConstants::LookUpValue(const std::string& header_name, const std::string& item_name, int value_index)
{
	Verify(m_NotationFile.GetPointer() != 0);

	Stuff::Page* page = 0;

	if ((m_LastPage != 0) &&
		(stricmp(header_name.c_str(),m_LastPage->GetName()) == 0))
	{
		page = m_LastPage;
	}
	else
	{
		page = m_NotationFile->FindPage(header_name.c_str());

		if (page == 0)
		{
			Verify(!"Item not found.  Make sure you have the latest CONTENT\\AI\\AI.CONSTANTS file.");
			m_Value[value_index] = 0;
			return;
		}

		m_LastPage = page;
 	}

	Stuff::Note* note = page->FindNote(item_name.c_str());
	m_Value[value_index] = 0;

	if (note == 0)
	{
		Verify(!"Item not found.");
	}
	else
	{
		note->GetEntry(&(m_Value[value_index]));
	}
}

void UserConstants::IncrementRefCount()
{
	if (m_Instance == 0)
	{
		m_Instance = new UserConstants();
	}

	++m_Instance->m_RefCount;
}

void UserConstants::DecrementRefCount()
{
	Verify(m_Instance != 0);
	Verify(m_Instance->m_RefCount > 0);

	--(m_Instance->m_RefCount);

	if (m_Instance->m_RefCount == 0)
	{
		delete m_Instance;
		m_Instance = 0;
	}
}

UserConstants* UserConstants::Instance()
{
	return (m_Instance);
}

Stuff::Scalar UserConstants::GetMultiplier_Tonnage(Stuff::Scalar tonnage) const
{
	return (FindCategorizedValue(tonnage,mech_tiny,mech_huge,if_tiny));
}

Stuff::Scalar UserConstants::GetMultiplier_CrouchState(bool fCrouched) const
{
	if (fCrouched == true)
	{
		return (Get(if_crouched));
	}

	return (1.0f);
}

Stuff::Scalar UserConstants::GetMultiplier_Speed(Stuff::Scalar kph) const
{
	return (FindCategorizedValue(kph,speed_stopped,speed_maximum,if_stopped));
}

Stuff::Scalar UserConstants::GetMultiplier_ShooterSpeed(Stuff::Scalar kph) const
{
	return (FindCategorizedValue(kph,speed_stopped,speed_maximum,if_shooter_stopped));
}

Stuff::Scalar UserConstants::GetMultiplier_Distance(Stuff::Scalar distance) const
{
	return (FindCategorizedValue(distance,distance_adjacent,distance_very_far,if_adjacent));
}

UserConstants::ID UserConstants::FindCategory(Stuff::Scalar user_value, UserConstants::ID first, UserConstants::ID last) const
{
	Verify(last > first);
	Verify(first >= ID_FIRST);
	Verify(last <= ID_LAST);

	UserConstants::ID category = first;

	{for (int i = first;
		  i < last;
		  ++i)
	{
		if (user_value > Get((UserConstants::ID)i))
		{
			category = (UserConstants::ID)((int)category + 1);
		}
		else
		{
			break;
		}
	}}

	Verify(category >= first);
	Verify(category <= last);

	return (category);
}

Stuff::Scalar UserConstants::FindCategorizedValue(Stuff::Scalar user_value, UserConstants::ID first, UserConstants::ID last, UserConstants::ID first_lookup) const
{
	Verify((first_lookup > last) || (first_lookup < first));
	Verify(first_lookup >= ID_FIRST);
	Verify(first_lookup <= ID_LAST);

	UserConstants::ID category = FindCategory(user_value,first,last);

	int difference = category - first;

	UserConstants::ID lookup = (UserConstants::ID)(first_lookup + difference);

	return (Get(lookup));
}

Stuff::Scalar UserConstants::GetMultiplier_Bearing(const Stuff::Point3D& self, const Stuff::LinearMatrix4D& shooting_at, Stuff::Scalar target_speed) const
{
	Stuff::Point3D delta;
	delta.Subtract(self,(Stuff::Point3D)shooting_at);

	if (delta.GetLengthSquared() == 0)
	{
		return (1);
	}

	Stuff::UnitVector3D delta_normalized(delta);

	Stuff::UnitVector3D local_forward;
	shooting_at.GetLocalForwardInWorld(&local_forward);

	Stuff::Scalar bearing = delta_normalized * local_forward;

	if (bearing < 0)
	{
		bearing = -bearing;
	}

	if (bearing > 1)
	{
		bearing = 1;
	}

	Stuff::Scalar multiplier = bearing + ((1 - bearing) * Get(if_bearing_90));

	switch (FindCategory(target_speed,speed_stopped,speed_maximum))
	{
		case speed_stopped:
		case speed_slow:
		{
			
			break;
		}
		case speed_medium:
		case speed_fast:
		{
			multiplier *= multiplier;
			break;
		}
		case speed_maximum:
		{
			multiplier *= multiplier;
			multiplier *= multiplier;
			break;
		}
		default:
		{
			Verify(!"Should never get here.");
			break;
		}
	}

	return (multiplier);
}

Stuff::Scalar UserConstants::GetMultiplier_TurnRate(Stuff::Scalar yaw_demand) const
{
	Verify(yaw_demand >= 0);
	Verify(yaw_demand <= 1);

	return ((1 - yaw_demand) + (yaw_demand * Get(if_turning)));
}
