:NAME shoot_control

:PARAM float32	initial_delay
:PARAM float32	burst_time
:PARAM float32	burst_delay
:PARAM float32	spread
:PARAM float32	bullet_delay
:PARAM float32	bullet_damage


:VAR uint32		state
:VAR float32	state_time
:VAR float32	last_bullet_time
:VAR boolean	ever_aware_of_enemy
:VAR boolean	disabled
:VAR boolean	burst_sound_played
:VAR s_marker*	muzzle_marker
:VAR c_sound*	sound_fire
:VAR c_sound*	sound_burst
:VAR c_object*	muzzle_flash
:VAR float32	miss_angle




-- -------------------- CONSTANTS --------------------

local vectorlib = require "scripts/vector_math"

k_state_none= 0
k_state_initial_delay= 1
k_state_firing= 2
k_state_delay= 3
k_state_aligning= 4

k_raycast_type_mask= e_value("e_physics_type", "k_physics_type_static_mask") + e_value("e_physics_type", "k_physics_type_dynamic_mask")
k_raycast_group_always_ignored= 31		-- note, must agree with code

k_shoot_distance= 1000
k_shoot_render_time= 0.02

k_shoot_angle_threshold= 0.9
k_shoot_angle_threshold_currently_firing= 0.8

k_shoot_color= {}
k_shoot_color.red= 1
k_shoot_color.green= 1
k_shoot_color.blue= 0

k_muzzle_marker_name= "muzzle"

k_game_time_mode= e_value("e_time_mode", "_time_mode_game")

-- -------------------- INITIALIZATION --------------------

function param_initialize(p)
	p.initial_delay= 1
	p.burst_time= 1
	p.burst_delay= 0.5
	p.spread= 10
	p.bullet_delay= 0.1
	p.bullet_damage= 5
	
end

function var_initialize(v)
	v.state= k_state_none
	v.state_time= -100
	v.last_bullet_time= -100
	v.ever_aware_of_enemy= false
	v.muzzle_marker= nil
	v.disabled= false
	v.sound_fire= nil
	v.sound_burst= nil
	v.burst_sound_played= false
	v.miss_angle= 0
end


-- -------------------- COMPONENT --------------------

function attach(object)
	var.muzzle_marker= object.marker_get(k_muzzle_marker_name)
	
	local sound= object.sound_get()
	if (sound) then
		var.sound_fire= sound.get_sound_by_name("fire")
		var.sound_burst= sound.get_sound_by_name("burst")
	end
end

function change_fire_state(s, time)
	if (var.state ~= s) then
		--print("new state: "..s)
		var.state= s
		var.state_time= time
		
		if (s == k_state_firing) then
			var.burst_sound_played= false
		end
	end
end

function aim_vector_get(object)

	local aim_vector= nil
	local laf= object.laf_get()
	if (laf) then
		aim_vector= laf.aim_state.vector
	else
		local aim= object.aim_get()
		if (aim) then
			aim_vector= aim.vector
		end
	end
	
	return aim_vector
end

function aim_aligned(object, target, threshold)

	local result= false
	
	local aim_vector= aim_vector_get(object)

	if (aim_vector) then
		local position= object.world_position_get()
		local target_position= target.target_object.world_position_get()
		
		local v= vectorlib.vector_from_points(position, target_position)
		vectorlib.normalize2d(v)

		local dot= vectorlib.dot2d(v, aim_vector)
		if (dot > threshold) then
			result= true
		end
	end
	
	return result
end

function recoil_start(object)

	local spring_ik= object.spring_ik_get()
	if (spring_ik) then
	
		local left_arm= spring_ik.chain_get_by_name("left_arm")
		local right_arm= spring_ik.chain_get_by_name("right_arm")
		local aim_vector= aim_vector_get(object)
		
		if (aim_vector) then
		
			local spine= object.spine_get()
			if (spine) then
				spine.accelerate(-20*aim_vector.x, -20*aim_vector.y)
			end

			-- -200
			vectorlib.scale(aim_vector, -600)
		
			-- this WAS in object space before
			if (left_arm) then
				--left_arm.accelerate(-200, 0);
				left_arm.accelerate(aim_vector.x, aim_vector.y);
			end

			if (right_arm) then
				--right_arm.accelerate(-200, 0);
				right_arm.accelerate(aim_vector.x, aim_vector.y);
			end
		end
	end
end

