local DEATH_IDLE_TIME = 10*60 -- секунд
SMART_TERRAIN_SECT = "smart_terrain"
smart_terrains_by_name = {}
local locations_ini			= ini_file("misc\\smart_terrain_masks.ltx")

nearest_to_actor_smart = {id = nil , dist = math.huge}

local path_fields = { "path_walk", "path_main", "path_home", "center_point" }

local valid_territory = {
	default = true,
	base = true,
	resource = true,
	territory = true
}

--' Проверка, что нпс подходит работе
local function job_avail_to_npc(npc_info, job_info, smart)
	--printf("job_avail_to_npc %s job %s smart %s", npc_info.se_obj:name(), tostring(job_info.job_id), smart:name())
	local job = smart.job_data[job_info.job_id]
	if job ~= nil then
		job = job.section
	end

	if smart.dead_time[job_info.job_id] ~= nil then
		return false
	end

	-- Проверка условия "монстровости"
	if job_info._precondition_is_monster ~= nil and job_info._precondition_is_monster ~= npc_info.is_monster then
		return false
	end

	--' Проверяем подходит ли нпс по предикату
	if job_info._precondition_function ~= nil then
		if not job_info._precondition_function(npc_info.se_obj, smart, job_info._precondition_params, npc_info) then
			return false
		end
	end
	return true
end


-- Итерируемся по НПС, начинаем со свободных нпс, потом НПС на низкоприоритетных работах, потом на высокоприоритетных.
-- Для каждого конкретного НПС ищем работу.
-- Отсеиваем в поиске работы, приоритет которых ниже, чем у текущей.
local function job_iterator(jobs, npc_data, selected_job_prior, smart)
	--printf("  iterate")
	-- итерируемся по работам
	local current_job_prior = selected_job_prior
	local selected_job_id = nil
	local selected_job_link = nil
	for k,v in pairs(jobs) do
		-- Если приоритет у проверяемой работы ниже, чем приоритет текущей выбранной работы НПС - завершаем выполнение
		if current_job_prior > v._prior then
			return selected_job_id, current_job_prior, selected_job_link
		end
		-- Проверяем, может ли НПС занять данную работу
		if job_avail_to_npc(npc_data, v, smart) then
			-- Это работа-кластер или работа-описание.
			if v.job_id == nil then
				-- Вызываем рекурсивно себя для списка работ кластера
				selected_job_id, current_job_prior, selected_job_link = job_iterator(v.jobs, npc_data, selected_job_prior, smart)
			else
				-- Если работа пустая или ее занимаем мы сами - выбираем ее.
				if v.npc_id == nil then
					return v.job_id, v._prior, v
				elseif v.job_id == npc_data.job_id then
					return v.job_id, v._prior, v
				end
			end
		end
	end
	return selected_job_id, current_job_prior, selected_job_link
end

-- Расстояние до работы
local function arrived_to_smart(obj, smart)
	local obj_gv, obj_pos

	local storage = db.storage[obj.id]

	if storage == nil then
		obj_gv, obj_pos = game_graph():vertex(obj.m_game_vertex_id), obj.position
	else
		local obj = db.storage[obj.id].object
		obj_gv, obj_pos = game_graph():vertex(obj:game_vertex_id()), obj:position()
	end

	local smart_gv = game_graph():vertex(smart.m_game_vertex_id)

	if obj.group_id then
		local squad = smart.board.squads[obj.group_id]
		if squad ~= nil and squad.current_action then
			if squad.current_action.name == "reach_target" then
				local squad_target = simulation_objects.get_sim_obj_registry().objects[squad.assigned_target_id]
				if squad_target ~= nil then
					return squad_target:am_i_reached(squad)
				else
					return alife():object(squad.assigned_target_id):am_i_reached(squad)
				end
			elseif squad.current_action.name == "stay_point" then
				return true
			end
		end
	end

	if obj_gv:level_id() == smart_gv:level_id() then
		return obj_pos:distance_to_sqr(smart.position) <= 10000 --Ближе 100 метров
	else
		return false
	end
end

----------------------------------------------------------------------------------------------------------------------
-- Класс "se_smart_terrain". Обеспечивает поддержку smart terrain в ОФЛАЙНЕ.
----------------------------------------------------------------------------------------------------------------------
class "se_smart_terrain" (cse_alife_smart_zone)
function se_smart_terrain:__init(section) super(section)
	self.initialized	= false
	self.b_registred	= false
	self.population		= 0

	self.npc_to_register = {}
	self.npc_by_job_section = {}
	self.dead_time = {}

	-- Таблица для хранения зарегистренных НПС
	self.npc_info  = {} -- Те, кто уже пришел и стал на работу
	self.arriving_npc  = {} -- Только идущие на работу.

	self.respawn_radius = 150
	self.respawn_time   = 1000
end
function se_smart_terrain:on_before_register()
	cse_alife_smart_zone.on_before_register(self)
	self.board = sim_board.get_sim_board()
	self.board:register_smart(self)
	self.smart_level	= alife():level_name(game_graph():vertex(self.m_game_vertex_id):level_id())
	--printf("SMARTLEVEL %s level %s", self:name(), tostring(self.smart_level))
end

function se_smart_terrain:on_register()
	cse_alife_smart_zone.on_register(self)
	-- Проверяем кастомдату обьекта на наличие стори айди.
	story_objects.check_spawn_ini_for_story_id(self)
	simulation_objects.get_sim_obj_registry():register(self)

	printf("register smart %s", self:name())

	if dev_dedug then
		self:refresh()
	end

	printf("Returning alife task for object [%s] game_vertex [%s] level_vertex [%s] position %s", self.id, self.m_game_vertex_id, self.m_level_vertex_id, vec_to_str(self.position))
 	self.smart_alife_task = CALifeSmartTerrainTask(self.m_game_vertex_id, self.m_level_vertex_id)

	smart_terrains_by_name[self:name()] = self
	self.b_registred = true

	self:load_jobs()

	self.board:init_smart(self)

	if self.need_init_npc == true then
		self.need_init_npc = false
		self:init_npc_after_load()
	end

	-- Регистрим персонажей, которые добавили до регистрации смарта. (отложенный список)
	self:register_delayed_npc()

	self.check_time = time_global()
end
-- анрегистрация объекта в симуляторе.
-- вызывается симулятором.
function se_smart_terrain:on_unregister()
	cse_alife_smart_zone.on_unregister(self)
	self.board:unregister_smart(self)
	smart_terrains_by_name[self:name()] = nil
	unregister_story_object_by_id(self.id)
	simulation_objects.get_sim_obj_registry():unregister(self)
