/*******************************************************************************
SABot - Stupid Angry Bot - release alpha 8
"I obey these words, written in my head. I think they are spelled wrong."

bot_sabot_ai
The brains of SABot. Braaaaaiiinnns!
********************************************************************************/

#define	SABOT_NEXT_ENEMY_LOOK						0.2
#define	SABOT_NEXT_GOAL_CHOOSE						1
#define	SABOT_NEXT_GOAL_LOOK						0.3
#define	SABOT_NEXT_AIM								0 //0.05

#define	SABOT_ENEMY_LOST_TIME						0.6
#define SABOT_SUBGOAL_LOST_TIME						3

#define SABOT_CHASE_TIME							12 // TinMan: Time to chase (out of view) enemy before giving up
#define SABOT_HOLD_TIME								90

#define	SABOT_CHECK_MOVE_DISTANCE					48

// TinMan: time between random strafe changes
#define	SABOT_STRAFE_MIN							1
#define	SABOT_STRAFE_MAX							3

// TinMan: Distances for following
#define SABOT_FOLLOWDIST_MAX						140
#define SABOT_FOLLOWDIST_MIN						100

#define SABOT_GOAL_NONE								0
#define SABOT_GOAL_MOVE								1
#define SABOT_GOAL_HOLD								2
#define SABOT_GOAL_FOLLOW							3
#define SABOT_GOAL_ATTACK							4

#define SABOT_ROLE_ROAM								0
#define SABOT_ROLE_OFFENCE                          1
#define SABOT_ROLE_DEFENCE							2

#define SABOT_MOVETYPE_ENTITY						0
#define SABOT_MOVETYPE_POSITION						1

#define	SABOT_ITEMTYPE_NONE							0
#define	SABOT_ITEMTYPE_HEALTH						1
#define	SABOT_ITEMTYPE_ARMOR						2
#define	SABOT_ITEMTYPE_WEAPON						3
#define	SABOT_ITEMTYPE_AMMO							4
#define	SABOT_ITEMTYPE_POWERUP						5
#define	SABOT_ITEMTYPE_TEAM							6
#define	SABOT_ITEMTYPE_ITEM							7

object bot_sabot {
	// TinMan: Movement control - script/bot->gamecode/fakeclient - Reset each frame
	boolean		AI_FORWARD;
	boolean		AI_BACKWARD;
	boolean		AI_STRAFE_LEFT;
	boolean		AI_STRAFE_RIGHT;
	boolean		AI_JUMP;
	boolean		AI_CROUCH;
	
	// TinMan: Movement control - script/bot->gamecode/fakeclient
	boolean		AI_RUN;
	
	// TinMan: Fire control - script/bot->gamecode/fakeclient - Reset each frame
	boolean		AI_WEAPON_FIRE;
	
	// TinMan: Body status - gamecode/fakeclient->script/bot
	boolean		AI_DEAD;
	boolean		AI_PAIN;
	boolean		AI_ONGROUND;
	boolean		AI_TELEPORT;

	// TinMan: Currently unused	
	boolean		AI_ATTACK_HELD;
	boolean		AI_WEAPON_FIRED;
	boolean		AI_ONLADDER;
	boolean		AI_HARDLANDING;
	boolean		AI_SOFTLANDING;
	boolean		AI_RELOAD;
	boolean		AI_TURN_LEFT;
	boolean		AI_TURN_RIGHT;
	
	// TinMan: Enemy status - gamecode->script
	boolean		AI_ENEMY_VISIBLE;
	boolean		AI_ENEMY_IN_FOV;
	boolean		AI_ENEMY_DEAD;
	
	// TinMan: Movement status - gamecode->script
	boolean		AI_MOVE_DONE;
	boolean		AI_DEST_UNREACHABLE;
	boolean		AI_BLOCKED;
	boolean		AI_OBSTACLE_IN_PATH;
	
	// TinMan: Currently unused
	boolean		AI_ENEMY_REACHABLE;
	boolean		AI_PUSHED;
	
	// TinMan: *todo* shoddy, pull out for q4
	boolean		BOT_COMMAND;
	float 		BOT_COMMAND_TYPE;
	string 		BOT_COMMAND_ENTITY;
	vector		BOT_COMMAND_VECTOR;

	// TinMan: Globalistics
	string		me;
	string		name;
	
	entity		body;

	entity		weapon;
	string		currentWeapon;
	vector		weaponRange;
	float		lastWeaponSwitch;


	entity 		goal;
	vector		goalPosition;
	float		goalType;
	float		goalPriority;
	
	entity 		oldGoal;
	
	float		moveType;
	
	// TinMan: alternate goal for comparing against current, oh yes the crudeness of script
	entity 		newGoal;
	vector		newGoalPosition;
	float		newMoveType;
	float		newGoalType;
	float		newGoalPriority;
	
	float		role;
	float		roleDefault;
	
	float 		team;
	// TinMan: *CTF*
	entity		ourFlag;
	entity		theirFlag;
	vector		ourCap;
	vector		theirCap;
	
	float 		gameType;
	
	float		viewHeight;
	float		jumpHeight;
	
	float		react_to_enemy;
	float		react_to_enemy_sound;
	float		react_fire;
	
	float		timerAmount;
	float		timerStart;
	float		timerEnd;
	
	vector		lastPosition;
	float		lastPositionTime;
	
	float		nextEnemyLookTime;
	
	vector		platTop;
	
	void 		debugPrint( string func, string message );
	void 		debugVis( string func, string message );

	// TinMan: States
	void 		state_Begin();
	void 		state_MoveToGoal();
	void 		state_Combat();
	void 		state_Chase();
	void		state_Retreat();
	void		state_Hold();
	void		state_Follow();
	void 		state_Killed();

	// TinMan: Functions
	void        turnAroundForEnemies( float useFov, float numListedEnemies );
	void 		setNewGoal();
	void		clearGoal();
	void		clearNewGoal();
	
	void 		moveForward();
	void 		moveBackward();
	void 		moveRight();
	void 		moveLeft();
	
	vector 		moveAction();

	void 		setTimer( float endTime );
	float 		getTimer();
	void 		clearTimer();
	boolean		nextEnemyLook();

	boolean 	checkGoal();
	boolean		gotFlag();
	
	boolean 	checkSelf();
	boolean 	checkLeader( entity thisLeader );
	boolean		checkWeapon();

	boolean 	checkStrafeLeft();
	boolean 	checkStrafeRight();
	boolean 	checkMoveForward();
	boolean 	checkMoveBack();
	float		checkMoves();
	
	boolean 	checkBlocked();

	boolean 	checkShot( boolean simple );
	boolean 	checkAimedShot( vector aimDir, float range );
	
	void 		chooseWeapon( float range );

	vector		predictTargetPosition( vector targetPosition, vector myPosition, vector targetVelocity );
	vector 		addAimVariation( vector aimDir );

	float 		getItemPriority( entity item );
	float 		getPlayerPriority( entity badass );

	entity 		chooseGoal( entity ignoreGoal );
	entity 		lookForGoal( float useFov );
	entity 		lookForEnemies( float useFov, boolean turnaround );
	entity		listenForEnemies();
	
	void		updateTeam();
	boolean 	isCTF();
};

/*
=====================
bot_sabot::init
=====================
*/
void bot_sabot::init() {
	me		= getName();
	name 	= getKey( "npc_name" );
	body 	= getBody(); // TinMan: Grab fakeclient entity.

	// TinMan: Grab your keys please
	viewHeight = body.getFloatKey( "pm_normalviewheight" );
	jumpHeight = body.getFloatKey( "pm_jumpheight" );
	
	react_to_enemy			= getFloatKey( "react_to_enemy" );
	react_to_enemy_sound	= getFloatKey( "react_to_enemy_sound" );
	react_fire				= getFloatKey( "react_fire" );
	
	roleDefault = SABOT_ROLE_ROAM;
	
	clearTimer();
	
	team = -1; // TinMan: set to non team value so updateTeam picks it up
	
	setState( "state_Begin" );
}

/*
=====================
bot_sabot::destroy
TinMan: Clean up stuff for when script is killed.
=====================
*/
void bot_sabot::destroy() {
	waitFrame();
}

/*
=====================
bot_sabot::debugPrint
TinMan: Sommat a leetle cleaner, lets you have more control over what bots are spamming out debug. For great justice!
=====================
*/
void bot_sabot::debugPrint( string func, string message ) {
	if ( getIntKey( "debug" ) ) {
		sys.println( "[" + int( sys.getTime() ) + "][" + me + "][" + func + "][" + message + "]" );
	}
}

/*
=====================
bot_sabot::debugVis
TinMan: do debug visuals
=====================
*/
void bot_sabot::debugVis( string func, string message ) {
	string text;
	text 	= func + "\n" 
				+ message + "\n";
	//if ( getIntKey( "debug" ) ) {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][" + func + "][" + message + "]" );
		// TinMan: *todo* DEFINE some colours
		vector org = body.getOrigin();
		org_z += 72;
		//sys.drawText( string text, vector origin, float scale, vector color, float align, float lifetime );
		sys.drawText( text, org, 0.24, '1 1 1', 1, 0 ); // scale 0.12

	//}
}

/***********************************************************************

	States - Main AI

***********************************************************************/

/*
=====================
bot_sabot::state_Begin
TinMan: Basic setup. Wakey wakey, rise 'n' shine.
=====================
*/
void bot_sabot::state_Begin() {
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Begin]" ); // TinMan *debug*
	
	// TinMan: Wait till you get your weapon. *todo* even an issue any more?
	//while( !weapon ) {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Begin][!weapon]" ); // TinMan *debug*
		weapon = body.getWeaponEntity();
	//	waitFrame();
	//}
	
	checkWeapon(); // TinMan: Update our weapon vars (currentweapon, weaponrange)
	AI_RUN = true;
	clearGoal();
	clearEnemy();
	role = roleDefault;
	gameType = getGameType(); // TinMan: *todo* see if gametype change restarts map (and bot), on brief look it does
	updateTeam();
	setState( "state_Idle" );
}

/*
=====================
bot_sabot::state_Idle
TinMan: Default fallback state 
*todo* With the redundancy I'm getting with aiming/firing, shortgoal/enemy over movetogoal/combat I'm thinking may hauling those here and not trapping in the state would be better.
=====================
*/
void bot_sabot::state_Idle() {
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Idle]" ); // TinMan *debug*
	float nextGoalChoose;
	float reactToEnemy;
	entity newEnemy;
	
	clearNewGoal();
	
	eachFrame {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Idle]" ); // TinMan *debug*

		// TinMan: Choose a role, which is used to decide on a goal. Curses rhyming strikes again! 
		// *todo* this should be removed in favour of prioritising goals in choosegoal
		if ( checkSelf() ) { // TinMan: Bot too beat up to be thinking abount anything
			//sys.println( "[" + int( sys.getTime() ) + "][state_Idle][checkSelf: not happy]" ); // TinMan *debug*
			role = SABOT_ROLE_ROAM;
		} else {
			if ( isCTF() ) {
				if ( role == SABOT_ROLE_ROAM ) {
					role = SABOT_ROLE_OFFENCE;
				}
				/*
				//sys.println( "[" + int( sys.getTime() ) + "][state_Idle][checkSelf: happy]" ); // TinMan *debug*
				// TinMan: *CTF* Choose a role
				if ( sys.random( 100 ) < 95 ) {
					//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Idle][SABOT_ROLE_OFFENCE]" ); // TinMan *debug*
					role = SABOT_ROLE_OFFENCE;
				} else {
					//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Idle][SABOT_ROLE_ROAM]" ); // TinMan *debug*
					role = SABOT_ROLE_ROAM;
				}
				*/
			}
		}
			
		if ( !goal && AI_ONGROUND && sys.getTime() > nextGoalChoose ) {
			nextGoalChoose = sys.getTime() + SABOT_NEXT_GOAL_CHOOSE;
			goal = chooseGoal( oldGoal );
			if ( !goal ) {
				sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Idle][Cannot find long range goal]" ); // TinMan *debug*
				stopMove();
				waitFrame(); // TinMan: Catch a breather.
				// TinMan: *todo* set up some kind of wander goal/state?
			}
		}
		
		//waitFrame(); // TinMan: *todo* just hiding a problem at the moment, there isn't any real reason for a waitframe here
		
		if ( goal ) {
			if ( goalType == SABOT_GOAL_MOVE ) {
				setState( "state_MoveToGoal" );
			} else if ( goalType == SABOT_GOAL_FOLLOW ) {
				setState( "state_Follow" );
			} else if ( goalType == SABOT_GOAL_HOLD ) {
				setState( "state_Hold" );
			}	else if ( goalType == SABOT_GOAL_ATTACK ) {
				setState( "state_Combat" );
			} else if ( goalType == SABOT_GOAL_NONE ) {
				sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Idle][!No goalType!]" ); // TinMan *debug*
			} else {
				sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Idle][!Unknown goalType!]" ); // TinMan *debug*
			}
		} else { // TinMan: no goal, just look for trouble *todo* something else 
			// TinMan: in case outside aas, push point and move towards
			vector bodyOrigin, pushPosition, moveDir;
			bodyOrigin = body.getOrigin();
			pushPosition = pushPointIntoAAS( bodyOrigin );
			if ( pushPosition != bodyOrigin ) {
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Idle][pushpos != bodyOrigin]" ); // TinMan *debug*
				moveDir = pushPosition - bodyOrigin;
				moveDir_z = 0; // TinMan: Only horizontal
				setMoveDirection( moveDir, 400 );
			}
			
			/*
			// TinMan: Just wander
			setMoveWander();
			vector movePosition = moveAction();
			vector aimDir = movePosition - body.getOrigin();
			setAimDirection( aimDir );
			
			checkBlocked();
			*/
			
			// TinMan: *todo* create a wander state for !goal - flip to idle after certian time to see if new goal
			if ( !reactToEnemy ) {
				if ( nextEnemyLook() || AI_PAIN || AI_ENEMY_VISIBLE ) {
					if ( AI_ENEMY_VISIBLE ) {
						reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Set react timer.
					} else {
						newEnemy = lookForEnemies( true, false );
						if ( newEnemy ) { // TinMan: We have a new challenger
							reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Set react timer.
						} else {
							// TinMan: Shh, I hear something.
							newEnemy = listenForEnemies();
							if ( !newEnemy && AI_PAIN ) { // TinMan: *cheaphack* work around due to fack heardsound is being shoehorned into something it's not desiged to handle - multiple players
								newEnemy = lookForEnemies( false, false );
							}
							if ( newEnemy ) {
								reactToEnemy = sys.getTime() + react_to_enemy_sound; // TinMan: Set react timer.
							}
						}
					}
				}
			} else {
				if ( reactToEnemy < sys.getTime() ) {
					setEnemy( newEnemy );
					setState( "state_Combat" );
				}
			}
		}
	}
}

/*
=====================
bot_sabot::state_MoveToGoal
TinMan: Only interested in moving here, ie grabbing item.
=====================
*/
void bot_sabot::state_MoveToGoal() {
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_MoveToGoal]" ); // TinMan *debug*
	vector movePosition, secondaryPosition;
	vector aimPosition, aimDir;
	float aimTarget;
	float nextGoalLook;
	float weaponRangeBuffer;
	float enemyLostTime;
	float nextAimTime;
	float reactToEnemy;
	float reactFire, fire;
	float lastVisibleEnemyDist;
	vector lastVisibleEnemyPos;
	entity newEnemy;
	reactToEnemy = 0;
	weaponRangeBuffer = sys.random( 16 ) + 16; // TinMan: *todo* rethink
	float health, temphealth;                  // SHREWM BE-END

	//stopMove();
	
	if ( goal ) {
		if ( moveType == SABOT_MOVETYPE_ENTITY ) {
			setMoveToEntity( goal );
		} else {
			setMoveToPosition( goalPosition );
		}
	}
