--[[

File:  amk .script
Copyright © AMK TEAM 2007-2008

26.08.08/1
Адаптация для ЧН.
Сон от АМК (Dan)
исправлена функция send_tip (Idler)
]]

----------------

is_debug = true

local timers={}   --хранит реал-тайм таймеры
local g_timers={} --хранит таймеры в игровом времени
local timer_trigger=nil
pda_news = xr_sound_new.get_safe_sound_object([[device\pda\pda_news]])
pda_tips = xr_sound_new.get_safe_sound_object([[device\pda\pda_tip]])
pda_task = xr_sound_new.get_safe_sound_object([[device\pda\pda_objective]])

tips_icons = {
	default  = "ui_iconsTotal_grouping",      -- ui_iconsTotal_grouping
	trader	 = "ui_iconsNpc_trader",    -- ui_iconsNpc_trader
	dolg     = "ui_iconsNpc_stalker_do_balon_4",
	freedom  = "ui_iconsTotal_grouping",
	ecolog   = "ui_iconsNpc_ecolog",      -- ui_iconsNpc_ecolog
	army     = "ui_iconsNpc_stalker_neytral_balon_6",    -- ui_iconsNpc_stalker_neytral_balon_6
	stalker  = "ui_iconsTotal_grouping",
	csky  	 = "ui_iconsTotal_grouping",
	krot     = "ui_iconsNpc_stalker_neytral_rukzak_4",     -- ui_iconsNpc_stalker_neytral_rukzak_4
	barman   = "ui_iconsNpc_barman",    -- ui_iconsNpc_barman
	wolf	 = "ui_iconsNpc_stalker_neytral_balon_1",    -- ui_iconsNpc_stalker_neytral_balon_1
	o_soznanie = "ui_iconsO_Soznanie",
	monolith = "ui_iconsTotal_grouping",
    saharov  = "ui_iconsNpc_ucheniy_2",    -- ui_iconsNpc_ucheniy_2
    prizrak  = "ui_iconsTotal_grouping",
    killer   = "ui_iconsTotal_grouping",
    bandit   = "ui_iconsTotal_grouping",
    renegade = "ui_iconsTotal_grouping",
	uniq  =  "ui_small_icon_border"    --{ 498, 188}
}
----------------
local bufferedmessages={}

function logct(msg,tag)
	if true and (tag and (tag=="mcbt" or tag=="temp")) then --(tag=="dram")) then
	    get_console():execute("load ~~~ "..string.sub(msg,1,200))		
	end
end

function rep(npc,msg,tag)
	if string.find(npc:name(),"gar_dm") then
		logct(msg,tag)
	end
end

function mylog(msg)
  if is_debug then 
	  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 ~~~ "..string.sub(msg,1,200))
	    get_console():execute("flush")
	  end

	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)
--  gps_habar.mylog(news_text)
  
  if sender == nil then
    sender = "default"
  end
  local ui_sender = tips_icons[sender]
  local news_text = "%c[default]"..news_text
  
  if db.actor and ui_sender then
	db.actor:give_game_news(header, game.translate_string(news_text), ui_sender, timeout*1000, showtime*1000)
  end
  return true
end



--старт таймера в реальном времени
function start_timer(name,delay,action)
  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["xt"..a] do
    a=a+1
    if a>100 then 
      return false 
    end
  end
  save_variable("xt"..a, name)
  save_variable("xd"..a, time+delay*1000*system_ini():r_float("alife","time_factor"))
  save_variable("xp"..a, action)

  return true
end

--старт таймера в игровом времени
function g_start_timer(name,delay_d,delay_h,delay_m,action)
    local time = level.get_time_days()*60*24+level.get_time_hours()*60+level.get_time_minutes()  --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["gt"..a] do
    a=a+1
    if a>100 then 
      return false 
    end
  end

  save_variable("gt"..a, name)
  save_variable("gd"..a, time+delay_d*60*24+delay_h*60+delay_m)
  save_variable("gp"..a, action)

  return true
end

function has_timer(name)
  for a=1,100,1 do
    tmp=load_variable("xt"..a,nil)
    if tmp and tmp==name then
      return true
    end
  end  
  return false
end

function has_g_timer(name)
  for a=1,100,1 do
    tmp=load_variable("gt"..a,nil)
    if tmp and tmp==name then
      return true
    end
  end  
  return false
end