end
-- чтение custom data.
function se_smart_terrain:read_params()
	self.ini  = self:spawn_ini()

	if not self.ini:section_exist( SMART_TERRAIN_SECT ) then
		abort( "[smart_terrain %s] no configuration!", self:name() )
		self.disabled = true
		return
	end
	local filename = utils.cfg_get_string(self.ini, SMART_TERRAIN_SECT, "cfg", 				self, false, "")
	local fs = getFS()
	if filename and filename~="" then
		if fs:exist("$game_config$",filename) then
			self.ini = ini_file(filename)
		else
			abort("There is no configuration file [%s] in smart_terrain [%s]", filename, 	self:name())
		end
	end
	local ini = self.ini
	self.sim_type		= utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "sim_type", 		self, false, "", "default")

	--' Вычитка симуляционных свойств
	if valid_territory[self.sim_type] == nil then
		abort("Wrong sim_type value [%s] in smart [%s]", self.sim_type, self:name())
	end

	self.squad_id		= utils.cfg_get_number(ini, SMART_TERRAIN_SECT, "squad_id", 		self, false, 0)
	self.respawn_sector = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "respawn_sector", 	self, false, "")
	self.respawn_radius  = utils.cfg_get_number(ini, SMART_TERRAIN_SECT, "respawn_radius", 	self, false, 150) -- радиус респауна (если актер ближе, то не спаунить)
	self.respawn_time   = utils.cfg_get_number(ini, SMART_TERRAIN_SECT, "respawn_time", 	self, false, 1000) -- в секундах игрового времени																										   
	if self.respawn_sector ~= nil then
		if self.respawn_sector == "default" then
			self.respawn_sector = "all"
		end
		self.respawn_sector = xr_logic.parse_condlist(nil, SMART_TERRAIN_SECT, "respawn_sector", self.respawn_sector)
	end

	self.mutant_lair 		= utils.cfg_get_bool(ini, SMART_TERRAIN_SECT, "mutant_lair", 	self, false)
	self.no_mutant 			= utils.cfg_get_bool(ini, SMART_TERRAIN_SECT, "no_mutant", 		self, false)
	if self.no_mutant == true then
		printf("Found no mutant point %s", self:name())
	end
	self.forbidden_point	= utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "forbidden_point", self, false, "")

	--' Рестрикторы для симуляции
	self.def_restr		= utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "def_restr", 	self, false, "", nil)
	self.att_restr		= utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "att_restr", 	self, false, "", nil)
	self.safe_restr		= utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "safe_restr", 	self, false, "", nil)

	self.spawn_point	= utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "spawn_point", 	self, false, "")

	self.arrive_dist	= utils.cfg_get_number(ini, SMART_TERRAIN_SECT, "arrive_dist", 	self, false, 30)

--	self.max_population = utils.cfg_get_number(ini, SMART_TERRAIN_SECT, "max_population", self, false, 0)
	local max_population = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "max_population", self, false, "", 0)
	local parsed_condlist = xr_logic.parse_condlist(nil, SMART_TERRAIN_SECT, "max_population", max_population)
	self.max_population = tonumber(xr_logic.pick_section_from_condlist(get_story_object("actor"), nil, parsed_condlist))

--	self.sim_avail		= utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "sim_avail", 	self, false, "")
--	if self.sim_avail ~= nil then
--		self.sim_avail = xr_logic.parse_condlist(nil, SMART_TERRAIN_SECT, "sim_avail", 	self.sim_avail)
--	end

	local respawn_params = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "respawn_params", 		self, false, "", nil)
	self.respawn_only_smart = utils.cfg_get_bool(ini, SMART_TERRAIN_SECT, "respawn_only_smart", self, false, false)

	local smart_control_section = utils.cfg_get_string(ini, SMART_TERRAIN_SECT, "smart_control", self, false, "", nil)

	if smart_control_section ~= nil then
		self.base_on_actor_control = smart_terrain_control.CBaseOnActorControl(self, ini, smart_control_section)
	end
	self.respawn_point = false
	if respawn_params ~= nil then
		self:check_respawn_params(respawn_params)
	end


	if level.patrol_path_exists(self:name() .. "_traveller_actor") then
		printf("Smart_terrain [%s] has no traveller_actor path!!!!!", self:name())
		self.traveler_actor_path = self:name() .. "_traveller_actor"
	end

	if level.patrol_path_exists(self:name() .. "_traveller_squad") then
		printf("Smart_terrain [%s] has no traveller_squad path!!!!!", self:name())
		self.traveler_squad_path = self:name() .. "_traveller_squad"
	end

	if not locations_ini:section_exist(self:name()) then
		printf("! SMART_TERRAIN [%s] has no terrain_mask section in smart_terrain_masks.ltx!!!",self:name())
	end
end

--*******************************************************
-- МЕТОДЫ ДЛЯ РАБОТЫ С НПС
--*******************************************************
-- заполнить информацию о персонаже
-- у монстров нету метода profile_name()
function se_smart_terrain:fill_npc_info(obj)
	local npc_info = {}
	printf("filling npc_info for obj [%s]", tostring(obj:name()))

	local is_stalker = IsStalker(obj)
	npc_info.se_obj			= obj
	npc_info.is_monster		= not is_stalker
	npc_info.need_job		= "nil"	-- Специально для смены гвардов. Указывает на какую работу хочет данный чувак.
	npc_info.job_prior		= -1
	npc_info.job_id			= -1
	npc_info.begin_job		= false

	if is_stalker then
		npc_info.stype = modules.stype_stalker
	else
		npc_info.stype = modules.stype_mobile
	end

	return npc_info
end

function se_smart_terrain:refresh_script_logic(obj_id)
	local object = alife():object(obj_id)
	local stype = modules.stype_mobile
	if IsStalker(object) then
		stype = modules.stype_stalker
	end
	xr_logic.initialize_obj(db.storage[object.id].object, db.storage[object.id], false, db.actor, stype)
end

-- добавить npc в smart terrain.
function se_smart_terrain:register_npc(obj)
	printf("[smart_terrain %s] register called obj=%s", self:name(), obj:name())
	self.population = self.population + 1

	if self.b_registred == false then
		table.insert(self.npc_to_register, obj)
		return
	end

	-- Только для монстров, чтобы ходили по смартам.
	if not IsStalker(obj) then
		obj:smart_terrain_task_activate()
	end

	obj.m_smart_terrain_id = self.id
	if arrived_to_smart(obj, self) then
		self.npc_info[obj.id] = self:fill_npc_info(obj)

		-- Затычка на случай если мы регистримся в смарт, из которого только что сами вынесли всех врагов.
		self.dead_time = {}

		-- тут надо найти чуваку работу
		self:select_npc_job(self.npc_info[obj.id])
	else
		self.arriving_npc[obj.id]  = obj
	end
