// ----------------------------------------------------------------------------
// #using
// ----------------------------------------------------------------------------
#using scripts\codescripts\struct;

#using scripts\shared\gameskill_shared;
#using scripts\shared\math_shared;
#using scripts\shared\statemachine_shared;
#using scripts\shared\system_shared;
#using scripts\shared\array_shared;
#using scripts\shared\util_shared;

#using scripts\shared\vehicle_shared;
#using scripts\shared\vehicle_death_shared;
#using scripts\shared\vehicle_ai_shared;

#using scripts\shared\ai\systems\blackboard;
#using scripts\shared\ai\blackboard_vehicle;

#using scripts\shared\turret_shared;
#using scripts\shared\weapons\_spike_charge_siegebot;
    	   	                                                                                                                                         	                                	                                                                                                                                                                                                                                                                                                                                                                    	        	     	              	    	   	               	                                              	                                                           	                               	                     	                                                                                                           	                                                                 	                                                              	                                                                                                                                             	                            	                                     	                                       	                                                                                                              	   	                  	       	                                                    	                   	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                 	                                    	                                                                                        	           	                        	                                            	                                             	                                                   	                                                             	                                                         	                                                                    	                                                                                                                                                                                                                                	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	    	         	                                                              	                                                          	                                   	                                   	                                                    	                                       	                                           	     	                                                                                                                                                                                                                                                      	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	                                                               	        	                                                                                                                              	                                                                                                                     	                               	                                                      	                          
       
                                                                                                            	  	  	              	   	                                                                                                                                       	                                                                                                                                                            	                                                                                                                                                                

                                                                  	                             	  	                                      




#namespace siegebot;

function autoexec __init__sytem__() {     system::register("siegebot",&__init__,undefined,undefined);    }

#using_animtree( "generic" );

function __init__()
{	
	vehicle::add_main_callback( "siegebot", &siegebot_initialize );
}

