--'******************************************************
--'*	Боевой отряд группировки. (Скриптовая версия)
--'*	Наследован от обычного боевого отряда.
--'*	Отличия:
--'*		- Детерминированные состав отряда
--'*		- Возможность жестко указать активное действие
--'******************************************************
local squad_behaviour_ini 	= ini_file("misc\\squad_behaviours.ltx")
local locations_ini			= ini_file("misc\\smart_terrain_masks.ltx")
--function printf()
--end

class "sim_squad_scripted" (cse_alife_online_offline_group)

--***********************************************************************************************
--*										INITIALIZATION											*
--***********************************************************************************************

function sim_squad_scripted:__init(section) super (section)
	--' смарт, в котором сейчас находится отряд.
	self.smart_id 			= nil
	self.board 				= sim_board.get_sim_board()
	self.current_spot_id 	= nil --' ID объекта на который сейчас установлен мапспот.

	--' Текущее действие
	self.current_action 	= nil

	self.current_target_id	= nil

	self.assigned_target_id	= nil

	--' Саундменеджер отряда
	self.sound_manager 		= sound_manager.get_sound_manager("squad_"..self:section_name() )

	self.settings_id 		= self:section_name()

	self:init_squad()

	self:set_squad_behaviour()
end
--' Инициализация
function sim_squad_scripted:init_squad()
	self.player_id 			= utils.cfg_get_string(sim_board.squad_ltx, self.settings_id, "faction", self, true, "")
	self.action_condlist	= xr_logic.parse_condlist(self, "assign_action", "target_smart", utils.cfg_get_string(sim_board.squad_ltx, self.settings_id, "target_smart", self, false, "", ""))
	self.death_condlist		= xr_logic.parse_condlist(self, "death_condlist", "on_death", utils.cfg_get_string(sim_board.squad_ltx, self.settings_id, "on_death", self, false, "", ""))
	self.invulnerability	= xr_logic.parse_condlist(self, "invulnerability", "invulnerability", utils.cfg_get_string(sim_board.squad_ltx, self.settings_id, "invulnerability", self, false, "", ""))
	self.relationship		= self.relationship or utils.cfg_get_string(sim_board.squad_ltx, self.settings_id, "relationship", self, false, "", nil)
	self.sympathy			= utils.cfg_get_number(sim_board.squad_ltx, self.settings_id, "sympathy", self, false, nil)
	self.show_spot			= xr_logic.parse_condlist(self, "show_spot", "show_spot", utils.cfg_get_string(sim_board.squad_ltx, self.settings_id, "show_spot", self, false, "", "false"))

	self.always_walk 		= utils.cfg_get_bool(sim_board.squad_ltx, self.settings_id, "always_walk", self, false)
	self.always_arrived		= utils.cfg_get_bool(sim_board.squad_ltx, self.settings_id, "always_arrived", self, false)
	self:set_location_types_section("stalker_terrain")
	self:set_squad_sympathy()
end

-- Инициализация отряда на загрузке
function sim_squad_scripted:init_squad_on_load()
	-- Прописываем сквад в нужные смарты
	printf("Init squad %s on load", self.id)
	self:set_squad_sympathy()
	self.board:assign_squad_to_smart(self, self.smart_id)

	if self.smart_id ~= nil then
		self.board:enter_smart(self, self.smart_id, true)
	end
	self.need_to_reset_location_masks = true
end

-- Вычитываем настройки поведения сквада.
function sim_squad_scripted:set_squad_behaviour()
	self.behaviour		= {}
	local behaviour_section = utils.cfg_get_string(sim_board.squad_ltx, self.settings_id, "behaviour", self, false, "", self.player_id)
	if not squad_behaviour_ini:section_exist(behaviour_section) then
		abort("There is no section ["..behaviour_section.."] in 'misc\\squad_behaviours.ltx'")
	end
	local n = squad_behaviour_ini:line_count(behaviour_section)
	for j=0,n-1 do
		local result, prop_name, prop_condlist = squad_behaviour_ini:r_line(behaviour_section,j,"","")
		self.behaviour[prop_name] = prop_condlist
	end
end

--***********************************************************************************************
--*										SCRIPT_TARGET_SELECTION									*
--***********************************************************************************************

--' Возвращает скриптовую цель.
function sim_squad_scripted:get_script_target()
	local new_target = xr_logic.pick_section_from_condlist(db.actor, self, self.action_condlist)

	if new_target == nil then
