--[[

File:  gps_habar.script
Copyright by Idler (and AMK TEAM 2007-2008 местами :)
rev: 23/08/08.01
]]

local is_gps_debug = false -- для включения локальной отладочной информации

local labels = {} -- в таблице ссылки на тайники помеченные маячками, а также реквизиты маячка (время до заряда  и т.д.). содержит всегда актуальные значения
local items_in_box = {}

--  переменные из конфига  misc\gps_habar.ltx
local activity_time = 0
local warning_time = 0
local warning_icons_time = 0
local habar_list_mode = true
local auto_recharge = true
local is_show_quantity = true
local is_update_on_time = true
local is_set_label_on_npc = true
local is_show_health = false
local is_warning_on_death = false

local is_gps_update_in_process = false -- для устранения коллизий при "on_actor_upade"

-- функции и переменные из amk
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 get_obj(id)
	local m_obj = nil
	if (id) then
		m_obj = level.object_by_id(id)
	end
	return m_obj
end

function IsNpcStalker(obj)
	if (obj and _g.IsStalker(obj)) then
		local m_comm  = get_npc_community(obj)
		if (m_comm == "actor" or m_comm == "actor_dolg" or m_comm == "actor_freedom" or m_comm == "stalker" or m_comm == "dolg" or m_comm == "freedom" or m_comm == "bandit" or m_comm == "csky" or m_comm == "renegade") then
			if (obj.name and obj:name() ~= "agr_ratcatcher") then
				return true
			end
		end
	end
	return false
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 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()
  stpk:r_seek(0)
  uppk:r_seek(0)
  local tbl=parse_stalker_packet({},stpk,uppk,size)
  return tbl
end

function get_npc_name(obj)
	local m_s_name = ""
	if (obj) then
		if (IsGameObject(obj)) then
			if (obj.character_name) then
				m_s_name = obj:character_name()
			end
		else
			local ob = get_obj(obj.id)
			if (ob and ob.character_name) then
				m_s_name = ob:character_name()
			else
				if (amk and read_stalker_params and IsNpcStalker(obj)) then
					local tbl = read_stalker_params(obj)
					if(tbl and tbl.charname) then
						m_s_name = tbl.charname
					end
				end
			end
		end
	end
	if (m_s_name == nil) then
		m_s_name = ""
	end
	if (m_s_name == "") then
		--m_s_name = get_npc_community(obj)
	end
	return m_s_name
end

function get_npc_community(obj)
	local m_s_c = ""
	if (obj) then
		if(_g.IsMonster(obj)) then
		else
			if (obj.character_community) then				
				m_s_c = obj:character_community()
			elseif (obj.community) then
				m_s_c = obj:community()
			end
		end
	end
	if (m_s_c == nil) then
		m_s_c = ""
	end
	if (obj and obj.name and m_s_c == "") then
		trace("get_npc_community returns '' for "..obj:name())
	end
	return m_s_c
end

function get_npc_rank(obj)
	local m_rank = ""
	if (obj) then
		m_rank = ranks.get_obj_rank_name(obj)
		if (m_rank == nil) then
			m_rank = ""
		end
	end
	return m_rank
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_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 unpack_array_from_string(_str)
  if not _str or _str == "" then 
	return {} 
  end
  
  if string.sub(_str, 1, 1) ~= string.char(1) then
    -- Старый формат упаковки
    return {}
  else
    -- новый формат упаковки тэгирован символом c кодом 1.
    return parse_new(string.sub(_str, 2, -1))
  end
end

function load_variable(variable_name, value_if_not_found)
  if db.actor then
	return xr_logic.pstor_retrieve(db.actor, variable_name, value_if_not_found)
  end
end

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

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

--записываем переменную
function save_variable(variable_name, value)
  if db.actor then
	  if not value then
	    del_variable(variable_name)
	  else
	    xr_logic.pstor_store(db.actor, variable_name, value)
	  end
  end
end

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 pack_array_to_string(_array)
  return string.char(1) .. pack_new(_array)
end

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

function add_spot_on_map(obj_id, _type, text)
	--возможные типы type смотри в ui\map_spots.xml 
	if obj_id then
		if not text then 
			text = " "
		end
		-- Ставим метку на серверный объект чтобы её не пришлось обновлять
		level.map_add_object_spot_ser(obj_id, _type, 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)
  end
end

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

function toboolean(_str)
	if string.find(string.lower(_str), "true") or _str == "1" then
		return true
	end
	
	return false