function update_shooting(object, target, time)

	if ((time - var.last_bullet_time) > param.bullet_delay) then

		local ws= world_state_get()

		var.last_bullet_time= time

		local target_object= target.target_object
		local muzzle_position= nil
		
		if (var.muzzle_marker) then
			muzzle_position= var.muzzle_marker.world_position_get(object);
		else
			muzzle_position= object.world_position_get()
		end
		
		local target_position= target_object.world_position_get()
		local v= vectorlib.vector_from_points(muzzle_position, target_position)
		vectorlib.normalize2d(v)
		local max_angle= param.spread*math.pi/360		-- note, we use the half-angle
		local angle= (2*math.random() - 1)*max_angle
		
		if (var.miss_angle > 0) then
			if (angle >= 0) then
				if (angle < var.miss_angle) then
					angle = angle + var.miss_angle
				end
			else
				if (angle > -var.miss_angle) then
					angle = angle - var.miss_angle
				end
			end
		end
		
		v= vectorlib.vector_rotate(v, angle)
		
		-- temporarily set my own raycast group to 31, so that I don't hit myself in the raycast below		
		local cached_raycast_group= object.raycast_group
		object.raycast_group= k_raycast_group_always_ignored
		local raycast_result= ws.raycast_type(object.plate, muzzle_position.x, muzzle_position.y, v.x, v.y, k_shoot_distance, k_raycast_type_mask)
		-- reset the raycast group		
		object.raycast_group= cached_raycast_group

		local hit_object= raycast_result.object_handle
		if (hit_object==nil or hit_object.is_static() or hit_object==target_object) then

			local endpoint= raycast_result.point

			-- actually fire a bullet
			if (hit_object==nil) then
				endpoint= vectorlib.offset_point(muzzle_position, v, k_shoot_distance)
			end
			
			-- hurt him!			
			if (hit_object==target_object) then
				local vitality= target_object._c.vitality
				if (vitality) then
					vitality.damage = vitality.damage + param.bullet_damage
				end
			end
			
			-- spawn a tracer
			local tracer_spawn= object.spawn_get("tracer")
			if (tracer_spawn) then
				local tracer_object= tracer_spawn.spawn()
				if (tracer_object) then
					local tracer= tracer_object.tracer_get()
					if (tracer) then
						tracer.set_data(muzzle_position.x, muzzle_position.y, endpoint.x, endpoint.y)
					end
				end
			end
			
			if (not var.muzzle_flash) then
				local muzzle_flash_spawn= object.spawn_get("muzzle_flash")
				if (muzzle_flash_spawn) then
					var.muzzle_flash= muzzle_flash_spawn.spawn()
				end
			end
			
			recoil_start(object)
			
			--if (var.sound_fire and not var.sound_fire.is_playing()) then
			--	var.sound_fire.play(k_game_time_mode)
			--end

			if (var.sound_fire) then
				if (var.sound_fire.is_playing()) then
					var.sound_fire.stop()
				end
				var.sound_fire.play(k_game_time_mode)
			end
			
			
			if (var.sound_burst and not var.burst_sound_played) then
				var.sound_burst.play(k_game_time_mode)
				var.burst_sound_played= true
			end
			
		end
	end
end


function update(object)

	local perception= object.perception_get()
	local time= world_state_get().time.time
	local target_valid= false
	
	if (var.muzzle_flash) then
		object_delete(var.muzzle_flash)
		var.muzzle_flash= nil
	end
	
	if (perception) then
		local target= perception.get_target()
		if (target and target.is_known()) then
		
--			print("CB: knowing about a target for the first time")
		
			local vitality= target.target_object._c.vitality
			if (vitality== nil or not vitality.dead) then
				local target_cache= perception.target_cache
				if (target_cache.is_visible()) then

					local state= var.state
					
					if (state == k_state_none) then
						if (not var.ever_aware_of_enemy) then 		-- CB: Modified this section
							--print("CB: Never known of the enemy before, so doing initial_delay.")
							change_fire_state(k_state_initial_delay, time)
							var.ever_aware_of_enemy = true
						else
							--print("CB: Known of enemy before...burst_delay only.")
							change_fire_state(k_state_delay, time)
						end
					elseif (state == k_state_initial_delay) then
						if ((time - var.state_time) > param.initial_delay) then
							change_fire_state(k_state_aligning, time)							
						end
					elseif (state == k_state_firing) then
						if (var.disabled or ((time - var.state_time) > param.burst_time) or not aim_aligned(object, target, k_shoot_angle_threshold_currently_firing)) then
							change_fire_state(k_state_delay, time)
						end
					elseif (state == k_state_delay) then
						if ((time - var.state_time) > param.burst_delay) then
							if ((not var.disabled) and aim_aligned(object, target, k_shoot_angle_threshold)) then
								change_fire_state(k_state_firing, time)
							else
								change_fire_state(k_state_aligning, time)
							end
						end
					elseif (state == k_state_aligning) then
						if ((not var.disabled) and aim_aligned(object, target, k_shoot_angle_threshold)) then
							change_fire_state(k_state_firing, time)
						end
					end
					
					--print("Fire state "..state)
				
					-- we can shoot at him!
					if (state == k_state_firing) then
						update_shooting(object, target, time)
					end
					
					target_valid= true

				elseif (not var.ever_aware_of_enemy) then  -- CB: Adding this... not "is_visible" but "is_known"...
					--print("CB: Know about a target, but can't see him, so disabling initial_delay from here on out.")
					var.ever_aware_of_enemy = true
				end
			end
		end
	end
	
	
	
	if (not target_valid) then
		change_fire_state(k_state_none, time)
	end
	
	--if ((time - var.last_shot_render_time) < k_shoot_render_time) then
	--	render_line(object.plate, var.last_shot_start, var.last_shot_end, k_shoot_color)
	--end

end



component_register(attach, update, nil, nil)