--		printf("[%s] get_script_target nil_condlist", self.id)
		return nil
	end

	if new_target ~= self.last_target then
		self.last_target = new_target
		--' Парсим новые задачи для отряда
		self.parsed_targets = utils.parse_names(new_target)

		if self.need_free_update ~= true then
			self.next_target = 1
		else
			self.need_free_update = false
		end
	end
	if self.parsed_targets[self.next_target] == nil then
		self.next_target = 1
	end


	local nt = self:pick_next_target()
	if nt == "nil" then
		return nil
	elseif nt == "loop" then
		self.next_target = 1
		nt = self:pick_next_target()
	end
	local point = self.board.smarts_by_names[nt]
	if point == nil then
		abort("Incorrect next point [%s] for squad [%s]", tostring(nt),  tostring(self.id))
	end
	return point.id
end


--' Выбор следующей цели
function sim_squad_scripted:pick_next_target()
	--printf("Picking next target for [%s] = %s by %s", self:section_name(), tostring(self.parsed_targets[self.next_target]), self.next_target)
	return self.parsed_targets[self.next_target]
end


--' Проверка пришел ли отряд в очередной поинт
function sim_squad_scripted:check_squad_come_to_point()
	--printf("[%s] check squad point", self.id)
	if self.parsed_targets == nil then
		return true
	end
	--' Если мы уже дошли до точки.
	local next_target = self.next_target or 0
	if self.assigned_target_id ~= nil and self.smart_id == self.assigned_target_id then
		if self.parsed_targets[next_target+1] ~= nil then
			self.next_target = next_target+1
			return true
		end
	end
	return false
end

--' Проверка пришел ли отряд в очередной поинт
function sim_squad_scripted:update_current_action()
	local is_finished =	self.current_action:update(false)
	if not is_finished then
		return false
	end
	return true
end

function sim_squad_scripted:update()
	cse_alife_online_offline_group.update (self)
	self:refresh()

	-- Апдейт доступности для симуляции.
	simulation_objects.get_sim_obj_registry():update_avaliability(self)

	self:check_invulnerability()

	local script_target = self:get_script_target()

	--printf("[%s] SCRIPTED UPDATE. Target[%s]", self.id, tostring(script_target))
	if script_target == nil then
		self:generic_update()
		if self.need_to_reset_location_masks then
			self:set_location_types()
			self.need_to_reset_location_masks = false
		end
		return
	end

	self.sound_manager:update()

	local need_to_find_new_action = false
	if self.assigned_target_id ~= nil and self.assigned_target_id == script_target then
		if self.current_action ~= nil then
			if self.current_action.name == "stay_point" then
				--' Если экшн stay_point. Проверяем пришли ли до экшна.
				if self:check_squad_come_to_point() then
					--' Пришли в точку, перевыбрали цель
					need_to_find_new_action = true
					--printf("[%s] SCRIPTED need_to_find_new_action %s", self.id, tostring(need_to_find_new_action))
				else
					--' Не пришли в точку, апдейтимся дальше
					need_to_find_new_action = self:update_current_action()
					--printf("[%s] SCRIPTED need_to_find_new_action %s", self.id, tostring(need_to_find_new_action))
				end
			else
				--' Если экшн attack_point. Проверяем пришли ли после экшна.
				if self:update_current_action() then
					self:check_squad_come_to_point()
					need_to_find_new_action = true
					--printf("[%s] SCRIPTED need_to_find_new_action %s", self.id, tostring(need_to_find_new_action))
				end
			end
		else
			--' Проверяем пришли ли
			self:check_squad_come_to_point()
			need_to_find_new_action = true
			--printf("[%s] SCRIPTED need_to_find_new_action %s", self.id, tostring(need_to_find_new_action))
		end
	else
		if self.current_action == nil then
			need_to_find_new_action = true
			--printf("[%s] SCRIPTED need_to_find_new_action %s", self.id, tostring(need_to_find_new_action))
		else
			if self.current_action.major == true then
				if self:update_current_action() then
					self:check_squad_come_to_point()
					need_to_find_new_action = true
					--printf("[%s] SCRIPTED need_to_find_new_action %s", self.id, tostring(need_to_find_new_action))
				end
			else
				need_to_find_new_action = true
				--printf("[%s] SCRIPTED need_to_find_new_action %s", self.id, tostring(need_to_find_new_action))
			end
		end
	end

	if need_to_find_new_action == true then
		self.assigned_target_id = script_target

		if self.current_action ~= nil then
			self.current_action:finalize()
			self.current_action = nil
		end
		--' Если у нас нет текущей цели - просим выдать нам задание.
		self:get_next_action(false)
	end
	if self.need_to_reset_location_masks then
		self:set_location_types()
		self.need_to_reset_location_masks = false
	end
end



--***********************************************************************************************
--*										SIMULATION_TARGET_SELECTION								*
--***********************************************************************************************

function sim_squad_scripted:clear_assigned_target()
	self.assigned_target_id = nil
end