// SHREWM BEGIN
	temphealth = getActorHealth( body );		// Lets check own health
	eachFrame {
        health = getActorHealth( body );		// again...safety 1st
        if (health < temphealth)                // sbds sh00ting m3!
            lookForEnemies( true, true );		// but hes not visible! h4xX0rZ >< SEARCH HIM
// SHREWM END
		//sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][goal: " + goal.getName() + "]" ); // TinMan *debug*
		//debugPrint( "state_MoveToGoal", goalPriority );
		//debugVis( "state_MoveToGoal", "" );
			
		// TinMan: Look for new goals. The shinier the better.
		if ( sys.getTime() > nextGoalLook ) {
			nextGoalLook = sys.getTime() + SABOT_NEXT_GOAL_LOOK;
			lookForGoal( true ); // TinMan: Will set newGoal. More important than it might seem, here is the ultimate decision whether to discard current goal (via a higher priority), which may be something set by chooseGoal, and something as important as capping flag.
			// TinMan: If pretty new thing better than current plaything.
			if ( newGoal ) {
				if ( newGoal != goal ) {
					if ( newGoalPriority > goalPriority ) {
						setNewGoal();
						break;
					}
				}
			}
		}
		
		// TinMan: Lookin for trouble with a big T.
		if ( !reactToEnemy ) {
			if ( nextEnemyLook() || AI_PAIN || AI_ENEMY_VISIBLE ) {
				if ( AI_ENEMY_VISIBLE ) {
					reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Current enemy back in view. Set react timer.
					newEnemy = getEnemy(); // TinMan: *todo* seems a bit of a cludge, but whatever
				} else {
					newEnemy = lookForEnemies( true, false );
					if ( newEnemy ) { // TinMan: We have a new challenger
						reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Set react timer.
					} else {
						// TinMan: Shh, I hear something.
						newEnemy = listenForEnemies();
						if ( !newEnemy && AI_PAIN ) { // TinMan: *cheaphack* work around due to fact heardsound is being shoehorned into something it's not desiged to handle - multiple enemies
							newEnemy = lookForEnemies( false, false );
						}
						if ( newEnemy ) {
							reactToEnemy = sys.getTime() + react_to_enemy_sound; // TinMan: Set react timer.
						}
					}
				}
			}
		} else {
			if ( reactToEnemy < sys.getTime() ) {
				setEnemy( newEnemy );
				reactToEnemy = 0;
				newGoalPriority = getPlayerPriority( newEnemy );
				if ( newGoalPriority > goalPriority ) {
					newGoal = newEnemy;
					newGoalType = SABOT_GOAL_ATTACK;
					setNewGoal();
					setState( "state_Combat" );	
				}
			}
		}
		
		// TinMan: Move along given path
		movePosition = moveAction();
		
		checkBlocked();
		
		//sys.debugBounds( '1 1 1', lastVisibleEnemyPos + '-1 -1 -1', lastVisibleEnemyPos + '1 1 1' , 0.01 ); // TinMan: *debug*
		
		if ( getEnemy() ) { // TinMan: *todo* look through and overhaul looking at enemy/fighting
			//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_MoveToGoal][lVisEnyPos: " + lastVisibleEnemyPos + "][lVisEnDist:" + lastVisibleEnemyDist + "]" ); // TinMan: *debug*
			if ( !AI_ENEMY_DEAD ) {
				lastVisibleEnemyPos = getEnemyPos();
				lastVisibleEnemyPos_z += viewHeight; // TinMan: cheap hack pos off ground since we'll be aiming at it when enemy dead
				lastVisibleEnemyDist = enemyRange();
			}
			
			// TinMan: Aim at enemy
			if ( nextAimTime < sys.getTime() ) { // TinMan: Roughen up aiming a bit
				nextAimTime = sys.getTime() + SABOT_NEXT_AIM;
				if ( AI_ENEMY_VISIBLE ) {
					entity enemy = getEnemy();	
					aimTarget = getIntKey( "target_" + currentWeapon );
					aimDir = getAimDir( getEnemy(), aimTarget ); // TinMan: new choose a point on enemy, and predict shot
					aimDir = addAimVariation( aimDir ); // TinMan: Screw up aim cause I'm mean.
					setAimDirection( aimDir ); // TinMan: Finally set our target to aim to.
					//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][aimPosition = " + aimPosition_x + " " + aimPosition_y + " " +aimPosition_z + "]" ); // TinMan: *debug*
				} else { // TinMan: Aim at teh dedders
					aimDir = lastVisibleEnemyPos - getViewPosition();
					setAimDirection( aimDir );
				}
			}
			
			// TinMan: Check shot is within weapon range. AND SMITE HIM!
			//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][CurrentWeapon: " + currentWeapon + "][Weaponrange]" ); // TinMan: *debug*
			if ( /* *off* *test* ( lastVisibleEnemyDist > weaponRange_y ) && */ ( lastVisibleEnemyDist < weaponRange_z ) )	{
				// TinMan: See if any enemies in shot.
				if ( !reactFire ) { // TinMan: Allready going to fire
					if ( checkShot( false ) ) { // TinMan: See if enemy in straight bounds trace.
						reactFire = sys.getTime() + react_fire;
					} else if ( checkShot( true ) ) { // TinMan: See if enemy in straight point trace.
						reactFire = sys.getTime() + react_fire;
					} else if ( checkAimedShot( aimDir, lastVisibleEnemyDist ) ) { // TinMan: See if we are aimed at position and shot will make it.
						reactFire = sys.getTime() + react_fire;
					} else {
						reactFire = 0;
						fire = false;
					}
				}
			}	else { // TinMan: Aw, out of weapon range.
				reactFire = 0;
				fire = false;
			}
	
			if ( reactFire != 0 ) {
				if ( reactFire < sys.getTime() ) {
					fire = true; // TinMan: Ratatat tat, boom, fwoosh!
				}
			}
			
			if ( fire && ( enemyLostTime < 0.2 || AI_ENEMY_DEAD ) ) { // TinMan: *cheaphack* The delayed fire is working out quite nicely at the moment except leads to the bot shooting into walls too much.
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_MoveToGoal][firing!]" ); // TinMan: *debug*
				AI_WEAPON_FIRE = true;
			}
		} else {
			// TinMan: Lookee where we damn be going.
			if ( /* AI_ONGROUND && */ movePosition != body.getOrigin() ) { // TinMan: *cheaphack* stop bot from looking down when movepos = body.org
				aimDir = movePosition - body.getOrigin();
				//sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][aimdir: " + aimDir + "]" ); // TinMan: *debug*
				setAimDirection( aimDir );
			}
		}
		
		// TinMan: Wherizy?
		if ( !AI_ENEMY_VISIBLE ) {
			if ( enemyLostTime == 0 ) {
				//sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][start lost timer]" ); // TinMan: *debug*
				enemyLostTime = RandomDelay( SABOT_ENEMY_LOST_TIME, SABOT_ENEMY_LOST_TIME + 0.5 );
			} else if ( enemyLostTime < sys.getTime() ) {
				//sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][lost]" ); // TinMan: *debug*
				//setState( "state_Chase" );
				clearEnemy();
			}
		} else if ( AI_ENEMY_VISIBLE ) {
			//sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][reset lost timer]" ); // TinMan: *debug*
			enemyLostTime = 0; // TinMan: Can see enemy again, so stop timer
		}
		
		// TinMan: See if we should still go for goal.
		if ( checkGoal() ) {
			clearGoal();
			break; // TinMan: Exit state
		}
		/* *off* *test* I have an odd loop bug here, I don't know what's tripping off move_done.
		if ( AI_MOVE_DONE ) {
			sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][movedone][" + goal.getName() + ":" + goalPriority + "]" ); // TinMan: *debug*
			clearGoal();
			break; // TinMan: Exit state
		}
        */
        // *todo* - doesnt work haha :P
        // SHREWM BEGIN             ||
        // Whydu disable this? :)   ||
        // SHREWM END               \/
        /*
		if ( AI_DEST_UNREACHABLE ||  movePosition_y > 50 ) { // TinMan: *todo* Hmm, disabling this is probably a "bad thing"
			sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][unreachable]" ); // TinMan: *debug*
			clearGoal();
			break; // TinMan: Exit state
		}
        */
        // Now I know why...because this part suxx like hell
        // dont know whats wrong with it but Ill go to bed now...
        
		checkWeapon(); // TinMan: Make sure your weapon is nice and shiny
	} // TinMan: End eachFrame/state
	
	if ( newEnemy ) {
		setEnemy( newEnemy );
	}
	
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_MoveToGoal][Exit state]" ); // TinMan: *debug*
	// TinMan: Exit state
	setState( "state_Idle" );
}

/*
=====================
bot_sabot::state_Combat
TinMan: Direct combat with enemy. Strike him down!
=====================
*/
void bot_sabot::state_Combat() {
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Combat]" ); // TinMan: *debug*
	
	float aimTarget;
	float weaponRangeBuffer;
	float nextAimTime;
	float nextGoalLook;
	float enemyLostTime;
	float fire, forwards, back, left, right;
	float nextStrafeTime;
	float moveFlags;
	float reactToEnemy, reactFire;
	float dist;
	float lastVisibleEnemyDist;
	float checkTime;
	vector lastVisibleEnemyPos;
	vector aimDir, aimPosition;
	entity enemy, newEnemy;
	
	if ( !getEnemy() ) {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Combat][!enemy]" ); // TinMan: *debug*
		clearGoal();
		setState( "state_Idle" );
	}
	
	reactToEnemy = false;
	enemyLostTime = 0;
	enemy = getEnemy();
	setMoveToEnemy();
	// TinMan: Flip out straight away if you can't do move. *todo* move to attack position instead
	/*
	if ( AI_DEST_UNREACHABLE ) {
		sys.println( "[" + int( sys.getTime() ) + "][state_Combat][Start:Cannot move to enemy!]" ); // TinMan: *debug*
		setState( "state_Idle" );
	}
	*/

	eachFrame {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Combat]" ); // TinMan: *debug*
		//debugVis( "state_Combat", "" );
		
		weaponRangeBuffer = sys.random( 16 ) + 16; // TinMan: *todo* rethink
		// TinMan: Look for new goals. The shinier the better.
		if ( sys.getTime() > nextGoalLook ) {
			nextGoalLook = sys.getTime() + SABOT_NEXT_GOAL_LOOK;
			lookForGoal( true ); // TinMan: Will set newGoal. More important than it might seem, here is the ultimate decision whether to discard current goal (via a higher priority), which may be something set by chooseGoal, and something as important as capping flag.
			// TinMan: If pretty new thing better than current plaything.
			if ( newGoal ) {
				if ( newGoal != goal ) {
					if ( newGoalPriority > goalPriority ) {
						setNewGoal();
						break;
					}
				}
			}
		}
		
		// TinMan: Look out for more imediate threats.
		if ( !reactToEnemy ) {
			if ( nextEnemyLook() ) {
				newEnemy = lookForEnemies( true, false );
				if ( newEnemy ) { // TinMan: We have a new challenger
					//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][Wah! New enemy closy close]" ); // TinMan: *debug*
					reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Set react timer.
				} else {
					// TinMan: Shh, I hear something.
					newEnemy = listenForEnemies();
					if ( !newEnemy && AI_PAIN ) { // TinMan: *cheaphack* work around due to fact heardsound is being shoehorned into something it's not desiged to handle - multiple enemies
						newEnemy = lookForEnemies( false, false );
					}
					if ( newEnemy ) { // TinMan: We have a new challenger
						reactToEnemy = sys.getTime() + react_to_enemy_sound; // TinMan: Set react timer.
					}
				}
			}
		} else {
			if ( reactToEnemy < sys.getTime() ) {
				//sys.println( "[" + int( sys.getTime() ) + "][" + me +"][state_Combat][newenemy!!]" ); // TinMan: *debug*
				//break; // TinMan: Exit state
				setEnemy( newEnemy );
				reactToEnemy = 0;
				newGoalPriority = getPlayerPriority( newEnemy );
				if ( newGoalPriority > goalPriority ) {
					//sys.println( "[" + int( sys.getTime() ) + "][" + me +"][state_Combat][newenemy!!]" ); // TinMan: *debug*
					newGoal = newEnemy;
					newGoalType = SABOT_GOAL_ATTACK;
					setNewGoal();
					setState( "state_Combat" );	
				}
			}
		}
		
		if ( getEnemy() ) {
			goalPriority = getPlayerPriority( getEnemy() ); // TinMan: Update goal/enemy priority
		}
		
		// TinMan: Check to see if enemy died, and set some reaction time
		if ( AI_ENEMY_DEAD || !getEnemy() ) {
			//sys.println( "[" + int( sys.getTime() ) + "][" + me + "[state_Combat][no enemy]" ); // TinMan *debug*
			if ( timerEnd == -1 ) {
				setMoveToPosition( lastVisibleEnemyPos );
				setTimer( 0.3 );
			} else if ( getTimer() ) {
				clearTimer();
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "[state_Combat][no enemy]" ); // TinMan *debug*
				clearGoal();
				break; // TinMan: Exit state
			}
		} else {
			lastVisibleEnemyPos = getEnemyPos();
			lastVisibleEnemyPos_z += viewHeight; // TinMan: cheap hack pos off ground since we'll be aiming at it when enemy dead
			lastVisibleEnemyDist = enemyRange();
		}
		
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Combat][lVisEnyPos: " + lastVisibleEnemyPos + "][lVisEnDist:" + lastVisibleEnemyDist + "]" ); // TinMan: *debug*
		
		/* if ( getEnemy() ) {
			entity en = getEnemy();
			sys.println( "[" + int( sys.getTime() ) + "][state_Combat][lastvisenemyDist: " + lastVisibleEnemyDist + "][enemydist: " + sys.vecLength( body.getOrigin() - en.getOrigin() ) + "]" ); // TinMan: *debug*
		} */
		
		// TinMan: The enemy might move to an unreachable spot, wall edge, or be midair, so we need to reset move *todo* Ideally we want to do a movetoattackposition
		if ( ( AI_DEST_UNREACHABLE || AI_MOVE_DONE ) && AI_ONGROUND ) {
			//debugVis( "state_Combat", "DEST_UNREACH || MOVE_DONE" );
			if ( AI_ENEMY_DEAD ) { // TinMan: *cheaphack*
				clearGoal();
				break; // TinMan: Exit state	
			}
			
			if ( checkTime < sys.getTime() ) {
				if ( getEnemy() ) {
					//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][reset move to enemy!]" ); // TinMan: *debug*
					checkTime = sys.getTime() + 0.3;
					if ( AI_MOVE_DONE ) { // TinMan: at last reachable enemy pos
						locateEnemy(); // TinMan: force update
					}
					setMoveToEnemy();
					//setMoveToAttackPosition( getEnemy() );
				}
			}
		}
		
		/* TinMan: *todo* NGS rejigger
		// TinMan: See if we even want to be fighting - if enemy too badass or too beatup
		if ( checkSelf() ) { 
			//if ( sys.random( 100 ) < 40 ) { // TinMan: *todo* demagic, set a limit/timer
				//if ( body.distanceTo( getEnemy() ) > 128 ) {
					waitFrame();
					setState( "state_Retreat" );
				//}
			//}
		}
		*/

		// TinMan: Aim at enemy
		if ( nextAimTime < sys.getTime() ) { // TinMan: Roughen up aiming a bit
			nextAimTime = sys.getTime() + SABOT_NEXT_AIM;
			if ( !AI_ENEMY_DEAD ) {
				aimTarget = getIntKey( "target_" + currentWeapon );

				aimDir = getAimDir( getEnemy(), aimTarget ); // TinMan: new choose a point on enemy, and predict shot
				
				aimDir = addAimVariation( aimDir ); // TinMan: Screw up aim cause I'm mean.
								
				setAimDirection( aimDir ); // TinMan: Finally set our target to aim to.
				//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][aimDir = " + aimDir_x + " " + aimDir_y + " " +aimDir_z + "]" ); // TinMan: *debug*
			} else { // TinMan: Aim at teh dedders
				aimDir = lastVisibleEnemyPos - getViewPosition();
				setAimDirection( aimDir );
			}
		}

		// TinMan: Check shot is within weapon range. AND SMITE HIM!
		//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][CurrentWeapon: " + currentWeapon + "][Weaponrange]" ); // TinMan: *debug*
		if ( /* *off* *test* ( lastVisibleEnemyDist > weaponRange_y ) && */ ( lastVisibleEnemyDist < weaponRange_z ) )	{
			// TinMan: See if any enemies in shot.
			if ( !reactFire ) { // TinMan: Allready going to fire
				if ( checkShot( false ) ) { // TinMan: See if enemy in straight bounds trace.
					reactFire = sys.getTime() + react_fire;
				} else if ( checkShot( true ) ) { // TinMan: See if enemy in straight point trace.
					reactFire = sys.getTime() + react_fire;
				} else if ( checkAimedShot( aimDir, lastVisibleEnemyDist ) ) { // TinMan: See if we are aimed at position and shot will make it.
					reactFire = sys.getTime() + react_fire;
				} else {
					reactFire = 0;
					fire = false;
				}
			}
		}	else { // TinMan: Aw, out of weapon range.
			reactFire = 0;
			fire = false;
		}

		if ( reactFire != 0 ) {
			if ( reactFire < sys.getTime() ) {
				fire = true; // TinMan: Ratatat tat, boom, fwoosh!
			}
		}
		
		if ( fire && ( enemyLostTime < 0.2 || AI_ENEMY_DEAD ) ) { // TinMan: *cheaphack* The delayed fire is working out quite nicely at the moment except leads to the bot shooting into walls too much. *todo* we should really be stopping firing after a delay like the starting fire does.
			AI_WEAPON_FIRE = true;
		}

		// TinMan: Move closer/ retreat/ strafe.. to get to weapon sweet spot
		if ( lastVisibleEnemyDist > ( weaponRange_x + weaponRangeBuffer ) ) { // TinMan *todo* rejigger to lastenpos
			// TinMan: Closer than weaponRange
			//if ( AI_ENEMY_VISIBLE ) {
				//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][Moving to sweet spot: " + weaponRange_x + "]" ); // TinMan: *debug*
				//if ( travelDistanceToPoint( lastVisibleEnemyPos ) < 600 || lastVisibleEnemyDist >= weaponRange_z ) { // TinMan: *todo* remove travdist check once better solution implemented
					// TinMan: *todo* old skoolas *rem*
					//if ( checkMoveForward() ) {
					//	moveForward();
					//}
// SHREWM BEGIN
                			if ((lastVisibleEnemyDist) < 260)
                			{
                    				AI_JUMP = true;
                    				if ( !AI_ONGROUND )
                        				AI_JUMP = false;
                                    else if ( AI_ONGROUND )
                                        AI_JUMP = true;
                			}
// SHREWM END
					moveAction();
				//}
			//}
		} else if ( lastVisibleEnemyDist < ( weaponRange_x - weaponRangeBuffer ) ) { // TinMan: EnemyRange Further than weaponRange
			//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][Moving backwards to sweet spot]" ); // TinMan: *debug*
			//AI_FORWARD = false;
			if ( checkMoveBack() ) {
				moveBackward();
			}
		} /* else {
			//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][At sweet spot]" ); // TinMan: *debug*
			debugVis( "state_Combat", "at sweet pos" );
		} */
		
		if ( nextStrafeTime < sys.getTime() ) { // TinMan: Randomly change strafe
			//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][Strafe]" ); // TinMan: *debug*
			nextStrafeTime = RandomDelay( SABOT_STRAFE_MIN, SABOT_STRAFE_MAX );
	
			left = false;
			right = false;
			left = checkStrafeLeft();
			right = checkStrafeRight();
			 	
			if ( left && right ) {
				right == !AI_STRAFE_RIGHT; // TinMan: went right last time
			 	left == !AI_STRAFE_LEFT;
			}
			
			if ( left && right ) {
				if ( sys.random( 100 ) < 50 ) {
					right = false;
				} else {
					left = false;
				}
			}
		}
		
		if ( AI_ENEMY_VISIBLE ) {		
			if ( right ) {
				moveRight();
			} else if ( left ) {
				moveLeft();
			} // else {
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Combat][can't strafe]" ); // TinMan: *debug*
			//} 
		}

		// TinMan: *cheaphack* stop bashing into stuff
		if ( AI_STRAFE_RIGHT && !checkStrafeRight() ) {
			AI_STRAFE_RIGHT = false;
			//nextStrafeTime = sys.getTime() + 0.05;
		}
		if ( AI_STRAFE_LEFT && !checkStrafeLeft() ) {
			AI_STRAFE_LEFT = false;		
			//nextStrafeTime = sys.getTime() + 0.05;
		}
		
		//if ( AI_FORWARD && !checkMoveForward() ) {
			// AI_FORWARD = false;
		//}

		if ( AI_BACKWARD && !checkMoveBack() ) {
			AI_BACKWARD = false;
		}

		// TinMan: Wherizy?
		if ( !AI_ENEMY_VISIBLE ) {
			if ( enemyLostTime == 0 ) {
				//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][start lost timer]" ); // TinMan: *debug*
				enemyLostTime = RandomDelay( SABOT_ENEMY_LOST_TIME, SABOT_ENEMY_LOST_TIME + 0.5 );
			} else if ( enemyLostTime < sys.getTime() ) {
				//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][chase]" ); // TinMan: *debug*
				setState( "state_Chase" );
			}
		} else if ( AI_ENEMY_VISIBLE ) {
			//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][reset lost timer]" ); // TinMan: *debug*
			enemyLostTime = 0; // TinMan: Can see enemy again, so stop timer
		}
		
		checkBlocked();
		
		//sys.debugBounds( '1 1 1', lastVisibleEnemyPos + '-1 -1 -1', lastVisibleEnemyPos + '1 1 1' , 0.01 ); // TinMan: *debug*

		checkWeapon();
		
		//debugVis( "state_Combat", "enemyDist:" + int( lastVisibleEnemyDist ) );
		
	} // TinMan: End eachFrame/state
	
	//sys.println( "[" + int( sys.getTime() ) + "][state_Combat][Exit state]" ); // TinMan: *debug*
	
	// TinMan: Exit state
	if ( newEnemy ) { // TinMan: *cheaphack* Fix the loosing enemy due to reactiontime and state change *todo* rethink
		setEnemy( newEnemy );
	}

	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Combat][Exit state]" ); // TinMan: *debug*
	setState( "state_Idle" );
}

