--[[------------------------------------------------------------------------------------------------
Helicoter binding
Чугай Александр

Сделать:
	- настройки в ltx
--------------------------------------------------------------------------------------------------]]

local init_chk = {}

---------------------------------------------------------------------------------------------
-- Функция для бинда
---------------------------------------------------------------------------------------------

game_heli = { 
	["esc_heli"]=true, 
	["agr_nii_heli"]=true, 
	["agr_helicopter1"]=true, 
	["agr_helicopter2"]=true, 
	["agr_helicopter3"]=true, 
	["agr_helicopter4"]=true, 
	["val_chase_heli"]=true, 
	["rostok_helicopter"]=true, 
	["yan_helicopter_1"]=true, 
	["yan_helicopter_2"]=true,
	["rad_helicopter"]=true,
	["pri_helicopter1"]=true,
	["pri_helicopter2"]=true,
	["pri_helicopter4"]=true,
	["pri_helicopter_21"]=true,
	["pri_helicopter_22"]=true,
	["pri_helicopter_24"]=true,
	["aes_helicopter"]=true,
	["aes_helicopter_0000"]=true,
	["aes_helicopter_0001"]=true,
	["aes_helicopter_0002"]=true,
	["aes_helicopter_0003"]=true,
	["aes_helicopter_0004"]=true,
	["aes_helicopter_0007"]=true,
	["aes_helicopter_0008"]=true,
	["aes_helicopter_0009"]=true,
	["aes_helicopter_0010"]=true
}

function bind( obj )
--    printf( "BIND HELICOPTER id=%d", obj:id() )

   	local ini = obj:spawn_ini()
   	if ini and ini:section_exist("logic") then
		obj:bind_object( heli_binder( obj, ini ) )
	end
end

---------------------------------------------------------------------------------------------
-- Биндер вертолётов
---------------------------------------------------------------------------------------------
class "heli_binder" ( object_binder )

function heli_binder:__init( obj, ini ) super( obj )
	self.ini         = ini
	self.initialized = false
	self.loaded      = false
end

function heli_binder:reload( section )
	object_binder.reload( self, section )
end

function heli_binder:reinit()
	object_binder.reinit( self )

	printf( "heli_binder:reinit()" )

	db.storage[self.object:id()] = {}
	self.st = db.storage[self.object:id()]
	self.id = self.object:id()

	self.heliObject = self.object:get_helicopter()

	self.object:set_callback( callback.helicopter_on_point, self.on_point, self )
	self.object:set_callback( callback.helicopter_on_hit,   self.on_hit,   self )

	self.st.combat = heli_combat.heli_combat( self.object, self.heliObject )
	self.st.combat.lasthealth = get_heli_health( self.heliObject, self.st )
	
	self.last_hit_snd_timeout = 0

	local ltx = system_ini()

	self.flame_start_health   = utils.cfg_get_number( ltx, "helicopter", "flame_start_health", self.object, true )
	self.st.flame_start_snd   = xr_sound.get_sound_object("heli_explosion", "random")

	-- на случай загрузки
	self.st.last_alt       = self.heliObject:GetRealAltitude()
	self.st.alt_check_time = time_global() + 1000
end

function heli_binder:update( delta )

	
	if not init_chk[self.object:id()] and not self.heliObject.m_flame_started then
		-- amk.logf("LAST HELI HEATH "..tostring(self.heliObject:GetfHealth()))
		self.heliObject:SetfHealth(1)
		self.st.combat.lasthealth = get_heli_health( self.heliObject, self.st )
		init_chk[self.object:id()] = true
	end	

	object_binder.update( self, delta )
	
	--' Апдейт объекта прицеливания

--	printf( "heli_binder update" )
--	printf( "%d", self.object:level_vertex_light( db.actor:level_vertex_id() ) )

	if not self.initialized and db.actor then
		self.initialized = true
		xr_logic.initialize_obj( self.object, self.st, self.loaded, db.actor, modules.stype_heli )
	end

	self.object:info_clear()

	if self.st.active_section ~= nil then
		xr_logic.issue_event( self.object, self.st[self.st.active_scheme], "update", delta )
	end

	local active_section = db.storage[self.object:id()].active_section
	if active_section then
		self.object:info_add('section -- ' .. active_section)
	end

	self:check_health()
end