function sim_squad_scripted:assigned_target_avaliable()
	local target_obj = self.assigned_target_id and alife():object(self.assigned_target_id)
	if target_obj == nil then
		return false
	end
	-- В данном случае нужно проверять попьюлейшн минус один, т.к. я уже состою в смарте.
	return target_obj:target_precondition(self, true)
end

local function can_help_actor(squad)
	if empty(xr_combat_ignore.fighting_with_actor_npcs) then
		return false
	end
	if game_graph():vertex(squad.m_game_vertex_id):level_id() ~= game_graph():vertex(alife():actor().m_game_vertex_id):level_id() then
		return false
	end
	if has_alife_info("sim_duty_help_harder") and squad:get_squad_community() == "duty" then
		return true
	elseif has_alife_info("sim_freedom_help_harder") and squad:get_squad_community() == "freedom" then
		return true
	elseif has_alife_info("sim_stalker_help_harder") and squad:get_squad_community() == "stalker" then
		return true
	end
	return false
end

local function get_help_target_id(squad)
	if not can_help_actor(squad) then
		return nil
	end
	for k,v in pairs (xr_combat_ignore.fighting_with_actor_npcs) do
		local enemy_squad_id = alife():object(k).group_id
		if enemy_squad_id ~= nil then
			local target_squad = alife():object(enemy_squad_id)
			if target_squad and squad.position:distance_to_sqr(target_squad.position) < 150^2 and
				game_relations.is_factions_enemies(squad:get_squad_community(), target_squad:get_squad_community()) then
				return enemy_squad_id
			end
		end
	end
	return nil
end


--' Апдейт отряда для выбора симуляционных целей.
function sim_squad_scripted:generic_update()
	--printf("Squad[%s] UPDATE", self.id)
	--' Если у нас есть незавершенное действие - ждем пока оно завершится.
	self.sound_manager:update()
	self:refresh()

	local help_target_id = get_help_target_id(self)

	if help_target_id then
		self.assigned_target_id = help_target_id
		self.current_action = nil
		self:get_next_action(false)
		return
	end
	if self.assigned_target_id and alife():object(self.assigned_target_id) and alife():object(self.assigned_target_id):clsid() ~= clsid.online_offline_group_s then
		local squad_target = self.board:get_squad_target(self)
		if squad_target:clsid() == clsid.online_offline_group_s then
			self.assigned_target_id = squad_target.id
			self.current_action = nil
			self:get_next_action(true)
			return
		end
	end

	if self.current_action ~= nil and self:assigned_target_avaliable() then
		--printf("[%s] CURRENT ACTION [%s]", self.id, self.current_action.name)
		local is_finished =	self.current_action:update(true)

		if is_finished then
			self.current_action:finalize()
			if self.current_action.name == "stay_point" or self.assigned_target_id == nil then
				self.assigned_target_id = self.board:get_squad_target(self).id
			end
			self.current_action = nil
		else
			return
		end
	else
		self.current_action = nil
		self.current_target_id = nil
		self.assigned_target_id = self.board:get_squad_target(self).id
	end
	self:get_next_action(true)
end
--' Получение следующего экшна на выполнение
function sim_squad_scripted:get_next_action(under_simulation)
	local squad_target = alife():object(self.assigned_target_id)
	--printf("assigned_target = [%s]", tostring(self.assigned_target_id))
	if self.current_target_id == nil then
		if squad_target == nil or squad_target:am_i_reached(self) then
			if squad_target ~= nil then
				squad_target:on_reach_target(self)
				squad_target:on_after_reach(self)
			end
			self.current_action = sim_squad_actions.stay_on_target(self)
			self.current_target_id = self.assigned_target_id
			self.current_action:make(under_simulation)
			return
		end
	end

	if (self.assigned_target_id == self.current_target_id) or self.assigned_target_id == nil then
		self.current_action = sim_squad_actions.stay_on_target(self)
		self.current_target_id = self.assigned_target_id
		self.current_action:make(under_simulation)
	else
		self.current_action = sim_squad_actions.reach_target(self)
		self.current_action:make(under_simulation)
	end
end

--***********************************************************************************************
--*										MEMBERS_CONTROL											*
--***********************************************************************************************
--' Удаление персонажей отряда
function sim_squad_scripted:remove_squad()
	local squad_npcs = {}
	for k in self:squad_members() do
		squad_npcs[k.id] = true
	end
--
	for j,v in pairs(squad_npcs) do
		local obj = alife():object(j)
		if obj ~= nil then
			self:unregister_member(j)
			alife():release(obj, true)
		end
	end
	self:hide()
--	alife():release(self, true)
end
--' Удаление конкретного персонажа отряда
function sim_squad_scripted:remove_npc(npc_id)
	local npc = alife():object(npc_id)
	self:on_npc_death(npc)
	--printf("releasing object ["..npc:name().."]")
	alife():release(npc, true)