/*
=====================
bot_sabot::state_Chase
TinMan: Move to restablish combat. Hunt him down!
=====================
*/
void bot_sabot::state_Chase() {
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Chase]" ); // TinMan: *debug*
	vector movePosition, aimDir, aimPosition;
	float chaseTime;
	float nextGoalLook;
	float reactToEnemy;
	entity newEnemy;
	
	if ( !getEnemy() ) {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Chase][!enemy]" ); // TinMan: *debug*
		clearGoal();
		setState( "state_Idle" );
	}

	// TinMan: Gentlemen, start your engines.
	//if ( canReachEntity( getEnemy() ) ) {
	setMoveToEnemy();
	//} else {
		//setMoveToAttackPosition( getEnemy() );
		//
		/*
		if ( AI_DEST_UNREACHABLE ) {
			// sys.println( "[" + int( sys.getTime() ) + "][state_Chase][Cannot reach enemy or attack position]" ); // TinMan: *debug*
			clearEnemy();
			setState( "state_Idle" );
		}
		*/
		// sys.println( "[" + int( sys.getTime() ) + "][state_Chase][Moving to attack position]" ); // TinMan: *debug*
	//}
	// TinMan: Flip out straight away if you can't do move.
	/*if ( AI_MOVE_DONE || AI_DEST_UNREACHABLE ) {
		setState( "state_Idle" );		
	}*/
	
	reactToEnemy = false;

	setTimer( SABOT_CHASE_TIME ); // TinMan: Start chase timer.
	//chaseTime = sys.getTime() + SABOT_CHASE_TIME; // TinMan: Start chase timer.

	eachFrame {	
		//sys.println( "[" + int( sys.getTime() ) + "][state_Chase]" ); // TinMan: *debug*
		// TinMan: Check to see if enemy died
		if ( AI_ENEMY_DEAD || !getEnemy() ) {
			clearGoal();
			break;
		}
		
		// TinMan: Move along given path
		movePosition = moveAction();
		
		// TinMan: Lookee where we damn be going.
		//movePosition = getMovePosition();
		if ( /* AI_ONGROUND && */ movePosition != body.getOrigin() ) {
			aimDir = movePosition - body.getOrigin();
			setAimDirection( aimDir );
		}
				
		checkBlocked();

		// TinMan: Look out for new badasses trying to kill you.
		if ( !reactToEnemy ) {
			if ( nextEnemyLook() || AI_PAIN || AI_ENEMY_VISIBLE ) {
				if ( AI_ENEMY_VISIBLE ) {
					reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Current enemy back in view. Set react timer.
					newEnemy = getEnemy(); // TinMan: *todo* seems a bit of a cludge, but whatever
				} else {
					newEnemy = lookForEnemies( true, false );
					if ( newEnemy ) { // TinMan: We have a new challenger
						reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Set react timer.
					} else {
						// TinMan: Shh, I hear something.
						newEnemy = listenForEnemies();
						if ( !newEnemy && AI_PAIN ) { // TinMan: *cheaphack* work around due to fact heardsound is being shoehorned into something it's not desiged to handle - multiple enemies
							newEnemy = lookForEnemies( false,false );
						}
						if ( newEnemy ) {
							reactToEnemy = sys.getTime() + react_to_enemy_sound; // TinMan: Set react timer.
						}
					}
				}
			}
		} else {
			if ( reactToEnemy < sys.getTime() ) {
				newGoalPriority = getPlayerPriority( newEnemy );
				
				setEnemy( newEnemy );
				reactToEnemy = 0;
				if ( newGoalPriority > goalPriority || newEnemy == goal ) {
					newGoal = newEnemy;
					newGoalType = SABOT_GOAL_ATTACK;
					setNewGoal();
					setState( "state_Combat" );	
				}
			}
		}
	
		checkWeapon();
		
		/*
		// TinMan: Get back into the fightening *todo* remove let ^ handle it
		if ( AI_ENEMY_IN_FOV ) {
			waitFrame();
			setState( "state_Combat" );
		}
		*/
			
		// TinMan: At last seen enemy pos and can't see enemy *todo* rethink
		if ( AI_MOVE_DONE || AI_DEST_UNREACHABLE ) {
			float checkTime, lostTime;
			if ( lostTime && lostTime < sys.getTime() ) {
				//sys.println( "[" + int( sys.getTime() ) + "][state_Chase][lostTime Up]" ); // TinMan: *debug*
				clearEnemy();
				break;
			}
			
			if ( checkTime < sys.getTime() ) {
				if ( getEnemy() ) {
					//sys.println( "[" + int( sys.getTime() ) + "][state_Chase][reset move to enemy!]" ); // TinMan: *debug*
					checkTime = sys.getTime() + 0.3;
					if ( AI_MOVE_DONE ) { // TinMan: Cheat a bit, move towards current enemy pos
						locateEnemy();
					}
					setMoveToEnemy();
					if ( AI_MOVE_DONE || AI_DEST_UNREACHABLE ) { // TinMan: Still can't reach enemy
						if ( !lostTime ) {
							//sys.println( "[" + int( sys.getTime() ) + "][state_Chase][lostTime start]" ); // TinMan: *debug*
							lostTime = sys.getTime() + 1; // TinMan: *todo* demagic
						}
					}
					//setMoveToAttackPosition( getEnemy() );
				}
			}
		} else {
			//sys.println( "[" + int( sys.getTime() ) + "][state_Chase][reset lostime]" ); // TinMan: *debug*
			lostTime = 0;
		}
		
		// TinMan: Only chase the enemy for a certain amount of time, if it's up you've lost him.
		//if ( chaseTime < sys.getTime() ) {
		if ( getTimer() ) {
			//sys.println( "[" + int( sys.getTime() ) + "][state_Chase][ChaseTime Up]" ); // TinMan: *debug*
			clearTimer();
			clearEnemy();
			clearGoal();
			break;
		}
	}
	
	if ( newEnemy ) {
		setEnemy( newEnemy );
	}
	
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Chase][Exit state]" ); // TinMan: *debug*
	setState( "state_Idle" );
}

/*
=====================
bot_sabot::state_Retreat
TinMan: Trying to escape enemy. Run Forest! Run!
*todo* currently unused
=====================
*/
void bot_sabot::state_Retreat() {
	// sys.println( "[" + int( sys.getTime() ) + "][state_Retreat]" ); // TinMan: *debug*
	float nextGoalLook;
	float reactToEnemy;
	float enemyLostTime;
	vector movePosition;
	vector aimDir;
	entity newEnemy;
	
	if ( !getEnemy() ) {
		setState( "state_Idle" );
	} 
	
	setMoveToCover();
	if ( AI_DEST_UNREACHABLE || AI_MOVE_DONE ) {
		sys.println( "[" + int( sys.getTime() ) + "][state_Retreat][Cannot move to cover]" ); // TinMan: *debug*
		/*setMoveOutOfRange( getEnemy(), 400 ); // TinMan: *todo* no magic
		if ( AI_DEST_UNREACHABLE || AI_MOVE_DONE ) {
			sys.println( "[" + int( sys.getTime() ) + "][state_Retreat][Cannot move out of range]" ); // TinMan: *debug*
		}	else {
			sys.println( "[" + int( sys.getTime() ) + "][state_Retreat][Moving out of range]" ); // TinMan: *debug*
		}
		*/
	} /*else {
		sys.println( "[" + int( sys.getTime() ) + "][state_Retreat][Moving to cover]" ); // TinMan: *debug*
	} */

	eachFrame {
		// TinMan: Check to see if enemy died	
		if ( AI_ENEMY_DEAD || !getEnemy() ) {
			break;
		}
		
		if ( AI_MOVE_DONE || AI_DEST_UNREACHABLE ) {
			//clearEnemy();
			break; // TinMan: Exit state
		}
		
		// TinMan: Don't want to be running all our lives
		if ( !AI_ENEMY_VISIBLE ) {
			if ( enemyLostTime == 0 ) {
				enemyLostTime = sys.getTime() + 12; // TinMan: *todo* demagic
			} else if ( enemyLostTime < sys.getTime() ) {
				clearEnemy();
				break; // TinMan: Exit state
			}
		} else {
			if ( AI_PAIN && AI_ENEMY_VISIBLE ) { // TinMan: *todo* set reaction timer
				setState( "state_CombatRetreat" );
			}
		}
		
		// TinMan: Move along given path
		movePosition = moveAction();
		
		// TinMan: Lookee where we damn be going.
		//movePosition = getMovePosition();
		if ( /* AI_ONGROUND && */ movePosition != body.getOrigin() ) {
			aimDir = movePosition - body.getOrigin();
			setAimDirection( aimDir );
		}
		
		checkBlocked();
		
		// TinMan: Look out for new badasses trying to kill you.
		if ( !reactToEnemy ) {
			if ( nextEnemyLook() || AI_PAIN ) {
				newEnemy = lookForEnemies( true, false );
				if ( newEnemy ) { // TinMan: We have a new challenger
					if ( body.distanceTo( newEnemy ) < enemyRange() ) { // TinMan: Closer than the chump I'm hunting.
						reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Set react timer.
					}
				} else {
					// TinMan: Shh, I hear something.
					newEnemy = listenForEnemies();
					if ( !newEnemy && AI_PAIN ) { // TinMan: *cheaphack* work around due to fack heardsound is being shoehorned into something it's not desiged to handle - multiple players
						newEnemy = lookForEnemies( false, false );
					}
					if ( newEnemy ) {
						if ( body.distanceTo( newEnemy ) < enemyRange() ) { // TinMan: Closer than the chump I'm hunting.
							reactToEnemy = sys.getTime() + react_to_enemy_sound; // TinMan: Set react timer.
						}
					}
				}
			}
		} else {
			if ( reactToEnemy < sys.getTime() ) {
				break; // TinMan: Exit state
			}
		}
				
		checkWeapon();
	} // TinMan: End eachFrame/state
	
	// TinMan: Exit state
	if ( newEnemy ) { // TinMan: *cheaphack* Fix the loosing enemy due to reactiontime and state change *todo* rethink
		setEnemy( newEnemy );
	}
	
	setState( "state_Idle" );
}

