--' Исправление потери содержимого тайников by Bardak
--' Динамическая выдача содержимого тайников взята из мода Alter Zone by Xiani, оригинального автора не знаю
--' Функция проверки, именной ли тайник, by AMK Team
--' Фикс от вылетов с модом "Патроны на поясе" by Real Wolf
--' Адаптация всего к моду Lost World Requital by Real Wolf

local treasure_manager = nil

function parse_spawns(line)
	if line == nil then
		return {}
	end

	--' если default-ов больше, чем значений в ini, то забить недостающие последним значением из ini
	local t = se_respawn.parse_names(line)
	local n = table.getn(t)
		
	local ret_table = {}
	local k = 1

	while k <= n do
		local spawn = {}
		spawn.section = t[k]
		-- Проверяем что это не последняя запись
		if t[k+1] ~= nil then
			local p = tonumber(t[k+1])
			-- проверяем что вторым числом задана вероятность, а не другая секция спавну
			if p then
				-- забиваем число
				spawn.prob = p
				k = k + 2
			else
				-- забиваем дефолт 1
				spawn.prob = 1
				k = k + 1
			end
		else
			spawn.prob = 1
			k = k + 1
		end
		table.insert(ret_table, spawn)
	end
	return ret_table
end


class "CTreasure"
function CTreasure:__init()
	--' На конструкторе вычитываем LTX и создаем заготовки тайников.
	self.ini = ini_file("misc\\treasure_manager.ltx")

	--' Итерируемся по всем настройкам
	if not self.ini:section_exist("list") then
		abort("There is no section [list] in treasure_manager.ltx")
	end
	local n = self.ini:line_count("list")
	local id, value = "",""

	--' начальная установка
	self.treasure_info = {}

	for i=0,n-1 do
		result, id, value = self.ini:r_line("list",i,"","")
		
		self.treasure_info[id] = {}

		self.treasure_info[id].target		= utils.cfg_get_number(self.ini, id, "target", nil, true)
		self.treasure_info[id].name		= utils.cfg_get_string(self.ini, id, "name", nil, true, "")
		self.treasure_info[id].description	= utils.cfg_get_string(self.ini, id, "description", nil, true, "")
		self.treasure_info[id].items		= parse_spawns(utils.cfg_get_string(self.ini, id, "items", nil, true, ""))

		local community	= parse_names(utils.cfg_get_string(self.ini, id, "community", nil, false, "", "stalker, bandit, dolg, freedom"))
		self.treasure_info[id].community = {}
		for k,v in pairs(community) do
			self.treasure_info[id].community[v] = true
		end


		if self.treasure_info[id].items == nil then
			abort("cant find 'items' in %s", id)
		end

		self.treasure_info[id].condlist	= xr_logic.parse_condlist(db.actor, "treasure_manager", "condlist", utils.cfg_get_string(self.ini, id, "condlist", nil, false, "", ""))
		
		--' Отметим тайник как новый
		self.treasure_info[id].active = false
		self.treasure_info[id].done = false

	end

	--' Вспомогательные таблицы для облегчения поиска
	self.treasure_by_target = {}
	for k,v in pairs(self.treasure_info) do
		self.treasure_by_target[v.target] = k	
	end
	
	self.loss_workaround_queue = {}