end
-- Регистрация НПС в список отложенных. Осуществляется на загрузке или на регистрации НПС, пока не зарегистрен смарт
function se_smart_terrain:register_delayed_npc()
	for k,v in pairs(self.npc_to_register) do
		self:register_npc(v)
	end
	self.npc_to_register = {}
end
-- отпустить npc
function se_smart_terrain:unregister_npc(obj)
	--callstack()
	printf("smart [%s] unregister npc [%s]", self:name(), obj:name())

	self.population = self.population - 1

	if self.npc_info[obj.id] ~= nil then
		-- TODO: Тут надо выгнать чувака с занимаемой им работы
        if self.npc_info[obj.id].job_link == nil then
            return
        end
		self.npc_info[obj.id].job_link.npc_id = nil
		self.npc_info[obj.id] = nil
		obj:clear_smart_terrain()

		if db.storage[obj.id] ~= nil then
			local object = db.storage[obj.id].object
			local stype = modules.stype_mobile
			if IsStalker(obj) then
				stype = modules.stype_stalker
			end
			xr_logic.initialize_obj(object, db.storage[obj.id], false, db.actor, stype)
		end
		return
	end
	if self.arriving_npc[obj.id] ~= nil then
		self.arriving_npc[obj.id] = nil
		obj:clear_smart_terrain()
		return
	end

	abort("self.npc_info[obj.id] = nil !!! obj.id=%d", obj.id)
end
-- Убрать убитого
function se_smart_terrain:clear_dead(obj)
	if self.npc_info[obj.id] ~= nil then
		-- Устанавливаем таймер смерти на работе
		self.dead_time[self.npc_info[obj.id].job_id] = game.get_game_time()

        if self.npc_info[obj.id].job_link == nil then
            return
        end
        
		self.npc_info[obj.id].job_link.npc_id = nil
		self.npc_info[obj.id] = nil
		obj:clear_smart_terrain()
		return
	end
	if self.arriving_npc[obj.id] ~= nil then
		self.arriving_npc[obj.id] = nil
		obj:clear_smart_terrain()
		return
	end

	abort("self.npc_info[obj.id] = nil !!! obj.id=%d", obj.id)
end
-- выдать объекту задание.
function se_smart_terrain:task(obj)
	if self.arriving_npc[obj.id] ~= nil then
		return self.smart_alife_task
	end
	return self.job_data[self.npc_info[obj.id].job_id].alife_task
end


--*******************************************************
-- Функции для работы с работами
--*******************************************************
-- Загрузка работ (из gulag_general)
function se_smart_terrain:load_jobs()
	--printf("LOAD JOBS %s", self:name())
	-- Загружаем иерархию работ
	self.jobs = gulag_general.load_job(self)

	-- Загружаем ltx работ.
	self.ltx, self.ltx_name = xr_gulag.loadLtx(self:name())
	-- Сортируем всю иерархию по уменьшению приоритета
	-- Рекурсивная функция сортировки
	local function sort_jobs(jobs)
		for k,v in pairs(jobs) do
			if v.jobs ~= nil then
				sort_jobs(v.jobs)
			end
		end
		table.sort(jobs, function(a,b) return a._prior > b._prior end )
	end

--	if self:name() == "jup_a10_smart_terrain" then
--		printf("before sort")
--		store_table(self.jobs)
--	end

	sort_jobs(self.jobs)

	--if self:name() == "jup_a10_smart_terrain" then
	--	printf("after sort")
	--	store_table(self.jobs)
	--end


	-- Надо сделать постобработку работ. Проинитить все неиниченные поля
	-- Для более быстрого доступа нужно вычленить параметры работ в отдельную таблицу вида:
	--self.job_data[job_id] = {}
	local id = 0
	self.job_data = {}
	local function get_jobs_data(jobs)
		for k,v in pairs(jobs) do
			if v.jobs ~= nil then
				get_jobs_data(v.jobs)
			else
				if v.job_id == nil then
					print_table(self.jobs)
					abort("Incorrect job table")
				end
				self.job_data[id] = v.job_id

				self.job_data[id]._prior = v._prior -- Кешируем для проверки
				v.job_id = id
				id = id + 1
			end
		end
	end

	get_jobs_data(self.jobs)
	-- Пробегаемся по работам и высчитываем для каждой работы alife_task
	for k,v in pairs(self.job_data) do
		local section = v.section
		local ltx = v.ini_file or self.ltx
		if not ltx:line_exist(section, "active") then
			abort("gulag: ltx=%s  no 'active' in section %s", self.ltx_name, section)
		end
		local active_section = ltx:r_string(section, "active")


--		printf("job_type %s  job_section %s", tostring(v.job_type), tostring(section))
		-- В зависимости от типа работы по разному считаем alife_path
		if v.job_type == "path_job" then  -- работа задается патрульным путем
			local path_field
			for i,vv in pairs(path_fields) do
				if ltx:line_exist(active_section, vv) then
					path_field = vv
					break
				end
			end

			--printf("path_field %s  prefix_name %s active_section %s", tostring(path_field), tostring(v.prefix_name), tostring(active_section))
			local path_name = ltx:r_string(active_section, path_field)
			if v.prefix_name ~= nil then
				path_name = v.prefix_name .. "_" .. path_name
			else
				path_name = self:name() .. "_" .. path_name
			end

			if path_field == "center_point" then --' TODO убрать затык когда переделаем кемпы на смарткаверы
				if level.patrol_path_exists(path_name .. "_task") then
					path_name = path_name .. "_task"
				end
			end

			v.alife_task = CALifeSmartTerrainTask(path_name)

		elseif v.job_type == "smartcover_job" then -- работа задается смарткавером
			local smartcover_name = ltx:r_string(active_section, "cover_name")
			local smartcover = se_smart_cover.registered_smartcovers[smartcover_name]
			if smartcover == nil then
				abort("There is an exclusive job with wrong smatrcover name [%s]    smartterrain [%s]", tostring(smartcover_name), self:name())
			end
			printf("Returning alife task for object [%s] game_vertex [%s] level_vertex [%s] position %s", smartcover.id, smartcover.m_game_vertex_id, smartcover.m_level_vertex_id, vec_to_str(smartcover.position))
			v.alife_task = CALifeSmartTerrainTask(smartcover.m_game_vertex_id, smartcover.m_level_vertex_id)

		elseif v.job_type == "point_job" then   -- работа задается позицией
			v.alife_task = self.smart_alife_task
		end

		v.game_vertex_id = v.alife_task:game_vertex_id()
		v.level_id		 = game_graph():vertex(v.game_vertex_id):level_id()
		v.position       = v.alife_task:position()
	end