/*
=====================
bot_sabot::state_Hold
TinMan: Hold position - Aka camp.
=====================
*/
void bot_sabot::state_Hold() {
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Hold]" ); // TinMan *debug*
	vector movePosition;
	entity newEnemy;
	float reactToEnemy;
	float nextGoalLook;
	reactToEnemy = 0;
	stopMove(); // TinMan: *note* All states should handle movement to that goal, if it doesn't do a move command at start then stopMove previous movement.
	
	setTimer( SABOT_HOLD_TIME ); // TinMan: Start timer if we don't want to wait forever
	
	eachFrame {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Hold][" + goal.getName() + "]" ); // TinMan *debug*
		// TinMan: Look for new goals. The shinier the better.
		if ( sys.getTime() > nextGoalLook ) {
			nextGoalLook = sys.getTime() + SABOT_NEXT_GOAL_LOOK;
			lookForGoal( true ); // TinMan: Will set newGoal. More important than it might seem, here is the ultimate decision whether to discard current goal (via a higher priority), which may be something set by chooseGoal, and something as important as capping flag.
			// TinMan: If pretty new thing better than current plaything.
			if ( newGoal ) {
				if ( newGoal != goal ) {
					if ( newGoalPriority > goalPriority ) {
						setNewGoal();
						break;
					}
				}
			}
		}
		
		// TinMan: Lookin for trouble with a big T.
		if ( !reactToEnemy ) {
			if ( nextEnemyLook() || AI_PAIN || AI_ENEMY_VISIBLE ) {
				if ( AI_ENEMY_VISIBLE ) {
					reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Current enemy back in view. Set react timer.
					newEnemy = getEnemy(); // TinMan: *todo* seems a bit of a cludge, but whatever
				} else {
					newEnemy = lookForEnemies( true, false );
					if ( newEnemy ) { // TinMan: We have a new challenger
						reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Set react timer.
					} else {
						// TinMan: Shh, I hear something.
						newEnemy = listenForEnemies();
						if ( !newEnemy && AI_PAIN ) { // TinMan: *cheaphack* work around due to fact heardsound is being shoehorned into something it's not desiged to handle - multiple enemies
							newEnemy = lookForEnemies( false, false );
						}
						if ( newEnemy ) {
							reactToEnemy = sys.getTime() + react_to_enemy_sound; // TinMan: Set react timer.
						}
					}
				}
			}
		} else {
			if ( reactToEnemy < sys.getTime() ) {
				setEnemy( newEnemy );
				reactToEnemy = 0;
				newGoalPriority = getPlayerPriority( newEnemy );
				//if ( newGoalPriority > goalPriority ) { // TinMan: A baddy is always more important than holding
					newGoal = newEnemy;
					newGoalType = SABOT_GOAL_ATTACK;
					setNewGoal();
					setState( "state_Combat" );	
				//}
			}
		}
	
		// TinMan: What are we waiting for?		
		if ( isCTF() ) {
			if ( goal == ourFlag ) {
				if ( getFlagStatus( team ) == FLAGSTATUS_INBASE ) {
					if ( body.distanceToPoint( goal.getOrigin() ) > 32 ) {
						goalType = SABOT_GOAL_MOVE;
						break; // TinMan: Exit state
					}
				}
			} else if ( goal == theirFlag ) {
				if ( getFlagStatus( 1 - team ) == FLAGSTATUS_INBASE ) {
					if ( body.distanceToPoint( goal.getOrigin() ) > 32 ) {
						goalType = SABOT_GOAL_MOVE;
						break; // TinMan: Exit state
					}
				}				
			}
			
			// TinMan: *cheaphack* look somewhere usefull (hopefully), better would be to do a quick predict towards theircap and look there.
			vector lookDir, capPos;
			if ( goal == ourFlag ) {
				capPos = theirCap;
			} else if ( goal == theirFlag ) {
				capPos = ourCap;
			}
			lookDir = capPos - body.getOrigin();
			lookDir_z = 0;
			setAimDirection( lookDir );
			
			// TinMan: We only hold for a certain amount of time in ctf
			if ( getTimer() ) {
				// sys.println( "[" + int( sys.getTime() ) + "][state_Hold][HoldTime Up]" ); // TinMan: *debug*
				clearTimer();
				clearGoal();
				break; // TinMan: Exit state
			}
		}
		
		/* TinMan: *todo* rejigger
		// TinMan: Look for other items to grab
		*/
	
		// TinMan: Move to hold position if too far away *todo* hardly triggers, nessesary?
		if ( body.distanceToPoint( goalPosition ) > 128 ) {
			// sys.println( "[" + int( sys.getTime() ) + "][" + getName() + "][state_Hold][too far away from goal: move]" ); // TinMan *debug*
			goalType = SABOT_GOAL_MOVE; // TinMan: Once the bot reaches the hold goal via movetogoal, the choosegoal will probably set goal back to hold. *todo* really need a stack
			break; // TinMan: Exit state
			//setState( "state_MoveToGoal" );
		}
		
		checkWeapon(); // TinMan: Make sure your weapon is nice and shiny
	} // TinMan: End eachFrame/state
	
	//sys.println( "[" + int( sys.getTime() ) + "][" + getName() + "][state_Hold][Exit state]" ); // TinMan *debug*
	
	// TinMan: Exit state
	if ( newEnemy ) { // TinMan: *cheaphack* Fix the loosing enemy due to reactiontime and state change *todo* rethink
		setEnemy( newEnemy );
	}
	
	setState( "state_Idle" );
}

/*
=====================
bot_sabot::state_Follow
TinMan: Follow teammate.
*todo* currently unused
=====================
*/
void bot_sabot::state_Follow() {
	//sys.println( "[" + int( sys.getTime() ) + "][state_Follow]" ); // TinMan *debug*
	vector movePosition;
	vector aimDir;
	float nextGoalLook;
	entity leader;
	entity newEnemy;
	float reactToEnemy;
	reactToEnemy = 0;
	if ( !goal ) {
		setState( "state_Idle" );
	}
	
	leader = goal;
	
	setMoveToPosition( goal.getOrigin() );

	eachFrame {
		if ( checkLeader( leader ) || !leader ) { // TinMan: See if we should still go for it
			setState( "state_Idle" );
		}
		
		// TinMan: Lookin for trouble with a big T.
		if ( !reactToEnemy ) {
			if ( nextEnemyLook() || AI_PAIN || AI_ENEMY_VISIBLE ) {
				if ( AI_ENEMY_VISIBLE ) {
					reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Current enemy back in view. Set react timer.
					newEnemy = getEnemy(); // TinMan: *todo* seems a bit of a cludge, but whatever
				} else {
					newEnemy = lookForEnemies( true, false );
					if ( newEnemy ) { // TinMan: We have a new challenger
						reactToEnemy = sys.getTime() + react_to_enemy; // TinMan: Set react timer.
					} else {
						// TinMan: Shh, I hear something.
						newEnemy = listenForEnemies();
						if ( !newEnemy && AI_PAIN ) { // TinMan: *cheaphack* work around due to fact heardsound is being shoehorned into something it's not desiged to handle - multiple enemies
							newEnemy = lookForEnemies( false, false );
						}
						if ( newEnemy ) {
							reactToEnemy = sys.getTime() + react_to_enemy_sound; // TinMan: Set react timer.
						}
					}
				}
			}
		} else {
			if ( reactToEnemy < sys.getTime() ) {
				setEnemy( newEnemy );
				reactToEnemy = 0;
				newGoalPriority = getPlayerPriority( newEnemy );
				if ( newGoalPriority > goalPriority ) {
					newGoal = newEnemy;
					newGoalType = SABOT_GOAL_ATTACK;
					setNewGoal();
					setState( "state_Combat" );	
				}
			}
		}
		
		/* *todo* rejigger
		// TinMan: Look for other items to grab on the way
		*/
		
		setMoveToPosition( leader.getOrigin() ); // TinMan: Diferent from other states, our goal, the leader is on the move
		
		// TinMan: The actual following
		if  ( body.distanceTo( leader ) < SABOT_FOLLOWDIST_MIN ) {
			//setAimPosition( getAIAimTargets( leader, true ) );
			stopMove();
		} else if ( body.distanceTo( leader ) > SABOT_FOLLOWDIST_MAX ) {
			// TinMan: Move along given path
			movePosition = moveAction();
			
			// TinMan: Lookee where we damn be going.
			//movePosition = getMovePosition();
			if ( /* AI_ONGROUND && */ movePosition != body.getOrigin() ) {
				aimDir = movePosition - body.getOrigin();
				setAimDirection( aimDir );
			}		
		}
		
		checkBlocked();
		checkWeapon(); // TinMan: Make sure your weapon is nice and shiny
	}
}

/*
=====================
bot_sabot::state_Killed
TinMan: botAi hijacks script when bots fakeclient is AI_DEAD, sets state to this.
=====================
*/
void bot_sabot::state_Killed() {
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][Died]" ); // TinMan: *debug*

	stopMove();
	
	while( AI_DEAD ) {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][state_Killed][waiting]" ); // TinMan: *debug*
		
		float nextFire;
		if ( sys.getTime() > nextFire ) {
			nextFire = sys.getTime() + 0.1;
			AI_WEAPON_FIRE = true; // TinMan: Fire so bot respawns
		}
		waitFrame();
	}
	
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][Restarting]" ); // TinMan: *debug*
	setState( "state_Begin" );
}


/***********************************************************************

	Functions

***********************************************************************/

/*
=====================
bot_sabot::setNewGoal
TinMan: *todo* complete
=====================
*/
void bot_sabot::setNewGoal() {
	/*if ( goal ) {
		sys.println( "[" + int( sys.getTime() ) + "][" + me + "][setNewGoal][curgoal: " + goal.getName() + "][curprio: "  + goalPriority + "]" ); // TinMan: *debug*
		sys.println( "[" + int( sys.getTime() ) + "][" + me + "][setNewGoal][newgoal: " + newGoal.getName() + "][newprio: " + newGoalPriority + "]" ); // TinMan: *debug*
	}*/
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][setNewGoal][newgoal: " + newGoal.getName() + "][newprio: " + newGoalPriority + "]" ); // TinMan: *debug*
	goalType 			= newGoalType;
	moveType 			= newMoveType;
	goal 					= newGoal;
	goalPosition 	= newGoalPosition;
	goalPriority 	= newGoalPriority;
}

/*
=====================
bot_sabot::clearGoal
=====================
*/
void bot_sabot::clearGoal() {
	if ( goal ) {
		oldGoal = goal;
	}
	
	goalType = 0;
	moveType = 0;
	goal = $null_entity;
	goalPosition = '0 0 0'; // TinMan: *todo* hmm how do you null a vector?
	goalPriority = 0;
}

/*
=====================
bot_sabot::clearNewGoal
TinMan: *todo* complete
=====================
*/
void bot_sabot::clearNewGoal() {
	newGoalType = 0;
	newMoveType = 0;
	newGoal = $null_entity;
	newGoalPosition = '0 0 0'; // TinMan: *todo* hmm how do you null a vector?
	newGoalPriority = 0;
}

/*
=====================
bot_sabot::moveForward
=====================
*/
void bot_sabot::moveForward() {
	AI_FORWARD = true;
	AI_BACKWARD = false;
}

/*
=====================
bot_sabot::moveBackward
=====================
*/
void bot_sabot::moveBackward() {
	AI_BACKWARD = true;
	AI_FORWARD = false;
}

/*
=====================
bot_sabot::moveRight
=====================
*/
void bot_sabot::moveRight() {
	AI_STRAFE_RIGHT = true;
	AI_STRAFE_LEFT = false;
}

/*
=====================
bot_sabot::moveLeft
=====================
*/
void bot_sabot::moveLeft() {
	AI_STRAFE_LEFT = true;
	AI_STRAFE_RIGHT = false;
}

/*
=====================
bot_sabot::moveAction
TinMan: Folow path given by routing, check and do more complex navigation actions: Jumps, elevators 
*todo* Rough as guts, should be moved into botAi and overhauled.
Note: pathtype is allways ored against pathtype_walk which is enum 0 so doing equality tests is fine.
=====================
*/
vector bot_sabot::moveAction() {
	float pathType;
	vector movePosition, secondaryMovePosition, bodyOrigin, moveDir, velocity;
	float dist, moveSpeed;
	
	entity plat;
	float i, numListedEntities, radius;
	vector platOrigin, platStart, platBottom;
	vector waitPos;
	radius = 256;
	
	movePosition = getMovePosition();
	
	if ( AI_MOVE_DONE || AI_DEST_UNREACHABLE ) {
		return movePosition;
	}
	
	// TinMan: *todo* not even attempting air control at the moment, just go with the flow.
	/*if ( !AI_ONGROUND ) {
		return movePosition;
	} */
	pathType = getPathType();
	
	velocity = body.getLinearVelocity();
	bodyOrigin = body.getOrigin();
	
	//sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][velocity: " + sys.vecLength( velocity ) + "]" ); // TinMan *debug*
	
	// TinMan: *todo* *test* don't strafe on complicated paths
	/* if ( pathType != PATHTYPE_WALK ) {
		AI_STRAFE_RIGHT = false;
		AI_STRAFE_LEFT = false;
	}
	*/
	
	//sys.debugBounds( '1 1 0', movePosition + '-1 -1 -1', movePosition + '1 1 1' , 0.01 ); // TinMan: *debug*
	
	//sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][PATHTYPE: " + getPathType() + "]" ); // TinMan *debug*
	if ( pathType == PATHTYPE_WALK ) {
		//sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][PATHTYPE_WALK]" ); // TinMan *debug*
			
		// TinMan: Do actual movement
		moveDir = movePosition - bodyOrigin;
		moveDir_z = 0; // TinMan: Only horizontal
		
		setMoveDirection( moveDir, 400 );
		// AI_FORWARD = true;
		
		/*
		if ( !AI_ONGROUND ) {
		 AI_JUMP = false;
		} else if ( sys.vecLength( velocity ) > 319 && body.distanceToPoint( movePosition ) > 128 ) {
			AI_STRAFE_LEFT = true;
			AI_JUMP = true;
		}
		
		vector ang = body.getViewAngles();
		*/
		
		//sys.println( "[" + int( sys.getTime() ) + "][state_MoveToGoal][moveposition =" + movePosition +  "]" ); // TinMan *debug*
		//moveForward();
		
		//checkBlocked();
	//} else if ( getPathType() & PATHTYPE_WALKOFFLEDGE ) {
		
	} else if ( pathType & PATHTYPE_BARRIERJUMP ) {
		//sys.println( "[" + int( sys.getTime() ) + "][moveAction][PATHTYPE_BARRIERJUMP]" ); // TinMan *debug*
		//sys.debugBounds( '1 0 0', getSecondaryMovePosition() + '-1 -1 -1', getSecondaryMovePosition() + '1 1 1' , 0.01 ); // TinMan: *debug* show bound secondarypos
		//sys.println( "[" + int( sys.getTime() ) + "][moveAction][LinearVelocity:" + body.getLinearVelocity() + "][vecLength:" + sys.vecLength( body.getLinearVelocity() ) + "]" ); // TinMan *debug*
		if ( body.distanceToPoint( movePosition ) < 8 ) {
			//sys.println( "[" + int( sys.getTime() ) + "][moveAction][close to moveposition]" ); // TinMan *debug*
				
			// TinMan: Ping a vector from self through jump end (which will be directly above jump start position)
			//secondaryMovePosition = getSecondaryMovePosition();

			//moveDir = movePosition - bodyOrigin;
			//moveDir_z = 0; // TinMan: Only horizontal
			//movePosition = moveDir * 2048;
			//movePosition_z = bodyOrigin_z + viewHeight;
			//sys.debugBounds( '0 1 0', movePosition + '-1 -1 -1', movePosition + '1 1 1' , 0.01 ); // TinMan: *debug* show bound secondarypos
			//setAimPosition( movePosition );
				
			//moveForward();
			AI_JUMP = true;
			//AI_JUMP = !AI_JUMP;
		} else if ( AI_ONGROUND ) { // TinMan: Move to jump position *todo* need propper check to indicate we aren't interested in reach start but reach end.
			// TinMan: Do actual movement
			moveDir = movePosition - bodyOrigin;
			moveDir_z = 0; // TinMan: Only horizontal
			
			setMoveDirection( moveDir, 400 );
			//moveForward();
		}
		
		//sys.println( "[" + int( sys.getTime() ) + "][moveAction][vertical vel: " + velocity_z + "]" ); // TinMan *debug*
		
		// TinMan: Going down
		if ( velocity_z < 250 /*&& !AI_ONGROUND*/ ) {
			//sys.println( "[" + int( sys.getTime() ) + "][moveAction][falling]" ); // TinMan *debug*
			//moveForward();
				
			// TinMan: While it's more propper to use secondarypos, since in this case the reach end is right above reach start there isn't much of a difference, movepos even seems to be working a little better, haven't looked into why.
			//secondaryMovePosition = getSecondaryMovePosition();
			//moveDir = secondaryMovePosition - bodyOrigin;
			moveDir = movePosition - bodyOrigin;
			moveDir_z = 0; // TinMan: Only horizontal
				
			setMoveDirection( moveDir, 400 );
		}
		
		movePosition = bodyOrigin; // TinMan: *cheaphack* set movepos to origin so he looks ahead
	
	} else { // TinMan: Unknown pathtype
		// TinMan: Have a bash at it anyway
		//sys.println( "[" + int( sys.getTime() ) + "][moveAction][Unhandled pathtype: " + getPathType() + "]" ); // TinMan *debug*
		
		// TinMan: Do actual movement
		moveDir = movePosition - bodyOrigin;
		moveDir_z = 0; // TinMan: Only horizontal
		setMoveDirection( moveDir, 400 );
		
		//checkBlocked();
	}
	
	return movePosition;
}

