--/amk_rel_1/
----------------
local npc_spawner={}	--служебный массив, работает автоматически - не трогать шаловливыми русками

local timers={}		--хранит реал-тайм таймеры
local g_timers={}	--хранит таймеры в игровом времени
local markers={}	--хранит маркеры на карте
local x_objs={}     --хранит ИДшники объектов
local timer_trigger=nil
local StartTime

--переменные для типсов
pda_news = xr_sound.get_safe_sound_object([[device\pda\pda_news]])
pda_tips = xr_sound.get_safe_sound_object([[device\pda\pda_tip]])
pda_task = xr_sound.get_safe_sound_object([[device\pda\pda_objective]])

tips_icons = {
	default  = { 0, 658},
	trader	 = { 332, 893},
	dolg     = { 0, 658},
	freedom  = { 0, 658},
	ecolog   = { 498, 0},
	arena    = { 332, 141},
	stalker  = { 0, 658},
	krot     = { 332, 47},
	barman   = { 332, 235},
	wolf	 = { 332, 940},
	o_soznanie = { 498, 893},
	monolith = { 0, 658},
	saharov  = { 332, 470},
      prizrak  = { 0, 658},
      killer   = { 0, 658},
	death     = { 0, 752}
}
----------------
local bufferedmessages={}
function mylog(msg)

  if msg==nil then
    return 
  end
  if db and db.actor then
    if bufferedmessages then
      for k,v in ipairs(bufferedmessages) do
        db.actor:give_game_news(v, "ui\\ui_iconsTotal", Frect():set(0,658,83,47), 0, 15000)
      end
      bufferedmessages=nil
    end
  db.actor:give_game_news(msg, "ui\\ui_iconsTotal", Frect():set(0,658,83,47), 0, 15000)
  else
    if bufferedmessages then
      table.insert(bufferedmessages,msg)
    end
  end
  
  if get_console() then
    --get_console():execute("load "..msg)
    get_console():execute("flush")
  end
end

--показываем типс
function send_tip(news_text, header, timeout, showtime, sender, sound)
	if news_text==nil then return end
	if header==nil then header=game.translate_string("st_tip") end
	if timeout == nil then timeout = 0 end
	if showtime == nil then showtime = 5 end
	
	local player
	if sound=="news" then
		player=pda_news
	elseif sound=="task" then
		player=pda_task
	else
		player=pda_tips
	end		
	
	--' Играем дефолтный звук
	player:play(db.actor, timeout, sound_object.s2d)
	
	if sender == nil then
		sender = "default"
	end
	local x = tips_icons[sender][1]
	local y = tips_icons[sender][2]
	
	local news_text = "%c[255,160,160,160]"..header.."\\n".."%c[default]"..news_text
	db.actor:give_game_news(news_text, "ui\\ui_iconsTotal", Frect():set(x,y,83,47), timeout*1000, showtime*1000)
	return true
end

function add_spot_on_map(obj_id,type,text)
	--возможные типы type смотри в ui\map_spots.xml	
	if obj_id then
		if text==nil then text=" " end
		
		level.map_add_object_spot(obj_id, type, text)
		save_variable("x_marker_type_"..obj_id, type)
		save_variable("x_marker_text_"..obj_id, text)
	end
end

function remove_spot_from_map(obj_id,type)
	if obj_id and level.map_has_object_spot(obj_id, type)~= 0 then
		level.map_remove_object_spot(obj_id, type)
		del_variable("x_marker_type_"..obj_id)
		del_variable("x_marker_text_"..obj_id)
	end
end

--старт таймера в реальном времени
function start_timer(name,delay,action,func)
	if not delay then
		return false
	end
	
	if not action then
		action = ""
	end

    	local time = game.time() --time in seconds since 1970
	local a=1
	while db.storage[db.actor:id()].pstor["x_timer_"..a] do
		a=a+1
		if a>100 then 
			return false 
		end
	end

	save_variable("x_timer_"..a, name)
	save_variable("x_timer_"..a.."_delay", time+delay*10000)
	save_variable("x_timer_"..a.."_params", action)
	save_variable("x_timer_"..a.."_function", func)
	
	return true
end

--старт таймера в игровом времени
function g_start_timer(name,delay_d,delay_h,delay_m,action, func)
  	local t = game.get_game_time()
  	local time = math.floor(t:diffSec(StartTime) / 60)  --time in game minutes
	if delay_d==nil or delay_h==nil or delay_m==nil then
		return false
	end
	
	if action==nil then
		action = ""
	end

	local a=1
	while db.storage[db.actor:id()].pstor["x_gtimer_"..a] do
		a=a+1
		if a>100 then 
			return false 
		end
	end

	save_variable("x_gtimer_"..a, name)
	save_variable("x_gtimer_"..a.."_delay", time+delay_d*60*24+delay_h*60+delay_m)
	save_variable("x_gtimer_"..a.."_params", action)
	save_variable("x_gtimer_"..a.."_function", func)
	return true



end

--проверка таймеров, использует 3 следующие за ним функции для выбора действия
function check_timers()
	local tmp
	for a=1,100,1 do
		tmp=load_variable("x_timer_"..a,nil)
		if tmp~=nil then
			__timer_found(a)	
		end
	end
	
	for a=1,100,1 do
		tmp=load_variable("x_gtimer_"..a,nil)
		if tmp~=nil then
			__g_timer_found(a)	
		end
	end
end
function __timer_found(idx)
    	local time = game.time() --time in seconds since 1970
	local name,params
	if load_variable("x_timer_"..idx.."_delay", nil) <= time then
		name = load_variable("x_timer_"..idx, nil)
		params = load_variable("x_timer_"..idx.."_params", nil)
		func = load_variable("x_timer_"..idx.."_function", nil)

		if func ~= nil then
			local oFunction = loadstring(func)
			oFunction()
		end

		del_variable("x_timer_"..idx)
		del_variable("x_timer_"..idx.."_delay")
		del_variable("x_timer_"..idx.."_params")
		del_variable("x_timer_"..idx.."_function")

		__do_timer_action(name,params)
		return true
	end
	return false
end
function __g_timer_found(idx)
  	local t = game.get_game_time()
  	local gtime = math.floor(t:diffSec(StartTime) / 60)  --time in game minutes
	local name,params
	if load_variable("x_gtimer_"..idx.."_delay", nil)<=gtime then
		name=load_variable("x_gtimer_"..idx, nil)
		params=load_variable("x_gtimer_"..idx.."_params", nil)
		func = load_variable("x_gtimer_"..idx.."_function", nil)

		if func ~= nil then
			local oFunction = loadstring(func)
			oFunction()
		end

		del_variable("x_gtimer_"..idx)
		del_variable("x_gtimer_"..idx.."_delay")
		del_variable("x_gtimer_"..idx.."_params")
		__do_timer_action(name,params)
		return true
	end
	return false
end
function __do_timer_action(select_string,params_string)
	--[[
	здесь описываем вызовы, оформялять в виде

	if select_string=="название условия" then
		<вызов сторонних функций>
		-- можно передавать npc как параметр
	end
	
	]]