end

function game_minutes()
	local gtime = level.get_time_days()*60*24+level.get_time_hours()*60+level.get_time_minutes()  --time in game minutes
	return gtime
end

function game_hours()
	local gtime = level.get_time_days()*24+level.get_time_hours() --time in game hours
	return gtime
end

function objinfo(obj)
	local text = ""
	
	if is_gps_debug and obj then
		if IsGameObject(obj) then
			text = "G-OBJ:"
			if obj.id then
				text = text .. "[" .. tostring(obj:id()) .. "] "
			end
			if obj.section then
				text = text .. ", s_name=" .. tostring(obj:section())
			end
			if obj.character_name then
				text = text .. ", c_name=" .. tostring(obj:character_name())
			end
			if obj.health then
				text = text .. ", health=" .. tostring(obj.health)
			end
		else
			text = "A-OBJ:"
			if obj.id then
				text = text .. "[" .. tostring(obj.id) .. "] "
			end
			if obj.section_name then
				text = text .. ", s_name=" .. tostring(obj:section_name())
			end
			if obj.character_name then
				text = text .. ", c_name=" .. tostring(obj:character_name())
			end
			if obj.parent_id then
				text = text .. ", p_id=" .. tostring(obj.parent_id)
			end
			if obj.health then
				text = text .. ", health=" .. tostring(obj:health())
			end
		end
	end
	
	return text
end

function tableinfo(tbl)
	if is_gps_debug and tbl then
		return pack_array_to_string(tbl)
	end
	
	return ""
end

function mylog(msg)
	if is_gps_debug and msg and get_console() then
		get_console():execute("load ~~~ "..string.sub(tostring(msg),1,200))
--		get_console():execute("flush")
	end
end

function send_gps_tip(news_text)
	mylog("SEND_GPS_TIP:" .. news_text)

	if db.actor then
		xr_sound.set_sound_play(db.actor:id(), "pda_tips")
		db.actor:give_game_news("GPS", game.translate_string(news_text), "ui_iconsTotal_storyline", 0, 5000, 1)
	end
end

function IsHuman(object, class_id)
    local id = class_id or get_clsid (object)
    if id == clsid.actor or id == clsid.script_stalker or id == clsid.script_trader then
       return true
    end
    return false
end

function IsGameObject(obj)
	local bResult = false
	if (obj and obj.fov) then
		bResult = true
	end
	return bResult
end

-- получим Id тайника если знаем предмет в нем лежащий
function get_inventory_box_id(obj)
	if not obj then
		return
	end
	local aobj = alife():object(obj:id())
	if aobj and aobj.parent_id then
		return aobj.parent_id
	end
end

function get_health(npc)
	if npc then
		if IsGameObject(npc) then
			mylog("GET_HEALTH: " .. objinfo(npc))
			return npc.health
		elseif npc.health then
			mylog("GET_HEALTH: " .. objinfo(npc))
			return npc:health()
		end
	end
	
	return nil
end

function get_health_str(npc)
	if get_health(npc) <= 0 then
		return "погиб"
	end
	
	return "жив"
end

-- ищем существует-ли item в тайнике box
function is_find_item(boxId, itemId)
	if boxId and itemId and items_in_box and items_in_box[boxId] then
		for i, v in pairs(items_in_box[boxId]) do
			if v.id == itemId then
				return true
			end
		end
	end
	
	return false
end

-- что-то положили в тайник. Проверим, если к нам имеет отношение, добавим к списку. 
function insert_item(boxId, item)
	if boxId and boxId ~= 0 and boxId ~= 65535 and item then
		if not is_find_item(boxId, item.id) then 
			if not items_in_box[boxId] then items_in_box[boxId] = {} end
			table.insert(items_in_box[boxId], {id = item.id, name=item:name()})
			mylog("INSERT_ITEM: добавили в items_in_box[" .. boxId .. "] предмет:" .. objinfo(item))
		end
	end
end

-- что-то забрали из тайника, почистим нашу таблицу с хабаром
function remove_item(boxId, item)
	if boxId and item then
		local itemId = item.id
		if boxId and itemId and items_in_box[boxId] then
			for i, v in pairs(items_in_box[boxId]) do
				if v.id == itemId then
				mylog("REMOVE_ITEM: удалили из items_in_box[" .. boxId .. "] предмет: " .. objinfo(item))
				table.remove(items_in_box[boxId], i)
				end
			end
		end
	end
