Divergent/mods/HG Companion - Companions O.../gamedata/scripts/axr_beh.script

1233 lines
43 KiB
Plaintext
Raw Permalink Normal View History

2024-03-17 20:18:03 -04:00
--[[
scheme_type: active_section
author: Alundaio
TODO: Fix use camp
HiveGame: Fix use camp
--]]
---------------
beh_evid = 188111
beh_actid = 188111
---------------
-----------------------------
local level = level
local alife = alife
-----------------------------
-----------------------------
local keep_state_min_time = 2000
local function angle_dir(ang)
return vector():setHP(ang.y,ang.x):normalize()
end
local function init_custom_data(npc, ini, section, st, scheme)
local def = {}
--[[
{+npcx_beh_relax}
--]]
def.behavior_state = "{+npcx_beh_wait -npcx_beh_relax} beh_wait, {+npcx_beh_patrol_mode !is_squad_commander} beh_move, {+npcx_beh_patrol_mode} beh_patrol, {+npcx_beh_relax} beh_patrol, beh_move"
def.goto_target = "{!is_squad_commander} commander, actor"
def.walk_dist = "{+npcx_beh_patrol_mode} 60, {+npcx_beh_relax} 60, 3"
def.jog_dist = "{+npcx_beh_patrol_mode} 120, {+npcx_beh_relax} 120, 6"
def.keep_distance = "{+npcx_beh_distance_far} far, near"
def.near_desired_dist = "{+npcx_beh_patrol_mode =is_squad_commander} 1, {!is_squad_commander} 2, 3"
def.far_desired_dist = "{+npcx_beh_patrol_mode =is_squad_commander} 1, {!is_squad_commander} 2, 8"
def.wait_cond = "{+npcx_beh_substate_stealth} hide, guard"
def.walk_cond = "{+npcx_beh_patrol_mode -npcx_beh_substate_stealth} patrol,{+npcx_beh_substate_stealth} sneak, patrol"
def.jog_cond = "{+npcx_beh_substate_stealth} sneak_run, rush"
def.run_cond = "{+npcx_beh_substate_stealth} rush, assault"
def.delay_cond = "{+npcx_beh_substate_stealth} hide, guard"
--def.use_camp_cond = "{+npcx_beh_relax} true, false"
st.behavior_state = ini:r_string_to_condlist(section,"behavior_state",def.behavior_state)
st.goto_target = ini:r_string_to_condlist(section,"target",def.goto_target)
st.walk_dist = ini:r_string_to_condlist(section,"walk_dist",def.walk_dist)
st.jog_dist = ini:r_string_to_condlist(section,"jog_dist",def.jog_dist)
st.keep_distance = ini:r_string_to_condlist(section,"keep_dist",def.keep_distance)
st.near_desired_dist = ini:r_string_to_condlist(section,"near_desired_dist",def.near_desired_dist)
st.far_desired_dist = ini:r_string_to_condlist(section,"far_desired_dist",def.far_desired_dist)
st.wait_cond = ini:r_string_to_condlist(section,"wait_anim",def.wait_cond)
st.walk_cond = ini:r_string_to_condlist(section,"walk_anim",def.walk_cond)
st.jog_cond = ini:r_string_to_condlist(section,"jog_anim",def.jog_cond)
st.run_cond = ini:r_string_to_condlist(section,"run_anim",def.run_cond)
st.delay_cond = ini:r_string_to_condlist(section,"delay_anim",def.delay_cond)
st.path_end = ini:r_string_to_condlist(section,"path_end")
st.sound_idle = ini:r_string_ex(section,"sound_idle")
st.use_camp = ini:r_bool_ex(section,"use_camp")
st.camp_found = false
st.signals = {}
st.path_index = load_var(npc,"path_index",1)
st.wait_delay = nil
st.am_i_reached = nil
st.rally_lvid = nil
end
function am_i_reached(npc,index)
local st = npc and db.storage[npc:id()]
if (index) then
return st and st.active_scheme == "beh" and st.beh.am_i_reached and st.beh.path_index == index
end
return st and st.active_scheme == "beh" and st.beh.am_i_reached
end
local used_vids = {}
local assoc_tbl = {
idle = {director = {"sit"}, listener = {"sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa"}},
harmonica = {director = {"play_harmonica"}, listener = {"sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa"}},
guitar = {director = {"play_guitar"}, listener = {"sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa"}},
story = {director = {"sit"}, listener = {"sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa"}},
}
--------------------------------Action Base Evaluator ---------------------------------------------------------------------
class "evaluator_beh" (property_evaluator)
function evaluator_beh:__init(storage, name) super (nil, name)
self.st = storage
self.st.approved_actions = {"play_harmonica","play_guitar","sit","sit_ass","sit_knee","sit_ass_weapon","sit_ass_eat_bread","sit_ass_drink_vodka","sit_ass_drink_energy","sit_ass_eat_kolbasa" }
end
function evaluator_beh:evaluate()
return xr_logic.is_active(self.object, self.st) or false
end
--------------------------------------Action Base --------------------------------------------------------------------------------
class "action_beh" (action_base)
function action_beh:__init(npc_name, action_name, storage) super (nil, action_name)
self.st = storage
end
function action_beh:initialize()
action_base.initialize(self)
self.keep_state_until = time_global()
self.object:set_sight(look.direction, self.object:direction() , 0)
end
function action_beh:execute()
action_base.execute(self)
local keep_distance = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.keep_distance)
self.st.desired_distance = tonumber(xr_logic.pick_section_from_condlist(db.actor, self.object, self.st[keep_distance.."_desired_dist"]))
self.st.behavior = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.behavior_state,"behavior_state") or "beh_move"
self.st.wait_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.wait_cond)
self.st.walk_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.walk_cond)
self.st.jog_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.jog_cond)
self.st.run_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.run_cond)
self.st.delay_animation = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.delay_cond)
if (self.st.last_behavior ~= self.st.behavior) then
self.st.last_behavior = self.st.behavior
self.st.rally_lvid = nil
end
if (self:set_desired_target() and self[self.st.behavior]) then
self[self.st.behavior](self)
end
------------------ SURGE ------------------
if (xr_conditions.surge_started()) then
if (self.object:dont_has_info("npcx_beh_surge")) then
self.object:give_info_portion("npcx_beh_surge")
end
else
if (self.object:has_info("npcx_beh_surge")) then
self.object:disable_info_portion("npcx_beh_surge")
end
end
if (self.object:dont_has_info("npcx_beh_surge")) then
if (se_load_var(self.object:id(), self.object:name(), "cover") ~= nil) then
se_save_var(self.object:id(), self.object:name(), "cover", nil)
end
end
------------------ SURGE ------------------
------------------ RELAX ------------------
if (self.object:dont_has_info("npcx_beh_relax")) then
if (se_load_var(self.object:id(), self.object:name(), "campfire_step") ~= nil) then
se_save_var(self.object:id(), self.object:name(), "campfire_step", nil)
end
if (self.st.camp ~= nil) then
xr_sound.stop_sounds_by_id(self.object:id())
self.st.camp:unregister_npc(self.object:id())
self.st.camp = nil
end
end
------------------ RELAX ------------------
end
function action_beh:set_desired_target()
local target = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.goto_target)
if (target == nil or target == "" or target == "nil") then
return false
end
if (self.st.desired_target) then
if (self.st.target ~= target) then
self.st.target = target
self.st.desired_target.__keep_until = nil
self.st.rally_lvid = nil
end
local tg = time_global()
if (self.st.desired_target.__keep_until) then
-- do forever if keep_until is -1
if (self.st.desired_target.__keep_until == -1) then
return true
end
if (tg < self.st.desired_target.__keep_until) then
return true
end
end
if (self.st.desired_target.level_vertex_id) then
db.used_level_vertex_ids[self.st.desired_target.level_vertex_id] = nil
end
end
self.st.target = target
local npc = self.object
local t = self.st.desired_target and empty_table(self.st.desired_target) or {}
local function set_position_data(id)
local obj = level.object_by_id(id)
local sobj = alife_object(id)
if (obj) then
t.level_vertex_id = obj:level_vertex_id()
t.game_vertex_id = obj:game_vertex_id()
t.position = obj:position()
t.direction = obj:direction()
t.object = obj
elseif (sobj) then
t.level_vertex_id = sobj.m_level_vertex_id
t.game_vertex_id = sobj.m_game_vertex_id
t.position = sobj.position
t.direction = self.object:position():sub(t.position)
t.object = sobj
end
end
local obj = get_story_object(target)
if (obj) then
set_position_data(obj:id())
elseif (target == "enemy") then
local st = db.storage[self.object:id()]
local be = self.object:best_enemy() or st and st.enemy_id and level.object_by_id(st.enemy_id)
if (be and be:alive()) then
t.level_vertex_id = be:level_vertex_id()
t.position = be:position()
t.direction = self.object:position():sub(t.position)
t.object = be
end
elseif (target == "cover_near_actor") then
local vid = utils_obj.find_close_cover(self.object,db.actor:position(),db.actor:position()) or self.object:level_vertex_id()
if (vid) then
t.level_vertex_id = vid
t.position = level.vertex_position(vid)
t.direction = self.object:position():sub(t.position)
t.object = db.actor
db.used_level_vertex_ids[vid] = self.object:id()
t.__keep_until = time_global() + 3000
self.st.behavior = "beh_cover"
end
elseif (target == "cover_wait") then
local vid = utils_obj.try_go_cover(self.object,self.object,self.st.desired_target and self.st.desired_target.level_vertex_id or nil,3) or self.object:level_vertex_id()
if (vid) then
db.used_level_vertex_ids[vid] = self.object:id()
t.level_vertex_id = vid
t.position = level.vertex_position(vid)
t.direction = self.object:position():sub(t.position)
local dist = db.actor:position():distance_to_sqr(self.object:position())
t.object = dist <= 5 and db.actor or nil
t.__keep_until = -1
self.st.behavior = "beh_cover"
end
elseif (target == "task_target_anomaly") then
local name = db.actor and load_var(db.actor,"task_target_anomaly")
if (name) then
local anomaly = db.anomaly_by_name[name]
if (anomaly and anomaly.object) then
local vid = utils_obj.try_go_cover(self.object,anomaly.object:position(),self.st.desired_target and self.st.desired_target.level_vertex_id or nil)
if (vid) then
db.used_level_vertex_ids[vid] = self.object:id()
t.level_vertex_id = vid
t.position = level.vertex_position(vid)
t.direction = self.object:position():sub(t.position)
t.object = anomaly.object
t.__keep_until = time_global() + 8000
self.st.behavior = "beh_cover"
end
end
end
elseif (target == "task_target_hostage") then
local vid = utils_obj.try_go_cover(self.object,self.object,self.st.desired_target and self.st.desired_target.level_vertex_id or nil,3) or self.object:level_vertex_id()
if (vid) then
db.used_level_vertex_ids[vid] = self.object:id()
t.level_vertex_id = vid
t.position = level.vertex_position(vid)
t.direction = self.object:position():sub(t.position)
--t.object = db.actor
t.__keep_until = -1
self.st.behavior = "beh_cover"
self.st.rally_lvid = nil
end
------------------ SURGE ------------------
elseif (xr_conditions.surge_started()) then
local tg = time_global()
local actor = db.actor
local npcpos = npc:position()
if (se_load_var(npc:id(), npc:name(), "cover") ~= nil) then
local cover = se_load_var(npc:id(), npc:name(), "cover")
local coverl = se_load_var(npc:id(), npc:name(), "coverlast")
local dist = se_load_var(npc:id(), npc:name(), "coverdist")
local timerl = se_load_var(npc:id(), npc:name(), "covertimel")
local timern = se_load_var(npc:id(), npc:name(), "covertimen")
if (coverl == nil) then coverl = npcpos end
if (timerl == nil) then timerl = tg end
if (timern == nil) then timern = tg end
if (tg > timerl) then
if (npcpos:distance_to_sqr(cover) < 3) then
se_save_var(npc:id(), npc:name(), "covertimel", nil)
se_save_var(npc:id(), npc:name(), "covertimen", nil)
elseif (npcpos:distance_to_sqr(cover) < dist or npcpos:distance_to_sqr(coverl) > 1) then
dist = npcpos:distance_to_sqr(cover)
se_save_var(npc:id(), npc:name(), "coverlast", npcpos)
se_save_var(npc:id(), npc:name(), "coverdist", dist)
se_save_var(npc:id(), npc:name(), "covertimen", nil)
se_save_var(npc:id(), npc:name(), "covertimel", tg+2000)
else
se_save_var(npc:id(), npc:name(), "coverdist", nil)
se_save_var(npc:id(), npc:name(), "covertimel", nil)
se_save_var(npc:id(), npc:name(), "covertimen", tg+5000)
end
elseif (tg > timern) then
se_save_var(npc:id(), npc:name(), "cover", nil)
se_save_var(npc:id(), npc:name(), "coverlast", nil)
se_save_var(npc:id(), npc:name(), "coverdist", nil)
se_save_var(npc:id(), npc:name(), "covertimel", nil)
se_save_var(npc:id(), npc:name(), "covertimen", nil)
end
-- Move to savezone --
if (dist == nil) then
t.position = actor:position()
t.level_vertex_id = actor:level_vertex_id()
t.direction = actor:direction()
self.st.desired_target = t
self.st.behavior = "beh_move"
else
t.position = cover
t.level_vertex_id = level.vertex_id(cover)
t.direction = npc:position():sub(cover)
self.st.desired_target = t
self.st.behavior = "beh_path"
end
elseif (hg_companion) then
se_save_var(npc:id(), npc:name(), "coverlast", nil)
se_save_var(npc:id(), npc:name(), "coverdist", nil)
se_save_var(npc:id(), npc:name(), "covertimel", nil)
se_save_var(npc:id(), npc:name(), "covertimen", nil)
local covers = hg_companion:get_covers()
local dist = 10000
-- Search savezone --
local vid
for name,id in pairs(covers) do
local cover = db.zone_by_name[name]
if (cover) then
if (npcpos:distance_to_sqr(cover:position()) < dist) then
dist = npcpos:distance_to_sqr(cover:position())
se_save_var(npc:id(), npc:name(), "cover", cover:position())
se_save_var(npc:id(), npc:name(), "coverlast", npcpos)
se_save_var(npc:id(), npc:name(), "coverdist", dist)
se_save_var(npc:id(), npc:name(), "covertimel", tg+2000)
end
end
end
t.position = actor:position()
t.level_vertex_id = actor:level_vertex_id()
t.direction = actor:direction()
self.st.desired_target = t
self.st.behavior = "beh_move"
end
------------------ SURGE ------------------
------------------ RELAX ------------------
elseif (self.object:has_info("npcx_beh_relax")) then
local name = npc:character_name()
local npcid = npc:id()
local function save_step(npc, step)
se_save_var(npc:id(), npc:name(), "campfire_step", step)
end
if (se_load_var(npcid, npc:name(), "campfire_step") ~= nil) then
if (se_load_var(npcid, npc:name(), "campfire") ~= nil) then
local step = se_load_var(npcid, npc:name(), "campfire_step")
local campfire = se_load_var(npcid, npc:name(), "campfire")
local vid = se_load_var(npcid, npc:name(), "campfire_vid")
local look = se_load_var(npcid, npc:name(), "campfire_look")
local npos = npc:position()
if (step < 1) then
t.level_vertex_id = vid
t.position = level.vertex_position(vid)
t.direction = self.object:position():sub(t.position)
-- Move to camp --
self.st.desired_target = t
self.st.behavior = "beh_path"
-- Moved to camp --
if (npos:distance_to_sqr(t.position) < 1) then
local tg = time_global()
-- Sleep or talk--
local sleep = math.random(100)
if (sleep < 5) then self.current_action = "sleep_sit" else self.current_action = "sit_ass" end
state_mgr.set_state(self.object, self.current_action, nil, nil, {look_position = look})
se_save_var(npcid, npc:name(), "campfire_time", tg + 5000)
--printdbg("%s came to campfire", npc:character_name())
save_step(npc, 1)
end
else
-- ReMoved to camp --
if (npos:distance_to_sqr(level.vertex_position(vid)) > 2) then save_step(npc, 0) end
-- Camp --
local state = state_mgr.get_state(self.object)
local camp = sr_camp.get_current_camp(npc:position())
if (camp) then
if (state ~= "sleep_sit") then
self.st.camp = camp
camp:register_npc(npc:id())
self.in_camp = true
elseif (state == "sleep_sit") then
if (self.in_camp) then
camp:unregister_npc(npc:id())
self.in_camp = nil
self.st.camp = nil
end
end
elseif (self.in_camp == true and not self.st.camp) then
self.st.camp:unregister_npc(npc:id())
self.in_camp = nil
self.st.camp = nil
end
-- Animation --
local tg = time_global()
local tg_delay = se_load_var(npcid, npc:name(), "campfire_time")
if (tg > tg_delay) then
local state = self.current_action
local new_state = self:relax_animations()
if (new_state ~= state) then
--local camp_action, is_director = self.st.camp:get_camp_action(self.object:id())
--local role = is_director and "director" or "listener"
--printdbg("%s is %s", npc:character_name(), role)
state_mgr.set_state(self.object, new_state, nil, nil, {look_position = look})
se_save_var(npcid, npc:name(), "campfire_time", tg + math.random(120000,240000))
end
end
-- Sound --
self.st.sound_tg = self.st.sound_tg or tg + math.random(10000,30000)
if (tg > self.st.sound_tg) then
local sound = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.sound_idle)
if (sound and sound ~= "nil" and sound ~= "") then
xr_sound.set_sound_play(self.object:id(), sound)
end
self.st.sound_tg = nil
end
end
end
else
-- Save campfire --
if (se_load_var(npcid, npc:name(), "campfire") ~= nil) then
if (se_load_var(npcid, npc:name(), "fire") ~= nil) then
local fire = se_load_var(npcid, npc:name(), "fire")
if (not fire:is_on()) then fire:turn_on() end
end
local vid,look
local campfire = se_load_var(npcid, npc:name(), "campfire")
local dir = vector():set(campfire:direction())
local pos = campfire:position()
local member = se_load_var(npcid, npc:name(), "member_group") or 1
local all = se_load_var(npcid, npc:name(), "member_all") or 1
vid = self:vertex_create(pos,vector_rotate_y(dir,360/all*member),3)
look = pos
if (vid ~= nil) then
se_save_var(npcid, npc:name(), "campfire_vid", vid)
se_save_var(npcid, npc:name(), "campfire_look", look)
--printdbg("%s found campfire", npc:character_name())
save_step(npc, 0)
return
end
end
local npcpos = npc:position()
local dist = 10000
-- Search campfire --
for id,binder in pairs(bind_campfire.campfires_all) do
if (binder and binder.campfire) then
if (npcpos:distance_to_sqr(binder.object:position()) < dist) then
se_save_var(npcid, npc:name(), "campfire", binder.object)
se_save_var(npcid, npc:name(), "fire", binder.campfire)
dist = npcpos:distance_to_sqr(binder.object:position())
end
end
end
return
end
------------------ RELAX ------------------
elseif (target == "waypoint") then
t = self:get_current_waypoint()
self.st.behavior = "beh_path"
elseif (target == "commander") then
local squad = get_object_squad(self.object)
if not(squad:commander_id() == self.object:id()) then
set_position_data(squad:commander_id())
end
elseif (string.find(target,"lvid:")) then
local vid,pos
for s in string.gmatch(target,"lvid:(%d+)") do
vid = tonumber(s)
end
if (vid) then
t.level_vertex_id = vid
t.position = level.vertex_position(vid)
t.direction = self.object:position():sub(t.position)
t.object = nil
end
elseif (string.find(target,"id:")) then
local id
for s in string.gmatch(target,"id:(%w+)") do
id = tonumber(s)
end
if (id) then
set_position_data(id)
end
elseif (target == "scan_area") then
self.st.behavior = "beh_cover"
end
if not (t.level_vertex_id) then
return false
end
self.st.desired_target = t
return true
end
function action_beh:beh_cover()
if (self.st.desired_target and self.st.desired_target.level_vertex_id and self.object:level_vertex_id() ~= self.st.desired_target.level_vertex_id) then
state_mgr.set_state(self.object, "rush")
utils_obj.send_to_nearest_accessible_vertex(self.object,self.st.desired_target.level_vertex_id)
return
end
local t = time_global()
if (self.keep_state_until and t < self.keep_state_until) then
return
end
self.keep_state_until = t + keep_state_min_time
local new_state = self.st.wait_animation or "hide"
if (new_state == "rand") then
new_state = random_choice("binocular","idle","hide","caution")
end
state_mgr.set_state(self.object,new_state,nil,nil,{look_object = self.st.desired_target and self.st.desired_target.object or nil})
end
function action_beh:beh_wait()
local new_state = self.st.wait_animation
local state = state_mgr.get_state(self.object)
if (new_state and new_state ~= state) then
local target = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.goto_target)
local lobj = target and target ~= "" and get_story_object(target)
state_mgr.set_state(self.object, new_state,nil, nil, {look_object = lobj })
end
end
function action_beh:set_state()
local t = time_global()
if (self.keep_state_until and t < self.keep_state_until) then
return
end
self.keep_state_until = t + keep_state_min_time
local new_state = self.st.wait_animation
local dist_walk = tonumber(xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.walk_dist) or 5) or 5
local dist_jog = tonumber(xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.jog_dist) or 10) or 10
if (self.st.assist_point == nil or self.st.assist_point == self.object:level_vertex_id()) then
new_state = self.st.wait_animation
elseif (self.dist_to_assist_pt <= dist_walk ) then
new_state = self.st.walk_animation
elseif (self.dist_to_assist_pt <= dist_jog ) then
new_state = self.st.jog_animation
else
new_state = self.st.run_animation
end
state_mgr.set_state(self.object, new_state,nil, nil, nil, { fast_set = true, animation = true })
end
class "position_node"
function position_node:__init(amt)
self.node = {}
for i=1,amt do
self.node[i] = {}
end
end
function position_node:select_best_vertex_id(object,dir,lvid,distance)
local closest
local data_companion = hg_companion:return_squad()
if (object:has_info("npcx_is_companion") and data_companion[object:id()] ~= nil) then
local actor = db.actor
local hg_angle = 40
local hg_angle_rnd = 1
local movement = hg_companion:return_movement()
local hg_dist = 1
if (hg_companion) then hg_dist = hg_companion:return_distance() end
local k = data_companion[object:id()]
local m = k.move
local d = k.dist
local action = k.action
local s = 1
local angle = 180
-- Патруль
if (object:has_info("npcx_beh_patrol_mode")) then
distance = 3*(hg_dist/2)
s = k.squad
m = k.number
angle = 180-(hg_angle+20)*(s-2)+(hg_angle+20)*2*(m-1)
else
local formation = true
if (hg_companion) then
formation = hg_companion:return_formation()
end
lvid = actor:level_vertex_id()
dir = actor:direction()
if (time_global() > 10000) then
local lpos = se_load_var(object:id(), object:name(), "lpos")
if (lpos == nil) then
se_save_var(object:id(), object:name(), "lpos", actor:position())
elseif (actor:position():distance_to_sqr(lpos) > 3) then
if (formation == true) then
lvid = level.vertex_id(actor:position())
dir = actor:position():sub(lpos)
end
se_save_var(object:id(), object:name(), "lpos", actor:position())
se_save_var(object:id(), object:name(), "lvid", lvid)
se_save_var(object:id(), object:name(), "ldir", dir)
else
lvid = se_load_var(object:id(), object:name(), "lvid")
dir = se_load_var(object:id(), object:name(), "ldir")
if (lvid == nil) then lvid = actor:level_vertex_id() end
if (dir == nil) then dir = actor:direction() end
end
end
-- Походный строй
if (movement == 0) then
distance = d*m*(hg_dist/2)
-- Атакующий строй
elseif (movement == 1) then
distance = d*hg_dist
if (m < 3) then
m = m
if (data_companion.move > 1 or data_companion.move == 1) then s = 2 else s = data_companion.move end
angle = 180-hg_angle*(s-1)+hg_angle*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd)
elseif (m < 6) then
m = m - 2
if (data_companion.move > 4) then
s = 3
if (m == 2) then hg_dist = 1.4 else hg_dist = 1.6 end
else
s = data_companion.move - 2
hg_dist = 1.6
end
angle = 180-(hg_angle-20)*(s-1)+(hg_angle-20)*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd)
else
m = m - 5
s = data_companion.move - 5
angle = 180-(hg_angle-30)*(s-1)+(hg_angle-30)*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd)
hg_dist = 2
end
-- Защитный строй
elseif (movement == 2) then
distance = d*hg_dist
if (m < 4) then
s = 2
-- Дозорный (идет чуть спереди всегда на одном расстоянии)
if (m == 1) then
angle = -16+math.random(-hg_angle_rnd, hg_angle_rnd)
distance = 16
-- Боковые (идёт слева и справа)
else
angle = 180-(hg_angle+60)*(s-1)+(hg_angle+60)*2*(m-2)+math.random(-hg_angle_rnd, hg_angle_rnd)
hg_dist = 1.6
end
elseif (m < 6) then
m = m - 3
-- Тыловые (идут сзади)
if (data_companion.move > 4) then s = 2 else s = data_companion.move - 3 end
angle = 180-hg_angle*(s-1)+hg_angle*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd)
hg_dist = 1.4
else
-- Резерв (идут сзади)
m = m - 5
s = data_companion.move - 5
angle = 180-(hg_angle-30)*(s-1)+(hg_angle-30)*2*(m-1)+math.random(-hg_angle_rnd, hg_angle_rnd)
hg_dist = 2.4
end
-- Цепь
elseif (movement == 3) then
distance = (d + d*math.floor((m-1)/2))*hg_dist
angle = 90 + 180*math.floor((m)/2)
end
end
for i=1, #self.node do
if (bit_and(i,1)) then
self.node[i].desired_direction = vector_rotate_y(dir, -angle)
else
self.node[i].desired_direction = vector_rotate_y(dir, angle)
end
self.node[i].vertex_id = level.vertex_in_direction(lvid, self.node[i].desired_direction, distance)
if (self.node[i].vertex_id and utils_obj.accessible(object,self.node[i].vertex_id)) then
self.node[i].distance = self.node[i].vertex_id == lvid and -1 or object:position():distance_to_sqr(level.vertex_position(self.node[i].vertex_id))
if not (closest) then
closest = self.node[i].vertex_id
end
if (self.node[i-1] and self.node[i-1].distance and self.node[i-1].distance < self.node[i].distance) then
closest = self.node[i-1].vertex_id
end
end
end
else
for i=1, #self.node do
if (bit_and(i,1)) then
self.node[i].desired_direction = vector_rotate_y(dir, -math.random(170,190))
else
self.node[i].desired_direction = vector_rotate_y(dir, math.random(170,190))
end
self.node[i].vertex_id = level.vertex_in_direction(lvid, self.node[i].desired_direction, distance)
if (self.node[i].vertex_id and utils_obj.accessible(object,self.node[i].vertex_id)) then
self.node[i].distance = self.node[i].vertex_id == lvid and -1 or object:position():distance_to_sqr(level.vertex_position(self.node[i].vertex_id))
if not (closest) then
closest = self.node[i].vertex_id
end
if (self.node[i-1] and self.node[i-1].distance and self.node[i-1].distance < self.node[i].distance) then
closest = self.node[i-1].vertex_id
end
end
end
end
return closest and closest ~= -1 and closest
end
function action_beh:beh_move()
if not (self.st.desired_target.level_vertex_id) then
return
end
if (self.st.desired_target.object and not simulation_objects.is_on_the_same_level(self.object,self.st.desired_target.object)) then
local gvid = self.st.desired_target.game_vertex_id or self.st.desired_target.object.m_game_vertex_id
self.object:set_dest_game_vertex_id(gvid)
else
local select_new_pt = false
local target
local dist_from_self_to_goto_target = self.object:position():distance_to(self.st.desired_target.position)
if (self.st.rally_lvid or dist_from_self_to_goto_target >= self.st.desired_distance) then
select_new_pt = true
end
if ( select_new_pt ) then
local node = position_node(2)
self.st.assist_point = self.st.rally_lvid or node:select_best_vertex_id(self.object,self.st.desired_target.direction,self.st.desired_target.level_vertex_id,self.st.desired_distance) or self.st.desired_target.level_vertex_id --self.object:level_vertex_id()
self.keep_state_until = nil
end
if ( self.st.assist_point ) then
--self.object:set_dest_level_vertex_id(self.st.assist_point)
self.object:set_path_type(game_object.level_path)
self.object:set_desired_direction()
self.st.assist_point = utils_obj.send_to_nearest_accessible_vertex(self.object,self.st.assist_point,"axr_beh")
self.object:set_desired_position(level.vertex_position(self.st.assist_point))
end
end
self.dist_to_assist_pt = self.st.assist_point and level.vertex_position(self.st.assist_point):distance_to(self.object:position()) or 0
--self.dist_from_self_to_goto_target = dist_from_self_to_goto_target
self:set_state()
end
function action_beh:beh_path()
if not (self.st.desired_target.level_vertex_id) then
return
end
local next_index = self:next_waypoint_index()
if (self.object:level_vertex_id() ~= self.st.desired_target.level_vertex_id) or ((next_index) and (not self.st.desired_target.delay or self.st.desired_target.delay < 0)) then
self.st.am_i_reached = nil
if (self.object:level_vertex_id() == self.st.desired_target.level_vertex_id) then
self:increment_waypoint_index()
self.st.desired_target = self:get_current_waypoint()
if not (self.st.desired_target.level_vertex_id) then
return
end
end
self.object:set_path_type(game_object.level_path)
self.object:set_desired_direction()
--self.object:set_dest_level_vertex_id(self.st.desired_target.level_vertex_id)
--printf("lvid = %s",self.st.desired_target.level_vertex_id)
self.st.assist_point = utils_obj.send_to_nearest_accessible_vertex( self.object, self.st.desired_target.level_vertex_id, "axr_beh")
--self.object:set_desired_position(self.st.desired_target.position)
local pos = vector():set(self.object:position())
pos = vector():set(pos.x,self.st.desired_target.position.y,pos.z)
self.dist_to_assist_pt = pos:distance_to(self.st.desired_target.position)
self:set_state()
return
end
self.st.am_i_reached = true
if (self.st.desired_target.delay ~= 0) then
if not (self.st.desired_target.delay) then
self.st.desired_target.delay = 0
end
local tg = time_global()
if not (self.st.wait_delay) then
self.st.wait_delay = tg + self.st.desired_target.delay
end
if (tg > self.st.wait_delay) then
self:increment_waypoint_index()
return
end
else
local mgr = db.storage[self.object:id()].state_mgr
if (mgr) then
mgr.animation:set_state(nil)
if (mgr.animation.states.anim_marker == nil) then
self:increment_waypoint_index()
return
end
end
end
local state = state_mgr.get_state(self.object)
local new_state = self.st.desired_target.delay_animation or self.st.delay_animation
--[[
if (self.st.use_camp) then
self:fill_approved_actions(new_state)
self.camp = sr_camp.get_current_camp(self.object:position())
if (self.camp and not self.in_camp) then
self.camp:register_npc(self.object:id())
self.in_camp = true
end
if (self.in_camp) then
local camp_action, is_director = self.camp and self.camp:get_camp_action(self.object:id())
if (camp_action) then
local tbl = is_director and assoc_tbl[camp_action].director or assoc_tbl[camp_action].listener
local descr = new_state == "sit_knee" and "sit_ass" or new_state
local t = {}
local found
for i=1,#tbl do
for n=#self.st.approved_actions, 1, -1 do
if (self.st.approved_actions[n].name == descr..tbl[i]) then
table.insert(t,self.st.approved_actions[n].name)
found = true
end
end
end
local anm = found and t[math.random(#t)]
if (anm and anm ~= "") then
if (self.st.desired_target.animpoint) then
printf("camp_action = %s",anm)
state_mgr.set_state(self.object, anm, nil, nil, nil, {animation_position = self.st.desired_target.animpoint, animation_direction = self.st.desired_target.animpoint_dir})
else
state_mgr.set_state(self.object, anm)
end
return
end
end
end
elseif (self.in_camp) then
self.camp:unregister_npc(self.object:id())
self.in_camp = nil
else
local sound = self.st.desired_target.sound_idle or xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.sound_idle)
if (sound and sound ~= "nil" and sound ~= "") then
xr_sound.set_sound_play(self.object:id(), sound)
end
end
if (new_state and new_state ~= state) then
if (self.st.desired_target.animpoint) then
state_mgr.set_state(self.object, new_state,nil, nil,{look_object = self.st.desired_target.look_object, look_position = self.st.desired_target.look_position }, {animation_position = self.st.desired_target.animpoint, animation_direction = self.st.desired_target.animpoint_dir})
if (new_state == "zat_b106_wounded_idle") then
local mgr = db.storage[self.object:id()].state_mgr
if (mgr) then
mgr.animation:set_state(nil,true)
mgr.animation:set_control()
end
end
else
state_mgr.set_state(self.object, new_state,nil, nil,{look_object = self.st.desired_target.look_object, look_position = self.st.desired_target.look_position })
end
end
--]]
end
------------------------------- RELAX -----------------------------------------------------------------
function action_beh:vertex_create(pos, dir, radius)
local vertex_id = 4294967295
local tmp_pos = VEC_ZERO
while (vertex_id >= 4294967295) do
tmp_pos.x = pos.x + dir.x * radius
tmp_pos.z = pos.z + dir.z * radius
tmp_pos.y = pos.y
vertex_id = level.vertex_id(tmp_pos)
if (vertex_id >= 4294967295 or used_vids[vertex_id]) then
if (radius > 1) then
radius = radius - 1
else
break
end
end
end
return vertex_id
end
function action_beh:relax_animations()
if (self.st.camp ~= nil) then
local camp_action, is_director = self.st.camp:get_camp_action(self.object:id())
end
if not (camp_action) then
camp_action = "idle"
end
local role = is_director and "director" or "listener"
local tbl = assoc_tbl[camp_action][role]
self.current_action = tbl and tbl[math.random(#tbl)] or "sit_ass"
return self.current_action
end
------------------------------- RELAX -----------------------------------------------------------------
function action_beh:get_current_waypoint(use_var)
local st = db.storage[self.object:id()]
local str
if (self.object:has_info("npcx_beh_patrol_mode")) then
str = se_load_var(self.object:id(),self.object:name(),"pathpoint"..self.st.path_index) --load_var(self.object,"pathpoint"..self.st.path_index)
else
str = st.active_section and st.ini:r_string_ex(st.active_section,"pt"..tostring(self.st.path_index))
end
if not (str) then
return {}
end
if (self.st.desired_target and self.st.path_index == self.st.desired_target.index and st.active_section == self.st.desired_target.active_section) then
return self.st.desired_target
end
local t = {}
t.index = self.st.path_index
t.active_section = st.active_section
for s in string.gmatch(str,"look:(%A+)") do
local p = str_explode(s,",")
t.look_position = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3]))
end
for s in string.gmatch(str,"look_f:(%S+)") do
local p = str_explode(s,"@")
-- printf('%s|%s (%s)', p[1], p[2], #p)
if p and #p == 2 then
local script = p[1]
local func = p[2]
t.look_position = _G[script][func](self.object)
end
end
for s in string.gmatch(str,"animpoint:(%A+)") do
local p = str_explode(s,",")
t.animpoint = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3]))
p[4] = p[4] and tonumber(p[4]) or 0
p[5] = p[5] and tonumber(p[5]) or 0
p[6] = p[6] and tonumber(p[6]) or 0
t.animpoint_dir = vector():set(p[4],p[5],p[6])
end
for s in string.gmatch(str,"pos:(%A+)") do
local p = str_explode(s,",")
t.position = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3]))
end
for s in string.gmatch(str,"sig:(%a+)") do
t.sig = s
end
for s in string.gmatch(str,"sound_idle:(%a+)") do
t.sound_idle = s
end
for s in string.gmatch(str,"look_object:(%a+)") do
if (s ~= "enemy") then
t.look_object = get_story_object(s)
else
local st = db.storage[self.object:id()]
local be = self.object:best_enemy() or st and st.enemy_id and level.object_by_id(st.enemy_id)
t.look_object = be and be:alive() and self.object:see(be) and be
end
end
for s in string.gmatch(str,"look_object_f:(%S+)") do
local p = str_explode(s,"@")
if p and #p == 2 then
local script = p[1]
local func = p[2]
t.look_object = _G[script][func](self.object)
end
end
for s in string.gmatch(str,"look_job:(%a+)") do
s = str_explode(s,",")
local board = SIMBOARD
local smart = s[1] and board and board.smarts_by_names[s[1]]
local obj = smart and smart.npc_by_job_section["logic@"..s[2]]
obj = obj and level.object_by_id(obj)
if (obj) then
t.look_object = obj
--t.look_position = obj:bone_position("bip01_neck")
end
end
local pt = str_explode(str,"|")
pt = pt[1] and str_explode(pt[1],",")
if not (pt) then
return
end
if not (t.position) then
t.level_vertex_id = tonumber(pt[1])
t.position = level.vertex_position(t.level_vertex_id)
t.delay = pt[2] and tonumber(pt[2]) or 0
t.delay_animation = trim(pt[3])
else
t.level_vertex_id = level.vertex_id(t.position)
t.delay = pt[1] and tonumber(pt[1]) or 0
t.delay_animation = trim(pt[2])
end
t.direction = self.object:position():sub(t.position)
return t
end
function action_beh:next_waypoint_index()
local st = db.storage[self.object:id()]
local inc = self.st.path_reverse and -1 or 1
if (self.object:has_info("npcx_beh_patrol_mode")) then
return se_load_var(self.object:id(),self.object:name(),"pathpoint"..(self.st.path_index+inc))--load_var(self.object,"pathpoint"..tostring(self.st.path_index+inc))
end
return st.active_section and st.ini:r_string_ex(st.active_section,"pt"..tostring(self.st.path_index+inc))
end
function action_beh:increment_waypoint_index()
local st = db.storage[self.object:id()]
local inc = self.st.path_reverse and -1 or 1
local next_index
if (self.object:has_info("npcx_beh_patrol_mode")) then
next_index = se_load_var(self.object:id(),self.object:name(),"pathpoint"..(self.st.path_index+inc))--load_var(self.object,"pathpoint"..self.st.path_index+inc)
else
next_index = st.active_section and st.ini:r_string_ex(st.active_section,"pt"..self.st.path_index+inc)
end
if (next_index) then
self.st.am_i_reached = nil
self.st.path_index = self.st.path_index + inc
if (self.object:has_info("npcx_beh_patrol_mode")) then -- "Сompanions_obey" addon added logic
self.st.path_reverse = -1 -- "Сompanions_obey" addon added logic
end -- "Сompanions_obey" addon added logic
if (self.st.path_reverse and self.st.path_index == 1) then
self.st.path_reverse = nil
end
else
local new_section = self.st.path_end and xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.path_end)
if (new_section and new_section ~= "nil" and new_section ~= "") then
if (new_section == "reverse") then
self.st.path_reverse = true
self.st.path_index = self.st.path_index - 1
elseif (new_section == "loop") then
self.st.path_index = 1
else
xr_logic.switch_to_section(self.object, st.ini, new_section)
self.st.am_i_reached = nil
self.st.path_index = 1
end
end
end
if (self.st.desired_target.sig) then
self.st.signals[self.st.desired_target.sig] = true
end
self.st.wait_delay = nil
save_var(self.object,"path_index",self.st.path_index)
end
function action_beh:fill_approved_actions(state)
local app_act = self.st.approved_actions
iempty_table(app_act)
local size_t = 0
if (state == "sit_knee") then
state = "sit_ass"
end
self.st.description = state
local t = { "eat_bread", "drink_energy", "drink_vodka", "eat_kolbasa", "guitar", "harmonica", "weapon" }
for i=1, 6 do
if (axr_beh["can_"..t[i]] and axr_beh["can_"..t[i]](self.object)) then
size_t = size_t + 1
app_act[size_t] = { name = state.."_"..t[i] }
end
end
end
function action_beh:finalize()
action_base.finalize(self)
if (self.in_camp) then
if self.camp == nil then return end
self.camp:unregister_npc(self.object:id())
self.in_camp = nil
end
if (self.st.desired_target and self.st.desired_target.level_vertex_id) then
db.used_level_vertex_ids[self.st.desired_target.level_vertex_id] = nil
end
save_var(self.object,"path_index",1)
end
--
-------------------------- Scheme Handlers ------------------------------------------------------------
function set_scheme(npc, ini, scheme, section, gulag_name)
local st = xr_logic.assign_storage_and_bind(npc,ini,"beh",section)
st.logic = xr_logic.cfg_get_switch_conditions(ini, section, npc)
init_custom_data(npc, ini, section, st, "beh")
st.sound_idle = ini:r_string_ex(section,"sound_idle")
st.reach_movement = xr_logic.parse_condlist(npc, "reach_movement", "reach_movement", ini:r_string_ex(section,"reach_movement") or "patrol")
end
function add_to_binder(npc, ini, scheme, section, storage)
if not npc then return end
local manager = npc:motivation_action_manager()
if not manager then return end
local wp = world_property
local evaluator = evaluator_beh(storage, "evaluator_beh")
local action = action_beh(npc, "action_beh", storage)
if not evaluator or not action then return end
manager:add_evaluator(beh_evid,evaluator)
action:add_precondition( wp(beh_evid, true) )
action:add_effect( wp(beh_evid, false) )
action:add_effect( wp(xr_evaluators_id.state_mgr + 4, false) )
manager:add_action(beh_actid,action)
xr_logic.subscribe_action_for_events(npc, storage, action)
action:add_precondition( wp(stalker_ids.property_alive,true) )
action:add_precondition( wp(stalker_ids.property_enemy,false) )
action:add_precondition( wp(stalker_ids.property_danger,false) )
if (_G.schemes["corpse_detection"]) then
action:add_precondition( wp(xr_evaluators_id.corpse_exist, false) )
end
if (_G.schemes["gather_items"]) then
action:add_precondition( wp(xr_gather_items.evaid, false) )
end
action:add_precondition( wp(stalker_ids.property_items, false) )
--action:add_precondition( wp(xr_evaluators_id.wounded_exist,false) )
action:add_precondition( wp(xr_evaluators_id.stohe_meet_base + 1, false) )
-- action:add_precondition( wp(xr_evaluators_id.sidor_wounded_base + 0, false) )
action:add_precondition( wp(xr_evaluators_id.abuse_base, false) )
--printf("ACTION_ALIFE_ID(axr_beh.add_to_binder): " .. tostring(xr_actions_id.alife))
action = manager:action(xr_actions_id.alife)
if action then action:add_precondition( wp(beh_evid, false) ) end
action = manager:action(xr_actions_id.state_mgr + 2)
if action then action:add_precondition( wp(beh_evid, false) ) end
end
--[[
function reset_scheme(loading, npc)
local st = db.storage[npc:id()]
if (st.beh) then
init_custom_data(npc, st.ini, section, st.beh, scheme)
end
end
--]]