end

-- Апдейт работ смарттеррейна.
-- Если передается object, то значит нужно найти только для него
function se_smart_terrain:update_jobs()
	self:check_alarm()
	--printf("UPDATE JOBS %s", self:name())

	-- Проверяем, дошел ли кто-то до смарта
	for k,v in pairs(self.arriving_npc) do
		if arrived_to_smart(v, self) then
			self.npc_info[v.id] = self:fill_npc_info(v)

			-- Затычка на случай если мы регистримся в смарт, из которого только что сами вынесли всех врагов.
			self.dead_time = {}

			-- тут надо найти чуваку работу
			self:select_npc_job(self.npc_info[v.id])

			self.arriving_npc[k] = nil
		end
	end

	-- Сортируем НПС по увеличению приоритета занимаемой работы
	table.sort(self.npc_info, function(a,b) return a.job_prior < b.job_prior end )
	for k,v in pairs(self.npc_info) do
		self:select_npc_job(v)
	end
end
-- Выбор работы для персонажа
function se_smart_terrain:select_npc_job(npc_info)
	-- Выбираем работу
	local selected_job_id, selected_job_prior, selected_job_link = job_iterator(self.jobs, npc_info, 0, self)
	if selected_job_id == nil then
		print_table(self.jobs)
		abort("Insufficient smart_terrain jobs %s", self:name())
	end

	-- Назначаем работу
	if selected_job_id ~= npc_info.job_id and selected_job_link ~= nil then
		-- Установить себе выбранную работу
		--printf("NPC %s FOUND JOB %s SECTION %s", npc_info.se_obj:name(), selected_job_id, self.job_data[selected_job_link.job_id].section)
		-- Если НПС был на работе - выгоняем его с нее.
		if npc_info.job_link ~= nil then
			self.npc_by_job_section[self.job_data[npc_info.job_link.job_id].section] = nil
			npc_info.job_link.npc_id = nil
		end

		selected_job_link.npc_id = npc_info.se_obj.id
		self.npc_by_job_section[self.job_data[selected_job_link.job_id].section] = selected_job_link.npc_id

		npc_info.job_id = selected_job_link.job_id
		npc_info.job_prior = selected_job_link._prior
		npc_info.begin_job = false
		-- сохраняем ссылку на работу, для облегчения удаления
		npc_info.job_link = selected_job_link

		-- завершаем текущую работу
		local obj_storage = db.storage[npc_info.se_obj.id]
		if obj_storage ~= nil then
			xr_logic.switch_to_section(obj_storage.object, self.ltx, "nil")
		end
	end

	if npc_info.begin_job ~= true then
		-- Проверяем, дошел ли персонаж до работы (то есть может ли он начать ее выполнение)
		local job_data = self.job_data[npc_info.job_id]
        if job_data == nil then
            return
        end
		-- Начинаем выполнять работу
		printf("[smart_terrain %s] gulag: beginJob: obj=%s job= %s", self:name(), npc_info.se_obj:name(), job_data.section)
		-- Смена работы, очищаем память для оффлайнового обьекта.
		db.offline_objects[npc_info.se_obj.id] = {}
		npc_info.begin_job = true

		local obj_storage = db.storage[npc_info.se_obj.id]
		if obj_storage ~= nil then
			self:setup_logic(obj_storage.object)
		end
	end
end
-- настроить логику для объекта, который в онлайне.
function se_smart_terrain:setup_logic(obj)
	--printf("setup npc logic %s", obj:name())
--	callstack()
	local npc_data = self.npc_info[obj:id()]

    if npc_data == nil then
        return
    end

	local job = self.job_data[npc_data.job_id]
	local ltx = job.ini_file or self.ltx
	local ltx_name = job.ini_path or self.ltx_name

	xr_logic.configure_schemes(obj, ltx, ltx_name, npc_data.stype, job.section, job.prefix_name or self:name())

	local sect = xr_logic.determine_section_to_activate(obj, ltx, job.section, db.actor)
	if utils.get_scheme_by_section(job.section) == "nil" then
		abort("[smart_terrain %s] section=%s, don't use section 'nil'!", self:name(), sect)
	end

	xr_logic.activate_by_section(obj, ltx, sect, job.prefix_name or self:name(), false)
end
-- получить работу, которую занимает объект
function se_smart_terrain:getJob(obj_id)
	return self.npc_info[obj_id] and self.job_data[self.npc_info[obj_id].job_id]
end
-- Получение персонажа, который занимает указанную работу.
function se_smart_terrain:idNPCOnJob(job_name)
	return self.npc_by_job_section[job_name]
end
function se_smart_terrain:switch_to_desired_job(npc)
	-- Берем текущую работу НПС
	local npc_id = npc:id()
	local npc_info = self.npc_info[npc_id]

	--printf("***** %s -> %s", npc:name(), tostring(npc_info.need_job))
	local changing_npc_id = self.npc_by_job_section[npc_info.need_job]
	--printf("changing_npc_id %s", tostring(changing_npc_id))

	if changing_npc_id == nil then
		-- Мы не нашли с кем меняться, просто ресетим себя
		self.npc_info[npc_id].job_link = nil
		self.npc_info[npc_id].job_id = -1
		self.npc_info[npc_id].job_prior = -1
		self:select_npc_job(self.npc_info[npc_id])

		--print_table(self.npc_by_job_section)
		--abort("ERROR during channging NPC")
		return
	end

	if self.npc_info[changing_npc_id] == nil then
		-- Мы не нашли с кем меняться, просто ресетим себя
		self.npc_info[npc_id].job_link = nil
		self.npc_info[npc_id].job_id = -1
		self.npc_info[npc_id].job_prior = -1
		self:select_npc_job(self.npc_info[npc_id])

		--print_table(self.npc_by_job_section)
		--abort("ERROR during channging NPC")
		return
	end

	local desired_job = self.npc_info[changing_npc_id].job_id

	-- Переключаем НПС на желаемую работу
	if npc_info.job_link ~= nil then
		self.npc_by_job_section[self.job_data[npc_info.job_link.job_id].section] = nil
		npc_info.job_link.npc_id = nil
	end

	local selected_job_link = self.npc_info[changing_npc_id].job_link

	selected_job_link.npc_id = npc_info.se_obj.id

	self.npc_by_job_section[self.job_data[selected_job_link.job_id].section] = selected_job_link.npc_id

	npc_info.job_id = selected_job_link.job_id
	npc_info.job_prior = selected_job_link._prior
	npc_info.begin_job = true

	-- сохраняем ссылку на работу, для облегчения удаления
	npc_info.job_link = selected_job_link
	npc_info.need_job = "nil"

	local obj_storage = db.storage[npc_id]
	if obj_storage ~= nil then
		self:setup_logic(obj_storage.object)
	end

	-- Освобождаем НПС, который занимает желаемую работу и говорим ему перевыбрать работу
	self.npc_info[changing_npc_id].job_link = nil
	self.npc_info[changing_npc_id].job_id = -1
	self.npc_info[changing_npc_id].job_prior = -1
	self:select_npc_job(self.npc_info[changing_npc_id])
