--[[------------------------------------------------------------------------------------------------
Зоны пси-излучения. Отыгрывают постпроцесс и фантомов по настройкам из custom_data
--------------------------------------------------------------------------------------------------]]


class "PsyAntenna"
function PsyAntenna:__init				()
// ----------------------------------------------------------------------------------------
// settings
// ----------------------------------------------------------------------------------------
// phantom gen
// ----------------------------------------------------------------------------------------
	self.phantom_max 					= 8 //10	// max phantoms
	self.phantom_spawn_probability		= 0.2	// spawn probability (0..1)
	self.phantom_spawn_radius			= 30.0	// average radius 30.0m +-15m
	self.phantom_spawn_height			= 2.5 //3	// average height from actor pos +-1.5m
	self.phantom_fov					= 45   // ширина сектора перед глазами актёра, в котором могут рождаться фантомы
	// antenna
	self.hit_amplitude					= 1.0	// размер хита = self.hit_amplitude*self.hit_intensity
// ----------------------------------------------------------------------------------------
// class variables initialize
// ----------------------------------------------------------------------------------------
	self.eff_time						= 0
	self.hit_time						= 0
	self.phantom_time                   = 0
	self.intensity_inertion				= 0.05	// скорость изменения интенсивности ед/секунду
	self.hit_intensity					= 0
	self.sound_intensity				= 0
	self.sound_intensity_base			= 0	// базовая интенсивность. к ней стремится текущая со скоростью self.intensity_inertion	
	self.postprocess_count				= 0  --'счетчик зарегистрированных постпроцессов
	self.postprocess					= {} --'контейнер постпроцессов

// ----------------------------------------------------------------------------------------
// sound
// ----------------------------------------------------------------------------------------
	self.sound_initialized				= false

	self.sound_obj_right, self.sound_obj_left = xr_sound.get_sound_object("psy_voices", "random")

	self.sound_obj_left.volume			= self.sound_intensity
	self.sound_obj_right.volume			= self.sound_intensity
	self.snd_volume						= level.get_snd_volume()
	self.mute_sound_threshold			= 0 --' Предел до которого можно занижать звук уровня. 0 - можно заглушить полностью
	self.max_mumble_volume				= 10
end
function PsyAntenna:destroy				()
	level.set_snd_volume				(self.snd_volume)
end
function PsyAntenna:construct			()
end
function PsyAntenna:update_psy_hit(dt)
	local hud = get_hud()
	local custom_static = hud:GetCustomStatic("cs_psy_danger")
	if self.hit_intensity > 0.01 then
		if custom_static == nil then
			hud:AddCustomStatic("cs_psy_danger", true)
			hud:GetCustomStatic("cs_psy_danger"):wnd():SetTextST("st_psy_danger")
		end
	else
		if custom_static ~= nil then
			hud:RemoveCustomStatic("cs_psy_danger")
		end
	end
	if time_global() - self.hit_time > 5000 then
		self.hit_time					= time_global()

		local power						= self.hit_amplitude*self.hit_intensity
--		printf("HIT: power = %s", tostring(power))
		if power > 0.01 then
			local psy_hit 				= hit()
			psy_hit.power 				= power
			psy_hit.direction 			= vector():set( 0, 0, 0 )
			psy_hit.impulse 			= 0
			psy_hit.draftsman 			= db.actor
			psy_hit.type 				= hit.wound
			db.actor:hit				(psy_hit)

			if db.actor.health < 0.01 and db.actor:alive() then
				db.actor:kill( db.actor )
			end
		end
	end
end
function PsyAntenna:generate_phantoms()
	if self.phantom_idle == nil then
		self.phantom_idle = math.random(2000,5000)
	end
	if time_global() - self.phantom_time > self.phantom_idle then
		self.phantom_time				= time_global()
		self.phantom_idle = math.random(5000,10000)
		if math.random() < self.phantom_spawn_probability then
			if phantom_manager:phantom_count() < self.phantom_max then
				local radius = self.phantom_spawn_radius * ( math.random()/2.0+0.5 )
				local ang    = self.phantom_fov * math.random() - self.phantom_fov * 0.5
				local dir    = vector_rotate_y( db.actor:direction(), ang )

				phantom_manager.spawn_phantom( db.actor:position():add( dir:mul(radius) ) )
			end
		end
	end