end
--' Юзание инициатора (возможность выдать тайник)
function CTreasure:use(npc)
	--' Проверим, не вызывались ли мы уже для этого объекта. 
	local se_obj = alife():object(npc:id())
	if se_obj.treasure_processed == true then
		return
	end
	se_obj.treasure_processed = true

	if (npc and db.actor) then
		lwc.lootmoney(npc)
	end
	
	--' У военных тайник получить нельзя.
	if npc:character_community() == "military" then 
		return
	end

	--' Чем больше значение, тем меньше вероятность.
	local rarets = 80
	if math.random(100) < rarets then 
		return
	end

	--' Нужно рандомно выбрать один из тайников.
	local avail = {}
	local tr_sum = 0
	for k,v in pairs(self.treasure_info) do
		if v.done == false then
			local treasure_prob = xr_logic.pick_section_from_condlist(db.actor, npc, v.condlist)

			if treasure_prob == "" or treasure_prob == nil then
				treasure_prob = 0
			end

			if tonumber(treasure_prob) >= 0 and
			v.community[npc:character_community()] == true and
			v.active == false
			then
				if tonumber(treasure_prob) == 100 then
					self:give_treasure(k)
					return
				else
					table.insert(avail, {k = k, prob = treasure_prob})
					tr_sum = tr_sum + treasure_prob
				end
			end
		end
	end

	if tr_sum == 0 then
		return
	end

	local tr_w = math.random(tr_sum)
	for k,v in pairs(avail) do
		tr_w = tr_w - v.prob
		if tr_w <= 0 then
			--' Выдать тайник
			self:give_treasure(v.k, npc)
			break
		end
	end
end

--' Сохранение
function CTreasure:check()
	--' Проверка тайников
	for k,v in pairs(self.treasure_info) do
		--' Выдать тайник
		self:give_treasure(k)
	end
end

--' Выдача тайника
function CTreasure:give_treasure(k, npc)
	local v = self.treasure_info[k]
	local sim = alife()
	local obj = sim:story_object(v.target)
	if obj ~= nil then
		news_manager.send_treasure(v.name)

		--' Пометить на карте		
		local text = "%c[255,238,155,23]"..game.translate_string(v.name).."\\n".."%c[default]"..game.translate_string(v.description)
		level.map_add_object_spot_ser(obj.id, "treasure", text)

		--' Сгенерить вещи
		--'
		--' Из-за какого-то дефекта в xrGame.dll тайник потеряет
		--' сгенерированное содержимое в следующей ситуации:
		--'  1) получена ссылка на тайник, который находится в online;
		--'  2) игрок сохраняется и загружается;
		--'  3) между 1 и 2 не было переводов тайника в offline хотя бы раз.
		--' Для предотвращения этого создаём вещи у актёра, запоминаем
		--' идентификаторы, а в вызове actor_binder:update() переносим их
		--' в нужный ящик.

		local item_ids = {}
		local pos, lvid, gvid, pid, new_obj
		local need_workaround = level.object_by_id(obj.id) ~= nil

		if need_workaround then
			pos = db.actor:position()
			lvid = db.actor:level_vertex_id()
			gvid = db.actor:game_vertex_id()
			pid = db.actor:id()
		else
			pos = obj.position
			lvid = obj.m_level_vertex_id
			gvid = obj.m_game_vertex_id
			pid = obj.id
		end


		--' Проверка, не именной ли тайник, если нет, то заменим содержимое.
		if self:named_treasure(v) == false then	
			local ran_ini = ini_file("misc\\random_treasure.ltx")
			local rand = math.random(1, 100)
			local items_section = nil
			local vanilla_prob = utils.cfg_get_number(ran_ini, "random_treasure_settings", "vanilla_probability", nil, true)
			local theme_prob = utils.cfg_get_number(ran_ini, "random_treasure_settings", "theme_probability", nil, true)

			if rand > theme_prob + vanilla_prob then
                        	local glevel = game_graph():vertex(obj.m_game_vertex_id):level_id()
				items_section = sim:level_name(glevel) .. "_items"
			elseif rand > vanilla_prob then
				items_section = "theme_items"
                	end

			if items_section ~= nil and ran_ini:section_exist(items_section) then
				local num_lines = ran_ini:line_count(items_section)
				if num_lines > 0 then
					local line_idx = math.random(0, num_lines - 1)
					local res, key, item_list = ran_ini:r_line(items_section, line_idx, "", "")
					v.items = parse_spawns(item_list)
				end
			end
		end

		for kk,vv in pairs(v.items) do
			if ammo_section[vv.section] == true then
				local left, box_size = vv.prob, system_ini():r_u32(vv.section, "box_size")
				while left > box_size do
					new_obj = sim:create_ammo(vv.section, pos, lvid, gvid, pid, box_size)
					table.insert(item_ids, new_obj.id)
					left = left - box_size
				end
				if left > 0 then
					new_obj = sim:create_ammo(vv.section, pos, lvid, gvid, pid, left)
					table.insert(item_ids, new_obj.id)
				end
			else
				for i=1,vv.prob do
					new_obj = sim:create(vv.section, pos, lvid, gvid, pid)
					table.insert(item_ids, new_obj.id)
				end
			end
		end

		if need_workaround then
			self.loss_workaround_queue[v.target] = item_ids
                        lwc.ammo_tr_fix = true
		end
		
		--' Пометим тайник как выданный
		self.treasure_info[k].active = true
		self.treasure_info[k].done = true
	end