end


--*******************************************************
-- СЕЙВ/ЛОАД
--*******************************************************
-- сохранение
function se_smart_terrain:STATE_Write(packet)
	cse_alife_smart_zone.STATE_Write(self, packet)

	set_save_marker(packet, "save", false, "se_smart_terrain")

	-- Информацию о НПС, идущих в смарт
	local n = 0
	for k,v in pairs(self.arriving_npc) do
		n = n + 1
	end
	packet:w_u8(n)
	for k,v in pairs(self.arriving_npc) do
		packet:w_u16(k)
	end


	-- Информацию о НПС в смарте
	n = 0
	for k,v in pairs(self.npc_info) do
		n = n + 1
	end

	packet:w_u8(n)
	for k,v in pairs(self.npc_info) do
		packet:w_u16(k)
		packet:w_u8(v.job_prior)
		packet:w_u8(v.job_id)
		packet:w_bool(v.begin_job)
		packet:w_stringZ(v.need_job)
	end

	n = 0
	for k,v in pairs(self.dead_time) do
		n = n + 1
	end
	packet:w_u8(n)
	for k,v in pairs(self.dead_time) do
		packet:w_u8(k)
		utils.w_CTime(packet, v)
	end

	if self.base_on_actor_control ~= nil then
		packet:w_bool(true)
		self.base_on_actor_control:save(packet)
	else
		packet:w_bool(false)
	end

	if self.respawn_point then
		packet:w_bool(true)
		local n = 0
		for k,v in pairs(self.already_spawned) do
			n = n + 1
		end
		packet:w_u8(n)
		for k,v in pairs(self.already_spawned) do
			packet:w_stringZ(k)
			packet:w_u8(v.num)
		end

		if self.last_respawn_update ~= nil then
			packet:w_bool(true)
			utils.w_CTime(packet, self.last_respawn_update)
		else
			packet:w_bool(false)
		end
	else
		packet:w_bool(false)
	end

	if self.population < 0 then
		abort("Smart_terrain [%s] population can't be less than zero!!!", self:name())
	end
	packet:w_u8(self.population)

	set_save_marker(packet, "save", true, "se_smart_terrain")
end

-- восстановление
function se_smart_terrain:STATE_Read(packet, size)
	cse_alife_smart_zone.STATE_Read(self, packet, size)

	-- под LevelEditor не пытаться читать из пакета ничего
	if editor() then
		return
	end

	set_save_marker(packet, "load", false, "se_smart_terrain")
	self:read_params()

	-- Информацию о НПС, идущих в смарт
	local n = packet:r_u8()
	self.arriving_npc = {}
	for i = 1,n do
		local id = packet:r_u16()
		self.arriving_npc[id] = false
	end

	-- Информацию о НПС в смарте
	n = packet:r_u8()
	--printf("load %s npc", tostring(n))
	self.npc_info = {}
	for i = 1,n do
		local id = packet:r_u16()
		--printf("__ id %s", tostring(id))
		self.npc_info[id] = {}
		local npc_info = self.npc_info[id]
		npc_info.job_prior = packet:r_u8()
		--printf("__ job_prior %s", tostring(npc_info.job_prior))
		if npc_info.job_prior == 255 then
			npc_info.job_prior = -1
		end
		npc_info.job_id = packet:r_u8()
		--printf("__ job_id %s", tostring(npc_info.job_id))
		if npc_info.job_id == 255 then
			npc_info.job_id = -1
		end
		npc_info.begin_job = packet:r_bool()
		--printf("__ begin_job %s", tostring(npc_info.begin_job))
		npc_info.need_job = packet:r_stringZ()
	end

	n = packet:r_u8()
	self.dead_time = {}
	--printf("load %s dead_time", tostring(n))
	for i =1,n do
		local job_id = packet:r_u8()
		--printf("__ job_id %s", tostring(job_id))
		local dead_time = utils.r_CTime(packet)
		self.dead_time[job_id] = dead_time
	end

	self.need_init_npc = true

	if self.script_version > 9 then
		if packet:r_bool() == true then
			--self.base_on_actor_control
			self.base_on_actor_control:load(packet)
		end
	end

	local respawn_point = packet:r_bool()
	--printf("LOAD RESPAWN %s", self:name())
	if respawn_point then
		n = packet:r_u8()
		for i = 1, n do
			local id = packet:r_stringZ()
			local num = packet:r_u8()
			self.already_spawned[id].num = num
		end

		if self.script_version > 11 then
			local exist = packet:r_bool()
			if exist then
				self.last_respawn_update = utils.r_CTime(packet)
			else
				self.last_respawn_update = nil
			end
		end
	end

	self.population = packet:r_u8()

	set_save_marker(packet, "load", true, "se_smart_terrain")
end
-- Инициализация НПС после загрузки.
function se_smart_terrain:init_npc_after_load()
	local function find_job(jobs, npc_info)
		for k,v in pairs(jobs) do
			if v.jobs ~= nil then
				find_job(v.jobs, npc_info)
			else
				if v.job_id == npc_info.job_id then
					npc_info.job_link = v
					v.npc_id = npc_info.se_obj.id
					return
				end
			end
		end
	end


	local sim = alife()
	--printf("[%s] init_npc_after_load", self:name())
	for k,v in pairs(self.arriving_npc) do
		local sobj = sim:object(k)
		if sobj ~= nil then
			self.arriving_npc[k] = sobj
		else
			self.arriving_npc[k] = nil
		end
	end

	for k,v in pairs(self.npc_info) do
		local sobj = sim:object(k)
		if sobj ~= nil then
			local npc_info = self:fill_npc_info(sobj)

			npc_info.job_prior = v.job_prior
			npc_info.job_id = v.job_id
			npc_info.begin_job = v.begin_job
			npc_info.need_job = v.need_job

			--Теперь надо найти данную работу и выставить ссылку на нее.
			find_job(self.jobs, npc_info)

			self.npc_info[k] = npc_info
			if npc_info.job_link ~= nil then
				self.npc_by_job_section[self.job_data[npc_info.job_link.job_id].section] = k
			end
		else
			self.npc_info[k] = nil
		end
	end