end

-- считаем кол-во маячков в тайнике
function get_gps_count(boxId)
	if not boxId then
		return 0
	end
	if boxId == 0 or boxId == 65535 then 
		return 0 
	end

	local locObj
	local gps_count = 0
	
	if items_in_box[boxId] then
		for i, v in pairs(items_in_box[boxId]) do
			locObj = alife():object(v.id)
			if locObj and locObj:section_name() and locObj:section_name() == "amk_metka" then
				gps_count = gps_count + 1
			end
		end
	end
	
	mylog("GET_GPS_COUNT: в тайнике[" .. boxId .. "] нашли маячков: " .. tostring(gps_count))
	return gps_count
end

-- соствялем таблицу вещей в тайниках.
function load_items()
	for i = 1, 65534 do 
		aobj =  alife():object(i)
		if aobj and aobj.parent_id and aobj.parent_id ~= 0 and aobj.parent_id ~=65535 then
			if not items_in_box[aobj.parent_id] then items_in_box[aobj.parent_id] = {} end
			table.insert(items_in_box[aobj.parent_id], {id = aobj.id, name=aobj:name()})
			-- mylog("добавили в items_in_box[" .. aobj.parent_id .. "] предмет Id:" .. aobj.id)
		end
	end
end

function get_local_time(gtime)
	gdays = math.floor(gtime / 24);
	ghours = gtime - gdays * 24;
	
	local res = ""
	if gdays > 0 then
		res = tostring(gdays) .. "дн "
	end
	if ghours > 0 then
		res = res .. tostring(ghours) .. "ч"
	end
	
	return trim(res)
end

-- снимаем метки относящиеся к списку хабара с ящика
function remove_all_spots(boxId)
	if boxId then
		remove_spot_from_map(boxId, "habar_location")
		remove_spot_from_map(boxId, "habar_clear_location")
		remove_spot_from_map(boxId, "habar_location_w")
		remove_spot_from_map(boxId, "habar_clear_location_w")
		remove_spot_from_map(boxId, "gps_stalker")
	end
end

-- удаляем ящик из таблицы тайников с метками
function remove_label(boxId)
	if boxId then
		labels[boxId] = nil
--		table.remove(labels, boxId)
		mylog("REMOVE_LABEL: удалили метку с тайника [" .. boxId .. "]. Осталось:" .. tableinfo(labels))
	end
end

-- почистим тайник от удаленных вещей.
function reinit_box(boxId)
	if not boxId or boxId == 0 or boxId == 65535 then
		return
	end
	
	local aobj
	for i, v in pairs(items_in_box[boxId]) do
		if v.id then
			aobj = alife():object(v.id)
			if not aobj then
				table.remove(items_in_box[boxId], i)
			elseif aobj.parent_id ~= boxId then
				table.remove(items_in_box[boxId], i)
			end
		end
	end
end

-- перезарядка маячка
function recharge_gps(item, box)
	if not auto_recharge then
		return
	end
	
	if not box then return end
	
	-- если неизвестен тайник, получим его из предмета
	local boxId = nil
	if box then
		boxId = box:id()
	else
		boxId = get_inventory_box_id(item)
	end
	
	-- если предмет не в тайник и не из тайника - не наш случай
	if not boxId then
		return
	end
	if boxId == 0 or boxId == 65535 then 
		return 
	end
	
	-- если метки нет на тайнике,  перезаряжать нечего
	if not labels[boxId] then
		return
	end
	
	-- перезаряка новыми значениями
	labels[boxId].at = activity_time
	labels[boxId].wr = 0 -- не предупреждали о разряде сообщением
	labels[boxId].wi = 0 -- не предупреждали о разряде цветом
	labels[boxId].wt = warning_time
	labels[boxId].wit = warning_icons_time
	labels[boxId].gt = game_hours()
	labels[boxId].upd = game_minutes()
	labels[boxId].wh = 0 -- не предупреждали о гибели npc
	
	save_table("gps_labels", labels)
	mylog("RECHARGE_GPS: перезарядили маячок в тайнике [" .. tostring(boxId) .. "]")
end