end
--' Убийство НПС
function sim_squad_scripted:on_npc_death(npc)
	printf("Squad %s. Killed member %s", tostring(self.id), npc.id)

	self.sound_manager:unregister_npc(npc.id)
	self:unregister_member(npc.id)

	if self:npc_count() == 0 then
		printf("REMOVING DEAD SQUAD %s", tostring(self.id))
		if self.current_action ~= nil then
			self.current_action:finalize()
			self.current_action = nil
		end

		if self.death_condlist ~= nil then
			xr_logic.pick_section_from_condlist(db.actor, self, self.death_condlist)
		end
		self.board:remove_squad(self)
		return
	end
	self:refresh()
end
function sim_squad_scripted:assign_squad_member_to_smart(member_id, smart, old_smart_id)
	local obj = alife():object(member_id)
	if obj ~= nil then
		--printf("    npc [%s] smart [%s]", obj:name(), tostring(obj.m_smart_terrain_id))
		if obj.m_smart_terrain_id == self.smart_id then
			return
		end

		if obj.m_smart_terrain_id ~= 65535 and old_smart_id ~= nil and (obj.m_smart_terrain_id == old_smart_id) and self.board.smarts[old_smart_id] ~= nil then
			self.board.smarts[old_smart_id].smrt:unregister_npc(obj)
		end

		if smart ~= nil then
			smart:register_npc(obj)
		end
	end
end
--' Назначение смарта
function sim_squad_scripted:assign_smart(smart)
	local old_smart = self.smart_id
	self.smart_id = smart and smart.id
	--printf("  squad %s assign smart. old[%s] new[%s]", self.id, tostring(old_smart), tostring(self.smart_id))
	--callstack()
	for k in self:squad_members() do
		self:assign_squad_member_to_smart(k.id, smart, old_smart)
	end
end

function sim_squad_scripted:check_invulnerability()
	if self.squad_online ~= true then
		return
	end
	local invulnerability = xr_logic.pick_section_from_condlist(db.actor, self, self.invulnerability) == "true"

	for k in self:squad_members() do
		local npc_st = db.storage[k.id]
		if npc_st ~= nil then
			local npc = npc_st.object
			if npc:invulnerable() ~= invulnerabilty and utils.cfg_get_string(npc_st.ini, npc_st.active_section, "invulnerable", npc, false, "", nil) == nil  then
--				printf("RESET INVULNERABILITY. npc[%s] = [%s]", npc:name(), tostring(invulnerability))
				npc:invulnerable(invulnerability)
			end
		end
	end
end

function sim_squad_scripted:set_location_types_section (section)
--	printf("set_location_types_section[%s] for [%s]", tostring(section), tostring(self:name()))
	if locations_ini:section_exist(section) then
		local result, id, value	= locations_ini:r_line(section,0,"","")
		self:add_location_type(id)
	end
end
function sim_squad_scripted:set_location_types(new_smart_name)
	local default_location = "stalker_terrain"
	printf("set_location_types for squad [%s]", self:name())
	self:clear_location_types()
	--[[for k,v in pairs(simulation_objects.get_sim_obj_registry().objects) do
		if alife():object(k):clsid() == clsid.smart_terrain then
			self:set_location_types_section(alife():object(k):name())
		end
	end]]--

	if alife():object(self.assigned_target_id):clsid() == clsid.smart_terrain then
		self:set_location_types_section(default_location)
		local old_smart_name = self.smart_id and alife():object(self.smart_id) and alife():object(self.smart_id):name()
		--printf("old_smart_name = [%s]!", tostring(old_smart_name))
		if old_smart_name then
			self:set_location_types_section(old_smart_name)
		end
		--printf("new_smart_name = [%s]!", tostring(new_smart_name))
		if new_smart_name then
			self:set_location_types_section(new_smart_name)
		end
	else
		--printf("target is squad or actor setting [squad_terrain]!")
		self:set_location_types_section("squad_terrain")
		for k,v in pairs(simulation_objects.get_sim_obj_registry().objects) do
			if alife():object(k):clsid() == clsid.smart_terrain then
				local props_base = alife():object(k).props and alife():object(k).props["base"]
				if props_base and tonumber(props_base) == 0 then
					self:set_location_types_section(alife():object(k):name())
				end
			end
		end
	end
end

