1319 lines
48 KiB
Plaintext
1319 lines
48 KiB
Plaintext
|
---==================================================================================================================---
|
||
|
--- ---
|
||
|
--- Original Author(s) : GhenTuong ---
|
||
|
--- Edited : N/A ---
|
||
|
--- Date : 15/07/2022 ---
|
||
|
--- License : Proprietary ---
|
||
|
--- ---
|
||
|
--- Extension script for xr_logic.script. ---
|
||
|
--- ---
|
||
|
---==================================================================================================================---
|
||
|
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
Conditions
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
xr_conditions.dist_to_beh = function(actor,npc,p)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
local dt = st and st.beh and st.beh.desired_target
|
||
|
if (dt and (st.active_section == dt.active_section) and dt.position) then
|
||
|
return p and p[1] and npc:position():distance_to_sqr(dt.position) < p[1]*p[1]
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
xr_conditions.reach_beh = function(actor,npc,p)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
local dt = st and st.beh and st.beh.desired_target
|
||
|
if (dt and (st.active_section == dt.active_section) and dt.level_vertex_id and (dt.level_vertex_id == npc:level_vertex_id())) then
|
||
|
return true
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
xr_conditions.npc_squad_name = function(actor,npc,p)
|
||
|
local squad = get_object_squad(npc)
|
||
|
if (p and p[1] and squad and string.find(squad:section_name(),p[1])) then
|
||
|
return true
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
xr_conditions.enemy_squad = function(enemy,npc,p)
|
||
|
local squad = get_object_squad(enemy)
|
||
|
if (p and p[1] and squad and string.find(squad:section_name(),p[1])) then
|
||
|
return true
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
xr_conditions.check_squad_section = function(actor,squad,p)
|
||
|
if (p and p[1] and squad and string.find(squad:section_name(),p[1])) then
|
||
|
return true
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
xr_effects.squad_self_release = function(actor,squad,p)
|
||
|
return squad and squad.id and alife_release(squad)
|
||
|
end
|
||
|
|
||
|
xr_conditions.check_time_speech = function(actor,npc,p)
|
||
|
local st = db.storage[npc:id()]
|
||
|
local dt = st and st.beh and st.beh.desired_target
|
||
|
if not (dt and dt.speech_delay_until) then
|
||
|
return true
|
||
|
end
|
||
|
if (dt.speech_delay_until < time_global()) then
|
||
|
return true
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
xr_effects.speech = function(actor,npc,p)
|
||
|
local se = npc and (type(npc.id) == "function") and alife_object(npc:id())
|
||
|
se = se or (npc and (type(npc.id) == "number") and alife_object(npc.id))
|
||
|
local name = se and se:character_name()
|
||
|
local text = p and p[1] and game.translate_string(p[1])
|
||
|
if (name and text) then
|
||
|
dynamic_news_helper.send_tip(text,name,nil,15,"ui_npc_duty_girl_msg_image",nil,"npc")
|
||
|
if (tonumber(p[2])) then
|
||
|
local st = db.storage[npc:id()]
|
||
|
local dt = st and st.beh and st.beh.desired_target
|
||
|
if (dt) then
|
||
|
dt.speech_delay_until = time_global() + tonumber(p[2]) * 1000
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
function get_waypoint(npc,section,pt)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
if (st and st.ini and (section or st.active_section) and pt and tostring(pt)) then
|
||
|
local str = st.ini:r_string_ex(section or st.active_section,tostring(pt))
|
||
|
if (str) then
|
||
|
for s in string.gmatch(str,"pos:(%A+)") do
|
||
|
local v = str_explode(s,",")
|
||
|
if (tonumber(v[1]) and tonumber(v[2]) and tonumber(v[3])) then
|
||
|
return vector():set(tonumber(v[1]),tonumber(v[2]),tonumber(v[3]))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function set_position(actor,npc,p)
|
||
|
local pos = p and p[1] and p[2] and get_waypoint(npc,p[1],p[2])
|
||
|
if (pos) then
|
||
|
npc:set_npc_position(pos)
|
||
|
end
|
||
|
end
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
Stalker state
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
function state_idle(actor,npc,p)
|
||
|
local new_state = p and p[1]
|
||
|
local state = npc and state_mgr.get_state(npc)
|
||
|
if (new_state and state and (new_state ~= state)) then
|
||
|
state_mgr.set_state(npc,new_state)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
Movement
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
local function validate(npc,vid)
|
||
|
if (vid and vid < 4294967295 and npc:accessible(vid) and vid ~= npc:level_vertex_id()) then
|
||
|
return db.used_level_vertex_ids[vid] == nil or db.used_level_vertex_ids[vid] == npc:id()
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
local function lmove(npc,vid,st)
|
||
|
if (vid == nil or vid >= 4294967295) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (db.used_level_vertex_ids[vid] == npc:id()) then
|
||
|
npc:set_dest_level_vertex_id(vid)
|
||
|
return vid
|
||
|
end
|
||
|
|
||
|
if (st.vid) then
|
||
|
db.used_level_vertex_ids[st.vid] = nil
|
||
|
end
|
||
|
|
||
|
if not (npc:accessible(vid)) then
|
||
|
local vtemp = VEC_ZERO
|
||
|
vid, vtemp = npc:accessible_nearest(vid and level.vertex_position(vid) or npc:position(), vtemp )
|
||
|
end
|
||
|
|
||
|
db.used_level_vertex_ids[vid] = npc:id()
|
||
|
st.vid = vid
|
||
|
|
||
|
npc:set_dest_level_vertex_id(vid)
|
||
|
return vid
|
||
|
end
|
||
|
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
Animpoints
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
local campfire_state_tbl = {
|
||
|
idle = {director = {"", "_eat_bread", "_eat_kolbasa", "_drink_vodka", "_drink_energy", "_weapon", "_roast_kolbasa", "_roast_kolbasa_bred", "_roast_bred", "_sleep", "_use_pda"}, listener = {"", "_eat_bread", "_eat_kolbasa", "_drink_vodka", "_drink_energy", "_weapon", "_roast_kolbasa", "_roast_kolbasa_bred", "_roast_bred", "_sleep", "_use_pda"}},
|
||
|
harmonica = {director = {"_harmonica"}, listener = {"", "_eat_bread", "_eat_kolbasa", "_drink_vodka", "_drink_energy", "_weapon", "_roast_kolbasa", "_roast_kolbasa_bred", "_roast_bred", "_sleep", "_use_pda"}},
|
||
|
guitar = {director = {"_guitar"}, listener = {"", "_eat_bread", "_eat_kolbasa", "_drink_vodka", "_drink_energy", "_weapon", "_roast_kolbasa", "_roast_kolbasa_bred", "_roast_bred", "_sleep", "_use_pda"}},
|
||
|
story = {director = {"", "_weapon"}, listener = {"", "_eat_bread", "_eat_kolbasa", "_drink_vodka", "_drink_energy", "_weapon", "_roast_kolbasa", "_roast_kolbasa_bred", "_roast_bred", "_sleep", "_use_pda"}},
|
||
|
}
|
||
|
|
||
|
local state_sit_all = {"animpoint_sit","animpoint_sit_ass","animpoint_sit_knee"}
|
||
|
|
||
|
function xr_effects.animpoint(actor,npc)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
local beh = st and st.beh
|
||
|
if not (beh) then
|
||
|
return
|
||
|
end
|
||
|
local dt = beh.desired_target
|
||
|
|
||
|
if not (dt and (st.active_section == dt.active_section) and (dt.custom_logic == "animpoint")) then
|
||
|
local index = next_pt_index(npc)
|
||
|
dt = load_animpoint(npc,index)
|
||
|
end
|
||
|
|
||
|
if (dt and dt.wait_delay and (dt.wait_delay < time_global())) then
|
||
|
local index = next_pt_index(npc)
|
||
|
dt = load_animpoint(npc,index)
|
||
|
end
|
||
|
|
||
|
if not (dt.level_vertex_id) then
|
||
|
printf("GhenTuong: animpoint %s | %s",st.active_section,dt.level_vertex_id)
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (st.beh.use_camp and dt.animpoint) then
|
||
|
-- Move to camp
|
||
|
if (npc:level_vertex_id() ~= dt.level_vertex_id) then
|
||
|
npc:set_path_type(game_object.level_path)
|
||
|
npc:set_desired_direction()
|
||
|
beh.assist_point = utils_obj.send_to_nearest_accessible_vertex(npc,dt.level_vertex_id,"xr_custom.animpoint")
|
||
|
set_moving_state(npc)
|
||
|
return
|
||
|
end
|
||
|
|
||
|
-- Get camp
|
||
|
if not (beh.camp) then
|
||
|
beh.camp = sr_camp.get_current_camp(dt.position)
|
||
|
end
|
||
|
if (beh.camp and not beh.in_camp) then
|
||
|
beh.camp:register_npc(npc:id())
|
||
|
beh.in_camp = true
|
||
|
end
|
||
|
|
||
|
if not (dt.approved_actions and (#dt.approved_actions > 0)) then
|
||
|
local is_in_camp = beh.camp ~= nil
|
||
|
local p = {"animpoint_sit_ass","animpoint_sit_knee","animpoint_sit"}
|
||
|
dt.description_name = p[math.random(#p)]
|
||
|
dt.avail_actions = xr_animpoint_predicates.associations[dt.description_name]
|
||
|
|
||
|
if (dt.avail_actions) then
|
||
|
local lst = {}
|
||
|
for k,v in pairs(dt.avail_actions) do
|
||
|
if (v.predicate(npc:id(),is_in_camp) == true) then
|
||
|
lst[#lst+1] = v
|
||
|
end
|
||
|
end
|
||
|
dt.approved_actions = lst
|
||
|
|
||
|
for k,v in pairs(dt.approved_actions) do
|
||
|
printf("GhenTuong: load_campfire_actions [%s] %s is_in_camp %s %s",npc:name(),dt.active_section,is_in_camp,v and v.name)
|
||
|
end
|
||
|
else
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local tmp_actions = {}
|
||
|
local num = 0
|
||
|
|
||
|
local camp_action, is_director = beh.camp and beh.camp:get_camp_action(npc:id())
|
||
|
if not (camp_action) then
|
||
|
printf("GhenTuong: [%s] %s no camp",npc:name(),dt.active_section)
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local tbl = nil
|
||
|
if (is_director) then
|
||
|
tbl = campfire_state_tbl[camp_action].director
|
||
|
dt.__dtimer = time_global()
|
||
|
else
|
||
|
dt.__dtimer = (not dt.__dtimer and time_global() + math.random(10000,15000)) or dt.__dtimer
|
||
|
if (dt.current_action and time_global() < dt.__dtimer) then
|
||
|
return
|
||
|
end
|
||
|
tbl = campfire_state_tbl[camp_action].listener
|
||
|
end
|
||
|
|
||
|
local found = false
|
||
|
for k,v in pairs(dt.approved_actions) do
|
||
|
for i = 1, #tbl do
|
||
|
if (dt.description_name .. tbl[i] == v.name) then
|
||
|
num = num + 1
|
||
|
tmp_actions[num] = v.name
|
||
|
found = true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if not (found) then
|
||
|
num = num + 1
|
||
|
tmp_actions[num] = dt.description_name
|
||
|
end
|
||
|
|
||
|
dt.current_action = tmp_actions[math.random(num)]
|
||
|
|
||
|
for k,v in pairs(tmp_actions) do
|
||
|
printf("GhenTuong: animpoint load campfire action %s %s",dt.active_section,v)
|
||
|
end
|
||
|
|
||
|
local cur_state = state_mgr.get_state(npc)
|
||
|
local new_state = dt.current_action
|
||
|
if (cur_state and new_state and (cur_state ~= new_state)) then
|
||
|
local anim_pos = dt.animpoint and vector():set(dt.animpoint)
|
||
|
local anim_dir = dt.direction and vector():set(dt.direction)
|
||
|
state_mgr.set_state(npc,new_state,nil,nil,nil,{animation_position = anim_pos, animation_direction = anim_dir})
|
||
|
end
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (dt.animpoint) then
|
||
|
local cur_state = state_mgr.get_state(npc)
|
||
|
local new_state = nil
|
||
|
|
||
|
if (dt.delay_animation and string.find(dt.delay_animation,"@random")) then
|
||
|
if (dt.random_animation == nil) then
|
||
|
local str = str_explode(dt.delay_animation,"@")
|
||
|
local state_base = tostring(str[1])
|
||
|
state_base = (string.find(state_base,"animpoint_sit_all") and state_sit_all[math.random(#state_sit_all)]) or state_base
|
||
|
|
||
|
local p = {"guitar","harmonica","roast","weapon"}
|
||
|
local function check_state_exception(state_con,state)
|
||
|
for k,v in pairs(p) do
|
||
|
if (string.find(state,v) and not string.find(state_con,v)) then
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
local state_list = {}
|
||
|
local t = xr_animpoint_predicates.associations[state_base]
|
||
|
if (t) then
|
||
|
for k,v in pairs(t) do
|
||
|
if (v.name and check_state_exception(dt.delay_animation,tostring(v.name))) then
|
||
|
state_list[#state_list+1] = tostring(v.name)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (#state_list == 0) then
|
||
|
printf("GhenTuong: xr_logic_ex can not find %s %s",dt.active_section,dt.delay_animation)
|
||
|
return
|
||
|
end
|
||
|
dt.random_animation = state_list[math.random(#state_list)]
|
||
|
end
|
||
|
new_state = dt.random_animation
|
||
|
else
|
||
|
new_state = dt.delay_animation
|
||
|
end
|
||
|
|
||
|
if not (cur_state and new_state) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if ((cur_state == new_state) and (npc:position():distance_to_sqr(dt.animpoint) < 0.8)) then
|
||
|
if (dt.delay) then
|
||
|
if not (dt.wait_delay) then
|
||
|
dt.wait_delay = time_global() + dt.delay
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (dt.sound_idle) then
|
||
|
xr_sound.set_sound_play(npc:id(),dt.sound_idle)
|
||
|
end
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (npc:level_vertex_id() ~= dt.level_vertex_id) then
|
||
|
npc:set_path_type(game_object.level_path)
|
||
|
npc:set_desired_direction()
|
||
|
beh.assist_point = utils_obj.send_to_nearest_accessible_vertex(npc,dt.level_vertex_id,"xr_custom.animpoint")
|
||
|
set_moving_state(npc)
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (cur_state == new_state) then
|
||
|
if (dt.delay) then
|
||
|
if not (dt.wait_delay) then
|
||
|
dt.wait_delay = time_global() + dt.delay
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (dt.sound_idle) then
|
||
|
xr_sound.set_sound_play(npc:id(),dt.sound_idle)
|
||
|
end
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local reach_state = "animpoint_reach_ex"
|
||
|
|
||
|
if (cur_state ~= new_state) and (cur_state ~= reach_state) then
|
||
|
local anim_pos = dt.animpoint and vector():set(dt.animpoint)
|
||
|
local anim_dir = dt.direction and vector():set(dt.direction)
|
||
|
state_mgr.set_state(npc,reach_state,{turn_end_func = function() return end},nil,{look_dir = anim_dir},{animation_position = anim_pos})
|
||
|
end
|
||
|
|
||
|
if (cur_state == reach_state) and not (st.callback and st.callback.turn_end_func) then
|
||
|
local anim_pos = dt.animpoint and vector():set(dt.animpoint)
|
||
|
local anim_dir = dt.direction and vector():set(dt.direction)
|
||
|
--local look_pos = dt.look_position and vector():set(dt.look_position)
|
||
|
state_mgr.set_state(npc,new_state,nil,nil,nil,{animation_position = anim_pos, animation_direction = anim_dir})
|
||
|
end
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (npc:level_vertex_id() ~= dt.level_vertex_id) then
|
||
|
npc:set_path_type(game_object.level_path)
|
||
|
npc:set_desired_direction()
|
||
|
beh.assist_point = utils_obj.send_to_nearest_accessible_vertex(npc,dt.level_vertex_id,"xr_custom.animpoint")
|
||
|
set_moving_state(npc)
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local cur_state = state_mgr.get_state(npc)
|
||
|
local new_state = dt.delay_animation
|
||
|
if not (cur_state and new_state) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (cur_state == new_state) then
|
||
|
if (dt.delay) then
|
||
|
if not (dt.wait_delay) then
|
||
|
dt.wait_delay = time_global() + dt.delay
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (dt.sound_idle) then
|
||
|
xr_sound.set_sound_play(npc:id(),dt.sound_idle)
|
||
|
end
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local look_pos = dt.look_position and vector():set(dt.look_position)
|
||
|
state_mgr.set_state(npc,new_state,nil,nil,{look_position = look_pos})
|
||
|
end
|
||
|
|
||
|
function load_animpoint(npc,index)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
local str = st and st.beh and st.ini and st.active_section and st.ini:r_string_ex(st.active_section,"pt" .. index)
|
||
|
if not (str) then
|
||
|
return {}
|
||
|
end
|
||
|
|
||
|
local dt = {}
|
||
|
dt.active_section = tostring(st.active_section)
|
||
|
dt.custom_logic = "animpoint"
|
||
|
dt.path_index = tonumber(index)
|
||
|
|
||
|
for s in string.gmatch(str,"pos:(%A+)") do
|
||
|
local p = str_explode(s,",")
|
||
|
dt.position = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3]))
|
||
|
dt.level_vertex_id = dt.position and level.vertex_id(vector():set(dt.position.x,dt.position.y+0.5,dt.position.z))
|
||
|
end
|
||
|
|
||
|
if (string.find(str,"animpoint:pos")) then
|
||
|
dt.animpoint = dt.position and vector():set(dt.position)
|
||
|
else
|
||
|
for s in string.gmatch(str,"animpoint:(%A+)") do
|
||
|
local p = str_explode(s,",")
|
||
|
dt.animpoint = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3]))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
for s in string.gmatch(str,"dir:(%A+)") do
|
||
|
local p = str_explode(s,",")
|
||
|
dt.direction = tonumber(p[1]) and vector_rotate_y(vector():set(0,0,1),tonumber(p[1])):normalize()
|
||
|
end
|
||
|
|
||
|
local pos = dt.animpoint and vector():set(dt.animpoint) or dt.position and vector():set(dt.position)
|
||
|
local dir = dt.direction and vector():set(dt.direction)
|
||
|
if (pos and dir) then
|
||
|
dt.look_position = vector():set(pos.x + 10*dir.x, pos.y, pos.z + 10*dir.z)
|
||
|
end
|
||
|
|
||
|
if not (dt.direction and dt.look_position) then
|
||
|
for s in string.gmatch(str,"look:(%A+)") do
|
||
|
local p = str_explode(s,",")
|
||
|
dt.look_position = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3]))
|
||
|
end
|
||
|
if (pos and dt.look_position) then
|
||
|
dt.direction = vector():set(dt.look_position):sub(pos):normalize()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local pt = str_explode(str,"|")
|
||
|
pt = pt[1] and str_explode(pt[1],",")
|
||
|
|
||
|
if (pt) then
|
||
|
if (pt[1] and (pt[1] ~= "") and (pt[1] ~= "nil")) then
|
||
|
dt.delay = tonumber(pt[1])
|
||
|
end
|
||
|
|
||
|
if (pt[2] and (pt[2] ~= "") and (pt[2] ~= "nil")) then
|
||
|
dt.delay_animation = tostring(pt[2])
|
||
|
end
|
||
|
|
||
|
if (pt[3] and (pt[3] ~= "") and (pt[3] ~= "nil")) then
|
||
|
dt.sound_idle = tostring(pt[3])
|
||
|
end
|
||
|
end
|
||
|
|
||
|
st.beh.desired_target = dt
|
||
|
return st.beh.desired_target
|
||
|
end
|
||
|
|
||
|
function next_pt_index(npc)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
local dt = st and st.beh and st.beh.desired_target
|
||
|
local index = (dt and (st.active_section == dt.active_section) and tonumber(dt.path_index) or 0) + 1
|
||
|
if (st.ini and st.active_section and st.ini:r_string_ex(st.active_section,"pt" .. index)) then
|
||
|
return index
|
||
|
end
|
||
|
return 1
|
||
|
end
|
||
|
|
||
|
function set_moving_state(npc)
|
||
|
local beh = db.storage[npc:id()] and db.storage[npc:id()].beh
|
||
|
if not (beh) then return end
|
||
|
|
||
|
local t = time_global()
|
||
|
if (beh.keep_state_until and t < beh.keep_state_until) then
|
||
|
return
|
||
|
end
|
||
|
beh.keep_state_until = t + 500
|
||
|
|
||
|
local new_state = beh.run_animation
|
||
|
local dist_w = tonumber(xr_logic.pick_section_from_condlist(db.actor, npc, beh.walk_dist) or 5) or 5
|
||
|
local dist_j = tonumber(xr_logic.pick_section_from_condlist(db.actor, npc, beh.jog_dist) or 10) or 10
|
||
|
|
||
|
local pos = vector():set(npc:position())
|
||
|
local d = beh.desired_target and beh.desired_target.position and pos:distance_to_sqr(beh.desired_target.position)
|
||
|
|
||
|
if (beh.assist_point == nil or beh.assist_point == npc:level_vertex_id()) then
|
||
|
new_state = beh.wait_animation
|
||
|
elseif ((dist_w ~= 0) and d and (d < dist_w*dist_w)) then
|
||
|
new_state = beh.walk_animation
|
||
|
elseif ((dist_j ~= 0) and d and (d > dist_j*dist_j)) then
|
||
|
new_state = beh.jog_animation
|
||
|
end
|
||
|
|
||
|
state_mgr.set_state(npc,new_state,nil,nil,nil,{fast_set = true,animation = true})
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
Campfire
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
local storage_camp = {}
|
||
|
|
||
|
function generate_campfire_point(smart_name,campfire_name)
|
||
|
local function itr(obj)
|
||
|
local cf = {}
|
||
|
local pos = obj:position()
|
||
|
local dir = obj:direction()
|
||
|
if (pos and dir) then
|
||
|
|
||
|
end
|
||
|
|
||
|
if (storage_camp[smart_name] == nil) then
|
||
|
storage_camp[smart_name] = {}
|
||
|
end
|
||
|
storage_camp[smart_name][campfire_name] = cf
|
||
|
end
|
||
|
|
||
|
if (db.campfire_table_by_smart_names[smart_name]) then
|
||
|
for _,k in pairs(db.campfire_table_by_smart_names[smart_name]) do
|
||
|
if (k.object and (k.object:name() == campfire_name)) then
|
||
|
itr(camp)
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function xr_effects.beh_campfire(actor,npc)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
if not (st and st.beh) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local dt = st and st.beh and st.beh.desired_target
|
||
|
|
||
|
if not (dt and (st.active_section == dt.active_section) and (dt.custom_logic == "beh_campfire")) then
|
||
|
dt = load_beh_campfire(npc,index)
|
||
|
end
|
||
|
|
||
|
if not (dt and dt.smart_name and dt.campfire_name) then
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function load_beh_campfire(npc,st)
|
||
|
local ini = st.ini
|
||
|
local section = st.active_scheme
|
||
|
if not (ini and section and ini:section_exist(section)) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local dt = {}
|
||
|
dt.active_section = tostring(st.active_section)
|
||
|
dt.custom_logic = "beh_campfire"
|
||
|
dt.smart_name = ini:r_string_ex(section,"smart")
|
||
|
dt.campfire_name = ini:r_string_ex(section,"campfire")
|
||
|
|
||
|
st.beh.desired_target = dt
|
||
|
return st.beh.desired_target
|
||
|
end
|
||
|
|
||
|
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
Mutants
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
local mutant_state_move = {
|
||
|
["walk"] = move.walk_fwd,
|
||
|
["run"] = move.run_fwd,
|
||
|
["steal"] = move.steal,
|
||
|
}
|
||
|
|
||
|
local mutant_state_wait = {
|
||
|
["stand"] = anim.stand_idle,
|
||
|
["lie"] = anim.lie_idle,
|
||
|
["sleep"] = anim.sleep,
|
||
|
}
|
||
|
|
||
|
function xr_effects.mutant_path(actor,npc,p)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
if not (st and st.beh) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local dt = st and st.beh and st.beh.desired_target
|
||
|
|
||
|
if not (dt and (st.active_section == dt.active_section) and (dt.custom_logic == "mutant_path")) then
|
||
|
dt = mutant_load_path(npc)
|
||
|
end
|
||
|
|
||
|
if not (dt.level_vertex_id and dt.position) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (npc:get_enemy()) then
|
||
|
if (npc:clsid() == clsid.bloodsucker_s) then
|
||
|
npc:release_stand_sleep_animation()
|
||
|
end
|
||
|
return
|
||
|
end
|
||
|
|
||
|
xr_logic.mob_capture(npc,true)
|
||
|
|
||
|
local t = time_global()
|
||
|
if (dt.keep_state_until and t < dt.keep_state_until) then
|
||
|
return
|
||
|
end
|
||
|
dt.keep_state_until = t + 1000
|
||
|
|
||
|
--local squad = get_object_squad(npc)
|
||
|
--printf("GhenTuong: mutant_path current_action = %s | assigned_target_id = %s",squad.current_action,squad.assigned_target_id)
|
||
|
|
||
|
if (npc:level_vertex_id() ~= dt.level_vertex_id) then
|
||
|
local state = xr_logic.pick_section_from_condlist(db.actor,npc,dt.move_animation)
|
||
|
local new_state = (state and mutant_state_move[state]) or mutant_state_move["walk"]
|
||
|
action(npc,move(new_state,dt.level_vertex_id,dt.position),cond(cond.move_end))
|
||
|
|
||
|
if (npc:clsid() == clsid.bloodsucker_s) then
|
||
|
npc:release_stand_sleep_animation()
|
||
|
end
|
||
|
--printf("GhenTuong: mutant_path %s %s",npc:name(),dt.position:distance_to_sqr(npc:position()))
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (dt.look_position) then
|
||
|
local state = xr_logic.pick_section_from_condlist(db.actor,npc,dt.wait_animation)
|
||
|
if (state == "bloodsucker_sleep") then
|
||
|
local rot_y = vector_angle_diff(npc:direction(),dt.direction)
|
||
|
if (rot_y and rot_y < 10) then
|
||
|
if not (dt.force_stand_sleep_animation_index) then
|
||
|
dt.force_stand_sleep_animation_index = math.random(0,1)
|
||
|
end
|
||
|
npc:force_stand_sleep_animation(dt.force_stand_sleep_animation_index)
|
||
|
return
|
||
|
end
|
||
|
action(npc,anim(mutant_state_wait["stand"],0),look(look.point,dt.look_position),cond(cond.look_end))
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (npc:clsid() == clsid.bloodsucker_s) then
|
||
|
npc:release_stand_sleep_animation()
|
||
|
end
|
||
|
local new_state = (state and mutant_state_wait[state]) or mutant_state_wait["stand"]
|
||
|
action(npc,anim(new_state,0),look(look.point,dt.look_position),cond(cond.look_end))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function mutant_load_path(npc)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
local str = st and st.beh and st.ini and st.active_section and st.ini:r_string_ex(st.active_section,"pt1")
|
||
|
if not (str) then
|
||
|
return {}
|
||
|
end
|
||
|
|
||
|
local dt = {}
|
||
|
dt.active_section = tostring(st.active_section)
|
||
|
dt.custom_logic = "mutant_path"
|
||
|
|
||
|
dt.move_animation = st.ini:r_string_to_condlist(st.active_section,"move_animation","walk")
|
||
|
dt.wait_animation = st.ini:r_string_to_condlist(st.active_section,"wait_animation","stand")
|
||
|
|
||
|
for s in string.gmatch(str,"smart:(%S+)") do
|
||
|
local smart = SIMBOARD.smarts_by_names[s]
|
||
|
dt.position = smart and vector():set(smart.position)
|
||
|
dt.level_vertex_id = smart and tonumber(smart.m_level_vertex_id)
|
||
|
end
|
||
|
|
||
|
for s in string.gmatch(str,"sound:(%S+)") do
|
||
|
dt.sound = tostring(s)
|
||
|
end
|
||
|
|
||
|
for s in string.gmatch(str,"pos:(%A+)") do
|
||
|
local p = str_explode(s,",")
|
||
|
if (tonumber(p[1]) and tonumber(p[2]) and tonumber(p[3])) then
|
||
|
dt.level_vertex_id = level.vertex_id(vector():set(tonumber(p[1]),tonumber(p[2])+0.5,tonumber(p[3])))
|
||
|
dt.position = dt.level_vertex_id and vector():set(level.vertex_position(dt.level_vertex_id))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
for s in string.gmatch(str,"dir:(%A+)") do
|
||
|
local p = str_explode(s,",")
|
||
|
if (tonumber(p[1]) and tonumber(p[2]) and tonumber(p[3])) then
|
||
|
dt.direction = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3])):normalize()
|
||
|
elseif (tonumber(p[1])) then
|
||
|
dt.direction = vector_rotate_y(vector():set(0,0,1),tonumber(p[1])):normalize()
|
||
|
end
|
||
|
local pos = vector():set(dt.position)
|
||
|
local dir = vector():set(dt.direction)
|
||
|
dt.look_position = pos and dir and vector():set(pos.x + 10*dir.x, pos.y, pos.z + 10*dir.z)
|
||
|
end
|
||
|
|
||
|
if not (dt.position) then
|
||
|
printf("GhenTuong: %s [%s] no dt.position",st.ini_filename,st.active_section)
|
||
|
end
|
||
|
if not (dt.level_vertex_id) then
|
||
|
printf("GhenTuong: %s [%s] no dt.level_vertex_id",st.ini_filename,st.active_section)
|
||
|
end
|
||
|
if not (dt.look_position) then
|
||
|
printf("GhenTuong: %s [%s] no dt.look_position",st.ini_filename,st.active_section)
|
||
|
end
|
||
|
--[[
|
||
|
printf("GhenTuong: mutant_load_path %s ", dt.level_vertex_id)
|
||
|
printf("GhenTuong: mutant_load_path %s ", dt.position)
|
||
|
printf("GhenTuong: mutant_load_path %s ", dt.look_position)
|
||
|
--]]
|
||
|
st.beh.desired_target = dt
|
||
|
return st.beh.desired_target
|
||
|
end
|
||
|
|
||
|
function vector_angle_diff(dir1,dir2)
|
||
|
if (dir1 and dir2) then
|
||
|
local v1 = -math.deg(math.atan2(dir1.x,dir1.z))
|
||
|
local v2 = -math.deg(math.atan2(dir2.x,dir2.z))
|
||
|
return math.abs(math.min(math.abs(v1-v2),360-math.abs(v1)-math.abs(v2)))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
Main
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
local storage_set_position = {}
|
||
|
local storage_logic = {}
|
||
|
local storage_squad = {}
|
||
|
|
||
|
function squad_on_update(squad)
|
||
|
if not (squad and ini_sys:line_exist(squad:section_name(),"logic")) then
|
||
|
return
|
||
|
end
|
||
|
if (axr_companions.companion_squads and axr_companions.companion_squads[squad.id]) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local ini_name = ini_sys:r_string_ex(squad:section_name(),"logic")
|
||
|
|
||
|
if not (storage_squad[ini_name] or load_storage_squad(ini_name)) then
|
||
|
return
|
||
|
end
|
||
|
squad_target_update(squad,storage_squad[ini_name])
|
||
|
|
||
|
if not (storage_logic[ini_name] or load_storage_logic(ini_name)) then
|
||
|
return
|
||
|
end
|
||
|
--printf("GhenTuong: squad_on_update %s",squad:section_name())
|
||
|
for k in squad:squad_members() do
|
||
|
local st = k and db.storage[k.id]
|
||
|
if (st) then
|
||
|
local npc = st.object or level.object_by_id(k.id)
|
||
|
if (npc and npc:alive()) then
|
||
|
npc_logic_update(npc,st,squad,ini_name,storage_logic[ini_name])
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function load_storage_squad(ini_name)
|
||
|
local ini = ini_file(ini_name)
|
||
|
if not (ini) then
|
||
|
printf("GhenTuong: xr_logic_ex | load_storage_squad file %s doesn't exist.",ini_name)
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
local tbl = {}
|
||
|
|
||
|
if (ini:section_exist("section@squad")) then
|
||
|
local n = ini:line_count("section@squad")
|
||
|
local t = 0
|
||
|
for k=0,n-1 do
|
||
|
local r,i,v = ini:r_line("section@squad",k)
|
||
|
if (i and v) then
|
||
|
if (string.find(i,"target")) then
|
||
|
t = t + 1
|
||
|
tbl[t] = {}
|
||
|
tbl[t].condlist = ini:r_string_to_condlist("section@squad",i)
|
||
|
tbl[t].teleport = string.find(i,"@") and true or false
|
||
|
--printf("GhenTuong: xr_logic_ex load_storage_squad | %s = %s",i,v)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
tbl.target_num = t
|
||
|
|
||
|
if (ini:line_exist("section@squad","condlist")) then
|
||
|
tbl.condlist = ini:r_string_to_condlist("section@squad","condlist")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
storage_squad[ini_name] = tbl
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function load_storage_logic(ini_name)
|
||
|
local ini = ini_file(ini_name)
|
||
|
if not (ini) then
|
||
|
printf("GhenTuong: xr_logic_ex | load_storage_logic file %s doesn't exist.",ini_name)
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
local tbl = {}
|
||
|
|
||
|
|
||
|
if (ini:section_exist("section@logic")) then
|
||
|
local n = ini:line_count("section@logic")
|
||
|
for k=0,n-1 do
|
||
|
local result,i,v = ini:r_line("section@logic",k)
|
||
|
if (i and (i ~= "") and (i ~= "nil") and ini:section_exist(i) and string.find(i,"logic")) then
|
||
|
tbl[i] = {}
|
||
|
tbl[i].prior = ini:r_float_ex(i,"prior") or 0
|
||
|
tbl[i].logic = ini:r_string_to_condlist(i,"suitable","true")
|
||
|
--printf("GhenTuong: xr_logic_ex load_storage_logic | section@logic %s ",i)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
storage_logic[ini_name] = tbl
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function squad_target_update(squad,tbl)
|
||
|
if (tbl.condlist) then
|
||
|
xr_logic.pick_section_from_condlist(db.actor,squad,tbl.condlist)
|
||
|
end
|
||
|
|
||
|
local new_target = nil
|
||
|
local offline_teleport = false
|
||
|
|
||
|
if (tbl.target_num) then
|
||
|
for i=1,tbl.target_num,1 do
|
||
|
local k = tbl[i]
|
||
|
local target = k and k.condlist and xr_logic.pick_section_from_condlist(db.actor,squad,k.condlist)
|
||
|
if (target and (target ~= "") and (target ~= "nil")) then
|
||
|
new_target = target
|
||
|
offline_teleport = k.teleport and true
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (new_target) then
|
||
|
local obj = nil
|
||
|
|
||
|
if (false) then
|
||
|
elseif (new_target == "self") then
|
||
|
if (squad.scripted_target ~= squad.id) then
|
||
|
squad.scripted_target = tonumber(squad.id)
|
||
|
end
|
||
|
obj = squad
|
||
|
elseif (SIMBOARD.smarts_by_names[new_target]) then
|
||
|
if (squad.scripted_target ~= new_target) then
|
||
|
squad.scripted_target = new_target
|
||
|
end
|
||
|
obj = SIMBOARD.smarts_by_names[new_target]
|
||
|
else
|
||
|
local se_obj = get_story_se_object(new_target)
|
||
|
if (se_obj and se_obj.id) then
|
||
|
if (squad.scripted_target ~= se_obj.id) then
|
||
|
squad.scripted_target = tonumber(se_obj.id)
|
||
|
end
|
||
|
obj = se_obj
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (offline_teleport and (squad.online ~= true)) then
|
||
|
if (obj and not simulation_objects.is_on_the_same_level(squad,obj)) then
|
||
|
local pos = obj.position
|
||
|
local vid = obj.m_level_vertex_id
|
||
|
local gid = obj.m_game_vertex_id
|
||
|
if (pos and vid and gid) then
|
||
|
TeleportSquad(squad,pos,vid,gid)
|
||
|
printf("GhenTuong: squad_target_update teleport [%s] %s %s",squad:name(),new_target,squad.scripted_target)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--printf("GhenTuong: xr_logic_ex | squad_target_update [%s] %s %s",squad:name(),new_target,squad.scripted_target)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function npc_logic_update(npc,st,squad,ini_name,tbl)
|
||
|
if (npc:has_info("npcx_is_companion")) then
|
||
|
--printf("GhenTuong: npc_logic_update squad isn't companion but npc is? [%s]",npc:name())
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local npc_id = npc:id()
|
||
|
|
||
|
-- Keep using current logic if it is still valid.
|
||
|
local using = st.section_logic and tbl[st.section_logic]
|
||
|
local check = using and (xr_logic.pick_section_from_condlist(db.actor,npc,using.logic) == "true")
|
||
|
|
||
|
-- Choose new logic
|
||
|
local new_logic = check and tostring(st.section_logic) or ""
|
||
|
local new_prior = check and tonumber(using.prior) or -1
|
||
|
|
||
|
-- Case of save/load
|
||
|
if not (using) then
|
||
|
for i,v in pairs(tbl) do
|
||
|
if (v.own_id == npc_id) then
|
||
|
check = xr_logic.pick_section_from_condlist(db.actor,npc,v.logic) == "true"
|
||
|
new_logic = check and tostring(i) or ""
|
||
|
new_prior = check and tonumber(v.prior) or -1
|
||
|
--printf("GhenTuong: xr_logic_ex | re-use [%s] %s %s",npc:name(),new_logic,new_prior)
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
for i,v in pairs(tbl) do
|
||
|
if ((v.prior > new_prior) and (xr_logic.pick_section_from_condlist(db.actor,npc,v.logic) == "true")) then
|
||
|
local k = v.own_id and (v.own_id ~= npc_id) and db.storage[v.own_id]
|
||
|
if not (k and k.object and k.object:alive() and k.section_logic and (k.section_logic == i)) then
|
||
|
new_logic = tostring(i)
|
||
|
new_prior = tonumber(v.prior)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--printf("GhenTuong: xr_logic_ex | npc_logic_update [%s] %s %s",npc:name(),st.section_logic,st.active_section)
|
||
|
if (new_logic and (new_logic ~= st.section_logic) and (new_logic ~= "") and (new_logic ~= "nil") and tbl[new_logic]) then
|
||
|
printf("GhenTuong: xr_logic_ex | old logic [%s] %s %s",npc:name(),st.section_logic,st.active_section)
|
||
|
tbl[new_logic].own_id = npc_id
|
||
|
npc_switch_new_logic(npc,ini_name,new_logic)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function npc_switch_new_logic(npc,ini_name,new_logic)
|
||
|
local ini = ini_file(ini_name)
|
||
|
local cls = npc.clsid and npc:clsid()
|
||
|
local sty = (IsStalker(nil,cls) and 0) or (IsMonster(nil,cls) and 1) or nil
|
||
|
--Active scheme section
|
||
|
xr_logic.configure_schemes(npc,ini,ini_name,sty,new_logic,"")
|
||
|
local new_section = xr_logic.determine_section_to_activate(npc,ini,new_logic,db.actor)
|
||
|
xr_logic.activate_by_section(npc,ini,new_section,"",false)
|
||
|
|
||
|
printf("GhenTuong: xr_logic_ex | new logic [%s] %s %s %s",npc:name(),ini_name,new_logic,new_section)
|
||
|
end
|
||
|
|
||
|
function npc_on_net(npc,se_obj)
|
||
|
if not (npc:alive()) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local squad = get_object_squad(npc)
|
||
|
|
||
|
if (squad and axr_companions.companion_squads and axr_companions.companion_squads[squad.id]) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (npc:has_info("npcx_is_companion")) then
|
||
|
--printf("GhenTuong: npc_on_net squad isn't companion but npc is? [%s]",npc:name())
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (squad and ini_sys:line_exist(squad:section_name(),"logic")) then
|
||
|
local ini_name = ini_sys:r_string_ex(squad:section_name(),"logic")
|
||
|
|
||
|
if not (storage_squad[ini_name] or load_storage_squad(ini_name)) then
|
||
|
return
|
||
|
end
|
||
|
squad_target_update(squad,storage_squad[ini_name])
|
||
|
|
||
|
if not (storage_logic[ini_name] or load_storage_logic(ini_name)) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
--Force squad to update target so don't fuck up my scheme setting for npc.
|
||
|
local script_target_id = squad:get_script_target()
|
||
|
if (script_target_id) then
|
||
|
squad:specific_update(script_target_id)
|
||
|
else
|
||
|
squad:generic_update()
|
||
|
end
|
||
|
|
||
|
local st = db.storage[npc:id()]
|
||
|
if (st) then
|
||
|
npc_logic_update(npc,st,squad,ini_name,storage_logic[ini_name])
|
||
|
end
|
||
|
end
|
||
|
-- For smart exclusive logic too. Not only my custom squad logic.
|
||
|
npc_set_position(npc,se_obj,squad)
|
||
|
end
|
||
|
|
||
|
function npc_set_position(npc,se_obj,squad)
|
||
|
local id = se_obj.id
|
||
|
local st = db.storage[id]
|
||
|
local ini = st.ini
|
||
|
local section_logic = st.section_logic
|
||
|
local active_section = st.active_section
|
||
|
|
||
|
if not (ini and section_logic and active_section and ini:line_exist(section_logic,"net_spawn")) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (db.spawned_vertex_by_id[id]) then
|
||
|
db.spawned_vertex_by_id[id] = nil
|
||
|
end
|
||
|
if (db.offline_objects[id] and db.offline_objects[id].level_vertex_id) then
|
||
|
db.offline_objects[id].level_vertex_id = nil
|
||
|
end
|
||
|
|
||
|
local target = xr_logic.pick_section_from_condlist(db.actor,npc,ini:r_string_to_condlist(section_logic,"net_spawn","nil"))
|
||
|
if not (target and (target ~= "") and (target ~= "nil")) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local pos = nil
|
||
|
|
||
|
if (false) then
|
||
|
elseif (target == "actor") then
|
||
|
pos = db.actor:position()
|
||
|
elseif (SIMBOARD.smarts_by_names[target]) then
|
||
|
pos = vector():set(SIMBOARD.smarts_by_names[target].position)
|
||
|
elseif (ini:line_exist(active_section,target)) then
|
||
|
local str = ini:r_string(active_section,target)
|
||
|
if (string.find(str,"pos:")) then
|
||
|
for s in string.gmatch(str,"pos:(%A+)") do
|
||
|
local p = str_explode(s,",")
|
||
|
if (tonumber(p[1]) and tonumber(p[1]) and tonumber(p[1])) then
|
||
|
pos = vector():set(tonumber(p[1]),tonumber(p[2]),tonumber(p[3]))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (storage_set_position[npc:name()]) then
|
||
|
--printf("GhenTuong: npc_set_position skip [%s]",npc:name())
|
||
|
return
|
||
|
end
|
||
|
storage_set_position[npc:name()] = true
|
||
|
|
||
|
npc:set_npc_position(pos)
|
||
|
--printf("GhenTuong: npc_set_position [%s] %s",npc:name(),pos)
|
||
|
end
|
||
|
|
||
|
function ignore_smart_job(npc)
|
||
|
local story_id = npc and (type(npc.id) == "number") and get_story_object_id(npc.id)
|
||
|
if (story_id and (string.find(story_id,"esc_2_12_stalker_trader") or string.find(story_id,"red_forester_tech"))) then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
local squad = npc and get_object_squad(npc)
|
||
|
if (squad and ini_sys:line_exist(squad:section_name(),"logic")) then
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
function monster_on_update(npc,st)
|
||
|
if not (st and st.ini and st.active_section and st.ini:line_exist(st.active_section,"target")) then
|
||
|
return
|
||
|
end
|
||
|
if not (db.actor and npc and npc:alive()) then
|
||
|
return
|
||
|
end
|
||
|
if not (st.beh and st.beh.target and (st.beh.active_section == st.active_section)) then
|
||
|
st.beh = {}
|
||
|
st.beh.active_section = tostring(st.active_section)
|
||
|
st.beh.target = st.ini:r_string_to_condlist(st.active_section,"target","nil")
|
||
|
end
|
||
|
xr_logic.pick_section_from_condlist(db.actor,npc,st.beh.target)
|
||
|
end
|
||
|
|
||
|
function npc_on_before_hit(npc,shit,bone_id,flags)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
if not (st and st.ini and st.active_section and st.ini:line_exist(st.active_section,"before_hit")) then
|
||
|
return
|
||
|
end
|
||
|
local str = st.ini:r_string_ex(st.active_section,"before_hit")
|
||
|
if (str) then
|
||
|
if string.find(str,"invulnerable") then
|
||
|
if (tonumber(npc.health) < 1) then
|
||
|
npc:set_health_ex(1)
|
||
|
end
|
||
|
flags.ret_value = false
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if string.find(str,"@") then
|
||
|
local p = str_explode(str,"@")
|
||
|
if (p and #p == 2) then
|
||
|
local v = _G[tostring(p[1])][tostring(p[2])](npc,shit,bone_id,flags)
|
||
|
|
||
|
if (tostring(v) and string.find(tostring(v),"invulnerable")) then
|
||
|
if (tonumber(npc.health) < 1) then
|
||
|
npc:set_health_ex(1)
|
||
|
end
|
||
|
flags.ret_value = false
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function npc_on_eval_danger(npc,flags)
|
||
|
local st = npc and db.storage[npc:id()]
|
||
|
if not (st and (st.active_scheme == "beh")) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local con = st.ini and st.active_section and st.ini:r_string_to_condlist(st.active_section,"danger_ignore")
|
||
|
if (con) then
|
||
|
if (xr_logic.pick_section_from_condlist(db.actor,npc,con) == "true") then
|
||
|
flags.ret_value = false
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local storage_smart = {}
|
||
|
|
||
|
function smart_terrain_on_update(smart)
|
||
|
if not (smart and smart.ini and smart.ini:section_exist("on_changing_level")) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (storage_smart[smart:name()]) then
|
||
|
return
|
||
|
end
|
||
|
storage_smart[smart:name()] = true
|
||
|
|
||
|
local n = smart.ini:line_count("on_changing_level")
|
||
|
for k=0,n-1 do
|
||
|
local r,i,v = smart.ini:r_line("on_changing_level",k)
|
||
|
if (i and v and string.find(i,"on_info")) then
|
||
|
--printf("smart_terrain_on_update %s",smart:name())
|
||
|
local con = smart.ini:r_string_to_condlist("on_changing_level",i,"nil")
|
||
|
xr_logic.pick_section_from_condlist(db.actor,smart,con)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function save_state(m_data)
|
||
|
m_data.xr_logic_ex_storage_set_position = storage_set_position
|
||
|
m_data.xr_logic_ex_storage_logic = storage_logic
|
||
|
m_data.xr_logic_ex_storage_smart = storage_smart
|
||
|
end
|
||
|
|
||
|
function load_state(m_data)
|
||
|
storage_set_position = m_data.xr_logic_ex_storage_set_position or {}
|
||
|
storage_logic = m_data.xr_logic_ex_storage_logic or {}
|
||
|
storage_smart = m_data.xr_logic_ex_storage_smart or {}
|
||
|
end
|
||
|
|
||
|
function on_level_changing()
|
||
|
storage_set_position = {}
|
||
|
storage_logic = {}
|
||
|
storage_smart = {}
|
||
|
end
|
||
|
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
Registers
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
function on_game_start()
|
||
|
RegisterScriptCallback("save_state",save_state)
|
||
|
RegisterScriptCallback("load_state",load_state)
|
||
|
RegisterScriptCallback("squad_on_update",squad_on_update)
|
||
|
RegisterScriptCallback("on_level_changing",on_level_changing)
|
||
|
RegisterScriptCallback("monster_on_update",monster_on_update)
|
||
|
RegisterScriptCallback("npc_on_net_spawn",npc_on_net)
|
||
|
RegisterScriptCallback("monster_on_net_spawn",npc_on_net)
|
||
|
RegisterScriptCallback("npc_on_before_hit",npc_on_before_hit)
|
||
|
RegisterScriptCallback("monster_on_before_hit",npc_on_before_hit)
|
||
|
|
||
|
RegisterScriptCallback("npc_on_eval_danger",npc_on_eval_danger)
|
||
|
|
||
|
RegisterScriptCallback("smart_terrain_on_update",smart_terrain_on_update)
|
||
|
end
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
States
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
function add_states()
|
||
|
return {
|
||
|
animpoint_reach_ex = { weapon = "strapped",
|
||
|
movement = nil,
|
||
|
mental = nil,
|
||
|
bodystate = nil,
|
||
|
animstate = nil,
|
||
|
animation = nil,
|
||
|
direction = CSightParams.eSightTypeAnimationDirection
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
copy_table(state_lib.states, add_states())
|
||
|
|
||
|
--[[----------------------------------------------------------------------------------------------------
|
||
|
Overrides
|
||
|
------------------------------------------------------------------------------------------------------]]
|
||
|
function xr_combat_ignore.ignore_enemy_by_overrides(obj,enemy,no_check_job)
|
||
|
if not (enemy) then
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
if (IsStalker(obj)) then
|
||
|
if (enemy:section() == "mar_smart_terrain_doc_dog" or enemy:section() == "mar_smart_terrain_base_dog_doctor") then
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local id = obj:id()
|
||
|
local ene_id = enemy:id()
|
||
|
|
||
|
local st = db.storage[id] and db.storage[id].overrides
|
||
|
|
||
|
-- This skips enemy_ignore of obj when enemy doesn't have overrides, considered a bug.
|
||
|
-- Ex: enemy that doesn't have logic at all, enemy squad on moving to a smart terrain.
|
||
|
--if not (st) then
|
||
|
-- return false
|
||
|
--end
|
||
|
|
||
|
-- combat_ignore_cond from custom data logic
|
||
|
local ignore = st and st.combat_ignore and xr_logic.pick_section_from_condlist(enemy, obj, st.combat_ignore.condlist)
|
||
|
if (ignore == "true") then
|
||
|
--obj:enable_memory_object(enemy,false)
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
-- enemy_ignore_cond override from custom data logic
|
||
|
-- if this is true then npc will IGNORE combat with this specific enemy
|
||
|
local ene_st = db.storage[ene_id] and db.storage[ene_id].overrides
|
||
|
if (ene_st) then
|
||
|
ignore = ene_st.enemy_ignore and xr_logic.pick_section_from_condlist(enemy, obj, ene_st.enemy_ignore.condlist)
|
||
|
if (ignore == "true") then
|
||
|
--obj:enable_memory_object(enemy,false)
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Ignore enemies because of no_combat_job
|
||
|
if (no_check_job ~= true) and (st and st.no_combat_job and xr_logic.pick_section_from_condlist(enemy, obj, st.no_combat_job.condlist) == "true") then
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
function xr_combat_camper.action_shoot:initialize()
|
||
|
action_base.initialize(self)
|
||
|
self.st.camper_combat_action = true
|
||
|
xr_sound.set_sound_play(self.object:id(),"fight_enemy")
|
||
|
end
|
||
|
|
||
|
function xr_combat_camper.action_shoot:execute()
|
||
|
action_base.execute(self)
|
||
|
local new_state = "hide_fire"
|
||
|
local st = db.storage[self.object:id()]
|
||
|
if (st and st.ini and st.active_section and st.ini:line_exist(st.active_section,"combat_camper_state_fire")) then
|
||
|
new_state = st.ini:r_string_ex(st.active_section,"combat_camper_state_fire")
|
||
|
end
|
||
|
state_mgr.set_state(self.object,new_state,nil,nil,{look_object = self.object:best_enemy()},{fast_set = true})
|
||
|
xr_sound.set_sound_play(self.object:id(),"fight")
|
||
|
end
|
||
|
|
||
|
function xr_combat_camper.action_look_around:reset()
|
||
|
self.forget_time = device():time_global() + 30000
|
||
|
self.change_dir_time = device():time_global() + 15000
|
||
|
|
||
|
-- если врага мы ещё не видели вообще, то всё равно повернуться к нему
|
||
|
if not self.st.last_seen_pos and self.object:best_enemy() ~= nil then
|
||
|
self.st.last_seen_pos = self.object:best_enemy():position()
|
||
|
end
|
||
|
|
||
|
local new_state = "hide"
|
||
|
local st = db.storage[self.object:id()]
|
||
|
if (st and st.ini and st.active_section and st.ini:line_exist(st.active_section,"combat_camper_state_look")) then
|
||
|
new_state = st.ini:r_string_ex(st.active_section,"combat_camper_state_look")
|
||
|
end
|
||
|
state_mgr.set_state(self.object,new_state,nil,nil,{look_position = self.st.last_seen_pos})
|
||
|
end
|
||
|
|
||
|
function xr_combat_camper.action_look_around:execute()
|
||
|
action_base.execute(self)
|
||
|
|
||
|
if (self.forget_time < device():time_global()) then
|
||
|
-- self.object:enable_memory_object( self.object:best_enemy(), false )
|
||
|
self.st.last_seen_pos = nil
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (self.change_dir_time < device():time_global()) then
|
||
|
self.change_dir_time = device():time_global() + math.random(2000,4000)
|
||
|
|
||
|
local ang = math.random(0,120) - 60
|
||
|
local dir = self.st.last_seen_pos and vector():set(self.st.last_seen_pos):sub(self.object:position()):normalize()
|
||
|
dir = dir and vector_rotate_y(dir,ang):normalize()
|
||
|
dir = dir and vector():set(dir.x * 10, dir.y * 10, dir.z * 10)
|
||
|
|
||
|
local new_state = "hide"
|
||
|
local st = db.storage[self.object:id()]
|
||
|
if (st and st.ini and st.active_section and st.ini:line_exist(st.active_section,"combat_camper_state_look")) then
|
||
|
new_state = st.ini:r_string_ex(st.active_section,"combat_camper_state_look")
|
||
|
end
|
||
|
state_mgr.set_state(self.object,new_state,nil,nil,{look_position = dir and self.object:position():add(dir)},{fast_set = true})
|
||
|
end
|
||
|
end
|