--user area
	
	ogsm_funcs.do_timer_action(select_string,params_string)
	
	if select_string=="timer_doktor" then
		dialogs_swamp.timer_doktor_end()
    end
	
	if select_string=="spawn_major" then
		dialogs_predbannik.spawner_major()
    end

	if select_string=="return_zona" then
		db.actor:give_info_portion("pre_soldier_dead")
    end
	
	if select_string=="optika" then
		db.actor:give_info_portion("optika_complete")
    end
	
	if select_string=="prodoljenie" then
		db.actor:give_info_portion("prodoljenie_2")
    end	
	
	if select_string=="varjag_gar_offline" then
		bar_dialogs.varjag_gar_offline()
    end

	if select_string=="sms_varjag_4" then
		dialogs_pripyat.sms_varjag_4()
    end	
	
	if select_string=="sms_varjag_3" then
		dialogs_pripyat.sms_varjag_3()
    end
	
	if select_string=="sms_varjag_2" then
		dialogs_pripyat.sms_varjag_2()
    end

	if select_string=="varjag_bar_offline" then
		bar_dialogs.varjag_bar_offline()
    end	
	
	if select_string=="varjag_agr_offline_2" then
		dialogs_agroprom.varjag_agr_offline_2()
    end	
	
	if select_string=="varjag_agr_offline" then
		dialogs_agroprom.varjag_agr_offline()
    end
	
	if select_string=="kruglov_offline" then
		dialogs_deadcity.kruglov_offline()
    end	

	if select_string=="varjag_esc_offline" then
		bar_dialogs.varjag_esc_offline()
    end
	
	if select_string=="varjag_offline" then
		bar_dialogs.varjag_offline()
    end

	if select_string=="teleport_kanjon" then
		teleports.teleport_pripjat_kanjon()
		db.actor:give_info_portion("pred_dialog")
    end
	
	if select_string=="sms_varjag" then
		bar_dialogs.sms_varjag()
    end
	
	if select_string=="sms_marsh" then
		dialogs_marsh.sms_marsh()
    end
	
	if select_string=="sms_sidor" then
		escape_dialog.sms_sidor()
    end
	
	if select_string=="final" then
		news_manager.send_tip(db.actor, "%c[255,255,128,128]Холод:\n%c[default] Здарова, Кайдан! Заглянул бы ты к нам на Болота, как время будет - есть разговор один.", nil, nil, 30000)
		teleport_kordon_1.teleport_kordon_1()
		teleport_kordon_2.teleport_kordon_2()
    end
	
	if select_string=="sms_voronin" then
		news_manager.send_tip(db.actor, "%c[255,255,128,128]Воронин:\n%c[default] Здравствуй, Кайдан! Вижу, что ты сейчас находишься в Окресностях Юпитера, потому обращаюсь к тебе с просьбой о помощи. В Зону прибыла специально подготовленная группа американских рейнджеров для проведения операции по захвату секретного военного архива. Им удалось взять капитана Кольцова, который нёс этот архив, чтобы передать отряду ГРУ. Надо помочь нашему человеку и не дать американцам вывезти архив. Сбрасываю тебе координаты места, откуда идёт сигнал маячка Кольцова.", nil, nil, 40000)
		db.actor:give_info_portion("sms_voronin")
    end	
	
	if select_string=="quest_koltsov" then
		db.actor:give_info_portion("quest_koltsov")
		spawn_restrictor.restr_spawn_soldier_usa()
    end	
	
	if select_string=="sanchast" then
		db.actor:give_info_portion("sanchast")
    end
	
	if select_string=="sultan" then
        db.actor:give_info_portion("zat_bandit_dead")
    end
	
	if select_string=="boroda" then
        db.actor:give_info_portion("reid_na_suhogruz")
    end	
	
	if select_string=="glaz_drakona" then
        db.actor:give_info_portion("zat_glaz_drakona_quest_stop")
		dialogs_zaton.remove_obj("af_podmena")
    end
	
	if select_string=="start_kino" then
        dialogs_pripyat.start_kino_2()
    end
	
	if select_string=="quest_mogila" then
        db.actor:give_info_portion("quest_mogila")
    end	

	if select_string=="start_quest_kukla" then
        dialogs_pripyat.spawn_kukla()
		dialogs_pripyat.spawn_monstr_pripyat_4()
    end

	if select_string=="start_quest_martyshka" then
        db.actor:give_info_portion("martyshka")
    end

	if select_string=="sms_nagrada" then
		dialogs_jupiter.spawn_nagrada()
		dialogs_jupiter.start_sms_krohobor()
		dialogs_jupiter.spawn_monstr_iupiter_2()
        news_manager.send_tip(db.actor, "%c[255,255,128,128]Шухов:\n%c[default] Спасибо! Навести полустанок.", nil, nil, 30000)
    end

	if select_string=="sms_krohobor" then
		db.actor:give_info_portion("sms_krohobor")
        news_manager.send_tip(db.actor, "%c[255,255,128,128]Крохобор:\n%c[default] Здарова, сыщик! Говорят, ты всё ещё по Зоне бегаешь, никак не успокоишься. ))) Если будет свободная минутка, загляни ко мне в подвальчик!", nil, nil, 30000)
    end	
	
	if select_string=="offline" then
        dialogs_predbannik.sheph_offline()
    end

	if select_string=="brodyaga" then
        dialogs_pripyat.cmc()
		dialogs_pripyat.start_help_brodyaga()
    end
	
	if select_string=="brodyaga_2" then
		db.actor:give_info_portion("brodyaga")
    end

	if select_string=="ataka_start" then
		db.actor:give_info_portion("dialog")
    end		

	if select_string=="show_news" then
		amk_mod.show_news()
	end
	if select_string=="gg_need_sleep" then
		amk_mod.test_for_need_sleep()
	end
	if select_string=="sleep_nrg" then
		amk_mod.test_for_need_sleep_nrg(params_string)
	end
	if select_string=="sleep_vodka" then
		amk_mod.test_for_need_sleep_vodka(params_string)
	end

            -- repair_kit
	if select_string == "rem" then
		remkit_main.call_menu_repair_kit(params_string)
	end
	if select_string == "rkc" then
		remkit_main.search_repair_kit()
	end
	--end repair kit

	if select_string=="scr" then
		lwc.spawn_fn2000()
		db.actor:give_info_portion("secret_talk")
	end
	
	if select_string=="sleep_matras" then
		amk_mod.test_for_need_sleep_matras(params_string)
	end
	if select_string=="block_sleep_menu" then
		save_variable("block_sleep_menu",0)
	end
	if select_string=="radar_fix" then
		amk_mod.radar_fix()
	end
	if string.find(select_string,"af_transform_")~=nil then
		amk_mod.af_transform_done(params_string)
	end
	if select_string=="order_for_duty_exo" then
		lwc_order_duty.allowed_to_get()
	end
	if select_string=="order_for_mercenary_exo" then
		lwc_order_merc.allowed_to_get()
	end
	if select_string=="order_for_monolit_exo" then
		lwc_order_mono.allowed_to_get()
	end
	if select_string=="order_for_freedom_exo" then
		lwc_order_free.allowed_to_get()
	end
	if select_string=="order_barmen" then
		lwc_order_barmen.allowed_to_get()
	end
	if select_string=="order_kruger" then
		lwc_order_kruger.allowed_to_get()
	end
	if select_string=="order_sidor" then
		lwc_order_sidor.allowed_to_get()
	end
	if select_string=="order_novikov" then
		lwc_order_novikov.allowed_to_get()
	end
	if select_string=="upg_set_cond" then
		local slot = lwc_ui_slot_upgrade.slot
		local nobj = db.actor:item_in_slot(slot)
		nobj:set_ammo_elapsed(0)
	end
	if select_string=="det_accum" then
		lwc_accum.DetCond()
	end
	if select_string=="stop_det" then
		db.actor:iterate_inventory(function (dummy, item) 
			if (string.find(item:section(),"_active") and string.find(item:section(),"detector_")) then
				alife():release(alife():object(item:id()), true)
			end
		end, nil)
	end
	if select_string=="outfit_cond_3" then
		local a = level.object_by_id(tonumber(params_string))
		a:set_condition((math.random(35)+35)/100)
	end
	if select_string=="sms_morder" then
		db.actor:give_info_portion("pre_morder_dialog_for_quest")
		amk.send_tip("Загляни к нам на огонёк, дело есть.","Мордер", nil, 10, nil, "task")
		local sobj = alife():story_object(1602)
		if sobj then add_spot_on_map(sobj.id,"blue_location","Мордер") end
	end
    if select_string=="autosave" then
    u3_utils.savegame(params_string)
	end