end

--' Перенос содержимого для предотвращения возможной потери.
function CTreasure:loss_workaround()
	qqq("transfering items start")
	for k,v in pairs(self.loss_workaround_queue) do
		box = level_object_by_sid(k)
		if box == nil then
			abort("inv box nil")
		end
		for kk,vv in pairs(v) do
			local lobj = level.object_by_id(vv)
			if lobj then
				qqq("transfering items: "..lobj:section())
				db.actor:transfer_item(lobj, box)
			end
		end
	end
	self.loss_workaround_queue = {}
	lwc.ammo_tr_fix = false
end

--' Снимаем отметку с тайника.
function CTreasure:treasure_empty(box, box_story_id)
	local k = self.treasure_by_target[box_story_id]

	if k == nil or self.treasure_info[k] == nil then 
		return
	end

	self.treasure_info[k].active = false
	level.map_remove_object_spot(box:id(), "treasure")
end

--' Сохранение.
function CTreasure:save(p)
	--' Сохраняем размер таблицы
	local size = 0
	for k,v in pairs(self.treasure_info) do
		size = size + 1
	end
	p:w_u16(size)
	for k,v in pairs(self.treasure_info) do
		p:w_u16(v.target)
		p:w_bool(v.active)
		p:w_bool(v.done)
	end
end

--' Загрузка.
function CTreasure:load(p)
	local t = p:r_u16()
	for i = 1,t do
		local k = self.treasure_by_target[p:r_u16()]
		self.treasure_info[k].active = p:r_bool()
		self.treasure_info[k].done = p:r_bool()
	end
end

--' Проверка, является ли тайник именным, если нет, его содержимое не меняется.
function CTreasure:named_treasure(v)
 	if v.name == "agr_secret_0000_name" --Тайник Серого
   	or v.name == "bar_secret_0011_name" --Тайник Арни
   	or v.name == "esc_secret_0013_name" --Тайник Ворпала
   	or v.name == "gar_secret_0002_name" --Вещи Гризли
   	or v.name == "gar_secret_0011_name" --Склад группы Стрелка
   	or v.name == "gar_secret_0021_name" --Рюкзак Дохляка
   	or v.name == "pri_secret_0003_name" --Хабар Клыка
   	or v.name == "pri_secret_0004_name" --Запасы лидера Свободы
   	or v.name == "rad_secret_0000_name" --Клад Угрюмого
   	or v.name == "rad_secret_0004_name" --Запас группы отчаянных
   	or v.name == "ros_secret_0015_name" --Тайник Счастливчика
   	or v.name == "ros_secret_0018_name" --Ящик с медикаментами
   	or v.name == "val_secret_0028_name" --Тайник Борова
   	or v.name == "x18_secret_0000_name" --Тайник Копченого
   	or v.name == "yan_secret_0011_name" --Тайник учёного
 	then
   		return true
 	else
   		return false
 	end    
end

function get_treasure_manager()
	if treasure_manager == nil then
		treasure_manager = CTreasure()
	end
	return treasure_manager
end

function take_item_from_box(box, box_story_id)
	if box:is_inv_box_empty() == true then
		get_treasure_manager():treasure_empty(box, box_story_id)
	end
end

function save(p)
	get_treasure_manager():save(p)
end
function load(p)
	get_treasure_manager():load(p)
end
function clear_treasure_manager()
	treasure_manager = nil
end