-- Временное хранилище для переменных удалённых из pstor
local emerg_store
-- Удаляем переменные из pstor. Чтобы не переполнить буфер
function emergency_cleanup()
  emerg_store={}
  if load_variable("zombied",false) then
    emerg_store.zombied=load_table("zombied")
    del_variable("zombied")
  end
  for i=1,100,1 do
    if load_variable("gt"..i,"")=="af_transform" then
      emerg_store[i]={}
      emerg_store[i].gt=load_variable("gt"..i,"")
      emerg_store[i].gd=load_variable("gd"..i,"")
      emerg_store[i].gp=load_variable("gp"..i,"")
      del_variable("gt"..i)
      del_variable("gd"..i)
      del_variable("gp"..i)
    end
  end
  save_variable("emerg",true)
end

-- Восстанавливаем удалённые переменные
function emergency_restore()
  for k,v in pairs(emerg_store) do
    if k=="zombied" then
      save_table(k,v)
    else
      save_variable("gt"..k,v.gt)
      save_variable("gd"..k,v.gd)
      save_variable("gp"..k,v.gp)      
    end
  end
  del_variable("emerg")
end

function convert_timers()
  if load_variable("tmcv",true) then
    for a=1,100,1 do
      tmp=load_variable("x_timer_"..a,nil)
      if tmp~=nil then
        local name,delay,params=tmp,load_variable("x_timer_"..a.."_delay",0),load_variable("x_timer_"..a.."_params","")
        del_variable("x_timer_"..a)
        del_variable("x_timer_"..a.."_delay")
        del_variable("x_timer_"..a.."_params")
        save_variable("xt"..a,name)
        save_variable("xd"..a,delay)
        save_variable("xp"..a,params)
      end
    end
    for a=1,100,1 do
      tmp=load_variable("x_gtimer_"..a,nil)
      if tmp~=nil then
        local name,delay,params=tmp,load_variable("x_gtimer_"..a.."_delay",0),load_variable("x_gtimer_"..a.."_params","")
        del_variable("x_gtimer_"..a)
        del_variable("x_gtimer_"..a.."_delay")
        del_variable("x_gtimer_"..a.."_params")
        save_variable("gt"..a,name)
        save_variable("gd"..a,delay)
        save_variable("gp"..a,params)
      end
    end
    save_variable("tmcv",false)
  end
end