/*
=====================
bot_sabot::setTimer
TinMan: I am the very model of a modern major general
=====================
*/
void bot_sabot::setTimer( float endTime ) {
	//timerAmount = time;
	//timerStart = sys.getTime();
	timerEnd = sys.getTime() + endTime;
}

/*
=====================
bot_sabot::getTimer
TinMan: the bell, the bells
=====================
*/
float bot_sabot::getTimer() {
	if ( timerEnd == -1 ) { // TinMan: Timer not set
		//return -1;
		return false;
	} else if ( timerEnd < sys.getTime() ) { // TinMan: Timer over
		// TinMan: *todo* return Elapsed time
		//clearTimer(); // TinMan: Reset timer now it's over, since sometimes you'll be checking if theres a timer set `*todo* rethink this
		return true; 
	} else { // TinMan: Timer still going
		// TinMan: *todo* return Elapsed time
		return false; 
	}
}

/*
=====================
bot_sabot::clearTimer
TinMan: car button cloth
=====================
*/
void bot_sabot::clearTimer() {
	timerEnd = -1;
}

/*
=====================
bot_sabot::nextEnemyLook
TinMan: Timer
=====================
*/
boolean bot_sabot::nextEnemyLook() {
	if ( sys.getTime() > nextEnemyLookTime ) {
		nextEnemyLookTime = sys.getTime() + SABOT_NEXT_ENEMY_LOOK;
		return true;
	}
	return false;
}

/***********************************************************************

	Checks

***********************************************************************/
/*
=====================
bot_sabot::checkGoal
TinMan: See if we still want this goal, bumps off goal if it's old and mankey, or if we just want to rechoose goal.
=====================
*/
boolean bot_sabot::checkGoal() {
	vector origin, goalOrigin;
	float goalDist;

	if ( moveType == SABOT_MOVETYPE_ENTITY ) {
		if ( !goal ) {
			return true;
		}
		
		goalDist = body.distanceTo( goal );
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][checkGoal][dist: " + goalDist + "]" ); // TinMan *debug*

		if ( goalDist < 128 ) {
			//sys.println( "[" + int( sys.getTime() ) + "][checkGoal][close, checking]" ); // TinMan *debug*
		
			if ( goal.isHidden() ) {
				return true;
			} else if ( body.touches( goal ) ) {
				//sys.println( "[" + int( sys.getTime() ) + "][checkGoal][touched goal]" ); // TinMan *debug*
				return true;
			} else if ( goalDist < 32 ) {
				return true;
			}	
		}
	} else { // TinMan: SABOT_MOVETYPE_POSITION
		goalDist = body.distanceToPoint( goalPosition );
		if ( goalDist < 32 ) {
			return true;
		}	
	}

	return false;
}

/*
=====================
bot_sabot::gotFlag
TinMan: *cheaphack* a fix untill single goal/priority is sorted
*todo* expand into more general flag checking?
=====================
*/
boolean bot_sabot::gotFlag() {
	if ( getFlagCarrier( team ) == body ) {
		// TinMan: Did we get flag somehow? If so dump goal and go to idle where chooseGoal will sort it out.
		if ( goal != ourFlag ) {
			//sys.println( "[" + int( sys.getTime() ) + "][state_hold][!have flag!]" ); // TinMan *debug*
			return true;
		}
	}
	
	return false;
}

/*
=====================
bot_sabot::updateTeam
TinMan: Make sure team stuff is updated. kill red. kill red. kill red.
=====================
*/
void bot_sabot::updateTeam() {
	if ( getTeam( body ) == team ) {
		return;
	}
	
	team 				= getTeam( body );
	
	if ( isCTF() ) {	
		ourFlag 		= getFlag( team );
		theirFlag 	= getFlag( 1 - team );
		
		// TinMan: While having flags as the capture points is simpler, the d3ctf way of having them seperate was more versatile, and wasier for bots
		//ourCap 			= getCapturePoint( team );
		//theirCap 		= getCapturePoint( 1 - team );
		
		ourCap			= ourFlag.getVectorKey( "origin" ); // TinMan: since we cant rely on flag being at home base on spawn we'll have to rely on grabbing the original origin key, a better solution would to be to extend ctf funcs.
		theirCap		= theirFlag.getVectorKey( "origin" );
	}
}

/*
=====================
bot_sabot::checkSelf
TinMan: See if you are happy with your weapon/hp/armor. Returns true if not happy happy joy joy.
=====================
*/
boolean bot_sabot::checkSelf() {
	float weaponPriority;

	weaponPriority = getFloatKey( "priority_" + currentWeapon ); // TinMan: Grab priority from def
	
	if ( weaponPriority <= 3 && getActorHealth( body ) + getArmor( body ) < 40 ) { // TinMan: better weapon than pistol *todo* demagic happystrenght
		return true;
	}
	
	if ( weaponPriority < 40 && getActorHealth( body ) + getArmor( body ) <= 20 && !AI_PAIN ) { // TinMan: Weapon < chaingun and very beat up, if your allready getting hurt you're probably going to die anyway
		return true;
	}
	
	return false;
}

/*
=====================
bot_sabot::checkLeader
TinMan: 
=====================
*/
boolean bot_sabot::checkLeader( entity thisLeader ) {
	if ( !thisLeader ) {
		return true;
	}

	if ( getActorHealth( thisLeader ) <= 0 ) {
		return true;
	}

	return false;
}

/*
=====================
bot_sabot::checkWeapon
TinMan: Make sure your weapon is in order
=====================
*/
boolean bot_sabot::checkWeapon() {
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][goal: " + goal.getName() + "][prio: " + goalPriority + "]" ); // TinMan *debug* *todo* *rem*
	string weaponName;
	
	if ( getEnemy() ) {
		chooseWeapon( enemyRange() );
	} else {
		chooseWeapon( -1 );	
	}
	
	//nextBestWeapon(); // TinMan: *oldski* *rem*
	
	// TinMan: Unless you can be sure weapon only changes in above function (eg no auto switch or empty weapon switch ) you'll have to check and update weapon stuff here
	weaponName = body.getCurrentWeapon();
	if ( weaponName != currentWeapon ) {
		currentWeapon = weaponName;	
		weaponRange = getVectorKey( "range_" + currentWeapon ); // TinMan: Grab ranges from def
		//sys.println( "[" + int( sys.getTime() ) + "][checkweapon][currentWeapon: " + currentWeapon + "][weaponrange: " + weaponRange + "]" ); // TinMan *debug*
	}

	return false;
}

/*
=====================
bot_sabot::checkStrafeLeft
TinMan: Crude as hell, but will do for now
=====================
*/
boolean bot_sabot::checkStrafeLeft() {
	vector	pos, ang, dir, tracePosition, pushPosition;
	float maxRange, frac, traceDistance;
	//entity	traceEntity;

	maxRange = SABOT_CHECK_MOVE_DISTANCE; // TinMan: Max a bounds trace can do is 4096.

	pos = getViewPosition();
	ang = body.getViewAngles();

	ang_x = 0;
	ang_z = 0;
	ang_y += 90;
	dir = sys.angToForward( ang ); // TinMan: Convert angles (pitch yaw roll), to forward vector (x y z).

	frac = sys.tracePoint( pos, pos + ( dir * maxRange ), CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL, body );


	//float debugTime = 0.01;
	/*
	traceDistance = frac * maxRange; // TinMan: Convert the fraction of trace completed to game units

	sys.debugLine( '0 1 1', pos, pos + ( dir * maxRange ), debugTime ); // TinMan: *debug* full trace
	sys.debugBounds( '1 0 0', pos + '-1 -1 -1', pos + '1 1 1' , debugTime ); // TinMan: *debug* show bound at eyes

	sys.debugLine( '0 1 0', pos, tracePosition,	debugTime ); // TinMan: *debug* trace
	sys.debugBounds( '1 1 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug* show bound where trace hit
	*/

	if ( frac != 1 ) {
		return false;
	}
		
	tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit
	pushPosition = pushPointIntoAAS( tracePosition );
	
	// TinMan: Do cheap canhit enemy from here
	entity enemy = getEnemy();
	if ( enemy ) {
		//sys.debugLine( '0 1 0', tracePosition, tracePosition + ( getAIAimTargets( enemy ) - tracePosition ) * 2048,	debugTime ); // TinMan: *debug* trace
		frac = sys.tracePoint( tracePosition, tracePosition + ( getAIAimTargets( enemy, 2 ) - tracePosition ) * 2048, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL, body );// TinMan: can you see enemy from this new position?
		if ( sys.getTraceEntity() != enemy ) {
			//tracePosition = sys.getTraceEndPos();
			//sys.debugBounds( '1 0 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug*
			//sys.println( "[" + sys.getTime() + "][checkStrafeLeft][cannot see enemy from strafe pos]" ); // TinMan: *debug*
			return false;
		}
	}
	
	
	//sys.debugBounds( '1 1 0', pushPosition + '-2 -2 -2', pushPosition + '2 2 2' , debugTime ); // TinMan: *debug*
	if ( !testMoveToPosition( pushPosition ) ) {
		// sys.println( "[" + sys.getTime() + "][checkStrafeLeft][cannot moveto strafe pos]" ); // TinMan: *debug*
		return false; 	
	}		

	return true;
}

/*
=====================
bot_sabot::checkStrafeRight
=====================
*/
boolean bot_sabot::checkStrafeRight() {
	vector	pos, ang, dir, tracePosition, pushPosition;
	float maxRange, frac, traceDistance;
	//entity	traceEntity;

	maxRange = SABOT_CHECK_MOVE_DISTANCE; // TinMan: Max a bounds trace can do is 4096.
	pos = getViewPosition();
	ang = body.getViewAngles();
	ang_x = 0;
	ang_z = 0;
	ang_y -= 90;
	dir = sys.angToForward( ang ); // TinMan: Convert angles (pitch yaw roll), to forward vector (x y z).

	frac = sys.tracePoint( pos, pos + ( dir * maxRange ), CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL, body );

	//float debugTime = 0.01;
	/*
	traceDistance = frac * maxRange; // TinMan: Convert the fraction of trace completed to game units
	tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit

	sys.debugLine( '0 1 1', pos, pos + ( dir * maxRange ), debugTime ); // TinMan: *debug* full trace
	sys.debugBounds( '1 0 0', pos + '-1 -1 -1', pos + '1 1 1' , debugTime ); // TinMan: *debug* show bound at eyes

	sys.debugLine( '0 1 0', pos, tracePosition,	debugTime ); // TinMan: *debug* trace
	sys.debugBounds( '1 1 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug* show bound where trace hit
	*/

	if ( frac != 1 ) {
		return false;
	}
		
	tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit
	pushPosition = pushPointIntoAAS( tracePosition );
	
	// TinMan: Do cheap canhit enemy from here
	entity enemy = getEnemy();
	if ( enemy ) {
		//sys.debugLine( '0 1 0', tracePosition, tracePosition + ( getAIAimTargets( enemy ) - tracePosition ) * 2048,	debugTime ); // TinMan: *debug* trace
		frac = sys.tracePoint( tracePosition, tracePosition + ( getAIAimTargets( enemy, 2 ) - tracePosition ) * 2048, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL, body );// TinMan: can you see enemy from this new position?
		if ( sys.getTraceEntity() != enemy ) {
			//tracePosition = sys.getTraceEndPos();
			//sys.debugBounds( '1 0 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug*
			//sys.println( "[" + sys.getTime() + "][checkStrafeLeft][cannot see enemy from strafe pos]" ); // TinMan: *debug*
			return false;
		}
	}
	
	
	//sys.debugBounds( '1 1 0', pushPosition + '-2 -2 -2', pushPosition + '2 2 2' , debugTime ); // TinMan: *debug*
	if ( !testMoveToPosition( pushPosition ) ) {
		// sys.println( "[" + sys.getTime() + "][checkStrafeLeft][cannot moveto strafe pos]" ); // TinMan: *debug*
		return false; 	
	}		

	return true;
}

/*
=====================
bot_sabot::checkMoveForward
=====================
*/
boolean bot_sabot::checkMoveForward() {
	vector	pos, ang, dir, tracePosition;
	float maxRange, frac, traceDistance;
	entity	traceEntity;
	maxRange = SABOT_CHECK_MOVE_DISTANCE; // TinMan: Max a bounds trace can do is 4096.
	//pos = getViewPosition();
	pos = body.getOrigin();
	ang = body.getViewAngles();
	ang_x = 0;
	ang_z = 0;
	dir = sys.angToForward( ang ); // TinMan: Convert angles (pitch yaw roll), to forward vector (x y z).
	frac = sys.tracePoint( pos, pos + ( dir * maxRange ), CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL, body );
	traceDistance = frac * maxRange; // TinMan: Convert the fraction of trace completed to game units
	tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit

	/*
	float debugTime = 0.01;
	sys.debugLine( '0 1 1', pos, pos + ( dir * maxRange ), debugTime ); // TinMan: *debug* full trace
	sys.debugBounds( '1 0 0', pos + '-1 -1 -1', pos + '1 1 1' , debugTime ); // TinMan: *debug* show bound at eyes
	*/

	/*
	sys.debugLine( '0 1 0', pos, tracePosition,	debugTime ); // TinMan: *debug* trace
	sys.debugBounds( '1 1 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug* show bound where trace hit
	*/

	if ( frac == 1 ) {
		return false;
	}
		
	tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit
	tracePosition = pushPointIntoAAS( tracePosition );
	//sys.debugBounds( '1 1 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug*
	if ( !testMoveToPosition( tracePosition ) ) {
		return false; 	
	}
	
	return true;
}

/*
=====================
bot_sabot::checkMoveBack
=====================
*/
boolean bot_sabot::checkMoveBack() {
	vector	pos, ang, dir, tracePosition;
	float maxRange, frac, traceDistance;
	//entity	traceEntity;
	maxRange = SABOT_CHECK_MOVE_DISTANCE; // TinMan: Max a bounds trace can do is 4096.
	pos = getViewPosition();
	ang = body.getViewAngles();
	ang_x = 0;
	ang_z = 0;
	ang_y += 180;
	dir = sys.angToForward( ang ); // TinMan: Convert angles (pitch yaw roll), to forward vector (x y z).
	frac = sys.tracePoint( pos, pos + ( dir * maxRange ), CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL, body );

	/*
	traceDistance = frac * maxRange; // TinMan: Convert the fraction of trace completed to game units
	tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit

	float debugTime = 0.01;
	sys.debugLine( '0 1 1', pos, pos + ( dir * maxRange ), debugTime ); // TinMan: *debug* full trace
	sys.debugBounds( '1 0 0', pos + '-1 -1 -1', pos + '1 1 1' , debugTime ); // TinMan: *debug* show bound at eyes

	sys.debugLine( '0 1 0', pos, tracePosition,	debugTime ); // TinMan: *debug* trace
	sys.debugBounds( '1 1 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug* show bound where trace hit
	*/

	if ( frac == 1 ) {
		return false;
	}
		
	tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit
	tracePosition = pushPointIntoAAS( tracePosition );
	//sys.debugBounds( '1 1 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug*
	if ( !testMoveToPosition( tracePosition ) ) {
		return false; 	
	}
	
	return true;
}