-----------
end

--спавним объекты на карту
--для спавна неписей смотрим config\creatures\spawn_sections.ltx - там написаны имена секций для разных типов неписей
--' Добавил количество вещей, если спавним больше одного, как объект не вернёт, только спавн. Real Wolf
function spawn_item(spawn_item, pos, lv, gv, count)
	if count ~= nil and count > 1 then
		for i=1,count do
		 	alife():create(spawn_item, pos, lv, gv)
		end
	else
 		return alife():create(spawn_item, pos, lv, gv)
	end
end

--для спавна патронов используем spawn_ammo_in_inv
--' Добавил количество вещей, если спавним больше одного, как объект не вернёт, только спавн. Real Wolf
function spawn_item_in_inv(spawn_item, npc, count)
	if npc==nil then npc=db.actor end
	if count ~= nil and count > 1 then
		for i=1,count do
			alife():create(spawn_item,npc:position(),npc:level_vertex_id(),npc:game_vertex_id(),npc:id())
		end
	else
		return alife():create(spawn_item,npc:position(),npc:level_vertex_id(),npc:game_vertex_id(),npc:id())
	end
end

--используем для спавна патронов
function spawn_ammo_in_inv(spawn_item,number,npc)
	if npc==nil then 
		npc=db.actor 
	end
	if number > 0 then
		return se_respawn.create_ammo(spawn_item,	
				npc:position(),
				npc:level_vertex_id(),	
				npc:game_vertex_id(),
				npc:id(),
				number)
	end
end

-- удаляем объект из игры
function remove_item(remove_item)
	if remove_item~=nil then
		local sobj = alife():object(remove_item:id())
		if sobj then
			alife():release(sobj, true)
		end
		return true
	end
	return false
end

-- выбрасываем объект из инвентаря, применимо к ГГ
function drop_item(npc,item)
	if item~=nil then
	    npc:mark_item_dropped(item)
	end
end

--убиваем непися
function make_suicide(npc)
	npc:kill(npc)
end

--узнаем отношение одного непися к другому
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

--задаем отношение одного непися к другому
function set_npc_relation(obj,target,relation)
	local rel
	if relation=="neutral" then
		rel=game_object.neutral
	elseif relation=="friend" then
		rel=game_object.friend
	elseif relation=="enemy" then
		rel=game_object.enemy
	else
		return false
	end	
	obj:set_relation(rel,target)
	return true
end

-- узнаем группировку непися, применимо к ГГ
function get_npc_community(npc)
	return npc:community()
end

-- выставляем группировку непися, можно ГГ
function set_npc_community(npc,community_string)
	--значения для community_string можно узнать в config\creatures\game_relations.ltx
	return npc:set_character_community(community_string, 0, 0)
end

--удаляем предмет из инвентаря по имени
function remove_item_from_inventory_by_name(remove_item_name,npc)
	return remove_item_from_inventory(npc:object(remove_item_name),npc)
end

--удаляем предмет из инвентаря
function remove_item_from_inventory(remove_item,npc)
	if npc==nil then npc=db.actor end
	if remove_item~=nil then
	    npc:mark_item_dropped(remove_item)
		local sobj = alife():object(remove_item:id())
		if sobj then
			alife():release(sobj, true)
		end
		return true
	end
	return false
end

--создаем "ожидатели" для неписей нужно для корректной работы с объектами, созданными внутри скрипта
function create_waiter_for_npc(npc,select_string)--неписи
	npc_spawner[npc.id]=select_string
end
--

--очищаем инвентарь непися, можно ГГ, использует следующую фунцию для удаления предмета
function clear_npc_inventory(npc)
    npc:iterate_inventory(__del_item, npc)
end
function __del_item(npc, item)
    local section = item:section()

    if section == "bolt" or section == "device_torch" then
        return
    end
    npc:mark_item_dropped(item)
	local sobj = alife():object(item:id())
	if sobj then
		alife():release(sobj, true)
	end
end
---------------------------

--проверка запущена ли игра
function check_game()
	if level.present() and (db.actor ~= nil) and db.actor:alive() then
		return true
	end
	return false
end

--записываем переменную
function save_variable(variable_name, value)
	if value ~= nil then
		xr_logic.pstor_store(db.actor, variable_name, value)
	end
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 check_npc_in_box(npc, p1,p2)
	local pos=npc:position()
	if is_point_inside_interval(pos.x,p1.x,p2.x) and
		is_point_inside_interval(pos.y,p1.y,p2.y) and
		is_point_inside_interval(pos.z,p1.z,p2.z) then
		return true
	else
		return false	
	end
end
function is_point_inside_interval(x,p1,p2)
	if p1>p2 then 
		p1,p2 = p2,p1 
	end
	
	if x>p1 and x<p2 then 
		return true
	else 
		return false
	end
end
----------------------------

--инвентарное название объекта
function get_inv_name(section)
	return system_ini():r_string(section,"inv_name")
end

---------------------------------------------
--callback section
---------------------------------------------

--колбэк на получение инфопоршена
function on_info()
end

--колбэк на взятие предмета в инвентарь ГГ
function on_item_take(obj)
end

--колбэк на взятие предмета в инвентарь ГГ из ящика
function on_item_take_from_box(obj)
end

-- Колбэк на потерю предмета из инвентаря ГГ
function on_item_drop(obj)
            remkit_main.check_repair_kit(obj) 
end

local xtn = 0
local gtn = 0
function check_timer(name) --проверка - запущен ли таймер?
	local a=1
	for a=1,100,1 do
		if load_variable("xt"..a, 0) == name then
			xtn = a
			return true
		elseif a == 100 then
			return false
		end
	end
end

function check_gtimer(name) --проверка - запущен ли таймер (игрового времени)?
	local a=1
	for a=1,100,1 do
		if load_variable("gt"..a, 0) == name then
			gtn = a
			return true
		elseif a == 100 then
			return false
		end
	end
end

