--=======================================================--
-- Скрипт cна                                            --
-- Авторы: Mechanic, Shoker, Z.E.N., Ab@dDon             --
-- Может кого не указал, но скрипт брал из Zenoban Mod'а --
--=======================================================--

local table_sleep_zone =
	{
	-- Кордон:
	
    -- Матрас на чердаке в доме возле Лиса
    {"l01_escape", "m", nil, 140.09, 3.5, 336.91, 444961, 232, nil}, 

    -- Матрасы в подвале в деревне новичков
    {"l01_escape", "m", "no_icon_1", -210.73, -22.9, -122.30, 42632, 59, nil},
    {"l01_escape", "m", "icon_1", -210.34, -22.9, -125.75, 42632, 59, nil}, 

	-- Свалка:
	
    -- Матрас в ангаре возле группы Серого
    {"l02_garbage", "m", nil, -70.34, -1.60, 19.88, 131025, 329, nil},

    -- Матрас в вагончике возле заставы долга
    {"l02_garbage", "m", nil, 46.73, 1.56, 243.93, 218601, 359, nil},

	-- Агропром:
	
	-- Матрасы в конце ж/д тунеля с аномалиями
	{"l03_agroprom", "m", "icon_2", 254.96, 0.15, 74.44, 428649, 498, nil},
	{"l03_agroprom", "m", "no_icon_2", 257.46, 0.15, 73.50, 429416, 498, nil},
	
	-- Подземелья Агропрома:
	
    -- Матрас в тайнике Стрелка
	{"l03u_agr_underground", "b", nil, -69.74, -6.87, -74.45, 3439, 717},
    {"l03u_agr_underground", "m", nil, -69.74, -6.35, -74.45, 3439, 717, "allways"},
	
	-- Бар:
	
    -- Матрасы на базе Долга
    {"l05_bar", "m", "icon_3", 232.28, -4.9, 131.44, 59301, 1200, nil},
    {"l05_bar", "m", "no_icon_3", 232.08, -4.9, 135.36, 59301, 1200, nil},
	
	-- Матрас в комнате Бармена (за барной стойкой)
	{"l05_bar", "b", nil, 115.85, -5.3, 12.0, 33756, 1239},
	{"l05_bar", "m", nil, 115.85, -4.6, 12.0, 33756, 1239, "allways"},

	-- Янтарь:
	
    -- Матрасы в бункере ученых на Янтаре
    {"l08_yantar", "m", "no_icon_4", 30.36, -10.98, -280.2, 54978, 1480, "allways"},
    {"l08_yantar", "m", "icon_4", 27.68, -10.98, -280.2, 54978, 1480, "allways"},

	-- Армейские склады:

    -- Матрасы на базе Свободы в казармах
    {"l07_military", "m", "no_icon_5", 14.07, -6.82, 13.1, 319840, 1582, nil},
    {"l07_military", "m", "icon_5", 11.18, -6.82, 13.27, 317158, 1582, nil},

    -- Матрас в комнате напротив Скряги
    {"l07_military", "m", nil, -25.39, -6.82, -26.74, 281271, 1593, nil}
	
	--[[
	-- Это места, где можно поспать (требует начало новой игры)
	{"1", "2", "3", 4, 5, 6, 7, 8, "9", "10"}
	1 - название уровня
	2 - m или b (m - невидимый объект, при клике на который мы спим, b - кровать, для декорации)
	3 - если написать что-нибудь в "" и !добавить! эту запись в таблицу ниже, то на глобальной/мини карте для этого матраса не будет иконки сна (только для невидимого объекта)
	4,5,6 - координаты x,y,z соответственно
	7,8 - level_vertex и game_vertex соответственно
	9 - если стоит "allways" значит можно спать независимо от наличия рядом врагов (только для невидимого объекта)
	]]
	}
 
-- таблица матрасов, при использовании которых никогда не ставить метки
local table_non_spot_sleep_zone =
	{
	["no_icon_1"]	= true,
	["no_icon_2"]	= true,
	["no_icon_3"]	= true,
	["no_icon_4"]	= true,
	["no_icon_5"]	= true
	}

local mattress_id = {}
local mattress_count = 0
local mattress_processed = 0
local first_scaling
local left_scaling
local last_health
local ENEMY_RADIUS = 15 -- радиус врагов

