
----------------------------------------------------------------------------------------------------
-- Mob remarks
----------------------------------------------------------------------------------------------------
-- Разработчик: Andrey Fidrya (Zmey) af@svitonline.com
-- Исходный скрипт (trader): Evgeniy Negrobov (Jon) jon@gsc-game.kiev.ua
----------------------------------------------------------------------------------------------------

local default_wait_time = 5000 -- время в ms, или nil для anim_end condition
local default_anim_standing = anim.stand_idle

local state_moving = 0
local state_standing = 1

class "mob_walker"

function mob_walker:__init(obj, storage)
	self.object = obj
	self.st = storage
end

function mob_walker:reset_scheme()
	printf("_bp: mob_walker:reset_scheme: %s", self.object:name())
	
	mob_state_mgr.set_state(self.object, db.actor, self.st.state)

	self.st.signals = {}

	xr_logic.mob_capture(self.object, not self.st.no_reset)

	self.patrol_walk = patrol(self.st.path_walk)
	if not self.patrol_walk then
		abort("object '%s': unable to find path_walk '%s' on the map",
			self.object:name(), self.st.path_walk)
	end
	
	if self.st.path_look then
		self.patrol_look = patrol(self.st.path_look)
		if not self.patrol_look then
			abort("object '%s': unable to find path_look '%s' on the map",
				self.object:name(), self.st.path_look)
		end
	else
		self.patrol_look = nil
	end

	if self.st.path_walk_info == nil then
		self.st.path_walk_info = utils.path_parse_waypoints(self.st.path_walk)
		self.path_walk_info = self.st.path_walk_info
	end
	if self.st.path_look_info == nil then
		self.st.path_look_info = utils.path_parse_waypoints(self.st.path_look)
		self.path_look_info = self.st.path_look_info
	end

	self.state = state_moving
	
	self.crouch = false
	self.running = true
	self.cur_anim_set = default_anim_standing
	self.pt_wait_time = default_wait_time -- сколько ждать в точке, где играем анимацию 

	self.scheduled_snd = nil

	self.last_index = nil
	self.last_look_index = nil
	
	action(self.object, move(move.run_fwd, patrol(self.st.path_walk, patrol.next, patrol.continue)),
		cond(cond.move_end))
end

function mob_walker:update(delta)
--	printf("__bp: mob_walker update: %d", time_global())
	--if not xr_logic.is_active(self.object, self.st) then
	--	return
	--end

	local actor = db.actor

	if mob_alife_mgr.alife_control(self.object, actor, self.st.alife_mode) then
		return
	end
	
	-- Монстр после выхода из alife может уже быть не под скриптом, поэтому взять опять под скрипт
	if not xr_logic.mob_captured(self.object) then
		self:reset_scheme()
		return
	end
--[[
	if self:arrived_to_first_waypoint() then
		if xr_logic.try_switch_to_another_section(self.object, self.st, actor) then
			return
		end
	end
]]
	if self.state == state_standing then
		if not self.object:action() then
			local patrol_walk_count = self.patrol_walk:count()
			if patrol_walk_count == 1 and utils.stalker_at_waypoint(self.object, self.patrol_walk, 0) then
				self.state = state_moving
				self:waypoint_callback(self.object, nil, self.last_index)
			else
				self.last_look_index = nil
				self.state = state_moving
				self:update_movement_state() -- идти дальше
			end
		end
	end
end

function mob_walker:arrived_to_first_waypoint()
	return self.last_index ~= nil
end