--колбэк на апдейт ГГ (удобно для проверки условий, так как вызывается постоянно, нельзя перегружать, а то будут лаги)
function on_actor_upade()
 --не удалять! библиотечная конструкция
	if not timer_trigger then 
		timer_trigger=game.time() 
	end
	if timer_trigger<=game.time() then
		timer_trigger=game.time()+5000
		check_timers()
	end
	
	amk_mod.check_radar_off()
	ogsm_surge.update_surge()
end

--колбэк на создание непися, использует следующую за ним функцию для выбора действия
function on_npc_spawn(npc)
	if npc == nil then return end
	for k,v in pairs(npc_spawner) do
		if k==npc:id() then 
			__npc_spawn_case(npc,v)
			npc_spawner[k]=nil
			return
		end
	end
end
-- Эта функция вызывается самой первой. Онлайновые объекты недоступны! db.actor недоступен!
function on_game_start()
	local strTime = system_ini():r_string("alife", "start_time")
	local t = str_explode(":", strTime, true)
	local d = str_explode(".",  system_ini():r_string("alife", "start_date"), true)
	StartTime = game.CTime()
	StartTime:set(d[3], d[2], 1, 0, 0, 0, 0)
	local temp = game.CTime()
	temp:setHMS(24,0,0)
	StartTime = StartTime - temp
end

function __npc_spawn_case(npc,select_string)
	--[[
	здесь описываем вызовы, оформялять в виде

	if select_string=="название условия" then
		<вызов сторонних функций>
		-- можно передавать npc как параметр
	end
	
	]]
--user area	
-----------
end
-----------------------

--колбэк на юзание объекта
function on_use(victim, who)
end

--колбэк на смерть непися
function on_death(victim, who)
end

--загружаем все переменные, которые нужно, вызывается загрузке игры, автоматически; вручную не вызывать
function on_game_load()

	if db.storage[db.actor:id()].pstor == nil then
		db.storage[db.actor:id()].pstor = {}
	end

	--first run check section
	if load_variable("x_first_run",true) then
		--здесь выполняются действия, которые будут выполнены только один раз при первом запуске мода
	
		amk_mod.first_run()
	
		save_variable("x_first_run",false)	--не трогать!
	end
	
	local tmp,tmp1
	for a=1,65534,1 do
		tmp=load_variable("x_marker_type_"..a,nil)
		if tmp~=nil then
			tmp1=load_variable("x_marker_text_"..a,nil)
			level.map_add_object_spot(a, tmp, tmp1)
		end
	end
	
--user area	
	amk_mod.test_sleep_pp()
-----------
	
end

function load_table(name)
  local var=load_variable(name)
  return (var==nil and {}) or unpack_array_from_string(var)
end

function save_table(name,tbl)
  save_variable(name,pack_array_to_string(tbl))
end

function update_table(name,id,val)
  local tbl=load_table(name)
  tbl[id]=val
  save_table(name,tbl)
  return tbl
end

function pack_array_to_string(array)
  return string.char(1)..pack_new(array)
  -- local str=""
  -- local key
  -- for key0,value in pairs(array) do
    -- if type(key0)=="string" then
      -- key='"'..key0..'"'
    -- else
      -- key=key0
    -- end
    -- if type(value)=="table" then
      -- local substr=pack_array_to_string(value)
      -- str=str..key.."=>{"..substr.."}|"
    -- elseif type(value)=="customdata" or type(value)=="function" then
      -- mylog("Custom data and function isn't supported")
    -- elseif type(value)=="boolean" or type(value)=="number" then
      -- str=str..key.."=>"..tostring(value).."|"
    -- else
      -- str=str..key..'=>"'..value..'"|'
    -- end
  -- end
  -- return str
end

function unpack_array_from_string(str)
  if str==nil or str=="" then return {} end
  if string.sub(str,1,1)~=string.char(1) then
    -- Старый формат упаковки
    return _parse(str)
  else
    -- новый формат упаковки тэгирован символом c кодом 1.
    return parse_new(string.sub(str,2,-1))
  end
end

local pack_type_num=1
local pack_type_string=2
local pack_type_bool=3
local pack_type_table=4
local pack_val_endtable=5

function pack_new(tbl)
  local ret=""
  for k,v in pairs(tbl) do
    if type(k)=="number" then
      ret=ret..string.char(pack_type_num)..k
    elseif type(k)=="string" then
      ret=ret..string.char(pack_type_string)..k
    else
      abort("unsupported key type "..type(k))
    end
    if type(v)=="number" then
      ret=ret..string.char(pack_type_num)..v
    elseif type(v)=="string" then
      ret=ret..string.char(pack_type_string)..v
    elseif type(v)=="boolean" then
      ret=ret..string.char(pack_type_bool)..v
    elseif type(v)=="table" then
      ret=ret..string.char(pack_type_table)..pack_new(v)..string.char(pack_val_endtable)
    end
  end
  return ret
end

function parse_new(str,idx)
  local ret={}
  idx=idx or 1
  while true do
    local key,value
    if idx>string.len(str) then
      return ret,idx
    end
    vtype,idx=get_byte(str,idx)
    if vtype==pack_type_num then
      key,idx=get_num(str,idx)
    elseif vtype==pack_type_string then
      key,idx=get_string(str,idx)
    elseif vtype==pack_val_endtable then
      return ret,idx
    else
      abort("unsupported key type "..tostring(vtype))
    end
    vtype,idx=get_byte(str,idx)
    if vtype==pack_type_num then
      value,idx=get_num(str,idx)
    elseif vtype==pack_type_string then
      value,idx=get_string(str,idx)
    elseif vtype==pack_type_bool then
      value,idx=get_bool(str,idx)
    elseif vtype==pack_type_table then
      value,idx=parse_new(str,idx)
    else
      abort("unsupported key type "..tostring(vtype))
    end
    ret[key]=value
  end
end

function get_byte(str,idx)
  return string.byte(string.sub(str,idx,idx)),idx+1
end

function get_string(str,idx)
  local idx1=string.len(str)+1
  for i=idx,string.len(str),1 do
    if string.byte(string.sub(str,i,i))<32 then
      idx1=i
      break
    end
  end
  return string.sub(str,idx,idx1-1),idx1
end

function get_num(str,idx)
  local st,idx1=get_string(str,idx)
  return st+0,idx1
end

function get_bool(str,idx)
  local st,idx1=get_string(str,idx)
  return st=="1",idx1
end


function _parse(str)
  local ret={}
  while str and str~="" do
    local i1,i2,key=string.find(str,'(.-)=>')
    str=string.sub(str,i2+1)
    i1,i2,val=string.find(str,'"(.-)"|')
    if val and i1==1 then
      -- строка
      _assign(ret,key,val)
    else
      i1,i2,val=string.find(str,'(%b{})|')
      if val and i1==1 then
        -- таблица
        _assign(ret,key,_parse(string.sub(val,2,-2)))
      else
        i1,i2,val=string.find(str,'(.-)|')
        -- число или булево значение
        if val=="true" then
          _assign(ret,key,true)
        elseif val=="false" then
          _assign(ret,key,false)
        else
          _assign(ret,key,val+0)
        end
      end
    end
    str=string.sub(str,i2+1)
  end
  return ret
end

--записываем все переменные, которые нужно, вызывается присохранении игры, автоматически; вручную не вызывать
function on_game_save()	
	
end

function readvu32u8(packet)
  local v={}
  local len=packet:r_s32()
  for i=1,len,1 do
    table.insert(v,packet:r_u8())
  end
  return v