end


--' Возвращает отформатированную строку свойств смарта
function se_smart_terrain:get_smart_props()
	local props = smart_names.get_smart_terrain_name(self)
	if(props==nil) or (_G.dev_debug) then
		props = self:name().."  ["..self.id.."]\\n"..
					self.sim_type.."\\n"..
					"squad_id = "..tostring(self.id).."\\n"..
					"capacity = "..tostring(self.max_population).." ("..sim_board.get_sim_board():get_smart_population(self)..")\\n"

			if self.respawn_point ~= nil and self.already_spawned ~= nil then
					props = props.."\\nalready_spawned :\n"
					for k,v in pairs(self.already_spawned) do
						props = props.."["..k.."] = "..v.num.."("..xr_logic.pick_section_from_condlist(db.actor, nil,self.respawn_params[k].num)..")\\n"
					end
					if self.last_respawn_update then
						props = props.."\\ntime_to_spawn:"..tostring(self.respawn_time - game.get_game_time():diffSec(self.last_respawn_update)).."\\n"
					end
			end

			--' Добавляем информацию о находящихся в смарте отрядах
			for k,v in pairs(sim_board.get_sim_board().smarts[self.id].squads) do
					props = props .. tostring(v.id) .. "\\n"
			end
	end
	return props
end



--' Отрисовка смарта на игровом поле
function se_smart_terrain:show()
	local time = time_global()
	if(self.showtime~=nil) and (self.showtime+200>=time) then
		return
	end
	self.showtime = time

	local player = self.player_name
	local spot = "neutral"

	if self.sim_avail == nil or xr_logic.pick_section_from_condlist(db.actor or alife():actor(), self, self.sim_avail) == "true" then
		spot = "friend"
	else
		spot = "enemy"
	end


	if(self.smrt_showed_spot==spot) then
		level.map_change_spot_hint(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot, self:get_smart_props())
		return
	end

	if(_G.dev_debug) then
		if(self.smrt_showed_spot~=nil) then
			level.map_remove_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot)
		end
		level.map_add_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..spot, self:get_smart_props())
		self.smrt_showed_spot = spot
	else

	if(self.smrt_showed_spot~=nil) and
		(level.map_has_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot)~=0)
	then
		level.map_remove_object_spot(self.id, "alife_presentation_smart_base_"..self.smrt_showed_spot)
	end

	end
end
--' Обновление информации о смарте на игровом поле
function se_smart_terrain:refresh()
	self:show()
end
--' Убирание отрисовки смарта на игровом поле
function se_smart_terrain:hide()
	if self.smrt_showed_spot == nil then
		return
	end
	level.map_remove_object_spot(self.id, "alife_presentation_smart_"..self.sim_type.."_"..self.smrt_showed_spot)
end


local function is_only_monsters_on_jobs(npc_info)
	for k,v in pairs (npc_info) do
		if v.is_monster == false then
			return false
		end
	end
	return true
end

-- Обновление.
-- В онлайне вызывается через binder.
-- Также может вызваться принудительно из xr_effects
function se_smart_terrain:update()
	cse_alife_smart_zone.update( self )
	if dev_debug then
		self:refresh() -- Не забыть потом заремить
	end

	local current_time = time_global()

	if simulation_objects.is_on_the_same_level(self, alife():actor()) then
		local dist_to_actor = self.position:distance_to(alife():actor().position)
		local old_dist_to_actor = (nearest_to_actor_smart.id == nil and nearest_to_actor_smart.dist) or alife():object(nearest_to_actor_smart.id).position:distance_to(alife():actor().position)
		if dist_to_actor < old_dist_to_actor then
			nearest_to_actor_smart.id = self.id
			nearest_to_actor_smart.dist = dist_to_actor
		end
	end

	-- Апдейт респауна отрядов симуляции.
	if self.respawn_params ~= nil then
		self:try_respawn()
	end

	if self.check_time~=nil and current_time < self.check_time then
		return
	end

	--проверить есть ли кто-то в смарте, если есть и костры не включены то включить,
	--еще проверить есть ли актер, чтоб была гарантия что костры проспонились...
	if is_only_monsters_on_jobs(self.npc_info) and self.campfires_on then
		bind_campfire.turn_off_campfires_by_smart_name(self:name())
		self.campfires_on = false
	elseif not is_only_monsters_on_jobs(self.npc_info) and not self.campfires_on then
		bind_campfire.turn_on_campfires_by_smart_name(self:name())
		self.campfires_on = true
	end

	if db.actor ~= nil then
		local distance = db.actor:position():distance_to_sqr(self.position)
		local idle_time = math.max(60, 0.003 * distance)
		self.check_time = current_time + idle_time
	else
		self.check_time = current_time + 10
	end

	-- Проверяем, не истек ли запрет на занимание работы, на которой убили НПС
	local current_time = game.get_game_time()
	for k,v in pairs(self.dead_time) do
		if current_time:diffSec(v) >= DEATH_IDLE_TIME then
			self.dead_time[k] = nil
		end
	end

	-- Перевыбор работ
	self:update_jobs()

	-- Апдейтим контрол реакции базы на игрока
	if self.base_on_actor_control ~= nil then
		self.base_on_actor_control:update()
	end
	-- Апдейт доступности для симуляции.
	simulation_objects.get_sim_obj_registry():update_avaliability(self)
end
-- Переведение смарта в напряженное состояние
function se_smart_terrain:set_alarm()
	self.smart_alarm_time = game.get_game_time()
end
-- Проверяет. а не прошел ли аларм в смарте
function se_smart_terrain:check_alarm()
	if self.smart_alarm_time == nil then
		return
	end
	if game.get_game_time():diffSec(self.smart_alarm_time) > 21600 then -- 6 Игровых часов
		self.smart_alarm_time = nil
	end
end