-- Обновляем список хабара, зная или предмет или ящик
function update_gps(item, box, boxId, is_new_metka)

	is_gps_update_in_process = true

	-- если неизвестен тайник, получим его из предмета
	if not boxId then
		if box then
			boxId = box:id()
		else
			boxId = get_inventory_box_id(item)
		end
	end

	if boxId then
		box = alife():object(boxId)
	end
	
	-- если предмет не в тайник и не из тайника - не наш случай
	if not boxId then
		is_gps_update_in_process = false
		return
	end

	if not box then
		-- непись мог погибнуть, ну или тайник могли уничтожить (наверное)
		local aobj = alife():object(boxId)
		if not aobj then
			remove_label(boxId)
		end
		
		is_gps_update_in_process = false
		return
	end
	
	if boxId == 0 or boxId == 65535 then 
		is_gps_update_in_process = false
		return
	end

	if IsHuman(box) then
		mylog("UPDATE_GPS: начато обновление метки сталкера " .. objinfo(box))
		update_gps_stalker(box, boxId, is_new_metka)
	else
		mylog("UPDATE_GPS: начато обновление метки хабара " .. objinfo(box))
		update_gps_habar(boxId)
	end
	
	is_gps_update_in_process = false
end

function update_gps_stalker(box, boxId, is_new_metka)
	local gps_count = get_gps_count(boxId)
	local is_save_table = false
	
	if gps_count > 0 then
		local fl_send_tip_msg = false
		local text_metki = ""
		local gm = game_minutes()
		local gh = game_hours()
		
		-- новая метка, добавляем в таблицу
		if not labels[boxId] then
			-- нет команды помечать сталкера
			if is_new_metka ~= true then
				return
			end
			
			fl_send_tip_msg = true -- надо сообщить что ставим новую метку
			labels[boxId] = {wr = 0, wi = 0, gt = gh, wt = warning_time, at = activity_time, wit = warning_icons_time, upd = gm, wh = 0}
			is_save_table = true
			mylog("UPDATE_GPS_STALKER: уст. метка на сталкера " .. objinfo(box) .. ". " .. tableinfo(labels[boxId]))
		end

		-- помеченный погиб, предупредим ГГ
		local aobj = alife():object(boxId)
		if is_warning_on_death and aobj and get_health(aobj) <= 0 and labels[boxId].wh == 0 then
			labels[boxId].wh = 1
			is_save_table = true
			send_gps_tip("%c[255,255,128,128]Погиб сталкер " .. tostring(get_npc_name(box)) .. " с GPS-маячком")
		end
		
		-- на сталкера вешаем другие метки, чем на тайник
		local text_health = ""
		if is_show_health then
			text_health = ", " .. get_health_str(box)
		end
		text_metki = get_npc_name(box) .. " (" .. game.translate_string(get_npc_community(box)) .. ", " .. game.translate_string(get_npc_rank(box)) .. text_health .. ")."
		
		local location = "gps_stalker"
		if fl_send_tip_msg then
			send_gps_tip("Установлена метка на сталкера " .. get_npc_name(box))
		end

		--апдейтим список
		remove_all_spots(boxId)
		add_spot_on_map(boxId, location, text_metki)
	else
		remove_all_spots(boxId)
		remove_label(boxId)
		is_save_table = true
	end
	
	if is_save_table then
		save_table("gps_labels", labels)
	end
end