function sim_squad_scripted:add_squad_member(spawn_section, spawn_position, lv_id, gv_id, sect_number)
	local spawn_sections_ltx = system_ini()
	local custom_data = utils.cfg_get_string(spawn_sections_ltx, spawn_section, "custom_data", self, false, "", "default_custom_data.ltx")
	if custom_data ~= "default_custom_data.ltx" then
		printf("INCORRECT npc_spawn_section USED [%s]. You cannot use npc with custom_data in squads", spawn_section)
	end

	local position = spawn_position

	local obj =  alife():create(spawn_section,
								position,
								lv_id,
								gv_id)

	self:register_member(obj.id)
	self.sound_manager:register_npc(obj.id)

	if simulation_objects.is_on_the_same_level(obj, alife():actor()) and position:distance_to_sqr(alife():actor().position) <= alife():switch_distance()^2 then
		db.spawned_vertex_by_id[obj.id] = lv_id
	end

	return obj.id
end

--' Создание чуваков в отряд
function sim_squad_scripted:create_npc(spawn_smart)
	local ini = system_ini()
	local spawn_sections = utils.parse_names(utils.cfg_get_string(ini, self.settings_id, "npc", self, false, "", ""))

	local spawn_point = utils.cfg_get_string(ini, self.settings_id, "spawn_point", self, false, "","self") or
						utils.cfg_get_string(spawn_smart.ini, smart_terrain.SMART_TERRAIN_SECT, "spawn_point", self, false,"","self")
	spawn_point = xr_logic.parse_condlist(self, "spawn_point", "spawn_point", spawn_point)
	spawn_point = xr_logic.pick_section_from_condlist(db.actor, self, spawn_point)

	--print_table(debug.getinfo(1))
	printf("SPAWN SMART %s", spawn_smart:name())
	--' Высчитываем базовую позицию спауна
	local base_spawn_position = spawn_smart.position
	local base_lvi = spawn_smart.m_level_vertex_id
	local base_gvi = spawn_smart.m_game_vertex_id
	if spawn_point ~= nil then
		if spawn_point == "self" then
			base_spawn_position = spawn_smart.position
			base_lvi = spawn_smart.m_level_vertex_id
			base_gvi = spawn_smart.m_game_vertex_id
		else
			base_spawn_position = patrol(spawn_point):point(0)
			base_lvi = patrol(spawn_point):level_vertex_id(0)
			base_gvi = patrol(spawn_point):game_vertex_id(0)
		end
	elseif spawn_smart.spawn_point ~= nil then
		base_spawn_position = patrol(spawn_smart.spawn_point):point(0)
		base_lvi = patrol(spawn_smart.spawn_point):level_vertex_id(0)
		base_gvi = patrol(spawn_smart.spawn_point):game_vertex_id(0)
	end
	if #spawn_sections ~= 0 then
		for k,v in pairs(spawn_sections) do
			self:add_squad_member(v, base_spawn_position, base_lvi, base_gvi,k)
		end
	end

	-- подспаун рэндомных чуваков...
	local random_spawn = utils.cfg_get_string(ini, self.settings_id, "npc_random", self, false, "",nil)
	if random_spawn ~= nil then
		random_spawn 				= 	utils.parse_names(random_spawn)
		local count_min, count_max 	= utils.r_2nums( ini,  self.settings_id, "npc_in_squad", 1, 2 )
		if count_min > count_max then
			abort("min_count can't be greater then max_count [%s]!!!!!", self.settings_id)
		end
		local random_count	 		= math.random(count_min, count_max)
		for i = 1,random_count do
			local random_id = math.random(1, #random_spawn)
			self:add_squad_member(random_spawn[random_id], base_spawn_position, base_lvi, base_gvi,random_id)
		end
	elseif #spawn_sections == 0 then
		abort("You are trying to spawn an empty squad [%s]!!!", self.settings_id)
	end


	self.smart_id = spawn_smart.id
	self:refresh()
end

function sim_squad_scripted:set_squad_sympathy(sympathy)
	local symp = sympathy or self.sympathy
	if(symp~=nil) then
		for k in self:squad_members() do
			local npc = db.storage[k.id] and db.storage[k.id].object
			if(npc) then
				game_relations.set_npc_sympathy(npc, symp)
			else
				if(db.goodwill.sympathy==nil) then
					db.goodwill.sympathy = {}
				end
				db.goodwill.sympathy[k.id] = symp
			end
		end
	end
end
local function set_relation(npc1, npc2, new_relation)
	local goodwill = 0
	if(new_relation=="enemy") then
		goodwill = -1000
	elseif(new_relation=="friend") then
		goodwill = 1000
	end
	if npc1 and npc2 then
		npc1:force_set_goodwill(goodwill, npc2.id)
	else
		abort("Npc not set in goodwill function!!!")
	end
end
function sim_squad_scripted:set_squad_relation(relation)
	local rel = relation or self.relationship
	if(rel~=nil) then
		for k in self:squad_members() do
			local npc = db.storage[k.id] and db.storage[k.id].object
			if(npc) then
				game_relations.set_npcs_relation(npc, db.actor, rel)
			else
				set_relation(alife():object(k.id), alife():actor(),rel)
			end
		end
	end
end


local function reset_animation(npc)
	local state_mgr = db.storage[npc:id()].state_mgr
	if state_mgr == nil then
		return
	end
	local planner = npc:motivation_action_manager()

	state_mgr.animation:set_state(nil, true)
	state_mgr.animation:set_control()
	state_mgr.animstate:set_state(nil, true)
	state_mgr.animstate:set_control()

	state_mgr:set_state("idle", nil, nil, nil, {fast_set = true})

--	planner:update()
--	planner:update()
--	planner:update()

	state_mgr:update()
	state_mgr:update()
	state_mgr:update()
	state_mgr:update()
	state_mgr:update()
	state_mgr:update()
	state_mgr:update()

	npc:set_body_state(move.standing)
	npc:set_mental_state(anim.free)

end

function sim_squad_scripted:set_squad_position(position)
	if self.online == false then
		self:force_change_position(position)
	end

	for k in self:squad_members() do
		local cl_object = level.object_by_id(k.id)
		db.offline_objects[k.id].level_vertex_id = level.vertex_id(position)
		if cl_object then
			reset_animation(cl_object)
			printf("teleporting npc [%s]", k.id)
			cl_object:set_npc_position(position)
			printf("end of teleporting npc [%s]", k.id)
		else
			k.object.position = position
		end
	end
end

function sim_squad_scripted:has_detector()
	for k in self:squad_members() do
		if alife():object(k.id) and  alife():object(k.id):has_detector() then
			return true
		end
	end
	return false
end

function sim_squad_scripted:get_squad_community()
	local squad_community = squad_community_by_behaviour[self.player_id]
	if squad_community == nil then
		abort("squad community is 'nil' for player_id [%s]", self.player_id)
	end
	return squad_community
end

--***********************************************************************************************
--*										SAVE\LOAD												*
--***********************************************************************************************

function sim_squad_scripted:STATE_Write(packet)
	cse_alife_online_offline_group.STATE_Write (self, packet)
	set_save_marker(packet, "save", false, "sim_squad_scripted")


	packet:w_stringZ(tostring(self.current_target_id))
	packet:w_stringZ(tostring(self.respawn_point_id))
	packet:w_stringZ(tostring(self.respawn_point_prop_section))
	packet:w_stringZ(tostring(self.smart_id))

	set_save_marker(packet, "save", true, "sim_squad_scripted")
end
function sim_squad_scripted:STATE_Read(packet, size)
	cse_alife_online_offline_group.STATE_Read (self, packet, size)
	set_save_marker(packet, "load", false, "sim_squad_scripted")

	self.current_target_id = packet:r_stringZ()
	if self.current_target_id == "nil" then
		self.current_target_id = nil
	else
		self.current_target_id = tonumber(self.current_target_id)
	end
	self.respawn_point_id = packet:r_stringZ()
	if self.respawn_point_id == "nil" then
		self.respawn_point_id = nil
	else
		self.respawn_point_id = tonumber(self.respawn_point_id)
	end
	self.respawn_point_prop_section = packet:r_stringZ()
	if self.respawn_point_prop_section == "nil" then
		self.respawn_point_prop_section = nil
	end
	self.smart_id = packet:r_stringZ()
	if self.smart_id == "nil" then
		self.smart_id = nil
	else
		self.smart_id = tonumber(self.smart_id)
	end

	self:init_squad_on_load()
	set_save_marker(packet, "load", true, "sim_squad_scripted")
end
--***********************************************************************************************
--*										SERVER_OBJECT											*
--***********************************************************************************************

function sim_squad_scripted:on_register()
	cse_alife_online_offline_group.on_register( self )
	self.board.squads[self.id] = self
	-- Проверяем кастомдату обьекта на наличие стори айди.
	story_objects.check_spawn_ini_for_story_id(self)
	simulation_objects.get_sim_obj_registry():register(self)
end
function sim_squad_scripted:on_unregister()
	--' Отрегистрация в таскменеджере
	unregister_story_object_by_id(self.id)
		--' Удалить отряд
	self.board.squads[self.id] = nil
	self.board:assign_squad_to_smart(self, nil)
	cse_alife_online_offline_group.on_unregister( self )
	simulation_objects.get_sim_obj_registry():unregister(self)
	if self.respawn_point_id ~= nil then
		local smart = alife():object(self.respawn_point_id)
		-- затычка для дестроя левела(смарты дестроятся раньше сквадов)
		if smart == nil then
			return
		end
		smart.already_spawned[self.respawn_point_prop_section].num = smart.already_spawned[self.respawn_point_prop_section].num - 1
	end
end
function sim_squad_scripted:can_switch_offline()
	return cse_alife_online_offline_group.can_switch_offline(self)
end
function sim_squad_scripted:can_switch_online()
	return cse_alife_online_offline_group.can_switch_online(self)
end

--***********************************************************************************************
--*										MAP LOCATION											*
--***********************************************************************************************

--[[
monster_predatory_day
monster_predatory_night
monster_vegetarian
monster_zombied_day
monster_zombied_night
monster_special
]]--

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,
	["monster"]					= true
}