/*
=====================
bot_sabot::checkMoves
TinMan: Do simple trace check in each direction and return results as flags. *todo* can't seem to do a propper negative test, doesn't matter anyway these functions are just hacks untill I delve a bit deeper into pathing.
=====================
*/
float bot_sabot::checkMoves() {
	vector	pos, ang, dir, tracePosition;
	float maxRange, frac, traceDistance;
	float i, moveFlags;
	//entity	traceEntity;
	moveFlags = 0;
	maxRange = SABOT_CHECK_MOVE_DISTANCE; // TinMan: Max a bounds trace can do is 4096.
	pos = getViewPosition();
	ang = body.getViewAngles();
	ang_x = 0;
	ang_z = 0;

	//float debugTime = 0.01;
	//sys.debugBounds( '1 0 0', pos + '-1 -1 -1', pos + '1 1 1' , debugTime ); // TinMan: *debug* show bound at eyes

	// TinMan: Test fwd,right,back,left.
	for( i = 1; i < 10; i = i * 2 ) {
		dir = sys.angToForward( ang ); // TinMan: Convert angles (pitch yaw roll), to forward vector (x y z).

		frac = sys.tracePoint( pos, pos + ( dir * maxRange ), CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL, body );

		traceDistance = frac * maxRange; // TinMan: Convert the fraction of trace completed to game units
		tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit

		/*
		sys.debugLine( '0 1 1', pos, pos + ( dir * maxRange ), debugTime ); // TinMan: *debug* full trace

		sys.debugLine( '0 1 0', pos, tracePosition,	debugTime ); // TinMan: *debug* trace
		sys.debugBounds( '1 1 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug* show bound where trace hit
		*/

		if ( frac == 1 ) {
			moveFlags |= i;
		}

		ang_y += 90;
	}

	return moveFlags;
}

/*
=====================
bot_vladimir::checkBlocked
TinMan: Deals with being blocked while moving to destination. 
*todo* save obstacle pos and check if it's moved, if it hasn't return true so we can choose new path
*todo* this is currenly the only func that traps in a loop, rethink if you want this.
*todo* as of q4 blocked_by_monster etc not working
=====================
*/
boolean bot_sabot::checkBlocked() {
	//sys.println( "[" + sys.getTime() + "][checkBlocked()]" ); // TinMan: *debug*
	entity obstacle;
	vector obstacle_pos;
	vector delta;
	float dist;
	float endTime;
	vector movePosition;
	vector aimDir;
	
	float numListedEnemies;
	entity enemy;
	
	if ( AI_ENEMY_VISIBLE ) { // TinMan: *cheaphack*
		return false;
	}
	
	/*if ( moveStatus() > MOVE_STATUS_DONE ) {
		sys.println( "[" + sys.getTime() + "][checkBlocked()][Movestatus:" + moveStatus() + "]" ); // TinMan: *debug*
	}	*/
	
	// TinMan: *cheaphack* see if we're not moving by comparing against previous. !! Interferes with new routing (stopping for elev etc)
	if ( moveStatus() == MOVE_STATUS_MOVING ) {
		//sys.println( "[" + sys.getTime() + "][checkBlocked()][Movestatus:" + moveStatus() + "]" ); // TinMan: *debug*
		vector currentPosition;
		
		currentPosition = body.getOrigin();
		
		if ( lastPositionTime < sys.getTime() ) {
			lastPositionTime = sys.getTime() + 0.3;
			// TinMan: Compare current position with previous to get movement vector.
			delta = currentPosition  - lastPosition;
			delta_z = 0;
			dist = sys.vecLength( delta );
			
			lastPosition = currentPosition;
			
			// TinMan: Haven't moved enough?
			if ( dist <= 2 ) {
				//sys.println( "[" + sys.getTime() + "][checkBlocked][blocked by something]" ); // TinMan: *debug*
				saveMove();
				setMoveWander();
				endTime = RandomDelay( 0.6, 0.7 );
				while( sys.getTime() < endTime || dist > 16 ) {
					delta = currentPosition  - lastPosition;
					delta_z = 0;
					dist = sys.vecLength( delta ); // TinMan: set dist wandered	
					
					// TinMan: Do actual movement
					movePosition = moveAction();
					
					// TinMan: Lookee where we damn be going.
					//movePosition_z = movePosition_z + viewHeight;
					//setAimPosition( movePosition );
					
					// TinMan: Check for enemies
					if ( findEnemies( true ) ) {
						break;
					}
					enemy = heardSound( false );
					if ( canSeeEntity( enemy, false ) ) {
						break;
					}
					if ( AI_ENEMY_VISIBLE ) {
						break;
					}
					
					waitFrame();
				}
				restoreMove();
			}
		}
	}
	
	/*
	// TinMan: *todo* Hmm doesn't seem to really trigger much at all
	if ( AI_BLOCKED ) {
		sys.println( "[" + sys.getTime() + "][checkBlocked()][AI_BLOCKED]" ); // TinMan: *debug*
		saveMove();
		setMoveWander();
		endTime = sys.getTime() + 0.8;
		while( sys.getTime() < endTime ) {
			// TinMan: Lookee where we damn be going.
			movePosition = getMovePosition();
			movePosition_z = movePosition_z + viewHeight;
			setAimPosition( movePosition );
					
			// TinMan: Do actual movement
			moveForward();
				
			// TinMan: Check for enemies
			if ( findEnemies( true ) ) {
				break;
			}
			enemy = heardSound( false );
			if ( canSeeEntity( enemy, false ) ) {
				break;
			}
			waitFrame();
		}
		restoreMove();
	} else*/ if ( moveStatus() == MOVE_STATUS_BLOCKED_BY_MONSTER ) { // TinMan: *todo* as of q4 not set
		//sys.println( "[" + sys.getTime() + "][checkblocked][blocked by monster]" ); // TinMan: *debug*
		obstacle = getObstacle();
		obstacle_pos = obstacle.getOrigin(); // TinMan: This wil come in handy later.
	
		saveMove();
		setMoveWander();
		endTime = sys.getTime() + 0.8;
		while( sys.getTime() < endTime ) {
			// TinMan: Do actual movement
			movePosition = moveAction();
			
			// TinMan: Lookee where we damn be going.
			//movePosition = getMovePosition();
			if ( /* AI_ONGROUND && */ movePosition != body.getOrigin() ) {
				aimDir = movePosition - body.getOrigin();
				setAimDirection( aimDir );
			}
			
			// TinMan: Compare current position with previous to get movement vector.
			delta = obstacle_pos - obstacle.getOrigin();
			delta_z = 0;
			
			// TinMan: Check for enemies
			if ( findEnemies( true ) ) {
				break;
			}
			enemy = heardSound( false );
			if ( canSeeEntity( enemy, false ) ) {
				break;
			}
			
			// TinMan: Yay it moved enough.
			if ( sys.vecLength( delta ) >= 4 ) {
				//sys.println( "[" + sys.getTime() + "][" + me + "][checkBlocked][obstacle moved]" ); // TinMan: *debug*
				break;
			}
			waitFrame();
		}
		restoreMove();
	}
	return false;
}

/*
=====================
bot_sabot::checkShot
TinMan: Find what we would hit if we fired. Bounds shot from eyes along viewangles. Itchy itch trigger finger.
When bounds are used to decide whether to fire gives nice simulation of player pulling trigger when enemy near crosshair.
*todo* axis align bounding box if possible.
*todo* false if trace dist < min weapon range? checkaimedshot would override. also would have to make sure bot weaponswitches to something that can.
=====================
*/
boolean bot_sabot::checkShot( boolean simple ) {
	vector	pos, ang, dir, tracePosition;
	float maxRange, frac, traceDistance;
	entity	traceEntity;
	float gameType;
	float boundSize, halfBound;
	vector boundMins, boundMaxs;

	maxRange = 4090; // TinMan: Max a bounds trace can do is 4096.
	
	/*
	boundSize = 8;
	halfBound = boundSize / 2;
	
	boundMins_x = -halfBound;
	boundMins_y = -halfBound;
	boundMins_z = -halfBound;
	boundMaxs_x = halfBound;
	boundMaxs_y = halfBound;
	boundMaxs_z = halfBound;
	*/

	pos = getViewPosition();
	dir = sys.angToForward( body.getViewAngles() ); // TinMan: Convert angles (pitch yaw roll), to forward vector (x y z).

	if ( simple ) {
		frac = sys.tracePoint( pos, pos + ( dir * maxRange ), MASK_SOLID|CONTENTS_BODY, body );  //  CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL
	} else { // TinMan: Bounds trace
		frac = sys.trace( pos, pos + ( dir * maxRange ), '-4 -4 -4', '4 4 4', MASK_SOLID|CONTENTS_BODY, body );
	}
	traceEntity = sys.getTraceEntity(); // TinMan: Get what was hit by trace

	traceDistance = frac * maxRange; // TinMan: Convert the fraction of trace completed to game units
	// TinMan: Hmm, do straight maths, or call function ( which returns value allready calculated when trace func was called. )
	//tracePosition = pos + ( dir * traceDistance ); // TinMan: Get where trace hit
	tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit
/*
	float debugTime = 0.01;
	//sys.debugLine( '0 1 1', pos, pos + ( dir * maxRange ), debugTime ); // TinMan: *debug* full trace
	sys.debugBounds( '1 0 0', pos + '-1 -1 -1', pos + '1 1 1' , debugTime ); // TinMan: *debug* show bound at eyes

	sys.debugLine( '0 1 0', pos, tracePosition,	debugTime ); // TinMan: *debug* trace

	sys.debugBounds( '1 1 0', tracePosition + '-4 -4 -4', tracePosition + '4 4 4' , debugTime ); // TinMan: *debug* show bound where trace hit
	//sys.debugBounds( '0 0.5 1', traceEntity.getOrigin() + traceEntity.getMins(), traceEntity.getOrigin() + traceEntity.getMaxs(), debugTime ); // TinMan: *debug* hilite hit ent
	//sys.println( "[" + int( sys.getTime() ) + "][Trace Hit: " + traceEntity.getName() + "]" ); // TinMan: *debug*

	sys.debugBounds( '1 0 1', getAimPosition() + '-4 -4 -4', getAimPosition() + '4 4 4', debugTime ); // TinMan: *debug* show bound at aimPos
	//sys.debugBounds( '1 1 0', body.getOrigin() + getMins() + '-2 -2 -2', body.getOrigin() + getMaxs() + '2 2 2' , debugTime ); // TinMan: *debug* aimPos reset

	//sys.debugLine( '0 1 0', body.getOrigin(), body.getOrigin() + getLinearVelocity(),	debugTime ); // TinMan: *debug* trace velocity
*/

	// TinMan: Oh my god I think we hit something. Charles, I think we hit something!
	if ( traceEntity ) {
		string type = getClassType( traceEntity );
		if ( type == "idPlayer" ) {
			// TinMan: Friendly fire check. I swear his orange hunting jacket look just like a prize buck officer!
			if ( isTeamGame() ) {
				if ( getTeam( traceEntity ) != getTeam( body ) ) {
					return true;
				}
			} else {
				return true;
			}
		} else if ( traceDistance < 64 && type == "idBrittleFracture"  ) { // TinMan: Breakable glass *todo* this will hurt if the bot got rockets.
			return true;
		} /* else if ( traceDistance > 128 && type == "idExplodingBarrel"  ) { // TinMan: *todo* a wider cansee check for barrels to shoot when enemy is near.
			return true;
		} */
	}

	return false;
}

/*
=====================
bot_sabot::checkAimedShot
TinMan: See if bot is aimed at given position and if a shot would make it. *todo* test with diff aimrates, add FF check
=====================
*/
boolean bot_sabot::checkAimedShot( vector aimDir, float range ) {
	vector viewDir, tracePosition, eyePosition;
	float ang;
	float frac, maxRange;
	
	float aim_ang_delta = getFloatKey( "aim_ang_delta" ); // TinMan: *todo* global?

	maxRange = 4090; // TinMan: Max a bounds trace can do is 4096.

	eyePosition = getViewPosition();
	viewDir = sys.angToForward( body.getViewAngles() );	// TinMan: Vector of where bot is looking

	ang = sys.DotProduct( sys.vecNormalize( viewDir ), sys.vecNormalize( aimDir ) );
	ang = acos( ang ); // TinMan: Angle between viewDir and aimDir

	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][checkAimedShot][viewDir<>aimDir Ang:" + ang + "]" ); // TinMan: *debug*
	if ( ang < aim_ang_delta ) { // TinMan: Minimal angle between the two so must be looking at aimed spot. Frankenstein says: I like nice mice.
		// TinMan: Figure out if bots shot will make it to the intended range
		frac = sys.tracePoint( eyePosition, eyePosition + aimDir * maxRange, MASK_SOLID|CONTENTS_BODY, body );  //  CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL

		//traceDistance = frac * maxRange; // TinMan: Convert the fraction of trace completed to game units
		tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit

/*
		float debugTime = 0.01;
		vector pos = eyePosition;
		sys.debugBounds( '1 0 0', pos + '-1 -1 -1', pos + '1 1 1' , debugTime ); // TinMan: *debug* show bound at eyes

		sys.debugLine( '0 1 0', pos, tracePosition,	debugTime ); // TinMan: *debug* trace
		sys.debugBounds( '1 1 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug* show bound where trace hit

		sys.debugLine( '1 0 0', pos, pos + aimDir,	debugTime ); // TinMan: *debug* trace
//		sys.debugBounds( '1 0 0', aimPosition + '-2 -2 -2', aimPosition + '2 2 2' , debugTime ); // TinMan: *debug* show bound where trace hit
entity trent = sys.getTraceEntity();
*/

		// TinMan: Nope, tracce shorter than range. Something in the way of shot.
		if ( body.distanceToPoint( tracePosition ) + 32 < range ) { // TinMan: traceDistance should have buffer since it will be shorter than aimPosition due to the trace hitting the model, of course my buffer is rough and inaccurate.
			//sys.println( "[" + int( sys.getTime() ) + "][checkAimedShot][aimDir blocked][traceRange: " + body.distanceToPoint( tracePosition ) + "][range: " + range + "][Hit: " + trent.getName() + "]" ); // TinMan: *debug*
			return false;
		} else {
			return true;
		}
	}

	return false;
}

/*
=====================
bot_sabot::chooseWeapon
TinMan: Chose weapon based on range.
=====================
*/
void bot_sabot::chooseWeapon( float range ) {
	float w;
	string weap, bestWeapon;
	float maxWeapons, weaponPriority, bestPriority;
	
	vector weapRange;
	
	// TinMan: *cheaphack* limit weapon switching *todo* test'n'tweak
	if ( sys.getTime() < lastWeaponSwitch ) {
			return;
	}
	lastWeaponSwitch = sys.getTime() + 0.1;
	
	maxWeapons = 32;	

	for ( w = 0; w < maxWeapons; w++ ) {
		weap = body.getKey( "def_weapon" + w );
		if ( !weap ) {
			break;
		}
				
		//sys.println( "[" + sys.getTime() + "][chooseWeapon][weapon[" + w + "]: " + weap + "]" ); // TinMan: *debug*
		if ( hasWeapon( weap ) && hasAmmoForWeapon( weap ) ) { // TinMan: *todo* at least a certain ammount?
			//sys.println( "[" + sys.getTime() + "][chooseWeapon][hasWeapon: " + weap + "]" ); // TinMan: *debug*
			
			if ( range != -1 ) { // TinMan: Bypass range
				weapRange = getVectorKey( "range_" + weap ); // TinMan: Grab ranges from def
				if ( range < weapRange_y || range > weapRange_z ) { // TinMan: Weapon out of min max ranges of current enemy dist.
					//sys.println( "[" + sys.getTime() + "][chooseWeapon][" + weap + " range: " + weapRange + "][out of range: " + range + "]" ); // TinMan: *debug*
					continue; // TinMan: weapon not good for range, chose next weapon
				}
			}
			
			weaponPriority = getFloatKey( "priority_" + weap ); // TinMan: Grab priority from def *todo* ismultiplayer _mp
			//sys.println( "[" + sys.getTime() + "][chooseWeapon][weapon: " + weap + "][priority: " + weaponPriority + "]" ); // TinMan: *debug*			
			
			if ( weaponPriority > bestPriority ) {
				bestPriority = weaponPriority;
				bestWeapon = weap;	
				//sys.println( "[" + sys.getTime() + "][chooseWeapon][bestWeapon: " + bestWeapon + "]" ); // TinMan: *debug*			
			}
		}	
	}
	
	//sys.println( "[" + sys.getTime() + "][chooseWeapon][bestWeapon: " + bestWeapon + "]" ); // TinMan: *debug*
	if ( !bestWeapon ) {
		return;
	}
	
	if ( bestWeapon != currentWeapon ) {
		body.selectWeapon( bestWeapon );
	}
}