function heli_binder:net_spawn( data )
	if not object_binder.net_spawn( self, data ) then
		return false
	end

	if game_heli[self.object:name()] ~= true then
		bind_heli.heli_blow_up(self.object)
		return false
	end	

	db.add_obj( self.object )
	db.add_heli( self.object )

	return true
end

function heli_binder:net_destroy()
	db.del_obj( self.object )
	db.del_heli( self.object )

	object_binder.net_destroy( self )
end

function heli_binder:net_save_relevant()
	return true
end

function heli_binder:save( packet )
	object_binder.save( self, packet )

	xr_logic.save_obj( self.object, packet )

	self.st.combat:save( packet )
end

function heli_binder:load( packet )
	self.loaded = true

	--printf("generic_object_binder:load(): self.object:name()='%s'", self.object:name())
	object_binder.load( self, packet )

	printf( "heli_binder: load")

	xr_logic.load_obj( self.object, packet )

	self.st.combat:load( packet )
end

function heli_binder:check_health()
	local heli = self.heliObject

--	printf( "heli health: %d", heli:GetfHealth() )

	if not heli.m_dead then
		local health = get_heli_health( self.heliObject, self.st )

		if health < self.flame_start_health and not heli.m_flame_started then
			heli_start_flame( self.object )

			heli_snd.play_snd( self.st, heli_snd.snd_damaged, 3 )
		end

		if health <= 0.005 then
			heli_die( self.object )

			heli_snd.play_snd( self.st, heli_snd.snd_down, 4 )
		end
	end
end

------------------------------ callbacks ---------------------------------
function heli_binder:on_hit( power, impulse, hit_type, enemy_id )


	local health_loss = self.st.combat.lasthealth - self.heliObject:GetfHealth()
	if tonumber(hit_type) == 8 then -- пулевое повреждение с нужным импульсом ( импульс вместо бронебойности, спасибо разрабам)
		if tonumber(impulse) >= 1 then
			self.st.combat.lasthealth = self.st.combat.lasthealth - redefine_hit(power, health_loss)
		end
	else
		self.st.combat.lasthealth = self.st.combat.lasthealth - health_loss
	end

	self.heliObject:SetfHealth(self.st.combat.lasthealth)
	printf( "heli_binder: hit callback")

	local enemy        = level.object_by_id( enemy_id )
	local enemy_cls_id = get_clsid( enemy )

	-- если обидчик актёр или сталкер, то сделать его своим врагом
	if enemy_cls_id == clsid.actor or enemy_cls_id == clsid.script_stalker then
	
		if not self.st.combat.enemy_id then
			self.st.combat.enemy_id = enemy_id
		end

		local l_target
		local l_prior,t_prior = 0,0
		local target = level.object_by_id(enemy_id)

		t_prior = heli_target.get_target_priority(target)
			
		if self.st.combat.enemy_id then
			l_target = level.object_by_id(self.st.combat.enemy_id)
			l_prior = heli_target.get_target_priority(l_target)
		end
		if (l_prior < t_prior or 
			(l_prior == t_prior and
			not level.object_by_id(self.object:id()):get_helicopter():isVisible(l_target)))
			then
			self.st.combat.enemy_id = enemy_id
		end

		if self.st.hit then
			xr_logic.issue_event( self.object, self.st.hit, "hit_callback", self.object, power, nil, enemy, nil )
		end
	end
	if self.st.combat.lasthealth < self.flame_start_health and self.st.combat.lasthealth > 0.005 and not self.heliObject.m_flame_started then
		if not self.flame_started then
			self.flame_started = true
		end
	end
	-- пилоты ругаются по рации
	if self.last_hit_snd_timeout < time_global() then
		heli_snd.play_snd( self.st, heli_snd.snd_hit, 2 )
		self.last_hit_snd_timeout = time_global() + math.random( 4000, 8000 )
	end
end

function heli_binder:on_point( distance, position, path_idx )
	if self.st.active_section ~= nil then
		xr_logic.issue_event( self.object, self.st[self.st.active_scheme], "waypoint_callback", self.object, nil, path_idx )
	end
end