-- установить логику и сообщить смарту, что объект перешёл в онлайн.
-- вызывается из net_spawn() объектов
function setup_gulag_and_logic_on_spawn(obj, st, sobject, stype, loaded)
	local sim = alife()
	local sobject = alife():object(obj:id())
	if sim ~= nil and sobject then
		local strn_id = sobject.m_smart_terrain_id
		printf( "setup_gulag_and_logic_on_spawn obj=%s, strn_id=%s, loaded=%s", obj:name(), tostring(strn_id), tostring(loaded))

		if strn_id ~= nil and strn_id ~= 65535 then
			local strn                   = sim:object(strn_id)
			local need_setup_logic       = (not loaded) and (strn.npc_info[obj:id()] and strn.npc_info[obj:id()].begin_job == true)

			if need_setup_logic then
				strn:setup_logic(obj)
			else
				xr_logic.initialize_obj(obj, st, loaded, db.actor, stype)
			end
		else
			xr_logic.initialize_obj(obj, st, loaded, db.actor, stype)
		end
	else
		xr_logic.initialize_obj(obj, st, loaded, db.actor, stype)
	end
end

-- Убираем объект из смарта при смерти
function on_death(obj)
	local sim = alife()
	if sim then
		local obj = sim:object(obj.id)
		if obj == nil then return end
		local strn_id = obj:smart_terrain_id()
		if strn_id ~= 65535 then
			printf("clear dead object %s", obj:name())
			sim:object(strn_id):clear_dead(obj)
		end
	end
end


--***********************************************************************************************
--*										SIMULATION_TARGET_SMART									*
--***********************************************************************************************
-- Получить позицию, левел вертекс, гейм вертекс обьекта.
function se_smart_terrain:get_location()
	return self.position, self.m_level_vertex_id, self.m_game_vertex_id
end

-- Достигнут ли я отрядом выбравшим меня как цель.
function se_smart_terrain:am_i_reached(squad)
	local squad_pos, squad_lv_id, squad_gv_id = squad:get_location()
	local target_pos, target_lv_id, target_gv_id = self:get_location()
	if game_graph():vertex(squad_gv_id):level_id() ~= game_graph():vertex(target_gv_id):level_id() then
		return false
	end
	if IsMonster(alife():object(squad:commander_id())) and squad:get_script_target() == nil then
		return squad_pos:distance_to_sqr(target_pos) <= 25
	end
	return squad.always_arrived or squad_pos:distance_to_sqr(target_pos) <= self.arrive_dist^2
end

-- Вызывается 1 раз после достижения меня отрядом выбравшим меня как цель.
function se_smart_terrain:on_after_reach(squad)
	for k in squad:squad_members() do
		local obj = k.object
		squad.board:setup_squad_and_group(obj)
	end
	squad.current_target_id = self.id
end

-- Вызывается 1 раз в момент выбора меня как цели.
function se_smart_terrain:on_reach_target(squad)
--	squad.sound_manager:set_storyteller(squad:commander_id())
--	squad.sound_manager:set_story("squad_begin_attack")
	squad:set_location_types(self:name())
	self.board:assign_squad_to_smart(squad, self.id)
	for k in squad:squad_members() do
		if db.offline_objects[k.id] ~= nil then
			db.offline_objects[k.id] = {}
		end
	end
--	self.board:exit_smart(squad, squad.smart_id)
end

-- Возвращает CALifeSmartTerrainTask на меня, вызывается из smart_terrain:task()
function se_smart_terrain:get_alife_task()
	return self.smart_alife_task
end

function smart_terrain_squad_count(board_smart_squads)
	local count = 0
	for k,v in pairs(board_smart_squads) do
		if v:get_script_target() == nil then
			count = count + 1
		end
	end
	return count
end

function se_smart_terrain:sim_available()
	if self.base_on_actor_control ~= nil and self.base_on_actor_control.status ~= smart_terrain_control.NORMAL then
		return false
	end
	return true
end

