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

1233 lines
43 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--[[
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
--]]