function mob_walker:waypoint_callback(obj, action_type, index)
	printf("mob_walker:waypoint_callback(): name=%s, index=%d", self.object:name(), index)
	if index == -1 or index == nil then
		printf("ERROR: mob_walker: waypoint_callback: index is %s", if_then_else(index == -1, "-1", "nil"))
		return
	end

	self.last_index = index

	local suggested_snd = self.path_walk_info[index]["s"]
	if suggested_snd then
		self.scheduled_snd = suggested_snd
	end

	local suggested_crouch = self.path_walk_info[index]["c"]
	if suggested_crouch == "true" then
		self.crouch = true
	else
		self.crouch = false
	end

	local suggested_running = self.path_walk_info[index]["r"]
	if suggested_running == "true" then
		self.running = true
	else
		self.running = false
	end

	local sig = self.path_walk_info[index]["sig"]
	if sig then
		-- HACK, fixme:
		local npc_id = self.object:id()
		local scheme = db.storage[npc_id]["active_scheme"]
		local signals = db.storage[npc_id][scheme].signals
		signals[sig] = true
		printf("_bp: mob_walker [%s]: SIGNALLING: %s", self.object:name(), sig)
	end
	
	local beh = self.path_walk_info[index]["b"]
	if beh then
		mob_state_mgr.set_state(self.object, db.actor, beh)
	else
		mob_state_mgr.set_state(self.object, db.actor, self.st.state)
	end
	
	local search_for = self.path_walk_info[index].flags
	if search_for:get() == 0 then
		-- TODO: запретить одноточечные пути без соответствующего path_look по аналогии с move_mgr
		printf("_bp: no flags")
		self:update_movement_state() -- идти дальше
		return
	end

	local pt_chosen_idx = move_mgr.choose_look_point(self.patrol_look, self.path_look_info, search_for)
	if pt_chosen_idx then
		local suggested_wait_time = self.path_look_info[pt_chosen_idx]["t"]
		if suggested_wait_time then
			self.pt_wait_time = tonumber(suggested_wait_time)
		else
			local patrol_walk_count = self.patrol_walk:count()
			if patrol_walk_count == 1 and utils.stalker_at_waypoint(self.object, self.patrol_walk, 0) then
				self.pt_wait_time = time_infinite
			else
				self.pt_wait_time = default_wait_time
			end
		end

		local suggested_anim_set = self.path_look_info[pt_chosen_idx]["a"]
		if suggested_anim_set then
			if suggested_anim_set == "nil" then
				suggested_anim_set = nil
			end
			self.cur_anim_set = anim[suggested_anim_set]
		else
			self.cur_anim_set = default_anim_standing
		end

		local beh = self.path_walk_info[index]["b"]
		if beh then
			mob_state_mgr.set_state(self.object, db.actor, beh)
		else
			mob_state_mgr.set_state(self.object, db.actor, self.st.state)
		end

		if pt_chosen_idx ~= self.last_look_index then -- если уже смотрели туда - не поворачиваться
			self:look_at_waypoint(pt_chosen_idx) -- поворачиваемся
		end
		self.state = state_standing
		self:update_standing_state()

		-- Сразу же стартовать update, не ждать execute. Тогда, если мы уже смотрим
		-- в нужную сторону - не будет паузы в несколько миллисекунд на поворот.
		self:update(true)
	else
		abort("object '%s': path_walk '%s', index %d: cannot find corresponding point(s) on path_look '%s'",
			self.object:name(), self.path_walk, index, self.path_look)
	end
end

function mob_walker:update_movement_state()
	printf("_bp [%s]: update_movement_state", self.object:name())
	xr_logic.mob_capture(self.object, true)
	
	local m
	if self.running then
		m = move.run_fwd
	elseif self.crouch then
		m = move.steal
	else
		m = move.walk_fwd
	end

	if self.scheduled_snd then
		printf("_bp [%s]: playing scheduled sound", self.object:name())
		action(self.object, move(m, patrol(self.st.path_walk, patrol.next, patrol.continue)),
			sound(sound[self.scheduled_snd]), cond(cond.move_end))
		self.scheduled_snd = nil
	else
		action(self.object, move(m, patrol(self.st.path_walk, patrol.next, patrol.continue)), cond(cond.move_end))
	end
end

function mob_walker:update_standing_state()
	printf("_bp [%s]: update_standing_state", self.object:name())
	xr_logic.mob_capture(self.object, true)
	if self.scheduled_snd then
		printf("_bp [%s]: playing scheduled sound", self.object:name())
		action(self.object, anim(self.cur_anim_set, 0),
			sound(sound[self.scheduled_snd]), cond(cond.time_end, self.pt_wait_time))
		self.scheduled_snd = nil
	else
		action(self.object, anim(self.cur_anim_set, 0), cond(cond.time_end, self.pt_wait_time))
	end
end

function mob_walker:deactivate()
	xr_logic.mob_capture(self.object, true)
	action(self.object, move(move.steal, self.patrol_walk:point(0)), cond(cond.move_end))
end

function mob_walker:look_at_waypoint(pt)
	if not self.patrol_look then
		return
	end

	local look_pt = utils.vector_copy_by_val(self.patrol_look:point(pt)):sub(self.object:position())
	--self.object:set_sight(look.direction, look_pt, 0)

	xr_logic.mob_capture(self.object, true)
	action(self.object, look(look.direction, look_pt), cond(cond.look_end))
	
	self.last_look_index = pt
end

---------------------------------------------------------------------------------------------------------------------
function add_to_binder(npc, ini, scheme, section, storage)
	printf("DEBUG: add_to_binder: npc:name()='%s', scheme='%s', section='%s'", npc:name(), scheme, section)

	local new_action = mob_walker(npc, storage)

	-- Зарегистрировать все actions, в которых должен быть вызван метод reset_scheme при изменении настроек схемы:
	xr_logic.subscribe_action_for_events(npc, storage, new_action)
end

function set_scheme(npc, ini, scheme, section, gulag_name)
	local st = xr_logic.assign_storage_and_bind(npc, ini, scheme, section)

	st.logic     = xr_logic.cfg_get_switch_conditions(ini, section, npc)

	st.alife_mode      = mob_alife_mgr.cfg_get_mode(ini, section, npc)
	st.state           = mob_state_mgr.get_state(ini, section, npc)

	st.no_reset        = utils.cfg_get_bool(ini, section, "no_reset", npc, false)
	st.path_walk       = utils.cfg_get_string(ini, section, "path_walk", npc, true,  gulag_name)
	st.path_look       = utils.cfg_get_string(ini, section, "path_look", npc, false, gulag_name)

	st.path_walk_info = nil -- Будут инициализированы в reset(), сейчас пути могут быть еще
	st.path_look_info = nil -- не загружены.
end