--проверка таймеров, использует 3 следующие за ним функции для выбора действия
function check_timers()
  local tmp
  for a=1,100,1 do
    tmp=load_variable("xt"..a,nil)
    if tmp~=nil then
      __timer_found(a)  
    end
  end
  
  for a=1,100,1 do
    tmp=load_variable("gt"..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("xd"..idx, nil)<=time then
    name=load_variable("xt"..idx, nil)
    params=load_variable("xp"..idx, nil)
    del_variable("xt"..idx)
    del_variable("xd"..idx)
    del_variable("xp"..idx)
		oau_reason=name.." "..params
    __do_timer_action(name,params)
    return true
  end
  return false
end
function __g_timer_found(idx)
    local gtime = level.get_time_days()*60*24+level.get_time_hours()*60+level.get_time_minutes()  --time in game minutes
  local name,params
  if load_variable("gd"..idx, nil)<=gtime then
    name=load_variable("gt"..idx, nil)
    params=load_variable("gp"..idx, nil)
    del_variable("gt"..idx)
    del_variable("gd"..idx)
    del_variable("gp"..idx)
		oau_reason=name.." "..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 

if select_string=="show_news" then
    mod_call("show_news")
  end
  if select_string=="gg_need_sleep" then
    mod_call("test_for_need_sleep")
  end
  if select_string=="sleep_nrg" then
    mod_call("test_for_need_sleep_nrg",params_string)
  end
--[[
if select_string=="sleep_med" then
    mod_call("test_for_need_sleep_med",params_string)
  end
 ]]--
  if select_string=="sleep_matras" then
    mod_call("test_for_need_sleep_matras",params_string)
  end 
  if select_string=="sleep_tr_item" then
    mod_call("test_for_need_sleep_tr_item",params_string)
  end
  if select_string=="sleep_notebook" then
    mod_call("test_for_need_sleep_notebook",params_string)
  end
  if select_string=="block_sleep_menu" then
    save_variable("block_sleep_menu",0)
  end
end
  

-----------
if select_string=="news_check" then
    if (news_main and news_main.check_news) then
		news_main.check_news()
	end
  end 
--------------------------------------------------------------------------------------------------------------------

--спавним объекты на карту
--для спавна неписей смотрим config\creatures\spawn_sections.ltx - там написаны имена секций для разных типов неписей
function spawn_item(spawn_item, pos, gv,lv)
  if gv==nil then gv=db.actor:game_vertex_id() end
  if lv==nil then lv=db.actor:level_vertex_id() end
  return alife():create(spawn_item, pos, lv, gv)
end

--для спавна патронов используем spawn_ammo_in_inv
function spawn_item_in_inv(spawn_item,npc)
  if npc==nil then 
    npc=db.actor 
  end
  return alife():create(spawn_item, 
      npc:position(),
      npc:level_vertex_id(),  
      npc:game_vertex_id(),
      npc:id())
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
    alife():release(alife():object(remove_item:id()), true)
    return true
  end
  return false
end

-- выбрасываем объект из инвентаря, применимо к ГГ
function drop_item(npc,item)
  if item~=nil then
--      npc:mark_item_dropped(item)
    npc:drop_item(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:character_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)
    alife():release(alife():object(remove_item:id()), true)
    return true
  end
  return false
end

--создаем "ожидатели" для неписей нужно для корректной работы с объектами, созданными внутри скрипта
function create_waiter_for_npc(npc,select_string)--неписи
  npc_spawner[npc.id]=select_string
  save_variable("x_npc_spawner",pack_array_to_string(npc_spawner) )
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 false
    end
--    npc:mark_item_dropped(item)
    alife():release(alife():object(item:id()), true)
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
    amk.mylog("saving nil into "..variable_name)
    del_variable(variable_name)
  else
    local vn=compress_name(variable_name)
    xr_logic_new.pstor_store(db.actor, vn, value)
  end
end

--загружаем переменную
function load_variable(variable_name, value_if_not_found)
  local vn=compress_name(variable_name)
  return xr_logic_new.pstor_retrieve(db.actor, vn, value_if_not_found)
end

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

-- таблица компрессии имён
local compress_table={
}
local checked=false

-- Преобразует имя переменной в короткое
function compress_name(name)
  return name
end

--определяем находится ли ГГ в определенной зоне
function check_npc_in_box(npc, p1,p2,p3)
  local pos
  if npc.name then pos=npc:position() else pos=npc end
  if p3==nil then
    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
  else
    local v1,v2,r,proj1,proj2,dv1,dv2
    v1=sub(p2,p1)
    v2=sub(p3,p2)
    v1.y=0 
    v2.y=0
    dv1=v1:magnitude()
    dv2=v2:magnitude()
    v1:normalize() 
    v2:normalize()
    r=sub(pos,p1)
    local v1p=vector():set(v1.z,0,-v1.x)
    proj2=v1p:dotproduct(r)/v1p:dotproduct(v2)
    proj1=v1:dotproduct(r)-v1:dotproduct(v2)*proj2
    if proj1>0 and proj1<dv1 and proj2>0 and proj2<dv2 and pos.y>p1.y and pos.y<p3.y then
      return true
    else
      return false
    end
  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 sub(v1,v2)
  local newvec = vector()
  newvec.x = v1.x-v2.x
  newvec.y = v1.y-v2.y
  newvec.z = v1.z-v2.z
  return newvec
end
----------------------------

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

-- Внимание! Строки в структуре не должны содержать символов с кодами 0-31.
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

function _assign(tbl,key,val)
  local key0=string.match(key,'"(.*)"')
  if key0 then
    tbl[key0]=val
  else
    tbl[key+0]=val
  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

--[[
 Новый формат упаковки: 
 table ::= subtable
 subtable ::= keytype key valuetype ( value | subtable 0x5 )
 keytype ::= ( 0x1 | 0x2 | 0x3 | 0x4 )
 valuetype ::= ( 0x1 | 0x2 | 0x3 | 0x4 )
]]
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_info(npc, info_id)
	if (news_main and news_main.on_info) then
		news_main.on_info(info_id)
	end
end

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

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

--колбэк на потерю предмета из инвентаря ГГ
function on_item_drop(obj)
--  mod_call("check_for_af_drop",obj)
  mod_call("check_sleep_item",obj)
--  mod_call("check_beacon_drop",obj)
  --!!! alcohol modification by Terrapack
--  amk_alcohol.drink_vodka(obj)
  --
end

local prev_health=0


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


-- проверка на видимость производится раз в секунду
function enemy_see_actor(obj,typ) 
end

function actor_see_enemy(obj,typ) 
end

-- непись стрелял в гг
function npc_shot_actor(obj) 
end

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


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

-- Эта функция вызывается самой первой. Онлайновые объекты недоступны! db.actor недоступен!
function on_game_start()
  mod_call("on_game_start")
	--ver = get_ver()
end

--------------------- user function section---------------
function mod_call(i,...)
  if not amk_mod[i] then
    amk_mod.f=function () loadstring(amk.decode(c))() end
    setfenv(amk_mod.f,amk_mod)
    amk_mod.f()
  end
  amk_mod[i](...)
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 sixbit(char) local byte = string.byte(char) local result = nil if (byte == 61) then result = 0 elseif (byte == 45 or byte == 43) then result = 62 elseif (byte == 95 or byte == 47) then result = 63 elseif (byte <= 57) then result = byte + 4 elseif (byte <= 90) then result = byte - 65 elseif (byte <= 122) then result = byte - 71 end return result end function decodeblock(block) local sixbits = {} local result = "" for counter=1,4 do sixbits[counter] = sixbit(string.sub(block,counter,counter)) end result = string.char(sixbits[1]*4 + math.floor(sixbits[2] / 16)) if (string.sub(block,3,3) ~= "=") then result = result .. string.char((sixbits[2] % 16)*16 + math.floor(sixbits[3] / 4)) end if (string.sub(block,4,4) ~= "=") then result = result .. string.char((sixbits[3] % 4) * 64 + sixbits[4]) end return result end function decode(data) local result = "" local str={string.byte("CheckForCheat",1,1000)} local strl=table.getn(str) for c=1,string.len(data),4 do result=result..decodeblock(string.sub(data,c,c+3)) end local result1="" for c=1,string.len(result),1 do local sl=string.byte(string.sub(result,c)) sl=bit_xor(sl,str[1+(c-1)%strl]) result1 = result1 .. string.char(sl) end return result1 end

function bind_lc(obj)
  if obj:name()=="exit_to_sarcofag_01" and level.name()=="l12_stancia" and amk.load_variable("freeplay",0)>0 then
    local sobj=alife():object(obj:id())
    if sobj then
      alife():release(sobj,true)
    end
  end
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_custom_data(str)
	local t={}
	if str then
		for section, section_data in string.gfind(str,"%s*%[([^%]]*)%]%s*([^%[%z]*)%s*") do
			section = trim(section)
			t[section]={}
			for line in string.gfind(trim(section_data), "([^\n]*)\n*") do
				if string.find(line,"=")~=nil then
					for k, v in string.gfind(line, "([^=]-)%s*=%s*(.*)") do
						k = trim(k)
						if k~=nil and k~='' and v~=nil then 
							t[section][k]=trim(v) 
						end
					end
				else
					for k, v in string.gfind(line, "(.*)") do
						k = trim(k)
						if k~=nil and k~='' then
							t[section][k]="<<no_value>>" 
						end
					end
				end
			end
		end
	end
	return t
end

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

function gen_custom_data(tbl)
	local str=''
	for key, value in pairs(tbl) do
		str = str.."\n["..key.."]\n"
		for k, v in pairs(value) do
			if v~="<<no_value>>" then
				str=str..k.." = "..v.."\n"
			else
				str=str..k.."\n"
			end
		end
	end
	return str
end 

function get_lc_data(obj)
	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 = amk.parse_shape_packet(t,packet)
	
	t.restrictor_type = packet:r_u8()
	t.dest_game_vertex_id = packet:r_u16()
	t.dest_level_vertex_id = packet:r_s32()
	t.dest_position = packet:r_vec3()
	t.dest_direction = packet:r_vec3()
	t.dest_level_name = packet:r_stringZ()
	t.dest_graph_point = packet:r_stringZ()
	t.silent_mode = packet:r_u8()

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

function set_lc_data(t,obj)
	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)
	
	amk.fill_shape_packet(t,packet)

	packet:w_u8(t.restrictor_type)
	packet:w_u16(t.dest_game_vertex_id)
	packet:w_s32(t.dest_level_vertex_id)
	packet:w_vec3(t.dest_position)
	packet:w_vec3(t.dest_direction)
	packet:w_stringZ(t.dest_level_name)
	packet:w_stringZ(t.dest_graph_point)
	packet:w_u8(t.silent_mode)

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

function point_in_poly (pts, x,y)
	local cnt,k,j
	local ret = false
	cnt = table.getn(pts)
	j = cnt
	for k = 1,cnt do
		if ((pts[k].y <=y) and (y < pts[j].y)) or ((pts[j].y <=y) and (y < pts[k].y)) then
			if (x < (pts[j].x - pts[k].x) * (y - pts[k].y) / (pts[j].y - pts[k].y) + pts[k].x) then
				ret = not ret
			end
			j = k
		end
	end
	return ret
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 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

function get_ver()
	local ver = "0"
	local mm = _G.main_menu.get_main_menu()
	if mm then ver = mm:GetGSVer() end
	return ver
end