function sim_squad_scripted:refresh()
	if(self:commander_id()==nil) then
		self:hide()
		return
	end
	self:show()
end


function sim_squad_scripted:hide()
	if(self.current_spot_id==nil) or (self.spot_section==nil) then
		return
	end
	level.map_remove_object_spot(self.current_spot_id, self.spot_section)
	self.current_spot_id = nil
	self.spot_section = nil
end

--' Показать отряд на карте
function sim_squad_scripted:show()
	if self.show_disabled then
		self:hide()
		return
	end

    if(level.map_has_object_spot(self:commander_id(), "ui_pda2_trader_location")~=0) or
	(level.map_has_object_spot(self:commander_id(), "ui_pda2_mechanic_location")~=0) or
	(level.map_has_object_spot(self:commander_id(), "ui_pda2_scout_location")~=0) or
	(level.map_has_object_spot(self:commander_id(), "ui_pda2_quest_npc_location")~=0) or
	(level.map_has_object_spot(self:commander_id(), "ui_pda2_medic_location")~=0) then
		self.show_disabled = true
		return
	end

	if self.current_spot_id ~= self:commander_id() then
		self:hide()
		self.current_spot_id = self:commander_id()
		self:show()
		return
	end
	local spot = ""
	if dev_debug then -- IX-Ray: Fixed PDA squad rendering in Lua devmode
		if not(is_squad_monster[self.player_id]) then
			local relation = game_relations.get_squad_goodwill_to_actor_by_id(self.id)
			if(relation=="friends") then
				spot = "alife_presentation_squad_friend_debug"
			elseif(relation=="neutral") then
				spot = "alife_presentation_squad_neutral_debug"
			else 
				spot = "alife_presentation_squad_enemy_debug"
			end
		else
			spot = "alife_presentation_squad_monster_debug"
		end
	else
		if not(is_squad_monster[self.player_id]) then
			local relation = game_relations.get_squad_relation_to_actor_by_id(self.id)
			if(relation=="friends") then
				spot = "alife_presentation_squad_friend"
			elseif(relation=="neutral") then
				spot = "alife_presentation_squad_neutral"
			end
		end
	end
	if(spot~="") then
		if spot == self.spot_section then
			level.map_change_spot_hint(self.current_spot_id, self.spot_section, self:get_squad_props())
			return
		end
		if self.spot_section == nil then
			level.map_add_object_spot(self.current_spot_id, spot, self:get_squad_props())
		else
			level.map_remove_object_spot(self.current_spot_id, self.spot_section)
			level.map_add_object_spot(self.current_spot_id, spot, self:get_squad_props())
		end
		self.spot_section = spot
	elseif(self.spot_section~=nil) then
		level.map_remove_object_spot(self.current_spot_id, self.spot_section)
		self.spot_section = nil
	end