end

function readvu8u8(packet)
  local v={}
  local len=8
  for i=1,len,1 do
    table.insert(v,packet:r_u8())
  end
  return v
end

function readvu32u16(packet)
  local v={}
  local len=packet:r_s32()
  for i=1,len,1 do
    table.insert(v,packet:r_u16())
  end
  return v
end

function writevu32u8(pk,v)
  local len=table.getn(v)
  pk:w_s32(len)
  for i=1,len,1 do
    pk:w_u8(v[i])
  end
end

function writevu8u8(pk,v)
  local len=8 --table.getn(v)
  --pk:w_u8(len)
  for i=1,len,1 do
    pk:w_u8(v[i])
  end
end

function writevu32u16(pk,v)
  local len=table.getn(v)
  pk:w_s32(len)
  for i=1,len,1 do
    pk:w_u16(v[i])
  end
end

function parse_object_packet(ret,stpk,updpk)
  ret.gvid=stpk:r_u16()
  ret.obf32u1=stpk:r_float()
  ret.obs32u2=stpk:r_s32()
  ret.lvid=stpk:r_s32()
  ret.oflags=stpk:r_s32()
  ret.custom=stpk:r_stringZ()
  ret.sid=stpk:r_s32()
  ret.obs32u3=stpk:r_s32()
  return ret
end

function fill_object_packet(ret,stpk,updpk)
  stpk:w_u16(ret.gvid)
  stpk:w_float(ret.obf32u1)
  stpk:w_s32(ret.obs32u2)
  stpk:w_s32(ret.lvid)
  stpk:w_s32(ret.oflags)
  stpk:w_stringZ(ret.custom)
  stpk:w_s32(ret.sid)
  stpk:w_s32(ret.obs32u3)
end


function parse_visual_packet(ret,stpk,updpk)
  ret.visual=stpk:r_stringZ()
  ret.vsu8u1=stpk:r_u8()
  return ret
end

function fill_visual_packet(ret,stpk,updpk)
  stpk:w_stringZ(ret.visual)
  stpk:w_u8(ret.vsu8u1)
end

function parse_dynamic_object_visual(ret,stpk,updpk)
  parse_object_packet(ret,stpk,updpk)
  parse_visual_packet(ret,stpk,updpk)
  return ret
end

function fill_dynamic_object_visual(ret,stpk,updpk)
  fill_object_packet(ret,stpk,updpk)
  fill_visual_packet(ret,stpk,updpk)
end

function parse_creature_packet(ret,stpk,updpk)
  parse_dynamic_object_visual(ret,stpk,updpk)
  ret.team=stpk:r_u8()
  ret.squad=stpk:r_u8()
  ret.group=stpk:r_u8()
  ret.health=stpk:r_float()
  ret.crvu32u16u1=readvu32u16(stpk)
  ret.crvu32u16u2=readvu32u16(stpk)  
  ret.killerid=stpk:r_u16()
  ret.game_death_time=readvu8u8(stpk)

  ret.updhealth=updpk:r_float()
  ret.upds32u1=updpk:r_s32()
  ret.updu8u2=updpk:r_u8()
  ret.updpos={} -- или поставить вектор? ладно потом
  ret.updpos.x=updpk:r_float()
  ret.updpos.y=updpk:r_float()
  ret.updpos.z=updpk:r_float()
  ret.updmodel=updpk:r_float()
  ret.upddir={}
  ret.upddir.x=updpk:r_float()
  ret.upddir.y=updpk:r_float()
  ret.upddir.z=updpk:r_float()
  ret.updteam=updpk:r_u8()
  ret.updsquad=updpk:r_u8()
  ret.updgroup=updpk:r_u8()  
  return ret
end

function fill_creature_packet(ret,stpk,updpk)
  fill_dynamic_object_visual(ret,stpk,updpk)
  stpk:w_u8(ret.team)
  stpk:w_u8(ret.squad)
  stpk:w_u8(ret.group)
  stpk:w_float(ret.health)
  writevu32u16(stpk,ret.crvu32u16u1)
  writevu32u16(stpk,ret.crvu32u16u2)  
  stpk:w_u16(ret.killerid)
  writevu8u8(stpk,ret.game_death_time)

  updpk:w_float(ret.updhealth)
  updpk:w_s32(ret.upds32u1)
  updpk:w_u8(ret.updu8u2)
  updpk:w_float(ret.updpos.x)
  updpk:w_float(ret.updpos.y)
  updpk:w_float(ret.updpos.z)
  updpk:w_float(ret.updmodel)
  updpk:w_float(ret.upddir.x)
  updpk:w_float(ret.upddir.y)
  updpk:w_float(ret.upddir.z)
  updpk:w_u8(ret.updteam)
  updpk:w_u8(ret.updsquad)
  updpk:w_u8(ret.updgroup)
end

function parse_monster_packet(ret,stpk,updpk)
  parse_creature_packet(ret,stpk,updpk)
  ret.baseoutr=stpk:r_stringZ()
  ret.baseinr=stpk:r_stringZ()
  ret.smtrid=stpk:r_u16()
  ret.smtrtaskactive=stpk:r_u8()
  
  ret.updu16u1=updpk:r_u16()
  ret.updu16u2=updpk:r_u16()
  ret.upds32u3=updpk:r_s32()
  ret.upds32u4=updpk:r_s32()
  return ret
end

function fill_monster_packet(ret,stpk,updpk)
  fill_creature_packet(ret,stpk,updpk)
  stpk:w_stringZ(ret.baseoutr)
  stpk:w_stringZ(ret.baseinr)
  stpk:w_u16(ret.smtrid)
  stpk:w_u8(ret.smtrtaskactive)
  
  updpk:w_u16(ret.updu16u1)
  updpk:w_u16(ret.updu16u2)
  updpk:w_s32(ret.upds32u3)
  updpk:w_s32(ret.upds32u4)
end

function parse_trader_packet(ret,stpk,updpk)
  ret.money=stpk:r_s32()
  ret.profile=stpk:r_stringZ()
  ret.infammo=stpk:r_s32()
  ret.class=stpk:r_stringZ()
  ret.communityid=stpk:r_s32()
  ret.rank=stpk:r_s32()
  ret.reputation=stpk:r_s32()
  ret.charname=stpk:r_stringZ()  
  return ret
end

function fill_trader_packet(ret,stpk,updpk)
  stpk:w_s32(ret.money)
  stpk:w_stringZ(ret.profile)
  stpk:w_s32(ret.infammo)
  stpk:w_stringZ(ret.class)
  stpk:w_s32(ret.communityid)
  stpk:w_s32(ret.rank)
  stpk:w_s32(ret.reputation)
  stpk:w_stringZ(ret.charname)
end

function parse_human_packet(ret,stpk,updpk)
  parse_trader_packet(ret,stpk,updpk)
  parse_monster_packet(ret,stpk,updpk)
  ret.huvu32u8u1=readvu32u8(stpk)
  ret.huvu32u8u2=readvu32u8(stpk)  
  return ret
end

function fill_human_packet(ret,stpk,updpk)
  fill_trader_packet(ret,stpk,updpk)
  fill_monster_packet(ret,stpk,updpk)
  writevu32u8(stpk,ret.huvu32u8u1)
  writevu32u8(stpk,ret.huvu32u8u2)