end
function PsyAntenna:update_sound()
	if not self.sound_initialized then
		self.sound_obj_left:play_at_pos		(db.actor, vector():set(-1, 0, 1), 0, sound_object.s2d + sound_object.looped)
		self.sound_obj_right:play_at_pos	(db.actor, vector():set( 1, 0, 1), 0, sound_object.s2d + sound_object.looped)

		self.sound_initialized = true
	end

	local vol = 1 - ( self.sound_intensity ^ 3 ) * 0.9

	if vol < self.mute_sound_threshold then
		level.set_snd_volume( self.mute_sound_threshold )
	else
		level.set_snd_volume( vol )
	end

	self.sound_obj_left.volume  = 1 / vol - 1
	self.sound_obj_right.volume = 1 / vol - 1
end
function PsyAntenna:update_postprocess(pp)
	if pp.intensity == 0 then
		self.postprocess_count = self.postprocess_count - 1
		level.remove_pp_effector(pp.idx)
		return false
	end
	
	level.set_pp_effector_factor(pp.idx, pp.intensity, 0.3)
	return true
end
function PsyAntenna:update(dt)
	self.eff_time					= self.eff_time + dt

	function update_intensity(intensity_base, intensity)
		local di = self.intensity_inertion * dt * 0.001
		local ii = intensity_base
		if math.abs(intensity_base - intensity) >= di then
			if intensity_base < intensity then
				ii = intensity - di
			else
				ii = intensity + di
			end
		end

		if		ii < 0.0 then ii = 0.0 
		elseif	ii > 1.0 then ii = 1.0 end
		return ii
	end
	
	self:generate_phantoms()
	
	self.sound_intensity = update_intensity(self.sound_intensity_base, self.sound_intensity)
	self:update_sound()
	
	for k,v in pairs(self.postprocess) do
		v.intensity = update_intensity(v.intensity_base, v.intensity)
		local exist = self:update_postprocess(v)
		
		if exist == false then
			self.postprocess[k] = nil
		end
	end
	
	self:update_psy_hit(dt)
end
function PsyAntenna:save(p)
	p:w_float(self.hit_intensity)
	p:w_float(self.sound_intensity)
	p:w_float(self.sound_intensity_base)	
	p:w_float(self.mute_sound_threshold)

	p:w_u8(self.postprocess_count)
	for k,v in pairs(self.postprocess) do
		p:w_stringZ(k)	
		p:w_float(v.intensity)	
		p:w_float(v.intensity_base)	
		p:w_u16(v.idx)	
	end
end
function PsyAntenna:load(p)
	self.hit_intensity  = p:r_float()
	self.sound_intensity  = p:r_float()	
	self.sound_intensity_base = p:r_float()
	self.mute_sound_threshold = p:r_float()

	self.postprocess_count = p:r_u8()
	
	self.postprocess = {}
	for i=1, self.postprocess_count do
		local k = p:r_stringZ()
		local ii = p:r_float()
		local ib = p:r_float()
		local idx = p:r_u16()
		self.postprocess[k] = {intensity_base = ib, intensity = ii, idx = idx}
		level.add_pp_effector(k, idx, true)
		level.set_pp_effector_factor(idx, ii)		
	end
end

----------------------------------------------------------------------------------------------------
-- логическая схема для space restrictor
----------------------------------------------------------------------------------------------------


local state_outside = 0 -- актер снаружи
local state_inside  = 1 -- актер внутри
local state_void  = 2 -- неизвестный статус

psy_antenna = false


class "action_psy_antenna"
function action_psy_antenna:__init( obj, storage )
	self.object = obj
	self.st     = storage
	self.state  = state_void --' еще не ясно, в зоне он, или нет
end
function action_psy_antenna:reset_scheme( loading )
--'	if loading then
--'		self.state = xr_logic.pstor_retrieve( self.object, "inside"  )
--'	else
		if self.state == state_inside then
			self:zone_leave()
		end

		self.state = state_void

		self:switch_state(db.actor)
--'	end
--'	printf("[psy_antenna] reset_scheme %s, inside %s", tostring(loading), tostring(self.state))
end
function action_psy_antenna:deactivate()
	if self.state == state_inside then
		self:zone_leave()
	end
end
function action_psy_antenna:update( delta )
	local actor = db.actor

	if xr_logic.try_switch_to_another_section( self.object, self.st, actor ) then
		return
	end
	self:switch_state( actor )
end
function action_psy_antenna:switch_state( actor )
	if self.state ~= state_inside then
		if self.object:inside( actor:position() ) then
			self:zone_enter()
			return
		end
	else		
		if not self.object:inside( actor:position() ) then
			self:zone_leave()
			return
		end
	end