end

function sim_squad_scripted:get_squad_props()
	if dev_debug then
		local t = "["..tostring(self:name()).."]\\n"..
					"current_target = ["..tostring(self.current_target_id and alife():object(self.current_target_id) and alife():object(self.current_target_id):name()).."]\\n"..
					"assigned_target = ["..tostring(self.assigned_target_id and alife():object(self.assigned_target_id) and alife():object(self.assigned_target_id):name()).."]\\n"
		if self.current_action and self.current_action.name == "stay_point" then
			t = t.."stay_on_point = ["..tostring(self.current_action.idle_time - game.get_game_time():diffSec(self.current_action.start_time)).."]"
		end
		return t
	else
		return ""
	end
end




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

function sim_squad_scripted:get_current_task()
	if self.assigned_target_id ~= nil and alife():object(self.assigned_target_id) ~= nil then
		local smart_terrain = alife():object(self.assigned_target_id)
		if smart_terrain.arriving_npc ~= nil and smart_terrain.arriving_npc[self:commander_id()] == nil
			and smart_terrain.npc_info and smart_terrain.npc_info[self:commander_id()]
			and smart_terrain.npc_info[self:commander_id()].job_id and smart_terrain.job_data[smart_terrain.npc_info[self:commander_id()].job_id] then
			return smart_terrain.job_data[smart_terrain.npc_info[self:commander_id()].job_id].alife_task
		end
		return alife():object(self.assigned_target_id):get_alife_task()
	end
	return self:get_alife_task()
