e4s-sdk/gamedata/scripts/xr_patrol.script
2026-06-17 23:06:51 +03:00

570 lines
No EOL
22 KiB
Text

----------------------------------------------------------------------------------------------------------------------
-- Ñõåìà ïàòðóëü. Ìóæèêè â ïàòðóëå
----------------------------------------------------------------------------------------------------------------------
function printf()
end
local key = nil
local data = nil
--'patrol states
local patrol_move = 0 -- ïðîñòî äâèæåíèå
local patrol_hide = 1 -- ðàêîõîä
local patrol_sprint = 2 -- î÷åíü áûñòðî áåæèì
local patrol_run = 3 -- ïðîñòî íåñåìñÿ, àêè ëîñè â êóêóðóçó
local patrol_stop = 4 -- ñòîèì
local patrols = {}
local formations = {}
formations["line"] = {
{ dir = vector ():set (-1, 0, 0), dist = 2 },
{ dir = vector ():set (-1, 0, 0), dist = 4 },
{ dir = vector ():set (-1, 0, 0), dist = 6 },
{ dir = vector ():set (1, 0, 0), dist = 2 },
{ dir = vector ():set (1, 0, 0), dist = 4 },
{ dir = vector ():set (1, 0, 0), dist = 6 }
}
formations["back"] = {
{ dir = vector ():set (0.3, 0, -1), dist = 1.2 },
{ dir = vector ():set (-0.3, 0, -1), dist = 2.4 },
{ dir = vector ():set (0.3, 0, -1), dist = 3.6 },
{ dir = vector ():set (-0.3, 0, -1), dist = 4.8 },
{ dir = vector ():set (0.3, 0, -1), dist = 6 },
{ dir = vector ():set (-0.3, 0, -1), dist = 7.2 }
--[[
{ dir = vector ():set (-0.89442718, 0, -0.44721359), dist = 2.2360680 },
{ dir = vector ():set (0.89442718, 0, -0.44721359), dist = 2.2360680 },
{ dir = vector ():set (-0.37139067, 0, -0.92847669), dist = 5.3851647 },
{ dir = vector ():set (0.37139067, 0, -0.92847669), dist = 5.3851647 },
{ dir = vector ():set (-0.55470020, 0, -0.83205032), dist = 3.6055512 },
{ dir = vector ():set (0.55470020, 0, -0.83205032), dist = 3.6055512 }
]]
}
formations["around"] = {
{ dir = vector ():set (0.44721359, 0, -0.89442718), dist = 2.2360680 },
{ dir = vector ():set (-0.44721359, 0, -0.89442718), dist = 2.2360680 },
{ dir = vector ():set (1.0000000, 0, 0), dist = 2 },
{ dir = vector ():set (-1, 0, 0), dist = 2 },
{ dir = vector ():set (0.44721359, 0, 0.89442718), dist = 2.2360680 },
{ dir = vector ():set (-0.44721359, 0, 0.89442718), dist = 2.2360680 }
}
local accel_by_curtype = {
walk = "run",
patrol = "rush",
raid = "assault",
sneak = "sneak_run",
sneak_run = "assault"
}
class "PatrolManager"
function PatrolManager:__init(path_name)
self.path_name = path_name
self.npc_list = {}
self.current_state = "patrol"
self.commander_id = -1
self.formation = "back"
self.commander_lid = -1
self.commander_dir = vector ():set (0, 0, 1)
self.npc_count = 0
end
--' Äîáàâëåíèå íîâîãî ïåðñîíàæà
function PatrolManager:add_npc(npc, leader)
--'validate npc
if npc == nil or npc:alive() == false or self.npc_list[npc:id()] ~= nil then
return
end
--' Ïðîâåðêà íà òî ÷òî â ïàòðóëå íå áîëüøå 7 ñòàëêåðîâ (èíà÷å áóäåò âûëåòàòü äàëüøå)
if self.npc_count == 7 then
abort("[XR_PATROL] attempt to add more then 7 npc. [%s]", npc:name())
return
end
--' if this npc first in command then set him as commander
self.npc_list[npc:id ()] = {soldier = npc, dir = vector ():set (1, 0, 0), dist = 0}
self.npc_count = self.npc_count + 1
--' åñëè ìóæèê ïåðâûé èëè ñ÷èòàåòñÿ ëèäåðîì, òî óñòàíîâèì, êàê ëèäåðà
if self.npc_count == 1 or leader == true then
self.commander_id = npc:id ()
printf ("[XR_PATROL] ASSIGNED NPC %s AS PATROL COMMANDER", npc:name ())
end
printf ("[XR_PATROL] NPC %s added to patrol manager %s", npc:name (), self.path_name)
self:reset_positions ()
end
--' Óäàëåíèå ïåðñîíàæà
function PatrolManager:remove_npc(npc)
if npc == nil then return end
if self.npc_list[npc:id()] == nil then
return
end
printf ("[XR_PATROL] NPC %s removed from patrol manager %s", npc:name(), self.path_name)
self.npc_list[npc:id ()] = nil
self.npc_count = self.npc_count - 1
if npc:id () == self.commander_id then
self.commander_id = -1
self:reset_positions ()
end
end
--' Ïåðåñ÷åò çàíèìàåìûõ ïîçèöèé
function PatrolManager:reset_positions()
local form_ = formations[self.formation]
local index = 1
for key, data in pairs(self.npc_list) do
--óñòàíîâèì êîìàíäîðà, åñëè ýòî åùå íå ñäåëàíî
if self.commander_id == -1 and index == 1 then
self.commander_id = data.soldier:id ()
end
-- ïåðåñ÷èòàåì ïîçèöèè
if self.commander_id ~= self.npc_list[key].soldier:id () then
--printf("key[%s] index[%s]", tostring(key), tostring(index))
self.npc_list[key].dir = form_[index].dir
self.npc_list[key].dist = form_[index].dist
self.npc_list[key].vertex_id = -1
self.npc_list[key].accepted = true
index = index + 1
end
end
end
--' Óñòàíîâëåíèå ôîðìàöèè
function PatrolManager:set_formation(formation)
if formation == nil then
abort ("Invalid formation (nil) for PatrolManager[%s]", self.path_name)
end
if formation ~= "around" and formation ~= "back" and formation ~= "line" then
abort ("Invalid formation (%s) for PatrolManager[%s]", formation, self.path_name)
end
self.formation = formation
self:reset_positions ()
end
--' Ïîëó÷èòü êîìàíäèðà ïàòðóëÿ
function PatrolManager:get_commander(npc)
if npc == nil then
abort ("Invalid NPC on call PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name)
end
--çàêåøèðóåì àéäèøíèê íåïèñÿ
local npc_id = npc:id ()
--ïðîâåðêà íåïèñÿ íà ïðèñóòñâèå â ñïèñêå
if self.npc_list[npc:id ()] == nil then
abort ("NPC with name %s can't present in PatrolManager[%s]", npc:name (), self.path_name)
end
--ïðîâåðêà, ÷òîáû êîìàíäèð íå âçäóìàë çàäàâàòü ãëóïûõ âîïðîñîâ
if npc:id () == self.commander_id then
abort ("Patrol commander called function PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name)
end
--ïîëó÷èì äàííûå êîìàíäèðà
local commander = self.npc_list[self.commander_id].soldier
if commander == nil then
abort ("Patrol commander not present in PatrolManager[%s]", self.path_name)
end
return commander
end
--' Ïîëó÷èòü ïàðàìåòðû äåéñòâèÿ NPC
function PatrolManager:get_npc_command(npc)
if npc == nil then
abort("Invalid NPC on call PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name)
end
--'çàêåøèðóåì àéäèøíèê íåïèñÿ
local npc_id = npc:id ()
--'ïðîâåðêà íåïèñÿ íà ïðèñóòñâèå â ñïèñêå
if self.npc_list[npc:id ()] == nil then
abort("NPC with name %s can't present in PatrolManager[%s]", npc:name (), self.path_name)
end
--'ïðîâåðêà, ÷òîáû êîìàíäèð íå âçäóìàë çàäàâàòü ãëóïûõ âîïðîñîâ
if npc:id() == self.commander_id then
abort("Patrol commander called function PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name)
end
--'ïîëó÷èì äàííûå êîìàíäèðà
local commander = self.npc_list[self.commander_id].soldier
local dir = commander:direction()
local pos = vector():set(0, 0, 0)
local vertex_id = commander:location_on_path(5, pos)
--' Åñëè êîìàíäèð ñàì åùå íå äîøåë äî ïóòè (ðàññòîÿíèå äî âåðòåêñà áîëüøå ÷åì 5 ìåòðîâ)
--' òî çà îñíîâíóþ òî÷êó áåðåì âåðòåêñ êîìàíäèðà
if level.vertex_position(vertex_id):distance_to(self.npc_list[npc_id].soldier:position()) > 5 then
vertex_id = commander:level_vertex_id()
end
--'óáåðåì âëèÿíèå âåðòèêàëüíîé ñîñòàâëÿþùåé
dir.y = 0
dir:normalize()
--'ïîëó÷èì äàííûå ñàëàãè
local dir_s = self.npc_list[npc_id].dir
local dist_s = self.npc_list[npc_id].dist
--'ðàñ÷åò ïîçèöèè äëÿ ñàëàãè
--'ñíà÷àëà ïîëó÷èì íàïðàâëåíèå
local angle = yaw_degree(dir_s, vector():set(0, 0, 1))
local vvv = vector_cross(dir_s, vector():set(0, 0, 1))
if vvv.y < 0 then
angle = -angle
end
dir_s = vector_rotate_y(dir, angle)
local d = 2
--'òåïåðü àè íîäó îòíîñèòåëüíî êîìàíäîðà îðäåíà ðûöàðåé õðàìà
local vertex = level.vertex_in_direction(level.vertex_in_direction(vertex_id, dir_s, dist_s), dir, d)
self.npc_list[npc_id].vertex_id = vertex
--' ïðîâåðèì, à íå èìååò ëè ñìûñëà íåïèñþ ñêîððåêòèðîâàòü ñâîþ ïîçèöèþ
--local distance = level.vertex_position(vertex):distance_to(self.npc_list[npc_id].soldier:position())
-- íóæíî ñ÷èòàòü îòñòàâàíèå îò êîìàíäèðà, à íå îò ðàñ÷åòíîé òî÷êè.
local distance = commander:position():distance_to(self.npc_list[npc_id].soldier:position())
if distance > dist_s + 2 then
--' Èãðîê ñèëüíî îòñòàë. Íåîáõîäèìî èçìåíèòü òèï ïåðåìåùåíèÿ.
--' Òèï ïåðåìåùåíèÿ çàäàåì â çàâèñèìîñòè îò áàçîâîãî òèïà.
local new_state = accel_by_curtype[self.current_state]
if new_state ~= nil then
return vertex, dir, new_state
end
end
return vertex, dir, self.current_state
end
--' Óñòàíîâèòü äåéñòâèå ÍÏÑ
function PatrolManager:set_command(npc, command, formation)
if npc == nil or npc:alive () == false then
self:remove_npc(npc)
return
end
if npc:id () ~= self.commander_id then
return --abort ("NPC %s is not commander in PatrolManager[%s]", npc:name (), self.path_name)
end
self.current_state = command
if self.formation ~= formation then
self.formation = formation
self:set_formation (formation)
end
self.commander_lid = npc:level_vertex_id ()
self.commander_dir = npc:direction ()
self:update ()
end
--' ßâëÿåòñÿ ëè óêàçàííûé ÍÏÑ êîìàíäèðîì.
function PatrolManager:is_commander(npc_id)
return npc_id == self.commander_id
end
--' Íàõîäèòñÿ ëè êîìàíäèð ïàòðóëÿ â ñõåìå XR_MEET
function PatrolManager:is_commander_in_meet()
if self.commander_id == -1 then return false end
local npc = self.npc_list[self.commander_id].soldier
if npc ~= nil and npc:alive () == true then
return xr_meet.is_meet (npc)
end
return false
end
--' Àïäåéò
function PatrolManager:update ()
if tm_enabled == true then
self.tm:update ()
end
end
--'-------------------------------------------------------------------------------------------------------------------
--'Evaluators
--'--------------------------------------------------------------------------------------------------------------------
class "evaluator_patrol_end" (property_evaluator)
function evaluator_patrol_end:__init(name, storage) super (nil, name)
self.st = storage
end
function evaluator_patrol_end:evaluate()
return not xr_logic.is_active(self.object, self.st)
end
class "evaluator_patrol_comm" (property_evaluator)
function evaluator_patrol_comm:__init(name, storage) super (nil, name)
self.st = storage
end
function evaluator_patrol_comm:evaluate()
return patrols[self.st.patrol_key]:is_commander(self.object:id())
end
--'-------------------------------------------------------------------------------------------------------------------
--' Actions
--'--------------------------------------------------------------------------------------------------------------------
class "action_commander" (action_base)
function action_commander:__init(npc, action_name, storage) super(nil, action_name)
self.st = storage
self.move_mgr = db.storage[npc:id()].move_mgr
self.was_reset = false
self.cur_state = "patrol"
end
function action_commander:initialize()
action_base.initialize (self)
self.object:set_desired_position()
self.object:set_desired_direction()
self:activate_scheme ()
end
function action_commander:activate_scheme()
self.st.signals = {}
printf ("[XR_PATROL] SET PATROL PATH : %s", self.st.path_walk)
if self.st.path_walk_info == nil then
self.st.path_walk_info = utils.path_parse_waypoints(self.st.path_walk)
end
if self.st.path_look_info == nil then
self.st.path_look_info = utils.path_parse_waypoints(self.st.path_look)
end
self.move_mgr:reset(self.st.path_walk, self.st.path_walk_info, self.st.path_look, self.st.path_look_info,
self.st.team, self.st.suggested_state, {obj = self, func = self.formation_callback})
patrols[self.st.patrol_key]:set_command(self.object, self.cur_state, self.st.formation)
end
function action_commander:execute()
action_base.execute(self)
self.move_mgr:update()
local new_state = state_mgr.get_state(self.object)
local old_state = self.old_state
if old_state ~= new_state then
if self.st.silent ~= true then
if new_state == "sneak" then
xr_sound.set_sound_play(self.object:id(), "patrol_sneak")
elseif new_state == "sneak_run" then
xr_sound.set_sound_play(self.object:id(), "patrol_run")
elseif new_state == "run" then
xr_sound.set_sound_play(self.object:id(), "patrol_run")
elseif new_state == "assault" then
xr_sound.set_sound_play(self.object:id(), "patrol_run")
elseif new_state == "rush" then
xr_sound.set_sound_play(self.object:id(), "patrol_run")
else
if old_state == "sneak" or old_state == "sneak_run" or
old_state == "run" or old_state == "assault" or old_state == "rush"
then
xr_sound.set_sound_play(self.object:id(), "patrol_walk")
end
end
end
self.old_state = new_state
end
--printf("FORMATION %s", self.st.formation)
patrols[self.st.patrol_key]:set_command(self.object, new_state, self.st.formation)
end
----------------------
function action_commander:finalize()
if self.object:alive () == true then
--printf ("ACTION_COMMANDER:FINALIZE CALLED")
patrols[self.st.patrol_key]:set_command(self.object, "guard", self.st.formation)
self.move_mgr:finalize()
end
action_base.finalize(self)
end
----------------------
function action_commander:deactivate(npc)
patrols[self.st.patrol_key]:remove_npc(npc)
end
----------------------
function action_commander:death_callback(npc)
patrols[self.st.patrol_key]:remove_npc(npc)
end
----------------------
function action_commander:net_destroy(npc)
self:deactivate(npc)
end
----------------------
function action_commander:formation_callback(mode, number, index)
if number == 0 then
self.st.formation = "line"
elseif number == 1 then
self.st.formation = "around"
elseif number == 2 then
self.st.formation = "back"
end
printf("FORMATION CALLBACK. %s", self.st.formation)
end
class "action_patrol" (action_base)
function action_patrol:__init (npc, action_name, storage) super (nil, action_name)
self.st = storage
self.move_mgr = db.storage[npc:id()].move_mgr
self.l_vid = -1
self.dist = 0
self.dir = vector():set(0, 0, 1)
self.cur_state = "patrol"
self.on_point = false
self.was_reset = false
self.time_to_update = time_global () + 1000
end
function action_patrol:initialize()
action_base.initialize (self)
self.object:set_desired_position ()
self.object:set_desired_direction ()
self.on_point = false
end
function action_patrol:activate_scheme()
self.st.signals = {}
printf ("[XR_PATROL] SET SOLDIER PATROL PATH : %s", self.st.path_walk)
if self.st.path_walk_info == nil then
self.st.path_walk_info = utils.path_parse_waypoints(self.st.path_walk)
end
if self.st.path_look_info == nil then
self.st.path_look_info = utils.path_parse_waypoints(self.st.path_look)
end
self.move_mgr:reset(self.st.path_walk, self.st.path_walk_info, self.st.path_look, self.st.path_look_info,
self.st.team, self.st.suggested_state, {obj = self, func = self.formation_callback})
end
function action_patrol:execute()
action_base.execute (self)
--' if db.actor and xr_logic.try_switch_to_another_section (self.object, self.st, db.actor) then
--' return
--' end
if self.time_to_update - time_global() > 0 then
return
end
self.time_to_update = time_global() + 1000
self.l_vid, self.dir, self.cur_state = patrols[self.st.patrol_key]:get_npc_command(self.object)
self.l_vid = utils.send_to_nearest_accessible_vertex(self.object, self.l_vid)
local desired_direction = self.dir
printf("desired_direction = %s", vec_to_str(desired_direction))
if desired_direction ~= nil and not utils.vector_cmp(desired_direction, vector():set(0,0,0)) then
desired_direction:normalize()
self.object:set_desired_direction(desired_direction)
end
self.object:set_path_type(game_object.level_path)
--'ïåðåìåùàåìñÿ ïî ïóòè
state_mgr.set_state(self.object, self.cur_state)
end
function action_patrol:finalize()
if self.object:alive () == true then
self.move_mgr:finalize()
end
action_base.finalize (self)
end
function action_patrol:formation_callback(mode, number, index)
end
function action_patrol:death_callback(npc)
patrols[self.st.patrol_key]:remove_npc(npc)
end
function action_patrol:deactivate(npc)
patrols[self.st.patrol_key]:remove_npc(npc)
end
function action_patrol:net_destroy(npc)
self:deactivate (npc)
end
--'--------------------------------------------------------------------------------------------------------------------
--' Patrol binder
--'--------------------------------------------------------------------------------------------------------------------
function add_to_binder (object, ini, scheme, section, storage)
local operators = {}
local properties = {}
local manager = object:motivation_action_manager ()
properties["event"] = xr_evaluators_id.reaction
properties["patrol_end"] = xr_evaluators_id.sidor_patrol_base + 0
properties["patrol_comm"] = xr_evaluators_id.sidor_patrol_base + 1
properties["state_mgr_logic_active"] = xr_evaluators_id.state_mgr + 4
operators["action_patrol"] = xr_actions_id.sidor_act_patrol
operators["action_commander"] = xr_actions_id.sidor_act_patrol + 1
-- Evaluators
manager:add_evaluator (properties["patrol_end"], this.evaluator_patrol_end ("patrol_end", storage, "patrol_end"))
manager:add_evaluator (properties["patrol_comm"], this.evaluator_patrol_comm ("patrol_comm", storage, "patrol_comm"))
-- Actions
-- create action for patrol commander
local action = this.action_commander (object, "action_commander", storage)
action:add_precondition (world_property(stalker_ids.property_alive, true))
action:add_precondition (world_property(stalker_ids.property_danger,false))
action:add_precondition (world_property(stalker_ids.property_enemy, false))
action:add_precondition (world_property(stalker_ids.property_anomaly,false))
xr_motivator.addCommonPrecondition (action)
action:add_precondition (world_property(properties["patrol_end"], false))
action:add_precondition (world_property(properties["patrol_comm"], true))
action:add_effect (world_property(properties["patrol_end"], true))
action:add_effect (world_property(properties["state_mgr_logic_active"], false))
manager:add_action (operators["action_commander"], action)
xr_logic.subscribe_action_for_events (object, storage, action)
action = this.action_patrol (object,"action_patrol", storage)
action:add_precondition (world_property(stalker_ids.property_alive, true))
action:add_precondition (world_property(stalker_ids.property_danger, false))
action:add_precondition (world_property(stalker_ids.property_enemy, false))
action:add_precondition (world_property(stalker_ids.property_anomaly,false))
xr_motivator.addCommonPrecondition(action)
action:add_precondition (world_property(properties["patrol_end"], false))
action:add_precondition (world_property(properties["patrol_comm"], false))
action:add_effect (world_property(properties["patrol_end"], true))
action:add_effect (world_property(properties["state_mgr_logic_active"], false))
manager:add_action (operators["action_patrol"], action)
xr_logic.subscribe_action_for_events (object, storage, action)
action = manager:action (xr_actions_id.alife)
action:add_precondition (world_property(properties["patrol_end"], true))
end
-- âêëþ÷åíèå ïàòðóëÿ
function set_scheme(npc, ini, scheme, section, gulag_name)
printf ("XR_PATROL CALLED 'SET_SCHEME' FOR NPC %s", npc: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.path_name = utils.cfg_get_string(ini, section, "path_walk", npc, true, gulag_name)
st.path_walk = st.path_name
st.path_look = utils.cfg_get_string(ini, section, "path_look", npc, false, gulag_name)
if st.path_walk == st.path_look then
abort("You are trying to set 'path_look' equal to 'path_walk' in section [%s] for npc [%s]", section, npc:name())
end
st.formation = utils.cfg_get_string(ini, section, "formation", npc, false, "")
st.silent = utils.cfg_get_bool(ini, section, "silent", npc, false, false)
if st.formation == nil then
st.formation = "back"
end
st.move_type = utils.cfg_get_string(ini, section, "move_type", npc, false, "")
if st.move_type == nil then
st.move_type = "patrol"
end
st.suggested_state = {}
st.suggested_state.standing = utils.cfg_get_string(ini, section, "def_state_standing", npc, false, "")
st.suggested_state.moving = utils.cfg_get_string(ini, section, "def_state_moving1", npc, false, "")
st.suggested_state.moving = utils.cfg_get_string(ini, section, "def_state_moving", npc, false, "", st.suggested_state.moving)
--' st.animation = utils.cfg_get_string(ini, section, "animation", npc, false, "")
st.path_walk_info = nil --'Áóäóò èíèöèàëèçèðîâàíû â reset(), ñåé÷àñ ïóòè ìîãóò áûòü åùå
st.path_look_info = nil --'íå çàãðóæåíû.
st.commander = utils.cfg_get_bool(ini, section, "commander", npc, false, false)
st.patrol_key = st.path_name
local squad = get_object_squad(npc)
if squad ~= nil then
st.patrol_key = st.patrol_key .. tostring(squad.id)
end
if patrols[st.patrol_key] == nil then
patrols[st.patrol_key] = PatrolManager(st.path_name)
end
patrols[st.patrol_key]:add_npc(npc, st.commander)
end