end

function parse_skeleton_packet(ret,stpk,updpk)
  ret.skeleton=stpk:r_stringZ()
  ret.skeleton_flags=stpk:r_u8()
  ret.source_id=stpk:r_u16()
  
--  ret.updsku8u1=updpk:r_u8()
  return ret
end

function fill_skeleton_packet(ret,stpk,updpk)
  stpk:w_stringZ(ret.skeleton)
  stpk:w_u8(ret.skeleton_flags)
  stpk:w_u16(ret.source_id)
  
--  updpk:w_u8(ret.updsku8u1)
end

function parse_stalker_packet(ret,stpk,updpk,size)
  parse_human_packet(ret,stpk,updpk)
  parse_skeleton_packet(ret,stpk,updpk)
  ret.hellodlg=updpk:r_stringZ()
  ret.stunk1={}
  for i=stpk:r_tell(),size-1,1 do
    table.insert(ret.stunk1,stpk:r_u8())
  end
  return ret
end

function fill_stalker_packet(ret,stpk,updpk)
  fill_human_packet(ret,stpk,updpk)
  fill_skeleton_packet(ret,stpk,updpk)
  updpk:w_stringZ(ret.hellodlg)
  for i,v in ipairs(ret.stunk1) do
    stpk:w_u8(v)
  end
end

function parse_se_monster_packet(ret,stpk,updpk,size)
  parse_monster_packet(ret,stpk,updpk,size)
  parse_skeleton_packet(ret,stpk,updpk,size)
  ret.spec_obj_id=stpk:r_u16()
  ret.job_online=stpk:r_u8()
	if ret.job_online>3 then
		ret.state=true
		ret.job_online=ret.job_online-4
	else
		ret.state=false
	end
  if ret.job_online==3 then
    ret.job_online_condlist=stpk:r_stringZ()
  end
  ret.was_in_smtr=stpk:r_u8()
  ret.stunk1={}
  for i=stpk:r_tell(),size-1,1 do
    table.insert(ret.stunk1,stpk:r_u8())
  end
  return ret
end

function fill_se_monster_packet(ret,stpk,updpk)
  fill_monster_packet(ret,stpk,updpk)
  fill_skeleton_packet(ret,stpk,updpk)
  stpk:w_u16(ret.spec_obj_id)
	local st=0
	if ret.state then
		st=4
	end
  stpk:w_u8(ret.job_online+st)
  if ret.job_online==3 then
    stpk:w_stringZ(ret.job_online_condlist)
  end
  stpk:w_u8(ret.was_in_smtr)
  for i,v in ipairs(ret.stunk1) do
    stpk:w_u8(v)
  end  
end

function dump_table(tbl)
  for k,v in pairs(tbl) do
    if type(v)=="table" then
      get_console():execute("load ~~~ "..tostring(k).." => ")
      dump_table(v)
    else
      str="load ~~~ "..tostring(k).." => "..tostring(v)
      if string.len(str)>200 then
        str=string.sub(str,1,200)
      end
      get_console():execute(str)
    end
  end
  get_console():execute("flush")
end