end
-- Достигнут ли я отрядом выбравшим меня как цель.
function sim_squad_scripted:am_i_reached(squad)
	return self:npc_count() == 0
end
-- Вызывается 1 раз после достижения меня отрядом выбравшим меня как цель.
function sim_squad_scripted:on_after_reach(squad)

end
-- Вызывается 1 раз в момент выбора меня как цели.
function sim_squad_scripted:on_reach_target(squad)
	squad:set_location_types()
 	for k in squad:squad_members() do
		if db.offline_objects[k.id] ~= nil then
			db.offline_objects[k.id] = {}
		end
	end
	self.board:assign_squad_to_smart(squad, nil)
end
-- Возвращает CALifeSmartTerrainTask на меня, вызывается из smart_terrain:task()
function sim_squad_scripted:get_alife_task()
	--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))
	return CALifeSmartTerrainTask(self.m_game_vertex_id, self.m_level_vertex_id)
end

local smarts_by_no_assault_zones = {
									["zat_a2_sr_no_assault"] 	= "zat_stalker_base_smart",
									["jup_a6_sr_no_assault"] 	= "jup_a6",
									["jup_b41_sr_no_assault"] 	= "jup_b41"
}

function sim_squad_scripted:sim_available()
	for k,v in pairs (smarts_by_no_assault_zones) do
		local zone = db.zone_by_name[k]
		if zone and zone:inside(self.position) then
			local smart = sim_board.get_sim_board():get_smart_by_name(v)
			if smart and smart.base_on_actor_control ~= nil and smart.base_on_actor_control.status ~= smart_terrain_control.ALARM then
				return false
			end
		end
	end

	if self.smart_id == nil then
		return true
	end
	-- Удалять из регистра сим обьектов если мы идем на базу.
	local props_base = alife():object(self.smart_id).props and alife():object(self.smart_id).props["base"]
	if props_base and tonumber(props_base) > 0 then
		return false
	end


	local smart = alife():object(self.smart_id)
	if smart.base_on_actor_control ~= nil and smart.base_on_actor_control.status ~= smart_terrain_control.NORMAL then
		if db.zone_by_name[smart.base_on_actor_control.noweap_zone] == nil or not db.zone_by_name[smart.base_on_actor_control.noweap_zone]:inside(self.position) then
			return false
		end
	end
	return true
end

-- Мой прекондишн.
function sim_squad_scripted:target_precondition(squad)
	local squad_params = sim_board.simulation_activities[squad.player_id]
	if squad_params == nil or squad_params.squad == nil then
		return false
	end

	local self_params = squad_params.squad[self.player_id]
	if self_params == nil or self_params.prec(squad, self) == false then
		return false
	end
	return true

--[[
	if squad.player_id == "dolg" and
		(is_squad_monster[self.player_id] or self.player_id == "freedom") and
		in_time_interval(8,22) then
		return true
	end


	if squad.player_id == "bandit" and self.player_id == "stalker" then
		return true
	end
	if squad.player_id == "freedom" and self.player_id == "dolg" and in_time_interval(8,22) then
		return true
	end

	-- Травоядные днем атакуют проходящих мимо сталкеров.(плоть, кабан)
	if squad.player_id == "monster_vegetarian" and in_time_interval(8,22) and not is_squad_monster[self.player_id] then
		return true
	end

	-- Хищники днем атакуют сталкеров и травоядных(собаки, псевдособаки, пси собаки)
	if squad.player_id == "monster_predatory_day" and
		in_time_interval(8,22) and
		(self.player_id == "monster_vegetarian" or not is_squad_monster[self.player_id]) then
		return true
	end
	-- Хищники ночью атакуют сталкеров и травоядных(тушканы,кровосос, химера)
	if squad.player_id == "monster_predatory_night" and
		in_time_interval( 22 , 8) and
		(self.player_id == "monster_vegetarian" or not is_squad_monster[self.player_id]) then
		return true
	end
	-- Зомбиобразные днем атакуют проходящих мимо сталкеров.
	if squad.player_id == "monster_zombied_day" and
		in_time_interval(8,22) and
		(not is_squad_monster[self.player_id]) then
		return true
	end
	-- Зомбиобразные днем атакуют проходящих мимо сталкеров.
	if squad.player_id == "monster_zombied_night" and
		in_time_interval(22,8) and
		(not is_squad_monster[self.player_id]) then
		return true
	end
	-- Особенные днем атакуют проходящих мимо сталкеров.
	if squad.player_id == "monster_special" and
		in_time_interval(8,22) and
		(not is_squad_monster[self.player_id]) then
		return true
	end

	return false
	]]
end

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