end
function action_psy_antenna:zone_enter()
--'	printf("[psy_antenna] zone_enter")
	self.state = state_inside

	psy_antenna.sound_intensity_base = psy_antenna.sound_intensity_base + self.st.intensity
	psy_antenna.mute_sound_threshold = psy_antenna.mute_sound_threshold + self.st.mute_sound_threshold
	psy_antenna.hit_intensity  = psy_antenna.hit_intensity  + self.st.hit_intensity
	psy_antenna.phantom_spawn_probability = psy_antenna.phantom_spawn_probability + self.st.phantom_prob

--'	printf("[psy_antenna] zone_enter. hit_intensity=%s", tostring(psy_antenna.hit_intensity))

	if self.st.postprocess == "nil" then
		return
	end

	if psy_antenna.postprocess[self.st.postprocess] == nil then
		psy_antenna.postprocess_count = psy_antenna.postprocess_count + 1
		psy_antenna.postprocess[self.st.postprocess] = { intensity_base = 0, intensity = 0, idx = 1500+psy_antenna.postprocess_count}
		level.add_pp_effector(self.st.postprocess, psy_antenna.postprocess[self.st.postprocess].idx, true)
		level.set_pp_effector_factor(psy_antenna.postprocess[self.st.postprocess].idx, 0.01)
	end
	psy_antenna.postprocess[self.st.postprocess].intensity_base = psy_antenna.postprocess[self.st.postprocess].intensity_base + self.st.intensity		
end
function action_psy_antenna:zone_leave()
--'		printf("[psy_antenna] zone_leave. hit_intensity=%s", tostring(psy_antenna.hit_intensity))
	self.state = state_outside

	psy_antenna.sound_intensity_base = psy_antenna.sound_intensity_base - self.st.intensity
	psy_antenna.mute_sound_threshold = psy_antenna.mute_sound_threshold - self.st.mute_sound_threshold
	psy_antenna.hit_intensity  = psy_antenna.hit_intensity  - self.st.hit_intensity

	psy_antenna.phantom_spawn_probability = psy_antenna.phantom_spawn_probability - self.st.phantom_prob
	
--'	printf("[psy_antenna] zone_leave. hit_intensity=%s, minus=%s", tostring(psy_antenna.hit_intensity), tostring(self.st.hit_intensity))

	if self.st.postprocess == "nil" then
		return
	end

	if psy_antenna.postprocess[self.st.postprocess] ~= nil then
		psy_antenna.postprocess[self.st.postprocess].intensity_base = psy_antenna.postprocess[self.st.postprocess].intensity_base - self.st.intensity
	end
end
function action_psy_antenna:save()
	--'xr_logic.pstor_store( self.object, "inside", self.state )
end

---------------------------------------------------------------------------------------------------------------------
function save( p )
	if psy_antenna and not utils.level_changing() then
		p:w_bool( true )

		psy_antenna:save( p )
	else
		p:w_bool( false )
	end
end

function load(p)
	local b = p:r_bool()

	if b then
		if psy_antenna then
			abort("sr_psy_antenna.psy_antenna already exists!")
		end

		psy_antenna = PsyAntenna()
		psy_antenna:construct()

		psy_antenna:load(p)
	end
end

function add_to_binder(npc, ini, scheme, section, storage)
	printf("DEBUG: add_to_binder: scheme='%s', section='%s'", scheme, section)

	if not psy_antenna then
		psy_antenna = PsyAntenna()
		psy_antenna:construct()
	end

	local new_action = action_psy_antenna(npc, storage)

	-- Зарегистрировать все actions, в которых должен быть вызван метод reset_scheme при изменении настроек схемы:
	xr_logic.subscribe_action_for_events(npc, storage, new_action)
end

function set_scheme(npc, ini, scheme, section, gulag_name)
	local st = xr_logic.assign_storage_and_bind(npc, ini, scheme, section)
	st.logic = xr_logic.cfg_get_switch_conditions(ini, section, npc)

	st.intensity     = utils.cfg_get_number(ini, section, "eff_intensity", npc, true) * 0.01
	st.postprocess	 = utils.cfg_get_string(ini, section, "postprocess", npc, false, "", "psy_antenna.ppe")
	
	st.hit_intensity = utils.cfg_get_number(ini, section, "hit_intensity", npc, true) * 0.01
	st.phantom_prob  = utils.cfg_get_number(ini, section, "phantom_prob",  npc, false, 0) * 0.01
	
	st.mute_sound_threshold   = utils.cfg_get_number(ini, section, "mute_sound_threshold",  npc, false, 0)
end