-- серверный объект на входе
function read_stalker_params(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("rsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  local tbl=amk.parse_stalker_packet({},stpk,uppk,size)
  -- amk.mylog("rsp "..size.." "..size1.." "..stpk:r_tell())
  return tbl
end

function read_monster_params(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  stpk:r_seek(0)
  uppk:r_seek(0)
  local tbl=amk.parse_se_monster_packet({},stpk,uppk,size)
  return tbl
end

-- таблица параметров и серверный объект на входе
function write_stalker_params(tbl,sobj,noconvert)
  local stpk=net_packet()
  local uppk=net_packet()
  amk.fill_stalker_packet(tbl,stpk,uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("wsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)
  local npc=level.object_by_id(sobj.id)
  if npc and (not noconvert) then
    amk.convert_npc[sobj.id]=true
		npc:stop_talk()
    switch_offline(npc)
  end
end

function write_monster_params(tbl,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  amk.fill_se_monster_packet(tbl,stpk,uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("wsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)
  -- local npc=level.object_by_id(sobj.id)
  -- if npc then
    -- amk.convert_npc[sobj.id]=true
    -- switch_offline(npc)
  -- end
end

function get_anomaly_data(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  stpk:r_seek(0)
  uppk:r_seek(0)
  local t={}
	amk.parse_object_packet(t,stpk,uppk,size)
  amk.parse_shape_packet(t,stpk,uppk,size)

	t.restrictor_type = stpk:r_u8()
	
	t.max_power = stpk:r_float()
	t.owner_id = stpk:r_s32()
	t.enabled_time = stpk:r_s32()
	t.disabled_time = stpk:r_s32()
	t.start_time_shift = stpk:r_s32()
	
	t.offline_interactive_radius = stpk:r_float()
	t.artefact_spawn_count = stpk:r_u16()
	t.artefact_position_offset = stpk:r_s32()
	
	t.last_spawn_time_present = stpk:r_u8()
	
	if stpk:r_elapsed() ~= 0 then
--		abort("left=%d", stpk:r_elapsed())
	end
	return t
end

function set_anomaly_data(t,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
	
  amk.fill_object_packet(t,stpk,uppk)
  amk.fill_shape_packet(t,stpk,uppk)

	stpk:w_u8(t.restrictor_type)
	
	stpk:w_float(t.max_power)
	stpk:w_s32(t.owner_id)
	stpk:w_s32(t.enabled_time)
	stpk:w_s32(t.disabled_time)
	stpk:w_s32(t.start_time_shift)
	
	stpk:w_float(t.offline_interactive_radius)
	stpk:w_u16(t.artefact_spawn_count)
	stpk:w_s32(t.artefact_position_offset)
	
	stpk:w_u8(t.last_spawn_time_present)
	
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)
end

function parse_object_physic_packet(ret,stpk,updpk)
	ret.physic_type=stpk:r_s32()
	ret.mass=stpk:r_float()
	ret.fixed_bones=stpk:r_stringZ()
  return ret
end

function fill_object_physic_packet(ret,stpk,updpk)
	stpk:w_s32(ret.physic_type)
	stpk:w_float(ret.mass)
	stpk:w_stringZ(ret.fixed_bones)
end

function get_breakable_data(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("rsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  local t={}
	amk.parse_object_packet(t,stpk,uppk,size)
  amk.parse_visual_packet(t,stpk,uppk,size)
  amk.parse_object_physic_packet(t,stpk,uppk,size)
  -- amk.mylog("rsp "..size.." "..size1.." "..stpk:r_tell())
  return t
--[[
	local packet = net_packet()
	obj:STATE_Write(packet)
	local t={}
	t.game_vertex_id = packet:r_u16()
	t.distance = packet:r_float()
	t.direct_control = packet:r_s32()
	t.level_vertex_id = packet:r_s32()
	t.object_flags = packet:r_s32()
	t.custom_data = packet:r_stringZ()
	t.story_id = packet:r_s32()
	t.spawn_story_id = packet:r_s32()
	
	t.visual_name = packet:r_stringZ()
	t.visual_flags = packet:r_u8()
	
	t.health = packet:r_float()

	if packet:r_elapsed() ~= 0 then
		abort("left=%d", packet:r_elapsed())
	end
	return t
	]]
end

function set_breakable_data(t,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
	
  amk.fill_object_packet(t,stpk,uppk)
  amk.fill_visual_packet(t,stpk,uppk)
  amk.fill_object_physic_packet(t,stpk,uppk)
	
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("wsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)

	--[[
	local packet = net_packet()
	obj:STATE_Write(packet)
	packet:w_begin(t.game_vertex_id)
	packet:w_float(t.distance)
	packet:w_s32(t.direct_control)
	packet:w_s32(t.level_vertex_id)
	packet:w_s32(t.object_flags)
	packet:w_stringZ(t.custom_data)
	packet:w_s32(t.story_id)
	packet:w_s32(t.spawn_story_id)
	packet:w_stringZ(t.visual_name)
	packet:w_u8(t.visual_flags)
	packet:w_float(t.health)

	packet:r_seek(0)
	obj:STATE_Read(packet, packet:w_tell())
	]]
end

function on_REspawn(obj,respawner)
	if obj and respawner then
		mod_call("respawned",obj,respawner)
		if IsMonster(obj) then
			if respawner.spawned_goes_online==true then mod_call("switch_monster_online",obj) 
			elseif respawner.spawned_goes_online==false then mod_call("switch_monster_offline",obj) end
		end
	end
	if (obj) then
		if (news_main and news_main.on_spawn) then
			news_main.on_spawn(obj)
		end
	end
end

function get_spawner_data(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("rsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  local t={}
	amk.parse_object_packet(t,stpk,uppk,size)
  amk.parse_shape_packet(t,stpk,uppk,size)
	
	t.restrictor_type = stpk:r_u8()
	t.spawned_obj_count = stpk:r_u8()
  -- amk.mylog("rsp "..size.." "..size1.." "..stpk:r_tell())
  return t
end

function set_spawner_data(t,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
	
  amk.fill_object_packet(t,stpk,uppk)
  amk.fill_shape_packet(t,stpk,uppk)
	stpk:w_u8(t.restrictor_type)
	stpk:w_u8(t.spawned_obj_count)
	
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("wsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)
end

function parse_shape_packet(t,stpk,uppk)
	local shape_count = stpk:r_u8()
	t.shapes={}
	for i=1,shape_count do
		local shape_type = stpk:r_u8()
		t.shapes[i]={}
		t.shapes[i].shtype=shape_type
		if shape_type == 0 then
			-- sphere
			t.shapes[i].center = stpk:r_vec3()
			t.shapes[i].radius = stpk:r_float()
		else
			-- box
			t.shapes[i].v1 = stpk:r_vec3()
			t.shapes[i].v2 = stpk:r_vec3()
			t.shapes[i].v3 = stpk:r_vec3()
			t.shapes[i].offset = stpk:r_vec3()
		end
	end
end

function fill_shape_packet(t,stpk,updpk)
	stpk:w_u8(table.getn(t.shapes))
	for i=1,table.getn(t.shapes) do
		stpk:w_u8(t.shapes[i].shtype)
		if t.shapes[i].shtype == 0 then
			stpk:w_vec3(t.shapes[i].center)
			stpk:w_float(t.shapes[i].radius)
		else
			stpk:w_vec3(t.shapes[i].v1)
			stpk:w_vec3(t.shapes[i].v2)
			stpk:w_vec3(t.shapes[i].v3)
			stpk:w_vec3(t.shapes[i].offset)
		end
	end
end

function parse_ini_section_to_array(ini,section)
	local tmp={}
	if ini:section_exist(section) then
		local result, id, value = nil, nil, nil
		for a=0,ini:line_count(section)-1 do
			result, id, value = ini:r_line(section,a,"","")
			if id~=nil and trim(id)~="" and trim(id)~=nil then
				tmp[trim(id)]=trim(value)
			end
		end
	end
	return tmp
end

function str_explode(div,str,clear)
	local t={}
	local cpt = string.find (str, div, 1, true)
	if cpt then
		repeat
			if clear then
				table.insert( t, trim(string.sub(str, 1, cpt-1)) )
			else
				table.insert( t, string.sub(str, 1, cpt-1) )
			end
			str = string.sub( str, cpt+string.len(div) )
			cpt = string.find (str, div, 1, true)
		until cpt==nil
	end
	if clear then
		table.insert(t, trim(str))
	else
		table.insert(t, str)
	end
	return t
end

function quotemeta(str)
	return (string.gsub(s, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%1"))
end


function add(v1,v2)
	local nv=vector()
	nv.x=v1.x+v2.x
	nv.y=v1.y+v2.y
	nv.z=v1.z+v2.z
	return nv
end

function set_len(v,num)
	local cl = math.sqrt(v.x*v.x+v.y*v.y+v.z*v.z)
	cl = num/cl
	v.x= v.x*cl
	v.y= v.y*cl
	v.z= v.z*cl
	return v
end

function trim (s)
	return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end

function get_restrictor_data(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("rsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  local t={}
	amk.parse_object_packet(t,stpk,uppk,size)
  amk.parse_shape_packet(t,stpk,uppk,size)
	
	t.restrictor_type = stpk:r_u8()
  -- amk.mylog("rsp "..size.." "..size1.." "..stpk:r_tell())
  return t
end

function set_restrictor_data(t,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
	
  amk.fill_object_packet(t,stpk,uppk)
  amk.fill_shape_packet(t,stpk,uppk)
	stpk:w_u8(t.restrictor_type)
	
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("wsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)
end

function get_trader_data(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("rsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  local t={}
	amk.parse_object_packet(t,stpk,uppk,size)
  amk.parse_visual_packet(t,stpk,uppk,size)
  amk.parse_trader_packet(t,stpk,uppk,size)
  -- amk.mylog("rsp "..size.." "..size1.." "..stpk:r_tell())
  return t
end

function set_trader_data(t,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
	
  amk.fill_object_packet(t,stpk,uppk)
  amk.fill_visual_packet(t,stpk,uppk)
  amk.fill_trader_packet(t,stpk,uppk)
	
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("wsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)
end

function get_invbox_data(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("rsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  local t={}
	amk.parse_object_packet(t,stpk,uppk,size)
  amk.parse_visual_packet(t,stpk,uppk,size)
  -- amk.mylog("rsp "..size.." "..size1.." "..stpk:r_tell())
  return t
end

function set_invbox_data(t,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
	
  amk.fill_object_packet(t,stpk,uppk)
  amk.fill_visual_packet(t,stpk,uppk)
	
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("wsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)
end

function readvu8uN(packet,n)
  local v={}
  for i=1,n,1 do
    table.insert(v,packet:r_u8())
  end
  return v
end

function writevu8uN(pk,v)
  local len=table.getn(v)
  --pk:w_u8(len)
  for i=1,len,1 do
    pk:w_u8(v[i])
  end
end

function parse_item_packet(ret,stpk,updpk)
	ret.condition=stpk:r_float()
	ret.updnum_items=updpk:r_u8()
  ret.updpos={} -- или поставить вектор? ладно потом
  ret.updpos.x=updpk:r_float()
  ret.updpos.y=updpk:r_float()
  ret.updpos.z=updpk:r_float()
	ret.updcse_alife_item__unk1_q8v4=readvu8uN(updpk,4)
	ret.updcse_alife_item__unk2_q8v3=readvu8uN(updpk,3)
	ret.updcse_alife_item__unk3_q8v3=readvu8uN(updpk,3)
  return ret
end

function fill_item_packet(ret,stpk,updpk)
	stpk:w_float(ret.condition)
	updpk:w_u8(ret.updnum_items)
  updpk:w_float(ret.updpos.x)
  updpk:w_float(ret.updpos.y)
  updpk:w_float(ret.updpos.z)
	readvu8uN(updpk,ret.updcse_alife_item__unk1_q8v4)
	readvu8uN(updpk,ret.updcse_alife_item__unk2_q8v3)
	readvu8uN(updpk,ret.updcse_alife_item__unk3_q8v3)
  return ret
end

function parse_item_ammo_packet(ret,stpk,updpk)
	ret.ammo_left=stpk:r_u16()
	ret.updammo_left=updpk:r_u16()
  return ret
end

function fill_item_ammo_packet(ret,stpk,updpk)
	stpk:w_u16(ret.ammo_left)
	updpk:w_u16(ret.updammo_left)
  return ret
end

function get_ammo_params(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  stpk:r_seek(0)
  uppk:r_seek(0)
  local t={}
	amk.parse_object_packet(t,stpk,uppk,size)
  amk.parse_visual_packet(t,stpk,uppk,size)
  amk.parse_item_packet(t,stpk,uppk,size)
  amk.parse_item_ammo_packet(t,stpk,uppk,size)
  return t
end

function set_ammo_data(t,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
	
  amk.fill_object_packet(t,stpk,uppk)
  amk.fill_visual_packet(t,stpk,uppk)
  amk.fill_item_packet(t,stpk,uppk)
  amk.fill_item_ammo_packet(t,stpk,uppk)
	
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)
end

function cfg_get_string(ini,sect,name,def)
	if ini and ini:line_exist(sect,name) then
		return ini:r_string(sect,name)
	end
	return def
end

function get_destroyable_data(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("rsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  local t={}
	amk.parse_object_packet(t,stpk,uppk,size)
  amk.parse_visual_packet(t,stpk,uppk,size)
  amk.parse_skeleton_packet(t,stpk,uppk,size)
  amk.parse_object_physic_packet(t,stpk,uppk,size)
  -- amk.mylog("rsp "..size.." "..size1.." "..stpk:r_tell())
  return t
end

function set_destroyable_data(t,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
	
  amk.fill_object_packet(t,stpk,uppk)
  amk.fill_visual_packet(t,stpk,uppk)
  amk.fill_skeleton_packet(t,stpk,uppk)
  amk.fill_object_physic_packet(t,stpk,uppk)
	
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("wsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)

end

function get_weapon_data(sobj)
  local stpk=net_packet()
  local uppk=net_packet()
  sobj:STATE_Write(stpk)
  sobj:UPDATE_Write(uppk)
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("rsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  local t={}
  amk.parse_object_packet(t,stpk,uppk,size)
  amk.parse_visual_packet(t,stpk,uppk,size)
  amk.parse_item_packet(t,stpk,uppk,size)
  amk.parse_item_weapon_packet(t,stpk,uppk,size)
  -- amk.mylog("rsp "..size.." "..size1.." "..stpk:r_tell())
  return t
end

function set_weapon_data(t,sobj)
  local stpk=net_packet()
  local uppk=net_packet()
	
  amk.fill_object_packet(t,stpk,uppk)
  amk.fill_visual_packet(t,stpk,uppk)
  amk.fill_item_packet(t,stpk,uppk)
  amk.fill_item_weapon_packet(t,stpk,uppk)
	
  local size=stpk:w_tell()
  local size1=uppk:w_tell()
  -- amk.mylog("wsp "..size.." "..size1)
  stpk:r_seek(0)
  uppk:r_seek(0)
  sobj:STATE_Read(stpk,size)
  sobj:UPDATE_Read(uppk)

end

function parse_item_weapon_packet(ret,stpk,updpk)
	ret.ammo_current = stpk:r_u16()
	ret.ammo_elapsed = stpk:r_u16()
	ret.weapon_state = stpk:r_u8()
	ret.addon_flags = stpk:r_u8()
	ret.ammo_type = stpk:r_u8()
	
	ret.updcondition = updpk:r_u8()
	ret.updweapon_flags = updpk:r_u8()
	ret.updammo_elapsed = updpk:r_u16()
	ret.updaddon_flags = updpk:r_u8()
	ret.updammo_type = updpk:r_u8()
	ret.updweapon_state = updpk:r_u8()
	ret.updweapon_zoom = updpk:r_u8()
	ret.updcurrent_fire_mode = updpk:r_u8()
  return ret
end

function fill_item_weapon_packet(ret,stpk,updpk)
	stpk:w_u16(ret.ammo_current)
	stpk:w_u16(ret.ammo_elapsed)
	stpk:w_u8(ret.weapon_state)
	stpk:w_u8(ret.addon_flags)
	stpk:w_u8(ret.ammo_type)
	
	updpk:w_u8(ret.updcondition)
	updpk:w_u8(ret.updweapon_flags)
	updpk:w_u16(ret.updammo_elapsed)
	updpk:w_u8(ret.updaddon_flags)
	updpk:w_u8(ret.updammo_type)
	updpk:w_u8(ret.updweapon_state)
	updpk:w_u8(ret.updweapon_zoom)
	updpk:w_u8(ret.updcurrent_fire_mode)
  return ret
end

--------------------- user function section---------------

--[[
copiright © xStream

MMMMMMM    MMMMM    MMMMMM  MMMMMMMMMMMMM  MMMMMMMMMMM     MMMMMMMMMMM    MMMMMM    MMMMMMM     MMMMMM
 MMMMMMM MMMMMM  MMMMMMMMMM MMMMMMMMMMMMMM MMMMMMMMMMMMMM  MMMMMMMMMMM   MMMMMMMM   MMMMMMMM   MMMMMMM
  MMMMMMMMMMMM  MMMMMMMMMMMM MMMMMMMMMMMMM  MMMMMMMMMMMMMM MMMMMMMMMMM   MMMMMMMM    MMMMMMMM MMMMMMMM
   MMMMMMMMM     MMMMMMMM MMM   MMMMMMM     MMMMMMMMMMMMMM MMMMMMM      MMMMMMMMMM   MMMMMMMMMMMMMMMMM
    MMMMMMM       MMMMMMM       MMMMMM      MMMMMMM  MMMMM MMMMMMM      MMMMMMMMMM   MMMMMMMMMMMMMMMMM
    MMMMMMMM       MMMMMMM      MMMMMM      MMMMMMM MMMM   MMMMMMMMMM  MMMM MMMMMMM  MMMMMMMMMMMMMMMMM
   MMMMMMMMMM        MMMMM      MMMMMM      MMMMMMMMMMM    MMMMMMM     MMMM  MMMMMM  MMMMMMMMMMMMMMMMM
  MMMMM  MMMMM    MMMMMMMM      MMMMMM      MMMMMMMMMMMMMM MMMMMMMMMMM MMMMMMMMMMMMM MMMMM MMMMM MMMMM
 MMMMM   MMMMMM    MMMMMM       MMMMMM      MMMMMMM MMMMM  MMMMMMMMMM MMMMMMM MMMMMM MMMM   MM   MMMM 
 
]]