function update_gps_habar(boxId)
	-- если в тайнике есть метка, апдейтим или ставим список. иначе снимаем маркер
	local gps_count = get_gps_count(boxId)
	if gps_count > 0 then
		local fl_send_tip_msg = false
		local text_metki = ""
		local gm = game_minutes()
		local gh = game_hours()
		
		-- новая метка, добавляем в таблицу
		if not labels[boxId] then
			fl_send_tip_msg = true -- надо сообщить что ставим новую метку
			labels[boxId] = {wr = 0, wi = 0, gt = gh, wt = warning_time, at = activity_time, wit = warning_icons_time, upd = gm, wh = 0}
			save_table("gps_labels", labels)
			mylog("UPDATE_GPS_HABAR: уст. новая метка с хабаром на тайник. " .. tableinfo(labels[boxId]))
		end

		--составляем список предметов
		if items_in_box[boxId] then
			local secName, invName, locObj
			local itemsbox = {}
			local is_yes
			
			for i, v in pairs(items_in_box[boxId]) do
				if table.getn(itemsbox) > 51 then
					break
				end
				
				locObj = alife():object(v.id)
				if locObj and locObj:section_name() then
					secName = locObj:section_name()
					invName = game.translate_string(system_ini():r_string(secName, "inv_name"))
					
					is_yes = false
					-- mylog("itemsbox = " .. table.getn(itemsbox))
					for k, kk in pairs(itemsbox) do
						-- mylog("k = " .. k)
						if kk.sn == secName then
							kk.cnt = kk.cnt + 1
							is_yes = true
							break
						end
					end
					
					if not is_yes then
						table.insert(itemsbox, {sn = secName, inv = invName, cnt = 1})
					end
				end
			end
			
			-- лежит только мячок, считаем что тайник пустой
			if table.getn(itemsbox) == 1 and itemsbox[1].sn == "amk_metka" and itemsbox[1].cnt == 1 then
			else
				local items_count = 0
				for i, v in pairs(itemsbox) do
					--mylog("i = " .. i .. " name = " .. v.sn)
					if not (v.sn == "amk_metka" and v.cnt == 1) then
						items_count = items_count + 1
						if items_count > 50 then
							text_metki = text_metki .. ", ..."
							break
						end
						
						if text_metki ~= "" then 
							text_metki = text_metki .. ", "
						end
						text_metki = text_metki .. v.inv
						if is_show_quantity then 
							text_metki = text_metki .. "(" .. tostring(v.cnt) .. ")" 
						end
					end
				end
			end
		end

		-- mylog("Список хабара: " .. text_metki)
	
		--смотрим какого цвета нужны иконки
		local is_warning = false
		local labels_item = labels[boxId]
		if habar_list_mode and labels_item.at > 0 and labels_item.wit > 0 and ((gh - labels_item.gt) >= (labels_item.at - labels_item.wit)) then
			is_warning = true
		end
		
		-- смотрим пустой тайник или есть что-то?
		if text_metki == "" then
			if fl_send_tip_msg then
				send_gps_tip("В схроне нет хабара. Установлена метка пустого тайника.")
			end
			if is_warning then
				location = "habar_clear_location_w"
			else
				location = "habar_clear_location"
			end
			
			text_metki = "Пустой тайник."
		else
			text_metki = text_metki .. "."
			if fl_send_tip_msg then
				send_gps_tip("Установлена метка со списком хабара.")
			end
			if is_warning then
				location = "habar_location_w"
			else
				location = "habar_location"
			end
		end
		
		-- добавим инфу о разряде
		if habar_list_mode and labels_item.at > 0 then
			text_metki = get_local_time(labels_item.gt + labels_item.at - gh) .. ". " .. text_metki
		end
		
		--апдейтим список
		remove_all_spots(boxId)
		add_spot_on_map(boxId, location, text_metki)
	else
		remove_all_spots(boxId)
		remove_label(boxId)
		save_table("gps_labels", labels)
	end
end

function update_gps_state()
	local aobj, lvname
	local is_save_table = false
	
	-- если юзаем режим разряда маячков
	if habar_list_mode then
		local gm = game_minutes()
		local gh = game_hours()
		
		for i, v in pairs(labels) do
			-- если надо предупредить о разряде сообщением
			if v.at > 0 and v.wt > 0 and v.wr == 0 and gh - v.gt > v.at - v.wt then
				aobj = alife():object(i)
				lvname = ""
				if aobj then
					lvname = game.translate_string(alife():level_name(game_graph():vertex(aobj.m_game_vertex_id):level_id()))
				end

--					mylog("i = " .. i)
				v.wr = 1
				send_gps_tip("%c[255,255,128,128]Через ".. tostring(gh - v.gt) .. " ч. на маячке на локации " .. lvname .. " надо сменить батарею.")
				is_save_table = true
			end
			
			-- апдейтим информацию каждые 3 минуты или если надо предупредить о разряде сменой иконок
			if (gm - v.upd > 3 and is_update_on_time) or (v.wit > 0 and v.wi == 0 and gh - v.gt > v.at - v.wit) then
--						mylog("i = " .. i)
				v.wi = 1
				v.upd = gm;
				
				if not is_gps_update_in_process then
					update_gps(nil, nil, i)
				end
				is_save_table = true
			end
			
			-- если время вышло, выключаем метку
			if v.at > 0 and gh - v.gt > v.at then
				aobj = alife():object(i)
				lvname = ""
				if aobj then
					lvname = game.translate_string(alife():level_name(game_graph():vertex(aobj.m_game_vertex_id):level_id()))
				end
				send_gps_tip("%c[255,255,128,128]На маячке на локации " .. lvname .. " села батарея. Связь потеряна.")

				remove_all_spots(i)
				remove_label(i)
				is_save_table = true
			end	
		end
	end
	
	if is_save_table then
		save_table("gps_labels", labels)
	end