/*
=====================
bot_sabot::predictTargetPosition
Frankenstein says: I got my eyes from the corpse of the mikeBot.
TinMan: Lead target
*todo* sort out loop, it's a mess
*todo* throw out, has been replaced by getaimdir
=====================
*/
vector bot_sabot::predictTargetPosition( vector targetPosition, vector myPosition, vector targetVelocity ) {
	//sys.println( "[" + int( sys.getTime() ) + "][predictTargetPosition]" ); // TinMan *debug*
	float travelTime = 0.0; // time for projectile to reach target
	float timeEstimate; 		// estimated time for projectile arrival
	float predictError; 		// error in target location
	float loopCount;
	vector targetLocation = targetPosition; // = targetVelocity;

	float debugTime = 0.01;

	float predictLoopMaximum = 10;
	float predictErrorTollerance = 0.1;

	predictError = 100;

	float projectileSpeed = 0;
	projectileSpeed = getIntKey( "projectile_" + currentWeapon ); // TinMan: How speedy are the shooty shooty kill kills?
	
	if ( projectileSpeed == -1 ) { // TinMan: no predict
		return myPosition - targetLocation;	
	}
	
	// TinMan: *todo* maybe a bug here, gettin div by zeros, I think this is the only place I'm doing division. That would mean projectileSpeed is 0. Sort it out.
	if ( projectileSpeed == 0 ) {
		sys.println( "[" + int( sys.getTime() ) + "][predictTargetPosition][!!projectileSpeed == 0]" ); // TinMan *debug*
		projectileSpeed = 1;
	}

	for ( loopCount = 0; loopCount < predictLoopMaximum; loopCount++ ) {
		targetLocation = travelTime * targetVelocity + targetLocation; // TinMan: This will shift each loop as traveTime is changed
		//sys.debugBounds( '1 0.5 0.5', targetLocation + '-2 -2 -2', targetLocation + '2 2 2' , debugTime ); // TinMan: *debug* show bound where trace hit
		//sys.println( "[" + int( sys.getTime() ) + "][predictTargetPosition][" + loopCount + "]" ); // TinMan *debug*
		timeEstimate = sys.vecLength( myPosition - targetLocation ) / projectileSpeed; // TinMan: Estimate of how long it will take projectile to reach targetLocation
		predictError = timeEstimate - travelTime;
		travelTime = timeEstimate;
		if ( predictError < predictErrorTollerance ) {
			break;
		}
	}

	//sys.debugLine( '0 1 1', pos, pos + ( dir * maxRange ), debugTime ); // TinMan: *debug* full trace
	/* sys.debugLine( '1 0 0', myPosition, targetLocation,	debugTime ); // TinMan: *debug* trace from self to predicted position
	sys.debugLine( '0 1 0', targetPosition, targetPosition + targetVelocity,	debugTime ); // TinMan: *debug* trace from  target along predicted direction
	sys.debugLine( '1 0 1', myPosition, targetPosition,	debugTime );
	sys.debugBounds( '0.5 1 0.2', targetPosition + '-1 -1 -1', targetPosition + '1 1 1' , debugTime );
	sys.debugBounds( '1 0.5 0.5', targetLocation + '-2 -2 -2', targetLocation + '2 2 2' , debugTime );
	*/

	return targetLocation;
}

/*
=====================
bot_sabot::addAimVariation
TinMan: Add variation to aiming so it's not so damn good. I call this the drunkeness factor.
*todo* just a cheaphack needs rethinking.
=====================
*/
vector bot_sabot::addAimVariation( vector aimDir ) {
	float t, attackAccuracy;
	vector viewAng;
			
	attackAccuracy = getFloatKey( "aim_accuracy" ); // TinMan: *todo* global?
	
	//sys.println( "[" + int( sys.getTime() ) + "][addAimVariation][attackAccuracy: " + attackAccuracy + "]" ); // TinMan *debug*
	
	if ( attackAccuracy == 0 ) { // TinMan: No aim variation. uberbot.
		return aimDir;
	}

	t = sys.getTime() * 50;
	viewAng = sys.VecToAngles( aimDir );
	viewAng_x += sys.sin( t * 5 ) * attackAccuracy;
	viewAng_y	+= sys.sin( t * 6 ) * attackAccuracy;

	viewAng = sys.angToForward( viewAng );
	
	// TinMan: Convert angles back to position
	//viewAng = eyePosition + viewAng * sys.vecLength( aimDir );

	/*
	// TinMan: *debug*
	float maxRange = 2048; // TinMan: Max a bounds trace can do is 4096.
	vector viewDir = sys.angToForward( body.getViewAngles() ); // TinMan: Convert angles (pitch yaw roll), to forward vector (x y z).

	float frac = sys.tracePoint( eyePosition, eyePosition + viewDir * maxRange, MASK_SOLID|CONTENTS_BODY, body );  //  CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL

	vector tracePosition = sys.getTraceEndPos(); // TinMan: Get where trace hit

	float debugTime = 0.01;
	sys.debugBounds( '1 0 0',  eyePosition + '-1 -1 -1',  eyePosition + '1 1 1' , debugTime ); // TinMan: *debug* show bound at eyes
	sys.debugLine( '0 1 0',  eyePosition, tracePosition,	debugTime ); // TinMan: *debug* trace
	sys.debugBounds( '1 1 0', tracePosition + '-2 -2 -2', tracePosition + '2 2 2' , debugTime ); // TinMan: *debug* show bound where trace hit

	sys.debugBounds( '1 0 0', aimPosition + '-2 -2 -2', aimPosition + '2 2 2' , debugTime ); // TinMan: *debug*
	*/
	
	//sys.println( "[" + int( sys.getTime() ) + "][addAimVariation][before: " + aimDir + "][after: " + viewAng + "]" ); // TinMan *debug*

	return viewAng;
}

/*
=====================
bot_sabot::getItemPriority
TinMan: Find out what the priority of this entity is. How shiny shiny.
=====================
*/
float bot_sabot::getItemPriority( entity item ) {
	float priority;
	string itemName, prefix, classType;
	float	itemType;
	float len;
	float health, maxHealth, armor, maxArmor, ammo;

	itemName = getClassName( item ); // TinMan: Get the name of the item
	// TinMan: Strip mp suffix
	len = sys.strLength( itemName ) - 3;
	if ( sys.strSkip( itemName, len ) == "_mp" ) {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][itemName: " + sys.strLeft( itemName, len ) + "]" ); // TinMan *debug*
		itemName = sys.strLeft( itemName, len );
	}
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][itemName: " + itemName + "]" ); // TinMan *debug*
	
	classType = getClassType( item );
	if ( classType == "idMoveableItem" ) {
		itemName = sys.strSkip( itemName, sys.strLength( "moveable_item_" ) ); // TinMan: Nasty
		itemName = "weapon_" + itemName; // TinMan: Also cheap *todo* better, this obviously doesn't handle other movables, why did id soft name them moveable_item_machinegun etc instead of moveable_weapon_machinegun. Waaaaaaa!
	}
	
	// TinMan: Grab priority from def *todo* oldski *rem*
	priority = getFloatKey( "priority_" + itemName ); 
	if ( !priority ) {
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][No Priority for: " + itemName + "]" ); // TinMan *debug*
		return 0;
	}
	
	// TinMan: Lets us diferentiate between items
	prefix = sys.strLeft( itemName, 4 );
	if ( prefix == "ammo" ) {
		itemType = SABOT_ITEMTYPE_AMMO;
	} else if ( prefix == "weap" ) {
		itemType = SABOT_ITEMTYPE_WEAPON;
	} else if ( prefix == "item" ) {
		prefix = sys.strLeft( itemName, 10 ); // TinMan: item_medkit, item_armor
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][prefix: " + prefix + "]" ); // TinMan *debug*
		if ( prefix == "item_healt" ) {
			itemType = SABOT_ITEMTYPE_HEALTH;
		} else if ( prefix == "item_armor" ) {
			itemType = SABOT_ITEMTYPE_ARMOR;
		} else {
			itemType = SABOT_ITEMTYPE_ITEM;
		}
	} else if ( prefix == "powe" ) {
		itemType = SABOT_ITEMTYPE_POWERUP;
	} else if ( prefix == "mp_c" ) { // TinMan: mp_ctf_
		itemType = SABOT_ITEMTYPE_TEAM;
	} else {
		itemType = SABOT_ITEMTYPE_NONE;
	}

	if ( itemType == SABOT_ITEMTYPE_AMMO ) {
		priority = getFloatKey( "priority_goal_ammo" ); // TinMan: Get base priority
		priority += getFloatKey( "priority_" + itemName ); // TinMan: Add specific priority
		//len = sys.strLength( itemName ) - 6; // TinMan: Strip off suffix ( _small, _large = 6 chars )
		//itemName = sys.strLeft( itemName, len );
		ammo = hasAmmo( itemName );
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][bot has: " + ammo + ": " + itemName + "]" ); // TinMan *debug*
		if ( ammo >= body.getFloatKey( "max_" + itemName ) ) {
			//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][bot has full: " + itemName + "]" ); // TinMan *debug*
			priority = 0;
		} else if ( ammo < 10 ) {
			//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][bot has no: " + itemName + "]" ); // TinMan *debug*
			priority += 5;
		}
		
		// TinMan: *todo*
		//ammo current current weapon 20, 25?
		//	and low ammo 40 (higher than default enemy
		
	} else if ( itemType == SABOT_ITEMTYPE_WEAPON ) {
		
		/*
		priority = getFloatKey( "priority_goal_weapon" ); // TinMan: Get base priority
		priority += getFloatKey( "priority_" + itemName ); // TinMan: Add specific priority
		if ( !hasWeapon( itemName ) ) {
			//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][bot doesn't have: " + itemName + "]" ); // TinMan *debug*
			priority += 5;
		}
		if ( getFloatKey( "priority_" + itemName ) > getFloatKey( "priority_" + currentWeapon ) ) { // TinMan: It's a better weapon than current
			priority += 5;
		}
		*/
		
		// TinMan: *todo* and very low ammo + 10
		// TinMan: *todo* it's a dropped weapon +5
		

		if ( !hasWeapon( itemName ) || !hasAmmoForWeapon( itemName ) ) {
			priority += getFloatKey( "priority_goal_weapon" ); // TinMan: Get base priority
		}
		
		priority += getFloatKey( "priority_" + itemName ); // TinMan: Add specific priority		
		if ( getFloatKey( "priority_" + itemName ) > getFloatKey( "priority_" + currentWeapon ) ) { // TinMan: It's a better weapon than current
			priority += 5;
		}

		
	} else if ( itemType == SABOT_ITEMTYPE_HEALTH ) {
		priority = getFloatKey( "priority_goal_health" ); // TinMan: Get base priority
		priority += getFloatKey( "priority_" + itemName ); // TinMan: Add specific priority	
		
		health = getActorHealth( body );
		maxHealth = body.getFloatKey( "maxhealth" );
		
		// sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][Health: " + health + "][Maxhealth: " + maxHealth + "]" ); // TinMan *debug*
		
		if ( health < maxHealth ) {
			if ( health < 40 ) {
				priority += 10;
				//priority = 45;
			}
			if ( health < 20 ) {
				priority += 10;
				//priority = 55;
			}
		} else {
			if ( item.getFloatKey( "inv_bonushealth" ) > 50 ) { // TinMan: Handle stuff that can boost ya past max, ie mega
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][Health: " + health + "][Maxhealth: " + maxHealth + "]" ); // TinMan *debug*
				priority += 10;
			} else {
				priority = 0;
			}
		}
	} else if ( itemType == SABOT_ITEMTYPE_ARMOR ) {
		if ( itemName != "item_armor_shard" ) {
			priority = getFloatKey( "priority_goal_armor" ); // TinMan: Get base priority
		}
		priority += getFloatKey( "priority_" + itemName ); // TinMan: Add specific priority	
		armor = getArmor( body );
		maxArmor = body.getFloatKey( "maxarmor" );
		
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][Armor: " + armor + "][Maxarmor: " + maxArmor + "]" ); // TinMan *debug*
		
		if ( armor < maxArmor ) {
			if ( armor < 40 ) {
				priority += 5;
			}
			if ( armor < 20 ) {
				priority += 5;
			}
		} else {
			priority = 0;
		}
		
	} else if ( itemType == SABOT_ITEMTYPE_POWERUP ) { // TinMan: *todo* don't get powerup if allready have one
		priority = getFloatKey( "priority_goal_powerup" ); // TinMan: Get base priority
		priority += getFloatKey( "priority_" + itemName ); // TinMan: Add specific priority
		
		if ( powerUpActive() ) { // TinMan: allready have a powerup *todo* can you drop powerups? if so we might wan't to decide which is better
			priority = 0;	
		}	
		//sys.println( "[" + int( sys.getTime() ) + "][getItemPriority][Powerup: " + itemName + "][prio: " + priority + "]" ); // TinMan *debug*
	} else if ( itemType == SABOT_ITEMTYPE_TEAM ) { // TinMan: *todo* could change priorities on flags depending on team and role
		priority = getFloatKey( "priority_goal_team" ); // TinMan: Get base priority
		//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][Team: " + itemName + "][prio: " + priority + "]" ); // TinMan *debug*
		
		// mp_ctf_marine_flag
		// mp_ctf_strogg_flag
	} else {
		sys.warning( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][Unknown itemtype: " + itemName + "][prio: " + priority + "]" ); // TinMan *debug*
	}
	
	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][" + itemName + " Priority: " + priority + "]" ); // TinMan *debug*
	/* if ( priority ) {
		sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getItemPriority][" + itemName + " Priority: " + priority + "]" ); // TinMan *debug*
	} */
	
	return priority;
}

/*
=====================
bot_sabot::getPlayerPriority
TinMan: How much do we want to kill this badass
=====================
*/
float bot_sabot::getPlayerPriority( entity badass ) {
	float priority, weaponPriority;
	string playerWeapon;
	entity enemy;
		
	playerWeapon = badass.getCurrentWeapon();
	weaponPriority = getFloatKey( "priority_" + playerWeapon ); // TinMan: Grab priority from def
	//sys.println( "[" + int( sys.getTime() ) + "][getPlayerPriority][weapon: " + playerWeapon + "][" + weaponPriority + "]" ); // TinMan *debug*

	priority = getFloatKey( "priority_goal_enemy" ); // TinMan: Get base priority
	
	//priority += 10 - weaponPriority; // TinMan: weaker weapon = higher priority
	
	//if ( getFloatKey( "priority_" + currentWeapon ) > weaponPriority ) {
	 //priority += 5;
	//}
	//if ( getFloatKey( "priority_" + currentWeapon ) < weaponPriority ) {
		//priority -= 5;
	//}
	
	// TinMan: enemy gots my flag, kill im!
	if ( isCTF() ) {
		if ( getFlagCarrier( 1 - team ) == badass ) {
			priority += 20;
		}
	}
	
	float dist = body.distanceTo( badass );
	if ( dist < 128 ) { // TinMan: *todo* no magic
		priority += 20;	
	}
	
	enemy = getEnemy();
	if ( enemy ) {
		if ( badass != enemy ) {
			if ( body.distanceTo( badass ) < body.distanceTo( enemy ) - 128 ) { // TinMan: omigosh this guy is closer *todo* tweak fudge factor
				priority += 5;
			}
		} else {
			// TinMan: *todo* see where this is being used? yup current enemy is slipping in as newenemy because of cheapo handling of enemy coming back into view
			priority += 5;// TinMan: *cheaphack* ^
			//sys.warning( "[" + int( sys.getTime() ) + "][getPlayerPriority][wtf badass = enemy]" ); // TinMan *debug*
		}
	}
	
	entity obsticle = getObstacle();
	if ( badass == getObstacle() ) {// TinMan: if player blocking our path then that's a problem. *cheaphack* *todo* rethink
		priority += 20;
		//sys.println( "[" + int( sys.getTime() ) + "][getPlayerPriority][blockin me foo!][" + badass.getName() + " Priority: " + priority + "]" ); // TinMan *debug*
	}

/*
	sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getPlayerPriority][" + badass.getName() + " Priority: " + priority + "]" ); // TinMan *debug*
	if ( enemy ) {
		if ( enemy != badass ) {
			sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getPlayerPriority][" + enemy.getName() + " enemyPriority: " + goalPriority + "]" ); // TinMan *debug*	
		}
	}
*/

/*
	// TinMan: *todo* *test*
	if ( checkSelf() ) {
		priority = 0;
	}
*/

	//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][getPlayerPriority][" + badass.getName() + " HP: " + getActorHealth( badass ) + " ARM: " + getArmor( badass ) + " Weap: " + weaponPriority + " Priority: " + priority + "]" ); // TinMan *debug*
	return priority;
}