-- Обычный апдейт
function update(delta)

	local mcnt = load_variable("matras_cnt", 0)

	for i=1, mcnt do
		local obj = level.object_by_id(load_variable(i.."_matras", 65000))
		if obj then
			---------------------
			--get_console():execute("THIS_IS_SPARTA!")
			--get_console():execute(obj:name())
			local bone_name = get_ltx(obj:section(), "fixed_bone", "str")
			local ph_shell = obj:get_physics_shell()
			if not ph_shell then
				--get_console():execute("NO_PH_SHELL")
				return
			end
			local ph_element = ph_shell:get_element_by_bone_name(bone_name)
			if ph_element:is_fixed() then
				--get_console():execute("ART_FIXED")
			else
				--get_console():execute("FIXING_OBJECT")
				ph_element:fix()
			end
			---------------------
		end
	end

	if db.actor == nil  then
		return 
	end

	if not db.actor:alive() then
		local hud = get_hud()

		local custom_static = hud:GetCustomStatic("hud_mattress")
		if custom_static ~= nil then
			hud:RemoveCustomStatic("hud_mattress")
		end
	end

	local actor = db.actor
	local pos = actor:position()
	local zn = actor_in_sleep_zone(pos)

	if load_variable("bed_spawned", false) == false then
		spawn_mattress()
		save_variable("bed_spawned", true)
	end

	if zn == "m" then
		local hud = get_hud()
		local custom_static = hud:GetCustomStatic("hud_mattress")
		if custom_static == nil then
			hud:AddCustomStatic("hud_mattress", true)
		end
	else
		local hud = get_hud()

		local custom_static = hud:GetCustomStatic("hud_mattress")
		if custom_static ~= nil then
			hud:RemoveCustomStatic("hud_mattress")
		end
	end
end

-- Update матраса(вешаем колбек если ещё не повесили)
function mattress_update(obj)
	if mattress_count ~= mattress_processed then
		for kk,vv in pairs(mattress_id) do
			if vv[1] == obj:id() and vv[2] == false then
				vv[2] = true
				obj:set_callback(callback.use_object, this.mattress_use)
				obj:set_tip_text("tip_sleep")
				mattress_processed = mattress_processed + 1
			end
		end
	end
end

-- Отлавливаем спавн матрасов и запоминаем их id
function mattress_spawn(obj)
	mattress_count = mattress_count + 1
	local m = {obj:id(), false}
	table.insert(mattress_id, m)
end

-- Юзаем матрас
function mattress_use(obj, who)
	if test_for_can_sleep(obj) then
	-------------------------------
		local hud = get_hud()
		local spwn = ui_sleep.sleep(hud)
		local frst
		local scnd
		level.start_stop_menu(spwn,true)
		
		-- можно ли ставить спот
		local valid = true
			
		for name, val in pairs(db.storage[db.actor:id()].pstor) do
			if table_non_spot_sleep_zone[name] and load_variable(name, -1) == obj:id() then
				valid = false
			end
		end
		if valid then
			if not level.map_add_object_spot_ser(obj:id(), "sleep_pointer", "Место для сна") then
				level.map_add_object_spot_ser(obj:id(), "sleep_pointer", "Место для сна")
			end
		end
		
		for i = 1, 5 do
			frst = load_variable("no_icon_"..i, -1)
			scnd = load_variable("icon_"..i, -1)
			if frst == obj:id() and not level.map_add_object_spot_ser(scnd, "sleep_pointer", "Место для сна") then
			level.map_add_object_spot_ser(scnd, "sleep_pointer", "Место для сна")
			end
		end
	-------------------------------
	end
end
	
-- Игрок в зоне сна
function actor_in_sleep_zone(pos)
	for i,z in pairs(table_sleep_zone) do
		if z[1] == level.name() and z[2] == "m" and actor_in_zone(z[4], z[5], z[6], 2, pos) then
			return z[2]
		end
	end
	return ""
end

-- Спавним всё, что нужно
function spawn_mattress()
	for kk,vv in pairs(table_sleep_zone) do
		if vv[2] == "m" then
			local obj = alife():create("mattress", vector():set(vv[4], vv[5], vv[6]), vv[7], vv[8])
		 	if vv[9] == "allways" then
				save_variable(obj:name().."_allways_sleep", true)
			end
			if vv[3] then
				save_variable(vv[3], obj.id)
			end
		end
		if vv[2] == "b" then
			local obj = alife():create("bed_matras", vector():set(vv[4], vv[5], vv[6]), vv[7], vv[8])
			local i = load_variable("matras_cnt", 1)
			save_variable(i.."_matras", obj.id)
			save_variable("matras_cnt", i+1)
		end
	end
end

-- Проверка, можно ли спать
function test_for_can_sleep(matras)
	local allways = load_variable(matras:name().."_allways_sleep", false)

	-- проверка на врагов
	if not allways then
		for a=1,65535 do
			local obj = level.object_by_id(a)
			if obj then
				if ((IsStalker(obj) and get_npc_relation(obj,db.actor)=="enemy") or IsMonster(obj)) and obj:position():distance_to(db.actor:position())<ENEMY_RADIUS then
					news_manager.send_tip(db.actor, "sleep_warning_enemy", nil, "default", 5000)
					return false
				end
			end
		end
	end

	-- проверка на радиацию
	if db.actor.radiation > 0 then
		news_manager.send_tip(db.actor, "sleep_warning_radiation", nil, "default", 5000)
		return false
	end

	return true
end

-- Спать
function sleep(h)
	sleep_manager.start_sleep(h)
end