--------------------------------------------------------------------------
function redefine_hit(power,health_loss)
	local lucky_hit_chance = 0.05
	local fire_hit_immunity = 0.05
	local area -- зона, в которую пришлось попадание
	local hit_chance -- шанс задеть какой-нибудь важный узел
	local hit_immunity -- часть удара, идущая на разрушение узла
	local ap_chance = (2.5*power - 1.8)*10 -- вероятность пробития брони данным выстрелом, power <1.1 не пробьет,  power >1.6 пробьет 100% 

	local hit = 0
	if math.abs(0.1*power - health_loss) <= 0.0001*health_loss then 
		area = "pilot"
		hit_chance = 0.001 -- считаем, что задеть пилота сквозь броню очень-очень проблематично
		hit_immunity = 2.5 --  а вот сам пилот не сильно прочный
	elseif math.abs(0.2*power - health_loss) <= 0.0001*health_loss then
		area = "bprop"
		hit_chance = 1 -- открытый объект, попал - значит попал
		hit_immunity = 0.8 -- металл все-таки, но броней не прикрыт
	elseif math.abs(0.3*power - health_loss) <= 0.0001*health_loss then 
		area = "fprop"
		hit_chance = 1 -- открытый объект, попал - значит попал
		hit_immunity = 0.8 -- металл все-таки, но броней не прикрыт
	elseif math.abs(0.4*power - health_loss) <= 0.0001*health_loss then 
		area = "cocpit"
		hit_chance = 0.15 -- всякие провода, приборы, управление и прочая дрянь занимает достаточно много места
		hit_immunity = 0.8 -- вся эта дрянь особой прочностью не отличается
	elseif math.abs(0.5*power - health_loss) <= 0.0001*health_loss then 
		area = "tail"
		hit_chance = 0.1 -- только  тяги управления и привод хвостового винта. места занимают очень мало
		hit_immunity = 0.2 -- прочная сталь, разрушить трудно
	elseif math.abs(0.6*power - health_loss) <= 0.0001*health_loss then 
		area = "main" -- самая жопа. жизненно важные элементы и до хрена пустого объема.
		hit_chance = 0.4 -- двигатель занимает много места, но достать его снизу СКВОЗЬ вертолет нереально. 
		hit_immunity = 0.5 -- самая жизненно важная часть, но все системы дублированы
	else 
		area = "non_lethal" -- неповреждаемые пулями зоны. Ну, дырочка в крыле, и что?
		hit_chance = 0
		hit_immunity = 0
	end

	if math.random() < ap_chance and math.random() < hit_chance then -- броня пробита и задето что-то важное
		hit = power * fire_hit_immunity * hit_immunity
		if math.random() < lucky_hit_chance then
			hit = hit*15
		end
	end
	return hit
end
function get_heli_health( heli, st )
	local health

	if st.invulnerable then
	 --[[
		health = 1
		heli:SetfHealth( health )
		-- original ]]
		health = heli:GetfHealth() -- no invulnerable helicopters
		
		if health < 0 then
			heli:SetfHealth( 0 )
			health = 0
		end		

	else
		health = heli:GetfHealth()

		if health < 0 then
			heli:SetfHealth( 0 )
			health = 0
		end
	end

	return health
end

function is_heli_alive( obj )
	return get_heli_health( obj:get_helicopter(), db.storage[obj:id()] ) > 0.005
end

function heli_start_flame( obj )
	obj:get_helicopter():StartFlame()

	db.storage[obj:id()].flame_start_snd:play( obj )
end

function heli_die( obj )

	local heli = obj:get_helicopter()
	local st   = db.storage[obj:id()]

	heli:TurnEngineSound(false)
	heli:Die()

	db.del_heli( obj )
	
	st.last_alt       = heli:GetRealAltitude()
	st.alt_check_time = time_global() + 1000
end

function heli_blow_up( obj )
	local heli = obj:get_helicopter()
	local st   = db.storage[obj:id()]

	heli:TurnEngineSound(false)
	heli:Die()
	heli:Explode()
	
	db.del_heli( obj )
	
	st.last_alt       = heli:GetRealAltitude()
	st.alt_check_time = time_global() + 1000
	
	if checker_minefield.to_delete[obj:id()] == nil then
		checker_minefield.to_delete[obj:id()] = obj:id()
	end		
end

function heli_silent_remove( obj )
	local heli = obj:get_helicopter()
	local st   = db.storage[obj:id()]
	
	db.del_heli( obj )
	local sobj = alife():object(obj:id())
	if sobj then
		alife():release(sobj, true)
	end
	
	st.last_alt       = heli:GetRealAltitude()
	st.alt_check_time = time_global() + 1000
	
	if checker_minefield.to_delete[obj:id()] == nil then
		checker_minefield.to_delete[obj:id()] = obj:id()
	end		
end