501 lines
18 KiB
Text
501 lines
18 KiB
Text
--[[----------------------------------------------------------------------------------------------------------
|
|
Ñõåìà, âåäóùÿÿ ñòàëêåðà ê ìåñòó åãî ðàáîòû ïîä smart terrain.
|
|
Ïðèâîäèò ñòàëêåðà ê ìåñòó ðàáîòû åäèíîæäû. Äàëüøå ñòàëêåð õîäèò ïîä îáû÷íûìè ñõåìàìè.
|
|
×óãàé Àëåêñàíäð (chugai)
|
|
------------------------------------------------------------------------------------------------------------]]
|
|
--local function printf()
|
|
--end
|
|
----------------------------------------------------------------------------------------------------------------------
|
|
-- Ñõåìà ïàòðóëü äëÿ reach_task_location.
|
|
----------------------------------------------------------------------------------------------------------------------
|
|
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["back"] = {
|
|
{ dir = vector ():set (0.3, 0, -1), dist = 1.2 },
|
|
{ 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 = 2.4 },
|
|
{ dir = vector ():set (0.3, 0, -1), dist = 3.6 },
|
|
{ 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 = 4.8 },
|
|
{ dir = vector ():set (0.3, 0, -1), dist = 6 },
|
|
{ dir = vector ():set (-0.3, 0, -1), dist = 6 },
|
|
{ dir = vector ():set (0.3, 0, -1), dist = 7.2 },
|
|
{ dir = vector ():set (-0.3, 0, -1), dist = 7.2 },
|
|
{ dir = vector ():set (0.3, 0, -1), dist = 8.4 },
|
|
{ dir = vector ():set (-0.3, 0, -1), dist = 8.4}
|
|
}
|
|
|
|
|
|
local accel_by_curtype = {
|
|
walk = "run",
|
|
patrol = "rush",
|
|
raid = "assault",
|
|
sneak = "sneak_run",
|
|
sneak_run = "assault"
|
|
}
|
|
|
|
|
|
|
|
class "PatrolManager"
|
|
function PatrolManager:__init(target_id)
|
|
self.target_name = target_id
|
|
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)
|
|
--'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_REACH_TASK] 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:id(), dir = vector ():set (1, 0, 0), dist = 0}
|
|
self.npc_count = self.npc_count + 1
|
|
--' åñëè ìóæèê ïåðâûé èëè ñ÷èòàåòñÿ ëèäåðîì, òî óñòàíîâèì, êàê ëèäåðà
|
|
if npc:id() == get_object_squad(npc):commander_id() then
|
|
self.commander_id = npc:id ()
|
|
printf ("[XR_REACH_TASK] ASSIGNED NPC %s AS PATROL COMMANDER", npc:name ())
|
|
end
|
|
--printf ("[XR_REACH_TASK] NPC %s added to patrol manager %s", npc:name (), self.target_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_REACH_TASK] NPC %s removed from patrol manager %s", npc:name(), self.target_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
|
|
--óñòàíîâèì êîìàíäîðà, åñëè ýòî åùå íå ñäåëàíî
|
|
local se_npc = alife():object(data.soldier)
|
|
local squad = se_npc and get_object_squad(se_npc)
|
|
if squad == nil then
|
|
return
|
|
end
|
|
if self.commander_id == -1 then
|
|
self.commander_id = squad:commander_id()
|
|
end
|
|
-- ïåðåñ÷èòàåì ïîçèöèè
|
|
if self.commander_id ~= self.npc_list[key].soldier 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.target_name)
|
|
end
|
|
if formation ~= "around" and formation ~= "back" and formation ~= "line" then
|
|
abort ("Invalid formation (%s) for PatrolManager[%s]", formation, self.target_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.target_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.target_name)
|
|
end
|
|
--ïðîâåðêà, ÷òîáû êîìàíäèð íå âçäóìàë çàäàâàòü ãëóïûõ âîïðîñîâ
|
|
if npc:id () == self.commander_id then
|
|
abort ("Patrol commander called function PatrolManager:get_npc_command in PatrolManager[%s]", self.target_name)
|
|
end
|
|
--ïîëó÷èì äàííûå êîìàíäèðà
|
|
local commander = self.npc_list[self.commander_id].soldier
|
|
if commander == nil then
|
|
abort ("Patrol commander not present in PatrolManager[%s]", self.target_name)
|
|
end
|
|
return level.object_by_id(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.target_name)
|
|
end
|
|
--'çàêåøèðóåì àéäèøíèê íåïèñÿ
|
|
local npc_id = npc:id ()
|
|
if self.npc_list[self.commander_id] == nil then
|
|
return npc:level_vertex_id(), npc:direction(), self.current_state
|
|
end
|
|
--'ïðîâåðêà íåïèñÿ íà ïðèñóòñâèå â ñïèñêå
|
|
if self.npc_list[npc:id ()] == nil then
|
|
abort("NPC with name %s can't present in PatrolManager[%s]", npc:name (), self.target_name)
|
|
end
|
|
--'ïðîâåðêà, ÷òîáû êîìàíäèð íå âçäóìàë çàäàâàòü ãëóïûõ âîïðîñîâ
|
|
if npc:id() == self.commander_id then
|
|
abort("Patrol commander called function PatrolManager:get_npc_command in PatrolManager[%s]", self.target_name)
|
|
end
|
|
--'ïîëó÷èì äàííûå êîìàíäèðà
|
|
local commander = level.object_by_id(self.npc_list[self.commander_id].soldier)
|
|
if commander == nil then
|
|
abort("commander is nil!!!")
|
|
end
|
|
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(level.object_by_id(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(level.object_by_id(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
|
|
abort ("NPC commander possible dead in PatrolManager[%s]", self.target_name)
|
|
end
|
|
if npc:id () ~= self.commander_id then
|
|
return --abort ("NPC %s is not commander in PatrolManager[%s]", npc:name (), self.target_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 = level.object_by_id(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
|
|
|
|
local function squad_current_action (object)
|
|
local squad = get_object_squad(object)
|
|
return squad and squad.current_action
|
|
end
|
|
|
|
-- âêëþ÷åíèå ïàòðóëÿ
|
|
local function add_to_patrol(npc, target_id)
|
|
local squad_id = get_object_squad(npc).id
|
|
if patrols[target_id.."_to_"..squad_id] == nil then
|
|
patrols[target_id.."_to_"..squad_id] = PatrolManager(target_id)
|
|
end
|
|
patrols[target_id.."_to_"..squad_id]:add_npc(npc)
|
|
end
|
|
|
|
class "evaluator_reached_task_location" ( property_evaluator )
|
|
|
|
function evaluator_reached_task_location:__init( name, storage ) super ( nil, name )
|
|
self.st = storage
|
|
end
|
|
|
|
function evaluator_reached_task_location:evaluate()
|
|
local squad = get_object_squad(self.object)
|
|
if squad and squad.current_action and squad.current_action.name == "reach_target" then
|
|
local squad_target = alife():object(squad.assigned_target_id)
|
|
if squad_target == nil then
|
|
return false
|
|
end
|
|
return not squad_target:am_i_reached(squad)
|
|
end
|
|
return false
|
|
end
|
|
|
|
--------------------------------------------------------------------------------------------------------------
|
|
-- äåéñòâèå "äîâåñòè ñòàëêåðà ê ìåñòó ðàáîòû"
|
|
|
|
class "action_reach_task_location" ( action_base )
|
|
|
|
function action_reach_task_location:__init( name, storage ) super ( nil, name )
|
|
self.st = storage
|
|
end
|
|
function action_reach_task_location:initialize()
|
|
action_base.initialize( self )
|
|
self.target_id = get_object_squad(self.object).assigned_target_id
|
|
self.squad_id = get_object_squad(self.object).id
|
|
self.cur_state = "patrol"
|
|
self.formation = "back"
|
|
self.l_vid = -1
|
|
self.dist = 0
|
|
self.dir = vector():set(0, 0, 1)
|
|
self.on_point = false
|
|
self.was_reset = false
|
|
self.time_to_update = time_global () + 1000
|
|
|
|
self.object:set_desired_direction ()
|
|
self.object:set_movement_selection_type (game_object.alifeMovementTypeMask)
|
|
self.object:set_item (object.idle, self.object:best_weapon())
|
|
self.object:set_body_state (move.standing)
|
|
self.object:set_detail_path_type ( move.line )
|
|
self.object:set_mental_state (anim.free)
|
|
self.object:set_movement_type (move.walk)
|
|
local squad_target = alife():object(self.target_id)
|
|
self.object:set_dest_game_vertex_id (squad_target.m_game_vertex_id)
|
|
self.object:set_path_type ( game_object.game_path )
|
|
self.object:inactualize_patrol_path ()
|
|
self.object:set_sight (look.path_dir,nil,0)
|
|
add_to_patrol (self.object, self.target_id)
|
|
end
|
|
|
|
|
|
function action_reach_task_location:execute()
|
|
if self.object:id() == get_object_squad(self.object):commander_id() then
|
|
self:commander_execute()
|
|
else
|
|
self:soldier_execute()
|
|
end
|
|
action_base.execute (self)
|
|
end
|
|
|
|
function action_reach_task_location:finalize()
|
|
self.object:set_movement_selection_type(game_object.alifeMovementTypeRandom)
|
|
action_base.finalize( self )
|
|
end
|
|
|
|
function action_reach_task_location:commander_execute()
|
|
local squad = get_object_squad(self.object)
|
|
local squad_target = simulation_objects.get_sim_obj_registry().objects[squad.assigned_target_id]
|
|
|
|
if squad_target == nil and squad:get_script_target() ~= nil then
|
|
squad_target = alife():object(squad.assigned_target_id)
|
|
end
|
|
|
|
local function update_movement (target, object)
|
|
if target ~= nil and not object:is_talking() then
|
|
if xr_conditions.surge_started() then
|
|
object:set_movement_type(move.run)
|
|
object:set_mental_state (anim.free)
|
|
return
|
|
end
|
|
if target:clsid() == clsid.online_offline_group_s then
|
|
object:set_movement_type(move.run)
|
|
if target.position:distance_to_sqr(object:position()) <= 10000 then
|
|
object:set_mental_state (anim.danger)
|
|
else
|
|
object:set_mental_state (anim.free)
|
|
end
|
|
else
|
|
object:set_movement_type(move.walk)
|
|
object:set_mental_state (anim.free)
|
|
end
|
|
else
|
|
object:set_movement_type(move.stand)
|
|
end
|
|
end
|
|
|
|
if squad_target ~= nil and not self.object:is_talking() then
|
|
local pos, lv_id, gv_id = squad_target:get_location()
|
|
if self.object:game_vertex_id() ~= gv_id then
|
|
self.object:set_path_type (game_object.game_path)
|
|
self.object:set_dest_game_vertex_id (gv_id)
|
|
self.object:set_sight (look.path_dir,nil,0)
|
|
update_movement (squad_target, self.object)
|
|
patrols[self.target_id.."_to_"..self.squad_id]:set_command(self.object, self.cur_state, self.formation)
|
|
return
|
|
end
|
|
|
|
self.object:set_path_type ( game_object.level_path )
|
|
if not self.object:accessible(pos) then
|
|
local ttp = vector():set(0,0,0)
|
|
lv_id = self.object:accessible_nearest(pos, ttp)
|
|
pos = level.vertex_position(lv_id)
|
|
end
|
|
self.object:set_sight (look.path_dir,nil,0)
|
|
self.object:set_dest_level_vertex_id(lv_id)
|
|
self.object:set_desired_position(pos)
|
|
end
|
|
update_movement (squad_target, self.object)
|
|
patrols[self.target_id.."_to_"..self.squad_id]:set_command(self.object, self.cur_state, self.formation)
|
|
end
|
|
|
|
function action_reach_task_location:soldier_execute()
|
|
if self.time_to_update - time_global() > 0 then
|
|
return
|
|
end
|
|
|
|
local squad = get_object_squad(self.object)
|
|
local squad_target = simulation_objects.get_sim_obj_registry().objects[squad.assigned_target_id]
|
|
|
|
if squad_target == nil and squad:get_script_target() ~= nil then
|
|
squad_target = alife():object(squad.assigned_target_id)
|
|
end
|
|
|
|
self.time_to_update = time_global() + 1000
|
|
self.l_vid, self.dir, self.cur_state = patrols[self.target_id.."_to_"..self.squad_id]: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)
|
|
|
|
if squad_target == nil or squad_target:clsid() == clsid.online_offline_group_s or xr_conditions.surge_started() then
|
|
self.object:set_movement_type(level.object_by_id(squad:commander_id()):movement_type())
|
|
self.object:set_mental_state (level.object_by_id(squad:commander_id()):mental_state())
|
|
return
|
|
end
|
|
if level.object_by_id(get_object_squad(self.object):commander_id()):movement_type() == move.stand then
|
|
self.object:set_movement_type(move.stand)
|
|
return
|
|
end
|
|
if level.vertex_position(self.l_vid):distance_to(self.object:position()) > 5 then
|
|
self.object:set_movement_type(move.run)
|
|
else
|
|
self.object:set_movement_type(move.walk)
|
|
end
|
|
end
|
|
|
|
|
|
function action_reach_task_location:death_callback(npc)
|
|
if self.target_id ~= nil then
|
|
patrols[self.target_id.."_to_"..self.squad_id]:remove_npc(npc)
|
|
end
|
|
end
|
|
function action_reach_task_location:net_destroy(npc)
|
|
if self.target_id ~= nil then
|
|
patrols[self.target_id.."_to_"..self.squad_id]:remove_npc(npc)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------------------------------------
|
|
|
|
function set_reach_task( npc, ini, scheme)
|
|
local st = xr_logic.assign_storage_and_bind( npc, ini, scheme )
|
|
end
|
|
|
|
function add_to_binder(npc, ini, scheme, section, storage)
|
|
local manager = npc:motivation_action_manager()
|
|
local alife_action = manager:action(stalker_ids.action_alife_planner)
|
|
local alife_action_planner = cast_planner(alife_action)
|
|
local new_action = alife_action_planner:action(stalker_ids.action_smart_terrain_task)
|
|
xr_logic.subscribe_action_for_events(npc, storage, new_action)
|
|
end
|
|
|
|
function add_reach_task_action(npc)
|
|
|
|
local manager = npc:motivation_action_manager()
|
|
|
|
local alife_action = manager:action(stalker_ids.action_alife_planner)
|
|
|
|
local alife_action_planner = cast_planner(alife_action)
|
|
|
|
alife_action_planner:remove_evaluator(stalker_ids.property_smart_terrain_task)
|
|
|
|
alife_action_planner:add_evaluator(stalker_ids.property_smart_terrain_task, evaluator_reached_task_location( "reached_task_location", st))
|
|
|
|
alife_action_planner:remove_action(stalker_ids.action_smart_terrain_task)
|
|
|
|
local new_action = action_reach_task_location("reach_task_location", st)
|
|
|
|
new_action:add_precondition (world_property(stalker_ids.property_alife, true))
|
|
|
|
new_action:add_precondition (world_property(stalker_ids.property_smart_terrain_task, true))
|
|
|
|
new_action:add_effect (world_property(stalker_ids.property_smart_terrain_task, false))
|
|
|
|
alife_action_planner:add_action(stalker_ids.action_smart_terrain_task, new_action)
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|