end

function show_dlg_dots_npc(obj)
	if not obj or not is_set_label_on_npc then
		return
	end

	mylog("show_dlg_dots_npc: проверим объект " .. objinfo(obj))

	-- это не маячок или маячок, но лежит у ГГ в инвентаре, заканчиваем
	local secName = obj:section()
	local boxId = get_inventory_box_id(obj)
	if not boxId or boxId == 0 or not secName or secName ~= "amk_metka" then
		return
	end
	
	-- выкинули маячок на землю, предложим поставить текстовую метку
	if boxId == 65535 then
		local spwn=ui_dots.dots(get_hud(), obj:id())
		level.start_stop_menu(spwn, true)
		return
	end
	
	mylog("show_dlg_dots_npc: маячок куда-то положили: " .. objinfo(obj))
	-- маячок попал не сталкеру или метка уже установлена, заканчиваем.
	local box = alife():object(boxId)
	if not box then 
		return
	end
	if not IsHuman(box) or labels[boxId] then
		return
	end
	
	local spwn=ui_gps_trade.gps_trade(get_hud(), boxId)
	level.start_stop_menu(spwn, true)
end

-- вызываем из amk.script
-- при загрузке игры
function on_game_load()
	mylog("Инициализация переменных модуля gps_habar.")

	local gps_ini = ini_file("misc\\gps_habar.ltx")
	
	activity_time = tonumber(gps_ini:r_string("amk_metka", "activity_time"))
	warning_time = tonumber(gps_ini:r_string("amk_metka", "warning_time"))
	warning_icons_time = tonumber(gps_ini:r_string("amk_metka", "warning_icons_time"))
	habar_list_mode = toboolean(gps_ini:r_string("amk_metka", "habar_list_mode"))
	auto_recharge = toboolean(gps_ini:r_string("amk_metka", "auto_recharge"))
	is_show_quantity = toboolean(gps_ini:r_string("amk_metka", "show_quantity"))
	is_update_on_time = toboolean(gps_ini:r_string("amk_metka", "update_on_time"))
	
	-- метки на насталкерах
	is_set_label_on_npc = toboolean(gps_ini:r_string("amk_metka", "set_label_on_npc"))
	is_show_health = toboolean(gps_ini:r_string("amk_metka", "show_health"))
	is_warning_on_death = toboolean(gps_ini:r_string("amk_metka", "warning_on_death"))
	
	-- загружаем таблицу привязки ящиков к меткам.
	labels = load_table("gps_labels")
	mylog("Загрузили таблицу меток: " .. tableinfo(labels))
	load_items()
end

--колбэк на взятие предмета в инвентарь ГГ
-- вызываем из amk.script
function on_item_take(item)
	-- если взяли метку, проверяем всех помеченных. вдруг меку купили у одного из них.
	if item and item:section() and item:section() == "amk_metka" then
		for i, v in pairs(labels) do
			reinit_box(i)
			update_gps(nil, nil, i)
		end
		remove_spot_from_map(item:id(), "red_location") -- вдруг подняли стандартную метку, снимем пометку с карты.
	end
end

function on_item_take_from_box(box, item)
	mylog("ON_ITEM_TAKE_FROM_BOX: взят предмет " .. objinfo(item) .. ". Из ящика. Id: " .. tostring(boxId))
	-- если удалили из ящика почистим items_in_box
	if item and item:id() and box and box:id() then
		remove_item(box:id(), alife():object(item:id()))
	end
	
	recharge_gps(item, box)
	update_gps(item, box, nil)
end

--колбэк на потерю предмета из инвентаря ГГ
-- вызываем из amk.script
function on_item_drop(obj)

	mylog("ON_ITEM_DROP: потерян предмет: " .. objinfo(obj))
	if obj and obj:id() then
		insert_item(get_inventory_box_id(obj), alife():object(obj:id()))
	end

	show_dlg_dots_npc(obj)
	recharge_gps(obj, nil)
	update_gps(obj, nil, nil)
end

--колбэк на апдейт ГГ (удобно для проверки условий, так как вызывается постоянно, нельзя перегружать, а то будут лаги)
-- вызываем из amk.script
function on_actor_upade(delta)
	update_gps_state()
end

function on_trade(item, sell_bye)
	mylog("on_trade: предмет " .. objinfo(obj) .. ", sell_bye = " .. tostring(sell_bye))
end