/*
=====================
bot_sabot::chooseGoal
TinMan: 
Note: This function is somewhat expensive since nearly all entities found are reachability checked in getItemPriority.
Note: Debug output is off by one.
=====================
*/
entity bot_sabot::chooseGoal( entity ignoreGoal ) {
	float i, numListedEntities;
	float itemPriority, bestPriority;
	float dist, bestDist;
	float radius;
	entity item, bestGoal;
	
	clearGoal();
	
	// TinMan: *CTF* flag goals	
	if ( isCTF() ) {
		if ( getFlagCarrier( team ) == body ) { // TinMan: w00t we gots teh falg!
			//sys.println( "[" + int( sys.getTime() ) + "][chooseGoal][!have flag!]" ); // TinMan *debug*
			bestGoal = ourFlag;
			goalPosition = ourCap;
			moveType = SABOT_MOVETYPE_POSITION; // TinMan: can't do entity since the flag might be taken en route
			if ( body.distanceToPoint( goalPosition ) > 64 ) {
				goalType = SABOT_GOAL_MOVE;
			} else { // TinMan: Close enough, camp it
				goalType = SABOT_GOAL_HOLD;
			}
			goalPriority = getFloatKey( "priority_goal_team" );
		} else if ( role == SABOT_ROLE_OFFENCE ) {
			bestGoal = theirFlag;
			goalPosition = theirCap;
			moveType = SABOT_MOVETYPE_POSITION;
			
			if ( body.distanceToPoint( goalPosition ) > 128 ) { // TinMan: Not near goal, so move to it
				goalType = SABOT_GOAL_MOVE;
			} else { // TinMan: Close enough
				/*
				if ( getFlagStatus( 1 - team ) == FLAGSTATUS_INBASE ) { // TinMan: It's there, grab it
					bestGoal = theirFlag;
					moveType = SABOT_MOVETYPE_ENTITY;
					goalPriority = getFloatKey( "priority_goal_team" );
				} else { // TinMan: Not there, camp it */
					goalType = SABOT_GOAL_HOLD;
				// }
			}
			
			goalPosition = bestGoal.getOrigin();
			//goalPriority = getFloatKey( "priority_goal_team" );
			goalPriority = getFloatKey( "priority_goal_weapon" ); // TinMan: On route to flag we still want to grab goodies
		}  else if ( role == SABOT_ROLE_DEFENCE ) {
			sys.println( "[" + int( sys.getTime() ) + "][chooseGoal][defence][goal: ourFlag]" ); // TinMan *debug*
			bestGoal = ourFlag;
			goalPosition = ourCap;
			moveType = SABOT_MOVETYPE_POSITION;
			
			if ( body.distanceToPoint( goalPosition ) > 128 ) { // TinMan: Not near goal, so move to it
				goalType = SABOT_GOAL_MOVE;
			} else { // TinMan: Close enough
				goalType = SABOT_GOAL_HOLD;
			}
			
			goalPosition = bestGoal.getOrigin();
			//goalPriority = getFloatKey( "priority_goal_team" );
			goalPriority = getFloatKey( "priority_goal_weapon" ); // TinMan: On route to flag we still want to grab goodies
		} 
		
		// TinMan: Check if goal can be reached *todo* think about how you are going to handle unreachable goal. *todo* hmm it's ok if enemy is unreachable, he might not be later, or your still might be able to shoot him
		if ( bestGoal ) {
			if ( moveType = SABOT_MOVETYPE_POSITION ) {
				if ( !canReachPosition( bestGoal.getOrigin() ) ) {
					//sys.println( "[" + int( sys.getTime() ) + "][chooseGoal][can't reach goal position]" ); // TinMan *debug*
					//sys.println( "[" + int( sys.getTime() ) + "][chooseGoal][GoalPosition: " + goalPosition + "]" ); // TinMan *debug*
					clearGoal();
					bestGoal = $null_entity;
				}
			} else {
				if ( !canReachEntity( bestGoal ) ) {
					//sys.println( "[" + int( sys.getTime() ) + "][chooseGoal][can't reach goal]" ); // TinMan *debug*
					clearGoal();
					bestGoal = $null_entity;
				}
			}
		}
		
		if ( !bestGoal ) { // TinMan: Can't CTF so roam
			//sys.println( "[" + int( sys.getTime() ) + "][chooseGoal][!!No CTF Goal!!]" ); // TinMan *debug*
			role = roleDefault;
		}
	}
	
	// TinMan: Find an item to hunt. Want l00t!
	if ( role == SABOT_ROLE_ROAM ) {
		numListedEntities = findItems();
		// sys.println( "[" + int( sys.getTime() ) + "][" + me + "][chooseGoal][numListedEntities: " + numListedEntities + "]" ); // TinMan *debug*
		for ( i = 0; i < numListedEntities; i++ ) {
			item = getEntityList( i );
			//sys.println( "[" + int( sys.getTime() ) + "][chooseGoal][listedEntity[" + i + "] : " + item.getName() +  "]" ); // TinMan *debug* " : " + getClassName( item ) +
			if ( item == ignoreGoal || item.isHidden() ) { // TinMan: *todo* Shouldnt really do a ishidden but at the moment this is stopping ping pong and adding variation
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][chooseGoal][ignore or hidden]" ); // TinMan *debug*
				continue;
			}
			itemPriority = getItemPriority( item );
			if ( !itemPriority ) {
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][chooseGoal][no item priority]" ); // TinMan *debug*
				continue;
			}
			if ( itemPriority > bestPriority ) {
				if ( canReachEntity( item ) ) {
					//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][chooseGoal][canreach item: " + item.getName() + "]" ); // TinMan *debug*
					bestPriority = itemPriority;
					bestGoal = item;
				} /* else {
					sys.println( "[" + int( sys.getTime() ) + "][" + me + "][chooseGoal][!canreach item]" ); // TinMan *debug*
				} */
			} else if ( itemPriority == bestPriority ) { // TinMan: If item has same priority get closest, of course staright distance check is cheap and may not be the closest item due to travel time.
				dist = body.distanceTo( item );
				bestDist = body.distanceTo( bestGoal );
				//dist = travelDistanceToEntity( item ); // TinMan: expensive *todo* see how many enities reach this point, though of course the reall killed comes with far away ents
				//bestDist = travelDistanceToEntity( bestGoal );
				if ( dist < bestDist ) {
					if ( canReachEntity( item ) ) {
						bestGoal = item;
					}
				}
			}
		}
	
		goalType = SABOT_GOAL_MOVE;
		goalPriority = getFloatKey( "priority_goal_item" ); // TinMan: Pretty much anything will override this goal.
		
		/*if ( bestGoal ) {
			sys.println( "[" + int( sys.getTime() ) + "][" + me + "][chooseGoal][goal: " + getClassName( bestGoal ) + "][priority: " + goalPriority + "]" ); // TinMan *debug*
		} */
	}
	
	return bestGoal; // TinMan: This is the goodest loot
}

/*
=====================
bot_sabot::lookForGoal
TinMan: Look for nice and shiny baubles to gather.
=====================
*/
entity bot_sabot::lookForGoal( float useFov ) {
	float i, numListedEntities;
	float itemPriority, bestPriority;
	float dist, bestDist;
	float radius;
	float maxHeight, minHeight;
	entity item;
	vector origin, itemOrigin;
	string classType;
	float health, temphealth;

	entity bestGoal;
	
	clearNewGoal(); // TinMan: Wipe the slate, and your nose.

	radius = 512; // *todo*: *demagic* *tweak*
	
	bestDist = radius;
	
	maxHeight = 128;
	minHeight = 64;
	
	origin = body.getOrigin();
	
	numListedEntities = findInRadius( origin, radius );
	//sys.println( "[" + int( sys.getTime() ) + "][lookForGoal][numListedEntities: " + numListedEntities + "]" ); // TinMan *debug*
	for ( i = 0; i < numListedEntities; i++ ) {
		item = getEntityList( i );
		
		/*
		if ( !item ) {
			gameLocal.Error( "[" + int( sys.getTime() ) + "][lookForGoal][NULL item]" ); // TinMan: *debug*
		}
		*/
		
		/*
		if ( item == goal ) {
			continue;
		}
		*/
		
		// TinMan: *todo* rethink
		if ( item == oldGoal ) {
			continue;
		}
		
		// TinMan: Filter entities to the type we want
		classType = getClassType( item );
		if ( classType != "idItem" && classType != "idItemPowerup" && classType != "idMoveableItem" && classType != "rvItemCTFFlag" ) {
			continue;
		}
		
		// TinMan: *cheaphack* skip those too hight or low. I think I got tunnel vision *todo* rethink.
		itemOrigin = item.getOrigin();
		if ( itemOrigin_z > origin_z + maxHeight || itemOrigin_z < origin_z - minHeight ) {
			continue;
		}
		
		itemPriority = getItemPriority( item ); // TinMan: Sort out health and armour, stuff that he can't pick up when he's full, the glutton.
		
		if ( !itemPriority ) {
			continue;
		}
		
		if ( itemPriority < goalPriority ) {
				continue;
		}
		
		//sys.println( "[" + int( sys.getTime() ) + "][lookForGoal][listedEntity[" + i + "]:" + item.getName() + "][Prioriy: " + itemPriority + "]" ); // TinMan *debug* " 
		//if ( canSeePosition( item.getOrigin(), true ) && item.isHidden() ) { // TinMan: If we can see it's not there. *todo* is this working at all?
		if ( item.isHidden() ) {
			continue;
		}
		
		if ( classType == "idMoveableItem" && !canSeeEntity( item, false ) ) { // TinMan: we don't know where moveables are like static items
			continue;
		}
		
		if ( !AI_ENEMY_DEAD && !canSeeEntity( item, false ) ) { // TinMan: *cheaphack, only go for item you can see when fighting
			continue;
		}
		
		if ( isCTF() ) {
			if ( item == theirFlag ) {
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][" + getClassName( item ) +  "][thierflag]" ); // TinMan *debug* " 
				if ( getFlagStatus( 1 - team ) == FLAGSTATUS_TAKEN ) { // TinMan: Whee dawgy there goes that enemy flag
					sys.println( "[" + int( sys.getTime() ) + "][" + me + "][" + getClassName( item ) +  "][thierflag: taken]" ); // TinMan *debug* " 
					if ( getFlagCarrier( team ) != body ) { // TinMan: A buddy has it. For great justice.
						continue;
					} /* else {
						sys.println( "[" + int( sys.getTime() ) + "][" + me + "][" + getClassName( item ) +  "][thierflag]" ); // TinMan *debug* " 
					} */
				} else if ( getFlagStatus( 1 - team ) == FLAGSTATUS_STRAY ) { // TinMan: must sight test stray flags becuase we don't know where they are
					if ( !canSeeEntity( item, false ) ) {
						continue;
					}
				}
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][" + getClassName( item ) +  "][thierflag]" ); // TinMan *debug* " 
			} else if ( item == ourFlag ) {
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][" + getClassName( item ) +  "][ourflag]" ); // TinMan *debug* " 
				if ( getFlagStatus( team ) != FLAGSTATUS_STRAY || !canSeeEntity( item, false ) ) { // TinMan: Only time we want our flag is if it's stray.
					continue;
				}
				//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][" + getClassName( item ) +  "][ourflag]" ); // TinMan *debug* " 
			}
		}
		
		if ( dist < 128 ) { // TinMan: *cheaphack* Very close *todo* move to getitmempriority
			//sys.println( "[" + int( sys.getTime() ) + "][" + me + "][lookForGoal][very close:"  + getClassName( item ) +  "]" ); // TinMan: *debug*
			itemPriority += 10;
		}
		//dist = body.distanceTo( item ); // TinMan: Simple
		dist = travelDistanceToEntity( item ); // TinMan: expensive, but heaps better ( side benefit of route being cached for canreach and actual move )
		//if ( dist < bestDist ) {
		if ( bestPriority < itemPriority ) {
			if ( canReachEntity( item ) ) {

				/* if ( goal ) {
					if ( travelDistanceBetweenEntities( goal, item ) > travelDistanceToEntity( goal ) + 256 ) { // TinMan: *todo* *test*
						sys.println( "[" + int( sys.getTime() ) + "][" + me + "][lookForGoal][traveldist too high:"  + getClassName( item ) +  "]" ); // TinMan: *debug*
						continue;
					}
				}
				*/
				bestDist = dist;
				bestGoal = item;
				bestPriority = itemPriority;
			}
		}

		/* if ( bestGoal ) {
			sys.println( "[" + int( sys.getTime() ) + "][" + i + ": " + getClassName( item ) +  "][dist: " + int( dist ) + "][priority: " + bestPriority +  "]" ); // TinMan *debug* " 
		} */
	}
	
	/*
	if ( bestGoal ) {
		sys.println( "[" + int( sys.getTime() ) + "][lookForGoal][bestGoal: " + bestGoal.getName() + " of [" + numListedEntities + "]]" ); // TinMan *debug*
	}
	*/
	
	// TinMan: This is the goodest loot
	if ( bestGoal ) {
		newGoal = bestGoal;
		newGoalPriority = bestPriority;
		newGoalType = SABOT_GOAL_MOVE;
		newMoveType = SABOT_MOVETYPE_ENTITY;
	}

	return bestGoal;
}

/*
=====================
bot_sabot::lookForEnemies
TinMan: Searches for enemies. You must know where the hurty men are.
=====================
*/
entity bot_sabot::lookForEnemies( float useFov, boolean turnaround) { // SHREWM BE-ND
	float i, numListedEnemies;
	entity thisEnemy, bestEnemy;
	float priority, bestPriority, dist, bestDist;
	bestDist = 999999;
	float temphealth;
	
	numListedEnemies = findEnemies( useFov );
	temphealth = getActorHealth( body );
	for ( i = 0; i < numListedEnemies; i++ ) {
// SHREWM BEGIN
        	if(turnaround) {
          		turnAroundForEnemies( true, numListedEnemies );	// *dance*
		}
// SHREWM END
      	thisEnemy = getEntityList( i );
		
		priority = getPlayerPriority( thisEnemy );
		
		if ( priority > bestPriority ) {
			bestPriority = priority;
			bestEnemy = thisEnemy;
		}
		
		/*
		//sys.println( "[" + int( sys.getTime() ) + "][lookForEnemy][listedEnemy[" + i + "] : " + thisEnemy.getName() +  "]" ); // TinMan: *debug*
		dist = body.distanceTo( thisEnemy );
		if ( dist < ( bestDist ) ) { // TinMan: *cheaphack*
			if ( thisEnemy != getEnemy() ) {
				bestDist = dist;
				bestEnemy = thisEnemy;
			}
		}
		*/
	}

	/* if ( bestEnemy ) {
		sys.println( "[" + int( sys.getTime() ) + "][lookForEnemy][bestEnemy: " + bestEnemy.getName() + " of [" + i + "]]" ); // TinMan *debug*
	} */
	
	return bestEnemy; // TinMan: He be the badest of the bad
}

// SHREWM BEGIN
/*
=====================
bot_sabot::turnAroundForEnemies
shrewm: better turn around when getting attacked from behind right?
=====================
*/
void bot_sabot::turnAroundForEnemies( float useFov, float numListedEnemies) {
    float i;
    for ( i = 0; i < numListedEnemies; i++ ) {
        AI_JUMP = true;
        if ( !AI_ONGROUND ) {
            AI_JUMP = false;
        }
        AI_TURN_LEFT = true;
    }
}
// SHREWM END

/*
=====================
bot_sabot::listenForEnemies
TinMan: I think I hear something in the basement dear. heardSound is only really geared towards single player ai with one player causing a ruckus.
=====================
*/
entity bot_sabot::listenForEnemies() {
	entity newEnemy;
	
	/*
	if ( heardSound( false ) ) {
		sys.println( "[" + int( sys.getTime() ) + "][listenForEnemies][heardsound]" ); // TinMan: *debug*
	}
	*/

	newEnemy = heardSound( false );
	if ( newEnemy == getEnemy() || !canSeeEntity( newEnemy, false ) ) { // TinMan: Visible but not in fov *todo* hmm
		newEnemy = $null_entity;
	}

	return newEnemy;
}

/*
=====================
bot_sabot::isCTF
TinMan:
=====================
*/
boolean bot_sabot::isCTF() {
	if ( gameType == GAME_CTF || gameType == GAME_ARENA_CTF ) {
		return true;
	}
	return false;
}

// EOF