function start_sleep(scale)
	local sleep_ltx = ini_file ("scripts\\actorsleep.ltx")
	schemes["ar_sleep"] = "ar_sleep"
	ar_sleep.set_scheme(db.actor, sleep_ltx, "ar_sleep", logic)
	db.actor:stop_talk()
	db.actor:hide_weapon()
	level.disable_input()
	sleep_manager.starter (scale)
end

function starter(scale)
	last_health = db.actor.health	
	left_scaling = scale
	first_scaling = scale

	_G.mus_vol = get_console():get_float("snd_volume_music")
	_G.amb_vol = get_console():get_float("snd_volume_eff")
	get_console():execute("snd_volume_music 0")
	get_console():execute("snd_volume_eff 0")

	game.start_tutorial("time_scaling")
	level.set_time_factor(3460)
end

-- Вызывается каждый час сна
function stop_scaling()
	left_scaling = left_scaling - 1
	if left_scaling + 1 == first_scaling and last_health > db.actor.health then
		sleep_manager.dreamer()
		news_manager.send_tip(db.actor, "sleep_warning_satiety_or_bleeding", nil, "default", 5000)
	elseif left_scaling > 0 and last_health <= db.actor.health  then
		sleep_manager.hp_corrector(2)
		last_health = db.actor.health
	  	game.start_tutorial("time_scaling")
	elseif left_scaling > 0 and last_health > db.actor.health  then
		sleep_manager.hp_corrector(12)
		last_health = db.actor.health
		game.start_tutorial("time_scaling")
	elseif left_scaling <= 0 and last_health > db.actor.health  then
		sleep_manager.hp_corrector(12)
		db.actor.power = 1
		last_health = db.actor.health
		sleep_manager.dreamer()
	elseif left_scaling <= 0 and last_health <= db.actor.health then
		db.actor.power = 1
		sleep_manager.dreamer()
	end	
end

function dreamer()
	level.set_time_factor(system_ini():r_float("alife","time_factor"))
	if sleep_manager.is_sleep_active() then
		get_console():execute("snd_volume_music "..tostring(_G.mus_vol))
		get_console():execute("snd_volume_eff "..tostring(_G.amb_vol))
		_G.amb_vol = 0
		_G.mus_vol = 0
		sleep_manager.stopper()
	end
end

function stopper()
	level.set_time_factor(system_ini():r_float("alife","time_factor"))
	ar_sleep.disable_scheme()
	if not db.actor:alive() then
		sleep_manager.onliner()
	else 
		db.actor:restore_weapon()
	end
	level.enable_input()	
end

-- Игрок умер во время сна 
function onliner()
	get_console():execute("snd_volume_music "..tostring(_G.mus_vol))
	get_console():execute("snd_volume_eff "..tostring(_G.amb_vol))
	_G.amb_vol = 0
	_G.mus_vol = 0
end

function hp_corrector(fr)
	db.actor.health = fr/12
end

function is_sleep_active()
	if db.storage[db.actor:id()].active_scheme == "ar_sleep" then 
		return true 
	end
	return false
end

-----------------------------------------------------
-------------- Вспомогательные функции --------------
-----------------------------------------------------

-- Записываем переменную
function save_variable(variable_name, value)
	xr_logic.pstor_store(db.actor, variable_name, value)
end

-- Загружаем переменную
function load_variable(variable_name, value_if_not_found)
	return xr_logic.pstor_retrieve(db.actor, variable_name, value_if_not_found)
end

-- Удаляем переменную
function del_variable(variable_name)
	if db.storage[db.actor:id()].pstor[variable_name] then
		db.storage[db.actor:id()].pstor[variable_name] = nil
	end
end

-- Проверка на наличие актора в области
function actor_in_zone(x, y, z, r, pos)
	if math.pow(x-pos.x,2) + math.pow(y-pos.y,2) + math.pow(z-pos.z,2) <= math.pow(r,2) then
		return true
	else
		return false
	end
end

-- Узнаём отношения (взято из AMK)
function get_npc_relation(obj,target)
	local rel = obj:relation(target)
	local relation
	if rel == game_object.neutral then
		relation = "neutral"
	elseif rel == game_object.friend then
		relation = "friend"
	elseif rel == game_object.enemy then
		relation = "enemy"
	else
		return false
	end
	return relation
end

-- Получаем строку из LTX файла
function get_ltx(l_name,l_string,type)
	if system_ini():section_exist(l_name) and system_ini():line_exist(l_name, l_string) then
		if type=="bol" then
			return system_ini():r_bool(l_name, l_string)
		end
		if type=="num" then   -- целое число
			return tonumber(system_ini():r_u32(l_name, l_string))
		end
		if type=="num_float" then   -- число с запятой
			return tonumber(system_ini():r_float(l_name, l_string))
		end
		if type=="str" then
			return tostring(system_ini():r_string(l_name, l_string))
		end
		if type==nil then
			return system_ini():r_u32(l_name, l_string)
		end
	end
	return nil
end