function siegebot_initialize()
{
	self useanimtree( #animtree );
	
	blackboard::CreateBlackBoardForEntity( self );
	self Blackboard::RegisterVehicleBlackBoardAttributes();

	self.health = self.healthdefault;

	self vehicle::friendly_fire_shield();
	
	Target_Set( self, ( 0, 0, 84 ) );

	self EnableAimAssist();
	self SetNearGoalNotifyDist( 40 );
	
	self.fovcosine = 0.5; // +/-60 degrees = 120 fov
	self.fovcosinebusy = 0.5;
	self.maxsightdistsqrd = ( (10000) * (10000) );

	assert( isdefined( self.scriptbundlesettings ) );

	self.settings = struct::get_script_bundle( "vehiclecustomsettings", self.scriptbundlesettings );

	self.goalRadius = 9999999;
	self.goalHeight = 5000;
	self SetGoal( self.origin, false, self.goalRadius, self.goalHeight );
	
	self.overrideVehicleDamage = &siegebot_callback_damage;
	
	self siegebot_update_difficulty();
	
	self SetGunnerTurretOnTargetRange( 0, self.settings.gunner_turret_on_target_range );
	
	self ASMRequestSubstate( "locomotion@movement" );
	
	if( self.vehicletype === "spawner_enemy_boss_siegebot_zombietron" )
	{
		self ASMSetAnimationRate( 0.5 );
		self HidePart( "tag_turret_canopy_animate" );
		self HidePart( "tag_turret_panel_01_d0" );
		self HidePart( "tag_turret_panel_02_d0" );
		self HidePart( "tag_turret_panel_03_d0" );
		self HidePart( "tag_turret_panel_04_d0" );
		self HidePart( "tag_turret_panel_05_d0" );
	}
	else if( self.vehicletype == "zombietron_veh_siegebot" )
	{
		self ASMSetAnimationRate( 1.429 );
	}

	self initJumpStruct();

	//disable some cybercom abilities
	if( IsDefined( level.vehicle_initializer_cb ) )
	{
    	[[level.vehicle_initializer_cb]]( self );
	}

	self.ignoreFireFly = true;
	self.ignoreDecoy = true;
	self vehicle_ai::InitThreatBias();

	self thread vehicle_ai::target_hijackers();

	if( !SessionModeIsMultiplayerGame() )
		defaultRole();
}


function siegebot_update_difficulty()
{
	value = gameskill::get_general_difficulty_level();
	
	scale_up = mapfloat( 0, 9, 0.8, 2.0, value );
	scale_down = mapfloat( 0, 9, 1.0, 0.5, value );
	
	self.difficulty_scale_up = scale_up;
	self.difficulty_scale_down = scale_down;
}

function defaultRole()
{
	self vehicle_ai::init_state_machine_for_role( "default" );

    self vehicle_ai::get_state_callbacks( "combat" ).update_func = &state_combat_update;
    self vehicle_ai::get_state_callbacks( "combat" ).exit_func = &state_combat_exit;
    
    self vehicle_ai::get_state_callbacks( "driving" ).update_func = &siegebot_driving;

    self vehicle_ai::get_state_callbacks( "death" ).update_func = &state_death_update;

    self vehicle_ai::get_state_callbacks( "pain" ).update_func = &pain_update;

    self vehicle_ai::get_state_callbacks( "emped" ).enter_func = &emped_enter;
    self vehicle_ai::get_state_callbacks( "emped" ).update_func = &emped_update;
    self vehicle_ai::get_state_callbacks( "emped" ).exit_func = &emped_exit;
    self vehicle_ai::get_state_callbacks( "emped" ).reenter_func = &emped_reenter;

	self vehicle_ai::add_state( "jump",
		&state_jump_enter,
		&state_jump_update,
		&state_jump_exit );

	vehicle_ai::add_utility_connection( "combat", "jump", &state_jump_can_enter );
	vehicle_ai::add_utility_connection( "jump", "combat" );

	self vehicle_ai::add_state( "unaware",
		undefined,
		&state_unaware_update,
		undefined );
	// don't use this for now unless we really need it

	vehicle_ai::StartInitialState( "combat" );
}

// ----------------------------------------------
// State: death
// ----------------------------------------------
function state_death_update( params )
{
	self endon( "death" );	
	self endon( "nodeath_thread" );

	// Need to prep the death model
	StreamerModelHint( self.deathmodel, 6 );

	death_type = vehicle_ai::get_death_type( params );
	if ( !isdefined( death_type ) )
	{
		params.death_type = "gibbed";
		death_type = params.death_type;
	}

	self clean_up_spawned();

	self SetTurretSpinning( false );
	self stopMovementAndSetBrake();

	self vehicle::set_damage_fx_level( 0 );
	self playsound("veh_quadtank_sparks");

	if ( self.vehicletype === "spawner_enemy_boss_siegebot_zombietron" )
	{
		self ASMSetAnimationRate( 1.0 );
	}

	self.turretRotScale = 3;
	self SetTurretTargetRelativeAngles( (0,0,0), 0 );
	self SetTurretTargetRelativeAngles( (0,0,0), 1 );
	self SetTurretTargetRelativeAngles( (0,0,0), 2 );

	self ASMRequestSubstate( "death@stationary" );

	self waittill( "model_swap" ); // give the streamer time to load
	self vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
	self vehicle::do_death_dynents();
	self vehicle_death::death_radius_damage();
	
	self waittill( "bodyfall large" );

	// falling damage
	self RadiusDamage( self.origin + (0,0,10), self.radius * 0.8, 150, 60, self, "MOD_CRUSH" );

	//BadPlace_Box( "", 0, self.origin, 50, "neutral" );

	vehicle_ai::waittill_asm_complete( "death@stationary", 3 );

	self thread vehicle_death::CleanUp();
	self vehicle_death::FreeWhenSafe();
}

// ----------------------------------------------
// State: scripted
// ----------------------------------------------
function siegebot_driving( params )
{
	self thread siegebot_player_fireupdate();
	self thread siegebot_kill_on_tilting();

	self ClearTargetEntity();
	self CancelAIMove();
	self ClearVehGoalPos();	
}

function siegebot_kill_on_tilting()
{
	self endon( "death" );
	self endon( "exit_vehicle" );

	tileCount = 0;

	while( 1 )
	{
		selfup = AnglesToUp( self.angles );
		worldup = ( 0, 0, 1 );

		if ( VectorDot( selfup, worldup ) < 0.64 ) // angle diff more than 50 degree
		{
			tileCount += 1;
		}
		else
		{
			tileCount = 0;
		}

		if ( tileCount > 20 ) // more than 1 full second
		{
			driver = self GetSeatOccupant( 0 );
			self Kill( self.origin );
		}

		{wait(.05);};
	}
}

function siegebot_player_fireupdate()
{
	self endon( "death" );
	self endon( "exit_vehicle" );
	
	weapon = self SeatGetWeapon( 2 );
	fireTime = weapon.fireTime;
	driver = self GetSeatOccupant( 0 );

	self thread siegebot_player_aimUpdate();
	
	while( 1 )
	{
		if( driver attackButtonPressed() )
		{
			self FireWeapon( 2 );
			wait fireTime;
		}
		else
		{
			{wait(.05);};
		}
	}
}

function siegebot_player_aimUpdate()
{
	self endon( "death" );
	self endon( "exit_vehicle" );

	while( 1 )
	{
		self SetGunnerTargetVec( self GetGunnerTargetVec( 0 ), 1 );
		{wait(.05);};
	}
}
// State: scripted ----------------------------------

// ----------------------------------------------
// State: emped
// ----------------------------------------------
function emped_enter( params )
{
	if ( !isdefined( self.abnormal_status ) )
	{
		self.abnormal_status = spawnStruct();
	}

	self.abnormal_status.emped = true;
	self.abnormal_status.attacker = params.notify_param[1];
	self.abnormal_status.inflictor = params.notify_param[2];

	self vehicle::toggle_emp_fx( true );
}

function emped_update( params )
{
	self endon( "death" );
	self endon( "change_state" );

	self stopMovementAndSetBrake();
	
	if ( self.vehicletype === "spawner_enemy_boss_siegebot_zombietron" )
	{
		self ASMSetAnimationRate( 1.0 );
	}

	asmState = "damage_2@pain";
	self ASMRequestSubstate( asmState );	
	self vehicle_ai::waittill_asm_complete( asmState, 3 );
	
	self SetBrake( 0 );
	
	self vehicle_ai::evaluate_connections();
}

function emped_exit( params )
{
}

function emped_reenter( params )
{
	return false;
}
// State: emped ----------------------------------

// ----------------------------------------------
// State: pain
// ----------------------------------------------
function pain_toggle( enabled )
{
	self._enablePain = enabled;
}

function pain_update( params )
{
	self endon( "death" );
	self endon( "change_state" );

	self stopMovementAndSetBrake();
	
	if ( self.vehicletype === "spawner_enemy_boss_siegebot_zombietron" )
	{
		self ASMSetAnimationRate( 1.0 );
	}

	if ( self.newDamageLevel == 3 )
	{
		asmState = "damage_2@pain";
	}
	else
	{
		asmState = "damage_1@pain";
	}

	self ASMRequestSubstate( asmState );	
	self vehicle_ai::waittill_asm_complete( asmState, 1.5 );
	
	self SetBrake( 0 );
	
	self vehicle_ai::evaluate_connections();
}
// State: pain ----------------------------------

// ----------------------------------------------
// State: unaware
// ----------------------------------------------
function state_unaware_update( params )
{
	self endon( "death" );
	self endon( "change_state" );

	self SetTurretTargetRelativeAngles( (0,90,0), 1 );
	self SetTurretTargetRelativeAngles( (0,90,0), 2 );

	self thread Movement_Thread_Unaware();

	while ( true ) 
	{
		self vehicle_ai::evaluate_connections();
		wait 1;
	}
}

function Movement_Thread_Unaware()
{
	self endon( "death" );
	self endon( "change_state" );
	self notify( "end_movement_thread" );
	self endon( "end_movement_thread" );

	while( true )
	{
		self.current_pathto_pos = self GetNextMovePosition_unaware();

		foundpath = self SetVehGoalPos( self.current_pathto_pos, false, true );
		
		if ( foundPath )
		{
			locomotion_start();
			self thread path_update_interrupt();
			self vehicle_ai::waittill_pathing_done();
			self notify( "near_goal" );		// kill path_update_interrupt thread
			self CancelAIMove();
			self ClearVehGoalPos();

			Scan();
		}
		else
		{
			wait 1;
		}

		{wait(.05);};
	}
}

function GetNextMovePosition_unaware()
{
	if( self.goalforced )
	{
		return self.goalpos;
	}

	minSearchRadius = 500;
	maxSearchRadius = 1500;
	halfHeight = 400;
	spacing = 80;

	queryResult = PositionQuery_Source_Navigation( self.origin, minSearchRadius, maxSearchRadius, halfHeight, spacing, self, spacing );
	PositionQuery_Filter_DistanceToGoal( queryResult, self );
	vehicle_ai::PositionQuery_Filter_OutOfGoalAnchor( queryResult );

	forward = AnglesToForward( self.angles );
	foreach ( point in queryResult.data )
	{
		/#    if ( !isdefined( point._scoreDebug ) )    {     point._scoreDebug = [];    }    point._scoreDebug[ "random" ] = randomFloatRange( 0, 30 );    #/    point.score += randomFloatRange( 0, 30 );;

		pointDirection = VectorNormalize( point.origin - self.origin );
		factor = VectorDot( pointDirection, forward );
		if ( factor > 0.7 )
		{
			/#    if ( !isdefined( point._scoreDebug ) )    {     point._scoreDebug = [];    }    point._scoreDebug[ "directionDiff" ] = 600;    #/    point.score += 600;;
		}
		else if ( factor > 0 )
		{
			/#    if ( !isdefined( point._scoreDebug ) )    {     point._scoreDebug = [];    }    point._scoreDebug[ "directionDiff" ] = 0;    #/    point.score += 0;;
		}
		else if ( factor > -0.5 )
		{
			/#    if ( !isdefined( point._scoreDebug ) )    {     point._scoreDebug = [];    }    point._scoreDebug[ "directionDiff" ] = -600;    #/    point.score += -600;;
		}
		else
		{
			/#    if ( !isdefined( point._scoreDebug ) )    {     point._scoreDebug = [];    }    point._scoreDebug[ "directionDiff" ] = -1200;    #/    point.score += -1200;;
		}
	}

	vehicle_ai::PositionQuery_PostProcess_SortScore( queryResult );
	self vehicle_ai::PositionQuery_DebugScores( queryResult );

	if( queryResult.data.size == 0 )
		return self.origin;

	return queryResult.data[0].origin;
}

// ----------------------------------------------
// State: jump
// ----------------------------------------------
function clean_up_spawned()
{
	if ( isdefined( self.jump ) && isDefined(self.jump.linkEnt) )
	{
		self.jump.linkEnt Delete();
	}
}
function clean_up_spawnedOnDeath(entToWatch)
{
	self endon("death");
	entToWatch waittill("death");
	self delete();
}


function initJumpStruct()
{
	if ( isdefined( self.jump ) )
	{
		self Unlink();
		self.jump.linkEnt Delete();
		self.jump Delete();
	}

	self.jump = spawnstruct();
	self.jump.linkEnt = Spawn( "script_origin", self.origin );
	self.jump.linkEnt thread clean_up_spawnedOnDeath(self);
	self.jump.in_air = false;
	self.jump.highgrounds = struct::get_array( "balcony_point" );
	self.jump.groundpoints = struct::get_array( "ground_point" );

	//assert( self.jump.highgrounds.size > 0 );
	//assert( self.jump.groundpoints.size > 0 );
}

function state_jump_can_enter( from_state, to_state, connection )
{
	if(( isdefined( self.noJumping ) && self.noJumping ))
		return false;
	
	return ( self.vehicletype === "spawner_enemy_boss_siegebot_zombietron" );
}

function state_jump_enter( params )
{
	goal = params.jumpgoal;

	trace = PhysicsTrace( goal + ( 0, 0, 500 ), goal - ( 0, 0, 10000 ), ( -10, -10, -10 ), ( 10, 10, 10 ), self, (1 << 1) );
	if ( false )
	{
	/#debugstar( goal, 60000, (0,1,0) ); #/
	/#debugstar( trace[ "position" ], 60000, (0,1,0) ); #/
	/#line(goal, trace[ "position" ], (0,1,0), 1, false, 60000 ); #/
	}
	if ( trace[ "fraction" ] < 1 )
	{
		goal = trace[ "position" ];
	}

	self.jump.goal = goal;

	params.scaleForward = 40;
	params.gravityForce = (0, 0, -6);
	params.upByHeight = 50;
	params.landingState = "land@jump";

	self pain_toggle( false );

	self stopMovementAndSetBrake();
}

function state_jump_update( params )
{
	self endon( "change_state" );
	self endon( "death" );

	goal = self.jump.goal;

	self face_target( goal );

	self.jump.linkEnt.origin = self.origin;
	self.jump.linkEnt.angles = self.angles;

	{wait(.05);};

	self LinkTo( self.jump.linkEnt );

	self.jump.in_air = true;

	if ( false ) 
	{
	/#debugstar( goal, 60000, (0,1,0) ); #/
	/#debugstar( goal + (0,0,100), 60000, (0,1,0) ); #/
	/#line(goal, goal + (0,0,100), (0,1,0), 1, false, 60000 ); #/
	}

	// calculate distance and forces
	totalDistance = Distance2D(goal, self.jump.linkEnt.origin);
	forward = ( ((goal - self.jump.linkEnt.origin) / totalDistance)[0], ((goal - self.jump.linkEnt.origin) / totalDistance)[1], 0 );
	upByDistance = MapFloat( 500, 2000, 46, 52, totalDistance );
	antiGravityByDistance = 0;//MapFloat( 500, 2000, 0, 0.5, totalDistance );

	initVelocityUp = (0,0,1) * ( upByDistance + params.upByHeight );
	initVelocityForward = forward * params.scaleForward * MapFloat( 500, 2000, 0.8, 1, totalDistance );
	velocity = initVelocityUp + initVelocityForward;

	// start jumping
	if ( self.vehicletype === "spawner_enemy_boss_siegebot_zombietron" )
	{
		self ASMSetAnimationRate( 1.0 );
	}

	self ASMRequestSubstate( "inair@jump" );
	self waittill( "engine_startup" );
	self vehicle::impact_fx( self.settings.startupfx1 );
	self waittill( "leave_ground" );
	self vehicle::impact_fx( self.settings.takeofffx1 );

	while( true )
	{
		distanceToGoal = Distance2D(self.jump.linkEnt.origin, goal);

		antiGravityScaleUp = 1.0;//MapFloat( 0, 0.5, 0.6, 0, abs( 0.5 - distanceToGoal / totalDistance ) );
		antiGravityScale = 1.0;//MapFloat( (self.radius * 1.0), (self.radius * 3), 0, 1, distanceToGoal );
		antiGravity = (0,0,0);//antiGravityScale * antiGravityScaleUp * (-params.gravityForce) + (0,0,antiGravityByDistance);
		if ( false ) /#line(self.jump.linkEnt.origin, self.jump.linkEnt.origin + antiGravity, (0,1,0), 1, false, 60000 ); #/

		velocityForwardScale = MapFloat( (self.radius * 1), (self.radius * 4), 0.2, 1, distanceToGoal );
		velocityForward = initVelocityForward * velocityForwardScale;
		if ( false ) /#line(self.jump.linkEnt.origin, self.jump.linkEnt.origin + velocityForward, (0,1,0), 1, false, 60000 ); #/

		oldVerticleSpeed = velocity[2];
		velocity = (0,0, velocity[2]);
		velocity += velocityForward + params.gravityForce + antiGravity;

		if ( oldVerticleSpeed > 0 && velocity[2] < 0 )
		{
			self ASMRequestSubstate( "fall@jump" );
		}

		if ( velocity[2] < 0 && self.jump.linkEnt.origin[2] + velocity[2] < goal[2] )
		{
			break;
		}

		heightThreshold = goal[2] + 110;
		oldHeight = self.jump.linkEnt.origin[2];
		self.jump.linkEnt.origin += velocity;

		if ( self.jump.linkEnt.origin[2] < heightThreshold && ( oldHeight > heightThreshold || ( oldVerticleSpeed > 0 && velocity[2] < 0 ) ) )
		{
			self notify( "start_landing" );
			self ASMRequestSubstate( params.landingState );
		}

		if ( false ) /#debugstar( self.jump.linkEnt.origin, 60000, (1,0,0) ); #/
		{wait(.05);};
	}

	// landed
	self.jump.linkEnt.origin = ( self.jump.linkEnt.origin[0], self.jump.linkEnt.origin[1], 0 ) + ( 0, 0, goal[2] );
	self notify( "land_crush" );

	// don't damage player, but crush player vehicle
	foreach( player in level.players )
	{
		player._takedamage_old = player.takedamage;
		player.takedamage = false;
	}
	self RadiusDamage( self.origin + ( 0,0,15 ), self.radiusdamageradius, self.radiusdamagemax, self.radiusdamagemin, self, "MOD_EXPLOSIVE" );

	foreach( player in level.players )
	{
		player.takedamage = player._takedamage_old;
		player._takedamage_old = undefined;

		if ( Distance2DSquared( self.origin, player.origin ) < ( (200) * (200) ) )
		{
			direction = ( ( player.origin - self.origin )[0], ( player.origin - self.origin )[1], 0 );
			if ( Abs( direction[0] ) < 0.01 && Abs( direction[1] ) < 0.01 )
			{
				direction = ( RandomFloatRange( 1, 2 ), RandomFloatRange( 1, 2 ), 0 );
			}
			direction = VectorNormalize( direction );
			strength = 700;
			player SetVelocity( player GetVelocity() + direction * strength );

			if ( player.health > 80 )
			{
				player DoDamage( player.health - 70, self.origin, self );
			}
		}
	}

	self vehicle::impact_fx( self.settings.landingfx1 );
	self stopMovementAndSetBrake();

	//rumble for landing from jump
	//self clientfield::increment( "sarah_rumble_on_landing" );

	wait 0.3;

	self Unlink();
	
	{wait(.05);};

	self.jump.in_air = false;

	self notify ( "jump_finished" );

	vehicle_ai::Cooldown( "jump", 7 );

	self vehicle_ai::waittill_asm_complete( params.landingState, 3 );

	self vehicle_ai::evaluate_connections();
}

function state_jump_exit( params )
{
}

// ----------------------------------------------
// State: combat
// ----------------------------------------------
function state_combat_update( params )
{
	self endon( "death" );
	self endon( "change_state" );

	self thread Movement_Thread();
	self thread Attack_Thread_machinegun();
	self thread Attack_Thread_rocket();
}

function state_combat_exit( params )
{
	self ClearTurretTarget();
	self SetTurretSpinning( false );
}

function locomotion_start()
{
	if( self.vehicletype === "spawner_enemy_boss_siegebot_zombietron" )
	{
		self ASMSetAnimationRate( 0.5 );
	}

	self ASMRequestSubstate( "locomotion@movement" );
}

function GetNextMovePosition_tactical()
{
	if( self.goalforced )
	{
		return self.goalpos;
	}

	maxSearchRadius = 800;
	halfHeight = 400;
	innerSpacing = 50;
	outerSpacing = 60;

	queryResult = PositionQuery_Source_Navigation( self.origin, 0, maxSearchRadius, halfHeight, innerSpacing, self, outerSpacing );
	PositionQuery_Filter_DistanceToGoal( queryResult, self );
	vehicle_ai::PositionQuery_Filter_OutOfGoalAnchor( queryResult );

	if( isdefined( self.enemy ) )
	{
		PositionQuery_Filter_Sight( queryResult, self.enemy.origin, self GetEye() - self.origin, self, 0, self.enemy );
		self vehicle_ai::PositionQuery_Filter_EngagementDist( queryResult, self.enemy, self.settings.engagementDistMin, self.settings.engagementDistMax );
	}

	foreach ( point in queryResult.data )
	{
		/#    if ( !isdefined( point._scoreDebug ) )    {     point._scoreDebug = [];    }    point._scoreDebug[ "random" ] = randomFloatRange( 0, 30 );    #/    point.score += randomFloatRange( 0, 30 );;

		if( point.disttoorigin2d < 120 )
		{
			/#    if ( !isdefined( point._scoreDebug ) )    {     point._scoreDebug = [];    }    point._scoreDebug[ "tooCloseToSelf" ] = (120 - point.disttoorigin2d) * -1.5;    #/    point.score += (120 - point.disttoorigin2d) * -1.5;;
		}

		if( isdefined( self.enemy ) )
		{
			/#    if ( !isdefined( point._scoreDebug ) )    {     point._scoreDebug = [];    }    point._scoreDebug[ "engagementDist" ] = -point.distAwayFromEngagementArea;    #/    point.score += -point.distAwayFromEngagementArea;;

			if ( !point.visibility )
			{
				/#    if ( !isdefined( point._scoreDebug ) )    {     point._scoreDebug = [];    }    point._scoreDebug[ "visibility" ] = -600;    #/    point.score += -600;;
			}
		}
	}

	vehicle_ai::PositionQuery_PostProcess_SortScore( queryResult );
	self vehicle_ai::PositionQuery_DebugScores( queryResult );

	if( queryResult.data.size == 0 )
		return self.origin;

	return queryResult.data[0].origin;
}

function path_update_interrupt()
{
	self endon( "death" );
	self endon( "change_state" );
	self endon( "near_goal" );
	self endon( "reached_end_node" );
	
	canSeeEnemyCount = 0;
	
	old_enemy = self.enemy;
	
	startPath = GetTime(); // assume we just started a new path
	old_origin = self.origin;
	move_dist = 300;

	wait 1.5;
	
	while( 1 )
	{
		self SetMaxSpeedScale( 1 );
		self SetMaxAccelerationScale( 1 );
		self SetSpeed( self.settings.defaultMoveSpeed );
		
		if ( isdefined( self.enemy ) )
		{
			selfDistToTarget = Distance2D( self.origin, self.enemy.origin );
		
			farEngagementDist = self.settings.engagementDistMax + 150;
			closeEngagementDist = self.settings.engagementDistMin - 150;
		
			if( self VehCanSee( self.enemy ) )
			{
				self SetLookAtEnt( self.enemy );	// try to keep the basic orientation towards the enemy
				self SetTurretTargetEnt( self.enemy );
			
				// check the distance so we don't trigger a new path when we are already moving
				if( selfDistToTarget < farEngagementDist && selfDistToTarget > closeEngagementDist )
				{	
					canSeeEnemyCount++;

					// Stop if we can see our enemy	for a bit
					if( canSeeEnemyCount > 3 && ( vehicle_ai::TimeSince( startPath ) > 5 || Distance2DSquared( old_origin, self.origin ) > ( (move_dist) * (move_dist) ) ) )
					{
						self notify( "near_goal" );
					}
				}
				else
				{
					// too far go fast
					self SetMaxSpeedScale( 2.5 );
					self SetMaxAccelerationScale( 3 );
					self SetSpeed( self.settings.defaultMoveSpeed * 2 );
				}
			}
			else if( (!self VehSeenRecently( self.enemy, 1.5 ) && self VehSeenRecently( self.enemy, 15 )) || selfDistToTarget > farEngagementDist ) // move fast if we just lost sight of our target or we are too far
			{
				self SetMaxSpeedScale( 1.8 );
				self SetMaxAccelerationScale( 2 );
				self SetSpeed( self.settings.defaultMoveSpeed * 1.5 );
			}
		}
		else
		{
			canSeeEnemyCount = 0;
		}

		if ( isdefined( self.enemy ) )
		{
			if( !isdefined( old_enemy ) )
			{
				self notify( "near_goal" );		// new enemy
			}
			else if( self.enemy != old_enemy )
			{
				self notify( "near_goal" );		// new enemy
			}

			if( self VehCanSee( self.enemy ) && distance2dSquared( self.origin, self.enemy.origin ) < ( (150) * (150) ) && Distance2DSquared( old_origin, self.enemy.origin ) > ( (151) * (151) ) ) // don't walk pass the player
			{
				self notify( "near_goal" );
			}
		}
		
		wait 0.2;
	}
}

function weapon_doors_state( isOpen, waittime = 0 )
{
	self endon( "death" );
	self notify( "weapon_doors_state" );
	self endon( "weapon_doors_state" );

	if ( isdefined( waittime ) && waittime > 0 )
	{
		wait waittime;
	}

	self vehicle::toggle_ambient_anim_group( 1, isOpen );
}

function Movement_Thread()
{
	self endon( "death" );
	self endon( "change_state" );
	self notify( "end_movement_thread" );
	self endon( "end_movement_thread" );

	while ( 1 )
	{
		self.current_pathto_pos = self GetNextMovePosition_tactical();

		if( self.vehicletype === "spawner_enemy_boss_siegebot_zombietron" )
		{
			if ( vehicle_ai::IsCooldownReady( "jump" ) )
			{
				params = SpawnStruct();
				params.jumpgoal = self.current_pathto_pos;
				locomotion_start();
				wait 0.5;
				self vehicle_ai::evaluate_connections( undefined, params );
				wait 0.5;
			}
		}

		foundpath = self SetVehGoalPos( self.current_pathto_pos, false, true );
		
		if ( foundPath )
		{
			if ( isdefined( self.enemy ) && self VehSeenRecently( self.enemy, 1 ) )
			{
				self SetLookAtEnt( self.enemy );
				self SetTurretTargetEnt( self.enemy );
			}
			locomotion_start();
			self thread path_update_interrupt();
			self vehicle_ai::waittill_pathing_done();
			self notify( "near_goal" );		// kill path_update_interrupt thread
			self CancelAIMove();
			self ClearVehGoalPos();
			if ( isdefined( self.enemy ) && self VehSeenRecently( self.enemy, 2 ) )
			{
				self face_target( self.enemy.origin );
			}
		}

		wait 1;

		startAdditionalWaiting = GetTime();
		while ( isdefined( self.enemy ) && self VehCanSee( self.enemy ) && vehicle_ai::TimeSince( startAdditionalWaiting ) < 1.5 )
		{
			wait 0.4;
		}
	}
}

function stopMovementAndSetBrake()
{
	self notify( "end_movement_thread" );
	self notify( "near_goal" );
	self CancelAIMove();
	self ClearVehGoalPos();	
	self ClearTurretTarget();
	self ClearLookAtEnt();
	self SetBrake( 1 );
}

function face_target( position, targetAngleDiff )
{
	if ( !isdefined( targetAngleDiff ) )
	{
		targetAngleDiff = 30;
	}

	v_to_enemy = ( (position - self.origin)[0], (position - self.origin)[1], 0 );
	v_to_enemy = VectorNormalize( v_to_enemy );
	goalAngles = VectortoAngles( v_to_enemy );

	angleDiff = AbsAngleClamp180( self.angles[1] - goalAngles[1] );
	if ( angleDiff <= targetAngleDiff )
	{
		return;
	}

	self SetLookAtOrigin( position );
	self SetTurretTargetVec( position );
	self locomotion_start();

	angleAdjustingStart = GetTime();
	while( angleDiff > targetAngleDiff && vehicle_ai::TimeSince( angleAdjustingStart ) < 4 )
	{
		angleDiff = AbsAngleClamp180( self.angles[1] - goalAngles[1] );
		{wait(.05);};
	}

	self ClearVehGoalPos();
	self ClearLookAtEnt();
	self ClearTurretTarget();
	self CancelAIMove();
}

function Scan()
{
	angles = self GetTagAngles( "tag_barrel" );
	angles = (0,angles[1],0);	// get rid of pitch
	
	rotate = 360;
	
	while( rotate > 0 )
	{
		angles += (0,30,0);
		rotate -= 30;
		forward = AnglesToForward( angles );
		
		aimpos = self.origin + forward * 1000;
		self SetTurretTargetVec( aimpos );
		msg = self util::waittill_any_timeout( 0.5, "turret_on_target" );
		wait 0.1;
		
		if( isdefined( self.enemy ) && self VehCanSee( self.enemy ) )	// use VehCanSee, if recently attacked this will return true and not use FOV check
		{
			self SetTurretTargetEnt( self.enemy );
			self SetLookAtEnt( self.enemy );
			self face_target( self.enemy );
			return;
		}
	}
	
	// return the turret to forward
	forward = AnglesToForward( self.angles );
		
	aimpos = self.origin + forward * 1000;
	self SetTurretTargetVec( aimpos );
	msg = self util::waittill_any_timeout( 3.0, "turret_on_target" );
	self ClearTurretTarget();
}

function Attack_Thread_machinegun()
{
	self endon( "death" );
	self endon( "change_state" );
	self endon( "end_attack_thread" );
	self notify( "end_machinegun_attack_thread" );
	self endon( "end_machinegun_attack_thread" );

	self.turretrotscale = 1 * self.difficulty_scale_up;
	
	spinning = false;

	while( 1 )
	{
		if( isdefined( self.enemy ) && self VehCanSee( self.enemy ) )
		{
			self SetLookAtEnt( self.enemy );
			self SetTurretTargetEnt( self.enemy );

			if ( !spinning )
			{
				spinning = true;
				self SetTurretSpinning( true );
				wait 0.5;
				continue;
			}

			self SetGunnerTargetEnt( self.enemy, (0,0,0), 0 );
			self SetGunnerTargetEnt( self.enemy, (0,0,0), 1 );
	
			self vehicle_ai::fire_for_time( RandomFloatRange( 0.75, 1.5 ) * self.difficulty_scale_up, 1 );
			
			if( isdefined( self.enemy ) && IsAI( self.enemy ) )
			{
				wait( RandomFloatRange( 0.1, 0.2 ) );
			}
			else
			{
				wait( RandomFloatRange( 0.2, 0.3 ) ) * self.difficulty_scale_down;
			}
		}
		else
		{
			spinning = false;
			self SetTurretSpinning( false );
			self ClearGunnerTarget( 0 );
			self ClearGunnerTarget( 1 );
			wait 0.4;
		}
	}
}

function Attack_Rocket( target )
{		
	if ( isdefined( target ) )
	{
		self SetTurretTargetEnt( target );
		self SetGunnerTargetEnt( target, (0,0,-10), 2 );
		msg = self util::waittill_any_timeout( 1, "turret_on_target" );
		self FireWeapon( 2, target, (0,0,-10) );
		self ClearGunnerTarget( 1 );
	}
}

function Attack_Thread_Rocket()
{
	self endon( "death" );
	self endon( "change_state" );
	self endon( "end_attack_thread" );
	self notify( "end_rocket_attack_thread" );
	self endon( "end_rocket_attack_thread" );

	vehicle_ai::Cooldown( "rocket", 3 );

	while( 1 )
	{
		if ( isdefined( self.enemy ) && self VehSeenRecently( self.enemy, 3 ) && ( vehicle_ai::IsCooldownReady( "rocket", 1.5 ) ) )
		{
			self SetGunnerTargetEnt( self.enemy, (0,0,0), 0 );
			self SetGunnerTargetEnt( self.enemy, (0,0,-10), 2 );

			self thread weapon_doors_state( true );
			wait 1.5;
			if ( isdefined( self.enemy ) && self VehSeenRecently( self.enemy, 1 ) )
			{
				vehicle_ai::Cooldown( "rocket", 5 );
				Attack_Rocket( self.enemy );
				wait 1;
				if ( isdefined( self.enemy ) )
				{
					Attack_Rocket( self.enemy );
				}
				self thread weapon_doors_state( false, 1 );
			}
			else
			{
				self thread weapon_doors_state( false );
			}
		}
		else
		{
			self ClearGunnerTarget( 0 );
			self ClearGunnerTarget( 1 );
			wait 0.4;
		}
	}
}

function siegebot_callback_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
{
	num_players = GetPlayers().size;
	maxDamage = self.healthdefault * ( 0.40 - 0.02 * num_players );
	if ( sMeansOfDeath !== "MOD_UNKNOWN" && iDamage > maxDamage )
	{
		iDamage = maxDamage;
	}

	if ( vehicle_ai::should_emp( self, weapon, sMeansOfDeath, eInflictor, eAttacker ) )
	{
		minEmpDownTime = 0.8 * self.settings.empdowntime;
		maxEmpDownTime = 1.2 * self.settings.empdowntime;
		self notify ( "emped", RandomFloatRange( minEmpDownTime, maxEmpDownTime ), eAttacker, eInflictor );
	}

	if ( !isdefined( self.damageLevel ) )
	{
		self.damageLevel = 0;
		self.newDamageLevel = self.damageLevel;
	}

	newDamageLevel = vehicle::should_update_damage_fx_level( self.health, iDamage, self.healthdefault );
	if ( newDamageLevel > self.damageLevel )
	{
		self.newDamageLevel = newDamageLevel;
	}

	if ( self.newDamageLevel > self.damageLevel )
	{
		self.damageLevel = self.newDamageLevel;

		driver = self GetSeatOccupant( 0 );
		if ( !isdefined( driver ) ) // no pain for player driving version
		{
			self notify( "pain" );
		}

		vehicle::set_damage_fx_level( self.damageLevel );
	}

	return iDamage;
}