local is_squad_monster =
{
	["monster_predatory_day"] = true,
	["monster_predatory_night"] = true,
	["monster_vegetarian"] = true,
	["monster_zombied_day"] = true,
	["monster_zombied_night"] = true,
	["monster_special"] = true
}
function surge_stats()
	local sim_obj_registry = simulation_objects.get_sim_obj_registry().objects
	local sim_squads = {
						["zaton"] = {},
						["jupiter"] = {},
						["pripyat"] = {}
		}
	local sim_smarts = {
						["zaton"] = {},
						["jupiter"] = {},
						["pripyat"] = {}		
		}
	for k,v in pairs(sim_obj_registry) do
		if v:clsid() == clsid.smart_terrain and tonumber(v.props["surge"]) > 0 then
			local level_name = alife():level_name(game_graph():vertex(v.m_game_vertex_id):level_id())
			if sim_smarts[level_name] ~= nil then
				table.insert(sim_smarts[level_name], v)
			end
		end
		if v:clsid() == clsid.online_offline_group_s then
			local squad_params = sim_board.simulation_activities[v.player_id]
			if squad_params ~= nil then
				local smart_params = squad_params.smart.surge
				if smart_params ~= nil then
					local level_name = alife():level_name(game_graph():vertex(v.m_game_vertex_id):level_id())
					if sim_squads[level_name] ~= nil then
						table.insert(sim_squads[level_name], v)
					end
				end
			end
		end
	end
	local function print_smarts_and_squads_by_level(level_name)
		printf("LEVEL: [%s]", level_name)
		local max_capacity_total = 0
		for i = 1, #sim_smarts[level_name] do
			local smart = sim_smarts[level_name][i]
			max_capacity_total = max_capacity_total + smart.max_population
			local squad_count = smart_terrain_squad_count(sim_board.get_sim_board().smarts[smart.id].squads)
			printf("smart: [%s] max_population [%d] squad_count [%d]", smart:name(),smart.max_population, squad_count)
		end
		printf("TOTAL: capacity total : [%d] squads total [%d]" , max_capacity_total, #sim_squads[level_name])
	end
	print_smarts_and_squads_by_level("zaton")
	print_smarts_and_squads_by_level("jupiter")
	print_smarts_and_squads_by_level("pripyat")
end
-- Мой прекондишн.
function se_smart_terrain:target_precondition(squad, need_to_dec_population)
	if self.respawn_only_smart == true then
		return false
	end

	local squad_count = smart_terrain_squad_count(self.board.smarts[self.id].squads)
	if need_to_dec_population then
		squad_count = squad_count - 1
	end
	if squad_count ~= nil and (self.max_population <= squad_count) then
		--printf("smart terrain [%s] precondition returns false for squad [%s]", self:name(), squad:name())
--		if tonumber(self.props["surge"]) > 0 and xr_conditions.surge_started() then
--			printf("SURGE_SMART_STATS : smart [%s]\n max_population = %d \ squad_count = %d", self:name(), self.max_population, squad_count)
--		end
		return false
	end

	local squad_params = sim_board.simulation_activities[squad.player_id]
	if squad_params == nil or squad_params.smart == nil then
		--printf("smart terrain [%s] precondition returns false for squad [%s]", self:name(), squad:name())
		return false
	end

	if tonumber(self.props["resource"] )> 0 then
		local smart_params = squad_params.smart.resource
		if smart_params ~= nil and smart_params.prec(squad, self) then
			return true
		end
	end
	if tonumber(self.props["base"] )> 0 then
		local smart_params = squad_params.smart.base
		if smart_params ~= nil and smart_params.prec(squad, self) then
			return true
		end
	end
	if tonumber(self.props["lair"] )> 0 then
		local smart_params = squad_params.smart.lair
		if smart_params ~= nil and smart_params.prec(squad, self) then
			return true
		end
	end
	if tonumber(self.props["territory"] )> 0 then
		local smart_params = squad_params.smart.territory
		if smart_params ~= nil and smart_params.prec(squad, self) then
			return true
		end
	end
	if tonumber(self.props["surge"] )> 0 then
		local smart_params = squad_params.smart.surge
		if smart_params ~= nil and smart_params.prec(squad, self) then
			return true
		end
	end
	--printf("smart terrain [%s] precondition returns false for squad [%s]", self:name(), squad:name())
	return false


--[[
	local squad_count = smart_terrain_squad_count(self.board.smarts[self.id].squads)
	if squad_count ~= nil and (self.max_population <= squad_count) then return false end


	if squad.player_id == "stalker" and in_time_interval(9,19) and tonumber(self.props["resource"] )> 0 then
		return true
	end
	--if squad.player_id ~= "monster_predatory" and squad.player_id ~= "monster_vegetarian" then

	if not is_squad_monster[squad.player_id] then
		if tonumber(self.props["base"]) > 0 and in_time_interval(20,8) then
			return true
		end
		if tonumber(self.props["base"] ) > 0 and xr_conditions.surge_started() then
			return true
		end
	else
		if tonumber(self.props["lair"] ) > 0 and xr_conditions.surge_started() then
			return true
		end
		if tonumber(self.props["lair"] ) > 0 and in_time_interval(7,20) then
			return true
		end
	end
	return false
]]
end

-- Посчитать мой приоритет для отряда.
function se_smart_terrain:evaluate_prior(squad)
	return simulation_objects.evaluate_prior(self, squad)
end

-- Респаун симуляции.

function se_smart_terrain:check_respawn_params(respawn_params)
	--printf("CHECK RESPAWN PARAMS %s", self:name())
	self.respawn_params 	= {}
	self.already_spawned	= {}
	self.respawn_point		= true
	if not self.ini:section_exist(respawn_params) then
		abort("Wrong smatr_terrain respawn_params section [%s](there is no section)", respawn_params)
	end
	local n = self.ini:line_count(respawn_params)
	if n == 0 then
		abort("Wrong smatr_terrain respawn_params section [%s](empty params)", respawn_params)
	end
	for j=0,n-1 do
		local result, prop_name, prop_condlist = self.ini:r_line(respawn_params,j,"","")
		if not self.ini:section_exist(prop_name) then
			abort("Wrong smatr_terrain respawn_params section [%s] prop [%s](there is no section)", respawn_params, prop_name)
		end
		local spawn_squads = utils.cfg_get_string(self.ini, prop_name, "spawn_squads", 	self, false, "", nil)
		local spawn_num  	= utils.cfg_get_string(self.ini, prop_name, "spawn_num", 	self, false, "", nil)
		if spawn_squads == nil then
			abort("Wrong smatr_terrain respawn_params section [%s] prop [%s] line [spawn_squads](there is no line)", respawn_params, prop_name)
		elseif spawn_num == nil then
			abort("Wrong smatr_terrain respawn_params section [%s] prop [%s] line [spawn_num](there is no line)", respawn_params, prop_name)
		end
		spawn_squads = utils.parse_names(spawn_squads)
		spawn_num = xr_logic.parse_condlist(nil, prop_name, "spawn_num", spawn_num)
		self.respawn_params[prop_name] 			= {}
		self.already_spawned[prop_name]			= {}
		self.respawn_params[prop_name].squads	= spawn_squads
		self.respawn_params[prop_name].num		= spawn_num
		self.already_spawned[prop_name].num		= 0
	end
end

function se_smart_terrain:call_respawn()
	local available_sects = {}
	printf("respawn called from smart_terrain [%s]", self:name())
	for k,v in pairs(self.respawn_params) do
		if tonumber(xr_logic.pick_section_from_condlist(db.actor, nil,v.num)) > self.already_spawned[k].num then
			table.insert(available_sects,k)
		end
	end
	if #available_sects > 0 then
		local sect_to_spawn = available_sects[math.random(1,#available_sects)]
		local sect_to_spawn_params = self.respawn_params[sect_to_spawn]
		local squad = sect_to_spawn_params.squads[math.random(1,#sect_to_spawn_params.squads)]
		squad = self.board:create_squad(self, squad)
		squad.respawn_point_id = self.id
		squad.respawn_point_prop_section = sect_to_spawn
		self.board:enter_smart(squad, self.id)
		for m in squad:squad_members() do
			self.board:setup_squad_and_group(m.object)
		end
		self.already_spawned[sect_to_spawn].num = self.already_spawned[sect_to_spawn].num + 1
	end
end

function se_smart_terrain:try_respawn()
	--printf("TRY RESPAWN %s", self:name())
	local curr_time = game.get_game_time()
	if self.last_respawn_update == nil or curr_time:diffSec(self.last_respawn_update) > self.respawn_time then
		self.last_respawn_update = curr_time

		if self.sim_avail ~= nil and xr_logic.pick_section_from_condlist(db.actor or alife():actor(), self, self.sim_avail) ~= "true" then return end

		local squad_count = smart_terrain_squad_count(self.board.smarts[self.id].squads)
		if self.max_population <= squad_count then printf("%s cannot respawn due to squad_count %s of %s", self:name(), self.max_population, squad_count) return end

		local dist_to_actor = alife():actor().position:distance_to_sqr(self.position)
		if dist_to_actor < self.respawn_radius^2 then printf("%s cannot respawn due to distance", self:name()) return end

		self:call_respawn()
	end
end
