Divergent/mods/HG Companion/gamedata/scripts/axr_companions.script

2181 lines
62 KiB
Plaintext
Raw Normal View History

2024-03-17 20:18:03 -04:00
--[[
by Alundaio
Copyright (C) 2012 Alundaio
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
-------------
Tronex
Last modification: 2020/1/20
Dimissing/releasing companions and special squads is done here with safe releasing and fade out effect
Companions wheel:
Fast interaction with companions
Suppor for trade button
Command individual or all companions
-------------
Hive Game
Last modification: 2023/4/15
Companion groups
Companions list
--]]
-- SEE configs\ai_tweaks\axr_companions
allow_only_friends_as_companions = true
allow_simulation_squads_as_companions = false
max_actor_squad_size = 0 -- used only for non task companions
-- Companion tables. All companions are added to companion_squads table by their squad id. Only non-task companions are tracked by non_task_companions table.
companion_squads = {}
non_task_companions = {}
------------------------------------------
local assigned_items = {}
local states_squad_0 = {
["combat"] = 1,
["move"] = 2,
["stealth"] = 2,
["loot"] = 1,
["distance"] = 1,
}
local states_squad_1 = {
["combat"] = 1,
["move"] = 2,
["stealth"] = 2,
["loot"] = 1,
["distance"] = 1,
}
local states_squad_2 = {
["combat"] = 1,
["move"] = 2,
["stealth"] = 2,
["loot"] = 1,
["distance"] = 1,
}
local states_squad_3 = {
["combat"] = 1,
["move"] = 2,
["stealth"] = 2,
["loot"] = 1,
["distance"] = 1,
}
local _current_states = {
["squad_0"] = states_squad_0,
["squad_1"] = states_squad_1,
["squad_2"] = states_squad_2,
["squad_3"] = states_squad_3,
}
local _draw_part
local pos_by_id = {}
------------------------------------------
-- Localized Functions
------------------------------------------
local function on_fighting_actor(npc)
if not (IsStalker(npc)) then
return
end
--utils_data.debug_write(strformat("axr_companions.on_fighting_actor"))
local sim = alife()
for id,squad in pairs(companion_squads) do
xr_combat_ignore.safe_zone_npcs[id] = nil
if (squad and squad.commander_id) then
for k in squad:squad_members() do
local member = db.storage[k.id] and db.storage[k.id].object
if (member and member:alive()) then
member:set_relation(game_object.enemy,npc)
npc:set_relation(game_object.enemy,member)
--[[ Force danger when actor is shot on non-passive mode
if not (member:best_enemy() and sim:has_info(member:id(),"npcx_beh_ignore_actor_enemies")) then
xr_danger.set_script_danger(member,7000,npc:id(),vector():set(npc:position()))
printf("-set_script_danger(%s,%s)", member:name(), npc:name())
end
--]]
end
end
end
end
end
local function squad_on_npc_death(squad,se_npc)
--utils_data.debug_write(strformat("axr_companions.squad_on_npc_death"))
non_task_companions[se_npc.id] = nil
if (squad:npc_count() == 0) then
companion_squads[squad.id] = nil
end
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
end
local function squad_on_unregister(squad, type_name)
if (type_name ~= "sim_squad_scripted") then
return
end
companion_squads[squad.id] = nil
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
end
local function squad_on_first_update(squad)
if (companion_squads[squad.id] ~= nil) then
companion_squads[squad.id] = squad
--squad:set_squad_relation("friend")
squad.scripted_target = "actor"
end
-- backward save compatiblity. remove later
for k in squad:squad_members() do
if (k.id and k.object and se_load_var(k.id,k.object:name(),"companion")) then
if not (se_load_var(k.id,k.object:name(),"companion_hg")) then
se_save_var(k.id,k.object:name(),"companion_hg",1)
end
se_save_var(k.id,k.object:name(), "campfire_step", nil)
se_save_var(k.id,k.object:name(), "campfire", nil)
se_save_var(k.id,k.object:name(), "cover", nil)
companion_squads[squad.id] = squad
squad.scripted_target = "actor"
end
end
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
end
local function squad_on_update(squad)
--utils_data.debug_write(strformat("axr_companions.squad_on_update - heli_enemy_flag"))
if (companion_squads[squad.id]) then
squad.scripted_target = "actor"
for k in squad:squad_members() do
if (db.storage[k.id]) then
db.storage[k.id].heli_enemy_flag = load_var(db.actor,"heli_enemy_flag")
end
end
end
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
end
local function on_level_changing()
local sim = alife()
local se_actor = sim:actor()
local gg = game_graph()
local gvid = se_actor.m_game_vertex_id
local vert = gg:vertex(gvid)
local lvid = vert:level_vertex_id()
local pos = vert:level_point()
for id,v in pairs(companion_squads) do
local squad = sim:object(id)
if (squad and squad.commander_id) then
if companion_squad_can_teleport(squad) then
TeleportSquad(squad,pos,lvid,gvid)
end
end
end
end
local function save_state(m_data)
m_data.non_task_companions = non_task_companions
m_data.companion_squads = {}
for id,squad in pairs(companion_squads) do
if (squad and squad.commander_id) then
m_data.companion_squads[id] = false
end
end
m_data.companion_assigned_items = assigned_items
m_data.companion_current_states_hg = _current_states
end
local function load_state(m_data)
non_task_companions = m_data.non_task_companions or non_task_companions
m_data.non_task_companions = nil
if (m_data.companion_squads) then
companion_squads = m_data.companion_squads or companion_squads
for id,bool in pairs(m_data.companion_squads) do
companion_squads[id] = false
end
end
m_data.companion_squads = nil
assigned_items = m_data.companion_assigned_items or assigned_items
_current_states = m_data.companion_current_states_hg or _current_states
end
local function healing_timer() -- auto-healing every x seconds
ResetTimeEvent("cycle","companion_healing",10)
for id,squad in pairs(companion_squads) do
if (squad and squad.commander_id) then
for k in squad:squad_members() do
st = db.storage[k.id]
local member = st and st.object
if (member and member:alive()) then
local health = member.health
if (health < 1) then
health = clamp(health + 0.1, 0, 1)
member.health = health
end
end
end
end
end
return false
end
local function unstuck()
-- Tonex: this is a dumb yet effective hack to unstuck companions that got stuck randomly, by teleporting them into their position
ResetTimeEvent("cycle","companion_unstuck",30)
local sim = alife()
for id,squad in pairs(companion_squads) do
if (squad and squad.commander_id) then
for k in squad:squad_members() do
local npc = db.storage[k.id] and db.storage[k.id].object
if (npc and npc:alive()) then
local pos = npc:position()
local gvid = npc:game_vertex_id()
local lvid = npc:level_vertex_id()
local is_following = npc:dont_has_info("npcx_beh_wait") and true or false
local is_catching_up = (state_mgr.get_state(npc) == "panic")
local is_peaceful = (not npc:best_enemy()) and npc:dont_has_info("npcx_beh_ignore_combat") and true or false
local is_standing = pos_by_id[k.id] and (pos_by_id[k.id]:distance_to(pos) < 1) and true or false
local is_far = (db.actor:position():distance_to(pos) > 10) and true or false
local is_works = npc:dont_has_info("npcx_beh_relax") and true or false
pos_by_id[k.id] = pos
--printf("~Companion (%s): is_following: %s - is_catching_up: %s - is_peaceful: %s - is_standing: %s - is_far: %s", npc:name(), is_following, is_catching_up, is_peaceful, is_standing, is_far)
if is_following and is_catching_up and is_peaceful and is_standing and is_far and is_works then
--printf("-Companion (%s): teleport to unstuck!", npc:name())
-- Teleport in place
if (db.offline_objects[k.id]) then
db.offline_objects[k.id].level_vertex_id = nil
end
db.spawned_vertex_by_id[k.id] = nil
sim:teleport_object(k.id,gvid,lvid,pos)
end
end
end
end
end
return false
end
local function on_game_load()
-- Patriarch achievement doubles allowed squad sizes.
local ini = ini_file("ai_tweaks\\axr_companions.ltx")
if (game_achievements.has_achievement("patriarch")) then
max_actor_squad_size = ini:r_float_ex("main","max_actor_squad_size_patriarch") or 4
end
CreateTimeEvent("cycle","companion_healing",10,healing_timer)
CreateTimeEvent("cycle","companion_unstuck",30,unstuck)
-- backward save compatiblity, remove me later!
local c_idx = 1
local cid = load_var(db.actor,"companion_1")
while (cid ~= nil) do
local se_obj = alife_object(cid)
if (se_obj) then
non_task_companions[se_obj.id] = true
local squad = se_obj.group_id and se_obj.group_id ~= 65535 and alife_object(se_obj.group_id)
if (squad) then
companion_squads[squad.id] = squad
end
end
save_var(db.actor,"companion_"..c_idx,nil)
c_idx = c_idx + 1
cid = load_var(db.actor,"companion_"..c_idx)
printf("! axr_companions | cleared old data: %s", "companion_"..c_idx)
end
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
end
---------------------------------
-- Register Callbacks
---------------------------------
function on_game_start()
local ini = ini_file("ai_tweaks\\axr_companions.ltx")
Enabled = ini:r_bool_ex("main","enable",false)
if not (Enabled) then
return
end
local function actor_on_first_update()
activate_hud()
end
RegisterScriptCallback("npc_on_fighting_actor",on_fighting_actor)
RegisterScriptCallback("squad_on_update",squad_on_update)
RegisterScriptCallback("ActorMenu_on_item_before_move",OnItemBeforeMove)
RegisterScriptCallback("ActorMenu_on_item_after_move",OnItemAfterMove)
RegisterScriptCallback("on_key_release",on_key_release)
RegisterScriptCallback("actor_on_first_update",actor_on_first_update)
RegisterScriptCallback("squad_on_first_update",squad_on_first_update)
RegisterScriptCallback("squad_on_npc_death",squad_on_npc_death)
RegisterScriptCallback("server_entity_on_unregister",squad_on_unregister)
RegisterScriptCallback("on_level_changing",on_level_changing)
RegisterScriptCallback("save_state",save_state)
RegisterScriptCallback("load_state",load_state)
RegisterScriptCallback("on_game_load",on_game_load)
allow_only_friends_as_companions = ini:r_bool_ex("main","allow_only_friends_as_companions",false)
allow_simulation_squads_as_companions = ini:r_bool_ex("main","allow_simulation_squads_as_companions",false)
max_actor_squad_size = ini:r_float_ex("main","max_actor_squad_size") or 2
end
---------------------------------
-- Management
---------------------------------
function companion_squad_can_teleport(squad)
local id = squad:commander_id()
local sim = alife()
local se_obj = sim:object(id)
if (se_obj) then
if (se_load_var(se_obj.id,se_obj:name(),"companion_cannot_teleport")) then
return false
end
end
if (id) and (sim:has_info(id,"npcx_beh_patrol_mode") or sim:has_info(id,"npcx_beh_wait")) then
return false
end
return true
end
-- doesn't include task companions
function get_companion_count()
return size_table(non_task_companions)
end
function can_join_actor(npc)
local squad = get_object_squad(npc)
if squad ~= nil and squad:commander_id() ~= npc:id() then
return false
end
if (get_companion_count() + utils_obj.get_squad_count(npc) <= max_actor_squad_size) then
return true
end
return false
end
function list_actor_squad_by_id()
local t = {}
local size_t = 0
for id,squad in spairs(companion_squads, function(t,a,b) return a < b end) do
if (squad and squad.commander_id) then
if not (axr_task_manager.hostages_by_id[squad:commander_id()]) then
for k in squad:squad_members() do
--printf("member %s",k.id)
size_t = size_t + 1
t[size_t] = k.id
end
end
end
end
return t
end
function setup_companion_logic(npc,st,loaded,cannot_dismiss)
npc:give_info_portion("npcx_is_companion")
if (cannot_dismiss or se_load_var(npc:id(),npc:name(),"companion_cannot_dismiss")) then
npc:give_info_portion("npcx_beh_cannot_dismiss")
end
local ltx_name = "scripts\\beh_companion.ltx"
local ltx = ini_file(ltx_name)
if not (ltx) then
log("ERROR: do not have access to scripts\\beh_companion.ltx! Make sure you installed properly!")
return
end
local id = npc:id()
local sim = alife()
local se_npc = sim:object(id)
local unreg_id = se_npc and se_npc.m_smart_terrain_id
if (unreg_id and unreg_id ~= 65535) then
local unreg = sim:object(unreg_id)
if (unreg) then
unreg:unregister_npc(se_npc)
end
end
xr_logic.configure_schemes(npc, ltx, ltx_name, modules.stype_stalker, loaded and st.loaded_section_logic or "logic", "")
local section = loaded and st.loaded_active_section or xr_logic.determine_section_to_activate(npc, ltx, "logic", db.actor)
xr_logic.activate_by_section(npc, ltx, section, "", loaded)
--printf("setup complete")
end
function add_special_task_npc_to_actor_squad(npc)
local id = npc:id()
local sim = alife()
local se_npc = sim:object(id)
local unreg_id = se_npc and se_npc.m_smart_terrain_id
if (unreg_id and unreg_id ~= 65535) then
local unreg = sim:object(unreg_id)
if (unreg) then
unreg:unregister_npc(se_npc)
end
end
npc:inactualize_patrol_path()
npc:give_info_portion("npcx_is_companion")
xr_logic.set_new_scheme_and_logic(npc,"beh","beh@base","logic",nil,"scripts\\beh_companion.ltx")
setup_companion_logic(npc)
end
function add_to_actor_squad(npc)
local id = npc:id()
non_task_companions[id] = true
se_save_var(id,npc:name(),"companion",true)
se_save_var(id,npc:name(),"companion_hg",1)
npc:inactualize_patrol_path()
setup_companion_logic(npc,db.storage[id],false)
cycle_companions_move_mode(_current_states["squad_1"]["move"])
cycle_companions_stealth_mode(_current_states["squad_1"]["stealth"])
cycle_companions_loot_mode(_current_states["squad_1"]["loot"])
cycle_companions_combat_mode(_current_states["squad_1"]["combat"])
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
end
function remove_from_actor_squad(npc)
local squad = get_object_squad(npc)
if (squad) then
squad.scripted_target = nil
companion_squads[squad.id] = nil
if _G.WARFARE then
sim_squad_warfare.set_target(squad, smart_terrain.nearest_to_actor_smart.id)
end
end
stalker_generic.remove_level_spot(npc:id())
non_task_companions[npc:id()] = nil
npc:disable_info_portion("npcx_is_companion")
npc:disable_info_portion("npcx_beh_cannot_dismiss")
xr_logic.restore_scheme_and_logic(npc)
local se_obj = alife_object(npc:id())
if not (se_obj) then
return
end
se_save_var(se_obj.id,se_obj:name(),"companion",nil)
se_save_var(se_obj.id,se_obj:name(),"companion_cannot_dismiss",nil)
se_save_var(se_obj.id,se_obj:name(),"companion_cannot_teleport",nil)
se_save_var(se_obj.id,se_obj:name(),"companion_hg",nil)
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
end
function remove_all_from_actor_squad()
local sq_d = {}
for id,squad in pairs(companion_squads) do
if (squad) then
sq_d[#sq_d + 1] = squad
for k in squad:squad_members() do
local se_obj = k.object or alife_object(k.id)
stalker_generic.remove_level_spot(se_obj.id)
utils_obj.execute_script_on_squad(se_obj,remove_from_actor_squad) -- A fix to companions stacking in Relations tab
end
end
companion_squads[id] = nil
end
for id,b in pairs(non_task_companions) do
stalker_generic.remove_level_spot(id)
non_task_companions[id] = nil
end
return sq_d
end
-- Tronex
local fade_trigger = 0
local fade_tbl = {}
local function releaser()
local function fade_in_out()
for i=1,#fade_tbl do
fade_tbl[i]:remove_squad() -- Release
end
empty_table(fade_tbl)
fade_trigger = 0
return true
end
if (#fade_tbl > 0) and ((time_global() - fade_trigger) > 4000) then
level.add_pp_effector("fade_in_out.ppe", 1313, false) -- Play effect
CreateTimeEvent(0,"release_companion",4,fade_in_out)
fade_trigger = time_global()
else
printf("~ Companions | Can't play the effect when it's being played aleady")
end
end
local function stop_talking(squad)
for k in squad:squad_members() do
local npc = db.storage[k.id] and db.storage[k.id].object
if npc and npc:is_talking() then
db.actor:stop_talk()
npc:stop_talk()
end
end
end
function add_special_squad(squad, special, cannot_teleport)
cannot_teleport = cannot_teleport or false
if not (squad) then
printf("~ Companions | squad can't be added cause it doesn't exist?!")
return
end
if companion_squads[squad.id] then
printf("~ Companions | squad (%s) is already companion squad", squad.id)
return
end
companion_squads[squad.id] = squad
for k in squad:squad_members() do
local npc = k.id and (db.storage[k.id] and db.storage[k.id].object or level.object_by_id(k.id))
if (npc) then
se_save_var(k.id,k.object:name(),"companion",true)
se_save_var(k.id,k.object:name(),"companion_cannot_dismiss",true)
se_save_var(k.id,k.object:name(),"companion_cannot_teleport",cannot_teleport)
se_save_var(k.id,k.object:name(),"companion_hg",1)
if special then
local se_obj = alife_object(k.id)
if (se_obj) then
SIMBOARD:setup_squad_and_group(se_obj)
end
else
setup_companion_logic(npc,db.storage[k.id],false,true)
end
end
end
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
end
function dismiss_special_squad(squad)
if not (squad) then
printf("~ Companions | squad can't be dismissed cause it doesn't exist?!")
return
end
if companion_squads[squad.id] == nil then
printf("~ Companions | squad (%s) can't be dismissed cause it's not a companion squad", squad.id)
return
end
for k in squad:squad_members() do
local se_obj = k.object or alife_object(k.id)
utils_obj.execute_script_on_squad(se_obj,remove_from_actor_squad) -- A fix to companions stacking in Relations tab
end
companion_squads[squad.id] = nil -- remove from comp table
end
function release_special_squad(squad)
if not (squad) then
printf("~ Companions | squad can't be released cause it doesn't exist?!")
return
end
-- Dismiss
dismiss_special_squad(squad)
-- Stop talking
stop_talking(squad)
-- Release after fade out
fade_tbl[#fade_tbl + 1] = squad
releaser()
end
function release_all_squads()
-- Dismiss all
local sq_r = remove_all_from_actor_squad() or {}
-- Stop talking + Release after fade out
for i=1,#sq_r do
stop_talking(sq_r[i])
fade_tbl[#fade_tbl + 1] = sq_r[i]
end
releaser()
end
function epic_hack()
local companion = level.object_by_id(companion_id)
if companion and companion:alive() and (not companion:best_enemy()) then
for _,id in ipairs(db.heli_enemies) do
local target = level.object_by_id(id)
if target
and target:alive()
and target:best_enemy()
and ((target:best_enemy():id() == 0) or (target:best_enemy():id() == companion_id))
then
xr_danger.set_script_danger(companion,7000,target:id(),vector():set(target:position()))
end
end
end
end
---------------------------------
-- Tasks
---------------------------------
xr_effects.remove_special_companion_squad = function(actor,npc,p)
for i=1,#p do
local squad = get_story_squad(p[i])
release_special_squad(squad)
end
end
xr_effects.dismiss_special_companion_squad = function(actor,npc,p)
for i=1,#p do
local squad = get_story_squad(p[i])
dismiss_special_squad(squad)
end
end
xr_effects.remove_all_special_companion = function()
release_all_squads()
end
xr_effects.setup_companion_special_task = function(actor,npc,p)
level.add_pp_effector("black.ppe", 1500, false)
xr_effects.setup_companion_task(actor,npc,p)
end
-- remove a companion squad by story id
xr_effects.remove_task_companion = function(actor,npc,p)
local squad = p[1] and get_story_squad(p[1])
dismiss_special_squad(squad)
end
xr_effects.add_task_companion = function(actor,npc,p)
local squad = p[1] and get_story_squad(p[1])
local special = p[2] == "true" and true or false
local cannot_teleport = p[3] == "true" and true or false
if squad then
add_special_squad(squad, special, cannot_teleport)
else
printe("! ERROR add_task_companion | can't find story squad [%s]", p[1])
end
end
-- Task giver squad
xr_effects.add_task_giver_companion = function(actor,npc,p)
if (not p[1]) then
printe("! ERROR add_task_giver_companion | can't set up task giver as companion because task is not declared")
return
end
local tm = task_manager.get_task_manager()
local tsk = tm.task_info[p[1]]
if (not tsk) then
printe("! ERROR add_task_giver_companion | task [%s] is not active", p[1])
return
end
local id = tsk.task_giver_id
if (not id) then
printe("! ERROR add_task_giver_companion | task giver id not found for task [%s]", p[1])
return
end
local npc = db.storage[id] and db.storage[id].object or level.object_by_id(id)
if (not npc) then
printe("! ERROR add_task_giver_companion | can't find task giver object (%s) for task [%s]", id, p[1])
end
local squad = get_object_squad(npc)
add_special_squad(squad)
end
xr_effects.dismiss_task_giver_companion = function(actor,npc,p)
if (not p[1]) then
printe("! ERROR add_task_giver_companion | can't set up task giver as companion because task is not declared")
return
end
local tm = task_manager.get_task_manager()
local tsk = tm.task_info[p[1]]
if (not tsk) then
printe("! ERROR add_task_giver_companion | task [%s] is not active", p[1])
return
end
local id = tsk.task_giver_id
if (not id) then
printe("! ERROR add_task_giver_companion | task giver id not found for task [%s]", p[1])
return
end
local npc = db.storage[id] and db.storage[id].object or level.object_by_id(id)
if (not npc) then
printe("! ERROR add_task_giver_companion | can't find task giver object (%s) for task [%s]", id, p[1])
end
local squad = get_object_squad(npc)
dismiss_special_squad(squad)
end
-- Speaker squad
xr_effects.add_speaker_companion = function(actor,npc,p)
if (not db.actor:is_talking()) then
printe("! ERROR add_speaker_companion | actor is not talking")
return
end
local task_id = p[1]
local speaker = mob_trade.GetTalkingNpc()
local id = speaker:id()
if (not id) then
printe("! ERROR add_speaker_companion | speaker id not found for task [%s]", task_id)
return
end
local npc = db.storage[id] and db.storage[id].object or level.object_by_id(id)
if (not npc) then
printe("! ERROR add_speaker_companion | can't find speaker object (%s) for task [%s]", id, task_id)
end
local squad = get_object_squad(npc)
add_special_squad(squad)
local var = load_var(db.actor, task_id)
if var and task_id then
var.companion_squad_id = squad.id
save_var(db.actor, task_id, var)
printf("- %s | saved companion id (%s)", task_id, id)
end
end
xr_effects.dismiss_speaker_companion = function(actor,npc,p)
local task_id = p[1]
local var = task_id and load_var(db.actor, task_id)
local sq_id = var and var.companion_squad_id
if (not sq_id) and db.actor:is_talking() then
local speaker = mob_trade.GetTalkingNpc()
local se_speaker = alife_object(speaker:id())
if se_speaker then
sq_id = se_speaker.group_id
end
end
if (not sq_id) then
printe("! ERROR dismiss_speaker_companion | speaker squad id not found for task [%s]", task_id)
return
end
local squad = alife_object(sq_id)
dismiss_special_squad(squad)
end
xr_effects.setup_companion_task = function(actor,npc,p)
-- Spawn a squad that will become actor companion for special tasks
-- param 1 - squad section
-- param 2 - smart
-- param 3 - variable name. Will use smart_id in a pstor variable instead
-- param 4 - disable level transition for the squad
-- param 5 - is a hostage
local sq_sec = p[1]
local smrt_name = p[2]
local var_id = p[3]
local cant_teleport = p[4]
local is_hostage = p[5]
local var = var_id and var_id ~= "nil" and load_var(db.actor,var_id)
local smart_id = var and var.smart_id
local smart = smart_id and alife_object(smart_id) or smrt_name and smrt_name ~= "nil" and SIMBOARD.smarts_by_names[smrt_name]
--xQd
if _G.WARFARE and (not smrt_name or smrt_name == "false") and (not is_hostage or is_hostage == "false") then -- sim warfare escort
--printf("no smart terrain found for companion task")
for sname,sm in pairs(SIMBOARD.smarts_by_names) do
if sm.is_on_actor_level == true and sm.dist_to_actor <= 50 then
smart = sm
save_var(db.actor,"warfare_escort_task_smart",sname) -- store the name of the smart terrain where the escorted spawn
--printf("found smart to spawn companion task %s", sname)
break
end
end
end
--xQd end
if not (smart) then
printe("! setup_companion_task: no smart found! %s", smart_id or smrt_name)
return
end
if not (ini_sys:section_exist(sq_sec)) then
printe("! setup_companion_task: Trying to setup companion squad with a non-existent section!")
return
end
local squad = alife_create(sq_sec,smart.position,smart.m_level_vertex_id,smart.m_game_vertex_id)
-- Setup relation
squad:create_npc(smart)
squad:set_squad_relation()
-- Setup logic
companion_squads[squad.id] = squad
for k in squad:squad_members() do
local se_obj = k.object or k.id and sim:object(k.id)
if (se_obj) then
se_save_var(se_obj.id,se_obj:name(),"companion",true)
se_save_var(se_obj.id,se_obj:name(),"companion_cannot_dismiss",true)
se_save_var(se_obj.id,se_obj:name(),"companion_cannot_teleport",cant_teleport == "true")
se_save_var(se_obj.id,se_obj:name(),"companion_hg",1)
SIMBOARD:setup_squad_and_group(se_obj)
if (is_hostage == "true") then
axr_task_manager.hostages_by_id[se_obj.id] = smart.id
end
end
end
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
--[[ Setup logic
--companion_squads[squad.id] = squad
add_special_squad(squad, true, cant_teleport == "true")
if (is_hostage == "true") then
for k in squad:squad_members() do
local se_obj = k.object or k.id and alife_object(k.id)
if (se_obj) then
axr_task_manager.hostages_by_id[se_obj.id] = smart.id
end
end
save_var( db.actor, "drx_sl_hostage_giver_needed", true )
end
--]]
-- Add to ignore offline combat simulation list
sim_offline_combat.task_squads[squad.id] = true
--save_var(db.actor,var_id or "task_companion_slot_1",squad.id)
--CreateTimeEvent(0,"add_special_task_squad",5,add_special_task_squad,squad.id,p[4] == "true")
end
---------------------------------
-- Companion interactions
---------------------------------
function set_companion_allow_teleport(npc)
local se_obj = alife_object(npc:id())
if not (se_obj) then
return
end
se_save_var(se_obj.id,se_obj:name(),"companion_cannot_teleport",nil)
end
function set_companion_disable_teleport(npc)
local se_obj = alife_object(npc:id())
if not (se_obj) then
return
end
se_save_var(se_obj.id,se_obj:name(),"companion_cannot_teleport",true)
end
function set_companion_combat_type_camper(npc)
npc:disable_info_portion("npcx_beh_combat_tactics_monolith")
npc:give_info_portion("npcx_beh_combat_tactics_camper")
end
function set_companion_combat_type_monolith(npc)
npc:disable_info_portion("npcx_beh_combat_tactics_camper")
npc:give_info_portion("npcx_beh_combat_tactics_monolith")
end
function set_companion_combat_type_default(npc)
npc:disable_info_portion("npcx_beh_combat_tactics_monolith")
npc:disable_info_portion("npcx_beh_combat_tactics_camper")
end
function set_companion_hide_in_cover(npc)
npc:give_info_portion("npcx_beh_hide_in_cover")
npc:disable_info_portion("npcx_beh_wait")
end
function set_companion_to_wait_state(npc)
npc:give_info_portion("npcx_beh_wait")
save_var(npc,"fight_from_point",npc:level_vertex_id())
end
function set_companion_to_patrol_state(npc)
npc:give_info_portion("npcx_beh_patrol_mode")
end
function set_companion_to_follow_state(npc)
npc:disable_info_portion("npcx_beh_wait")
npc:disable_info_portion("npcx_beh_hide_in_cover")
npc:disable_info_portion("npcx_beh_patrol_mode")
save_var(npc,"fight_from_point",nil)
end
function set_companion_to_attack_state(npc)
npc:disable_info_portion("npcx_beh_ignore_combat")
npc:disable_info_portion("npcx_beh_ignore_actor_enemies")
end
function set_companion_to_ignore_combat_state(npc)
npc:give_info_portion("npcx_beh_ignore_combat")
npc:give_info_portion("npcx_beh_ignore_actor_enemies")
end
function set_companion_to_attack_only_actor_combat_enemy_state(npc)
npc:give_info_portion("npcx_beh_ignore_combat")
npc:disable_info_portion("npcx_beh_ignore_actor_enemies")
end
function set_companion_to_stealth_substate(npc)
npc:give_info_portion("npcx_beh_substate_stealth")
end
function set_companion_to_relax_substate(npc)
npc:give_info_portion("npcx_beh_substate_relax")
end
function set_companion_to_default_substate(npc)
npc:disable_info_portion("npcx_beh_substate_stealth")
npc:disable_info_portion("npcx_beh_substate_relax")
end
function set_companion_to_loot_items_and_corpses(npc)
npc:give_info_portion("npcx_beh_gather_items")
npc:give_info_portion("npcx_beh_loot_corpses")
end
function set_companion_to_loot_items_only(npc)
npc:give_info_portion("npcx_beh_gather_items")
npc:disable_info_portion("npcx_beh_loot_corpses")
end
function set_companion_to_loot_corpses_only(npc)
npc:disable_info_portion("npcx_beh_gather_items")
npc:give_info_portion("npcx_beh_loot_corpses")
end
function set_companion_to_loot_nothing(npc)
npc:disable_info_portion("npcx_beh_gather_items")
npc:disable_info_portion("npcx_beh_loot_corpses")
end
function set_companion_to_stay_close(npc)
npc:disable_info_portion("npcx_beh_distance_far")
end
function set_companion_to_stay_far(npc)
npc:give_info_portion("npcx_beh_distance_far")
end
-- Switch
function switch_companion_distance(npc)
if ( npc:has_info("npcx_beh_distance_far") ) then
npc:disable_info_portion("npcx_beh_distance_far")
else
npc:give_info_portion("npcx_beh_distance_far")
end
end
function switch_companion_patrol_mode(npc)
if ( npc:has_info("npcx_beh_patrol_mode") ) then
npc:disable_info_portion("npcx_beh_patrol_mode")
else
npc:give_info_portion("npcx_beh_patrol_mode")
end
end
function switch_companion_gather_items(npc)
if ( npc:has_info("npcx_beh_gather_items") ) then
actor_menu.set_msg(1, game.translate_string("st_disable_looting"),8)
npc:disable_info_portion("npcx_beh_gather_items")
else
actor_menu.set_msg(1, game.translate_string("st_enable_looting"),8)
npc:give_info_portion("npcx_beh_gather_items")
end
end
function switch_companion_loot_corpses(npc)
if ( npc:has_info("npcx_beh_loot_corpses") ) then
npc:disable_info_portion("npcx_beh_loot_corpses")
else
npc:give_info_portion("npcx_beh_loot_corpses")
end
end
function companion_remove_waypoints(npc)
npc:disable_info_portion("npcx_beh_patrol_mode")
local i = 1
local p = se_load_var(npc:id(),npc:name(),"pathpoint"..i)--load_var(npc,"pathpoint"..tostring(i))
while p do
se_save_var(npc:id(),npc:name(),"pathpoint"..i,nil)--save_var(npc,"pathpoint"..tostring(i),nil)
i = i + 1
p = se_load_var(npc:id(),npc:name(),"pathpoint"..i)--load_var(npc,"pathpoint"..tostring(i))
end
end
function companion_add_waypoints(npc,pos)
local i = 1
local p = se_load_var(npc:id(),npc:name(),"pathpoint"..i) --load_var(npc,"pathpoint"..tostring(i))
while p do
i = i + 1
p = se_load_var(npc:id(),npc:name(),"pathpoint"..i) --load_var(npc,"pathpoint"..tostring(i))
end
local pos = db.actor:position()
local s = "5000,patrol | pos:"..pos.x..","..pos.y..","..pos.z
se_save_var(npc:id(),npc:name(),"pathpoint"..i,s) --save_var(npc,"pathpoint"..tostring(i),s)
end
-- Get mode
function get_companion_combat_mode(npc)
local group = 1
if (hg_companion) then
group = hg_companion:return_group()
end
if (not npc) then
return _current_states["squad_"..group]["combat"]
else
return npc:has_info("npcx_beh_ignore_combat") and 2 or 1
end
end
function get_companion_move_mode(npc)
local group = 1
if (hg_companion) then
group = hg_companion:return_group()
end
if (not npc) then
return _current_states["squad_"..group]["move"]
else
return npc:has_info("npcx_beh_wait") and 1 or 2
end
end
function get_companion_stealth_mode(npc)
local group = 1
if (hg_companion) then
group = hg_companion:return_group()
end
if (not npc) then
return _current_states["squad_"..group]["stealth"]
else
return npc:has_info("npcx_beh_substate_stealth") and 1 or 2
end
end
function get_companion_distance_mode(npc)
local group = 1
if (hg_companion) then
group = hg_companion:return_group()
end
if (not npc) then
return _current_states["squad_"..group]["distance"]
else
return npc:has_info("npcx_beh_distance_far") and 2 or 1
end
end
function get_companion_loot_mode(npc)
local group = 1
if (hg_companion) then
group = hg_companion:return_group()
end
if (not npc) then
return _current_states["squad_"..group]["loot"]
else
return npc:has_info("npcx_beh_gather_items") and 1 or 2
end
end
-- Individual
function set_companion_squad_move_mode(mode,npc,squad)
local t = {"set_companion_to_wait_state","set_companion_to_follow_state"}
local f = this[t[mode]]
if (f) then
squad = squad or get_object_squad(npc)
if (squad and squad.commander_id) then
if not (axr_task_manager.hostages_by_id[squad:commander_id()]) then
for k in squad:squad_members() do
st = db.storage[k.id]
if (k.id == squad:commander_id()) then
if (st and st.beh) then
st.beh.rally_lvid = nil
end
end
local member = st and st.object
if (member and member:alive()) then
f(member)
end
end
end
end
end
end
function set_companion_squad_combat_mode(mode,npc,squad)
local t = {"set_companion_to_attack_state","set_companion_to_ignore_combat_state","set_companion_to_attack_only_actor_combat_enemy_state"}
local f = this[t[mode]]
if (f) then
squad = squad or get_object_squad(npc)
if (squad and squad.commander_id) then
if not (axr_task_manager.hostages_by_id[squad:commander_id()]) then
for k in squad:squad_members() do
local member = db.storage[k.id] and db.storage[k.id].object
if (member and member:alive()) then
f(member)
end
end
end
end
end
end
function set_companion_squad_stealth_mode(mode,npc,squad)
local t = {"set_companion_to_stealth_substate","set_companion_to_default_substate"}
local f = this[t[mode]]
if (f) then
squad = squad or get_object_squad(npc)
if (squad and squad.commander_id) then
if not (axr_task_manager.hostages_by_id[squad:commander_id()]) then
for k in squad:squad_members() do
local member = db.storage[k.id] and db.storage[k.id].object
if (member and member:alive()) then
f(member)
end
end
end
end
end
end
function set_companion_squad_loot_mode(mode,npc,squad)
local t = {"set_companion_to_loot_items_and_corpses","set_companion_to_loot_nothing"}
local f = this[t[mode]]
if (f) then
squad = squad or get_object_squad(npc)
if (squad and squad.commander_id) then
-- if not (axr_task_manager.hostages_by_id[squad:commander_id()]) then
for k in squad:squad_members() do
local member = db.storage[k.id] and db.storage[k.id].object
if (member and member:alive()) then
f(member)
end
end
-- end
end
end
end
function set_companion_squad_distance_mode(mode,npc,squad)
local t = {"set_companion_to_stay_close","set_companion_to_stay_far"}
local f = this[t[mode]]
if (f) then
squad = squad or get_object_squad(npc)
if (squad and squad.commander_id) then
if not (axr_task_manager.hostages_by_id[squad:commander_id()]) then
for k in squad:squad_members() do
local member = db.storage[k.id] and db.storage[k.id].object
if (member and member:alive()) then
f(member)
end
end
end
end
end
end
-- All
function cycle_companions_combat_mode(force_mode, no_msg)
-- local t = {"set_companion_to_attack_state","set_companion_to_ignore_combat_state","set_companion_to_attack_only_actor_combat_enemy_state"}
local t = {"set_companion_to_attack_state","set_companion_to_ignore_combat_state"}
cycle_state( t, "combat", true, force_mode, no_msg )
end
function cycle_companions_move_mode(force_mode, no_msg)
local t = {"set_companion_to_wait_state","set_companion_to_follow_state"}
cycle_state( t, "move", true, force_mode, no_msg )
end
function cycle_companions_stealth_mode(force_mode, no_msg)
local t = {"set_companion_to_stealth_substate","set_companion_to_default_substate"}
cycle_state( t, "stealth", true, force_mode, no_msg )
end
function cycle_companions_loot_mode(force_mode, no_msg)
-- local t = {"set_companion_to_loot_items_and_corpses","set_companion_to_loot_items_only","set_companion_to_loot_corpses_only","set_companion_to_loot_nothing"}
local t = {"set_companion_to_loot_items_and_corpses","set_companion_to_loot_nothing"}
cycle_state( t, "loot", true, force_mode, no_msg )
end
function cycle_companions_distance_mode(force_mode, no_msg)
local t = {"set_companion_to_stay_close","set_companion_to_stay_far"}
cycle_state( t, "distance", false, force_mode, no_msg )
end
function cycle_state(states, key, include_hostages, force_mode, no_msg)
local group = 1
if (hg_companion) then
group = hg_companion:return_group()
end
if (_current_states["squad_"..group][key] + 1 > #states) then
_current_states["squad_"..group][key] = 1
else
_current_states["squad_"..group][key] = _current_states["squad_"..group][key] + 1
end
_current_states["squad_"..group][key] = force_mode or _current_states["squad_"..group][key]
local next_state = states[_current_states["squad_"..group][key]]
if (not no_msg) then
actor_menu.set_msg(1, game.translate_string("st_"..next_state),8)
end
if (group == 0) then
_current_states["squad_1"][key] = _current_states["squad_"..group][key]
_current_states["squad_2"][key] = _current_states["squad_"..group][key]
_current_states["squad_3"][key] = _current_states["squad_"..group][key]
end
local f = this[next_state]
if (f) then
for id,squad in pairs(companion_squads) do
if (squad and squad.commander_id) then
if include_hostages or (not axr_task_manager.hostages_by_id[squad:commander_id()]) then
for k in squad:squad_members() do
if (group == 0 or group == se_load_var(k.id,k.object:name(),"companion_hg")) then
local st = db.storage[k.id]
local npc = st and st.object
if (npc and npc:alive()) then
if (group == 0 or group == se_load_var(npc:id(),npc:name(),"companion_hg")) then
if (npc:has_info("npcx_beh_relax")) then
npc:disable_info_portion("npcx_beh_relax")
xr_sound.stop_sounds_by_id(npc:id())
end
end
end
if (key == "move") and (k.id == squad:commander_id()) then
if (st and st.beh) then
st.beh.rally_lvid = nil
end
end
local member = st and st.object
if (member and member:alive()) then
f(member)
end
end
end
end
end
end
end
-- squad update
if (hg_companion) then hg_companion:hg_squad_update() end
end
function move_to_point(p)
local group = 1
if (hg_companion) then
group = hg_companion:return_group()
end
local lvid
if (p==1) then
actor_menu.set_msg(1, game.translate_string("st_hold_then_release"),8)
--_draw_part = particles_object("weapons\\light_signal")
_draw_part = particles_object("_samples_particles_\\flash_light")
--_draw_part = particles_object("static\\net_baspda_blue")
elseif (p==2) then
if (_draw_part) then
local r = level.get_target_dist and level.get_target_dist()
if (r) then
lvid = level.vertex_in_direction(level.vertex_id(device().cam_pos),device().cam_dir,r)
local pos = level.vertex_position(lvid)
--if not(_draw_part:playing()) then
_draw_part:play_at_pos(vector():set(pos.x,pos.y-0.5,pos.z))
--else
-- _draw_part:move_to(pos,pos)
--end
end
end
else
if (_draw_part) then
_draw_part:stop()
_draw_part = nil
end
local r = level.get_target_dist and level.get_target_dist()
if (r) then
lvid = level.vertex_in_direction(level.vertex_id(device().cam_pos),device().cam_dir,r)
if (lvid) then
_current_states["squad_"..group]["move"] = 1
actor_menu.set_msg(1, game.translate_string("st_move_to_point"),8)
for id,squad in pairs(companion_squads) do
if (squad and squad.commander_id) then
for k in squad:squad_members() do
if (group == 0 or group == se_load_var(k.id,k.object:name(),"companion_hg")) then
local st = db.storage[k.id]
local npc = st and st.object
if (npc and npc:alive()) then
if (group == 0 or group == se_load_var(npc:id(),npc:name(),"companion_hg")) then
if (npc:has_info("npcx_beh_relax")) then
npc:disable_info_portion("npcx_beh_relax")
xr_sound.stop_sounds_by_id(npc:id())
end
end
end
local member = st and st.object
if (member and member:alive()) then
set_companion_to_follow_state(member)
save_var(member,"fight_from_point",lvid)
end
if (not axr_task_manager.hostages_by_id[k.id]) then
if (st and st.beh) then
st.beh.rally_lvid = lvid
end
end
end
end
end
end
end
end
end
end
--------------------------------------------------
-- HG Companion
--------------------------------------------------
function companion_relax()
local group = 0
if (hg_companion) then
group = hg_companion:return_group()
end
if (group == 0) then
_current_states["squad_0"]["move"] = 1
_current_states["squad_1"]["move"] = 1
_current_states["squad_2"]["move"] = 1
_current_states["squad_3"]["move"] = 1
else
_current_states["squad_"..group]["move"] = 1
end
end
--------------------------------------------------
-- Inventory
--------------------------------------------------
function compare_smaller(a,b) return a[2] < b[2] end
function get_inventory_table(npc, npc_id, mode)
npc = npc or get_object_by_id(npc_id)
if (not npc) then
printf("!ERROR axr_companions.get_inventory_table | no npc object recieved")
return {}
end
npc_id = npc_id or npc:id()
local t = {}
-- Collect equipped items and usable ammo
local ignore_ids = {}
local ignore_sections = {}
if (mode == 3) then
for i=1,13 do
if (i ~= 4) then
local itm_slot = npc:item_in_slot(i)
if (itm_slot) then
ignore_ids[itm_slot:id()] = true
end
end
end
for i=1,3 do
local wpn = npc:item_in_slot(i)
if (wpn) then
local ammos = utils_item.get_ammo(wpn:section(), wpn:id(), true)
for sec,_ in pairs(ammos) do
ignore_sections[sec] = true
end
end
end
end
-- Iterate through NPC inventory to collect items
local function iterate(owner,item)
local item_id = item:id()
local item_sec = item:section()
-- Mode 1: player's assigned items
if mode == 1 then
if is_assigned_item(npc_id,item_id) then
t[item_id] = item_sec
elseif IsWeapon(item) and se_load_var(item_id, nil, "strapped_item") then -- Include player's strapped weapons
t[item_id] = item_sec
end
-- Mode 2: all excluding technical items
elseif (mode == 2) or (mode == 3) then
if ini_sys:r_bool_ex(item_sec,"can_trade")
and (not ini_sys:r_bool_ex(item_sec,"quest_item"))
and (not IsItem("anim", item_sec))
and (not IsBolt(item))
then
-- Mode 3: exclude items on belt and ammo
if (mode == 3) then
if (not ignore_ids[item_id])
and (not ignore_sections[item_sec])
and (not owner:is_on_belt(item))
then
t[item_id] = item_sec
end
else
t[item_id] = item_sec
end
end
-- No mode: all items
else
t[item_id] = item_sec
end
end
npc:iterate_inventory(iterate,npc)
return t
end
function transfer_item(item_id, npc_from, npc_to, no_snd)
local item = level.object_by_id(item_id)
if not (item and npc_from and npc_to) then
printe('ERROR axr_companions.trasnfer_item | no object recieved | item: %s - npc_from: %s - npc_to: %s', (item and true), (npc_from and true), (npc_to and true))
return
end
local id_from = npc_from:id()
local id_to = npc_to:id()
-- from player to companion
if (id_from == AC_ID) then
if not assigned_items[id_to] then
assigned_items[id_to] = {}
end
assigned_items[id_to][item_id] = true
-- from companion to player
else
if assigned_items[id_from] and assigned_items[id_from][item_id] then
assigned_items[id_from][item_id] = nil
if is_empty(assigned_items[id_from]) then
assigned_items[id_from] = nil
end
elseif IsWeapon(item) and se_load_var(item_id, nil, "strapped_item") then -- Include player's strapped weapons
--
else
printf('~ axr_companions.trasnfer_item | item [%s] is not assigned to companion [%s] | assigned companion: %s - assigned item: %s', item:name(), npc_from:name(), is_assigned_companion(npc_from), is_assigned_item(id_from,item_id))
return
end
end
-- transfer item
npc_from:transfer_item(item, npc_to)
-- play sound effect
if (not no_snd) then
itms_manager.play_item_sound(item)
end
end
function transfer_all_item(npc_from, npc_to)
local id_from = npc_from:id()
local mode = (id_from == AC_ID) and 3 or 1
-- collect inventory items
local inv = get_inventory_table(npc_from, id_from, mode)
if is_empty(inv) then
return
end
-- transfer items
for id,sec in pairs(inv) do
transfer_item(id, npc_from, npc_to, true)
end
-- play sound effect
utils_obj.play_sound("interface\\items\\inv_items_take_all")
end
function is_assigned_item(npc_id, item_id)
if not (npc_id and item_id) then
printf('! is_assigned_item: one or more arguments nil: %s %s',npc_id, item_id)
return false
end
if assigned_items[npc_id] then
return assigned_items[npc_id][item_id]
end
return false
end
function is_overweight(npc, npc_id, comp_weight)
--return utils_item.is_overweight(npc, npc_id)
npc = npc or get_object_by_id(npc_id)
if (not npc) then
return
end
npc_id = npc_id or npc:id()
local max_weight = 30
local curr_weight = 0
if assigned_items[npc_id] then
for id,_ in pairs(assigned_items[npc_id]) do
local obj = level.object_by_id(id)
if obj then
curr_weight = curr_weight + obj:weight()
end
end
end
if comp_weight then
return (comp_weight > (max_weight - curr_weight))
end
return curr_weight > max_weight, curr_weight, max_weight
end
function get_inventory_weight(tbl_inv)
local inv_weight = 0
for id,sec in pairs(tbl_inv) do
inv_weight = inv_weight + (ini_sys:r_float_ex(sec,"inv_weight") or 0)
end
return inv_weight
end
function OnItemBeforeMove(flags, npc_id, obj, mode, bag_from)
if (mode ~= "loot") or (bag_from ~= EDDListType.iActorBag) then
return
end
--if not (npc_id and non_task_companions[npc_id]) then
if not (npc_id and se_load_var(npc_id, nil, "companion")) then
return
end
local overweight, a, b = is_overweight(nil, npc_id) --utils_item.is_overweight(nil, npc_id)
if overweight then
flags.ret_value = false
end
end
function OnItemAfterMove(npc_id, obj, mode, bag_from)
if (mode ~= "loot") then
return
end
--if not (npc_id and non_task_companions[npc_id]) then
if not (npc_id and se_load_var(npc_id, nil, "companion")) then
return
end
local item_id = obj:id()
-- Move from actor to NPC
if (bag_from == EDDListType.iActorBag) then
if not assigned_items[npc_id] then
assigned_items[npc_id] = {}
end
assigned_items[npc_id][item_id] = true
-- Move from NPC to actor
elseif (bag_from == EDDListType.iDeadBodyBag) then
if assigned_items[npc_id] and assigned_items[npc_id][item_id] then
assigned_items[npc_id][item_id] = nil
if is_empty(assigned_items[npc_id]) then
assigned_items[npc_id] = nil
end
end
end
end
-- dialog
function start_trade(a,b)
local npc = a:id() > 0 and a or b
ui_companion_inv.start(npc)
end
function is_assigned_companion(a,b)
local npc = a:id() > 0 and a or b
local npc_id = npc and npc:id()
if not (npc_id) then
printf('! is_assigned_companion: id is nil: %s %s',npc_id)
return false
end
return assigned_items[npc_id] and true or false
end
function give_all_to_companion(a,b)
local npc = (a:id() ~= AC_ID) and a or b
transfer_all_item(db.actor, npc)
end
function take_all_from_companion(a,b)
local npc = (a:id() ~= AC_ID) and a or b
transfer_all_item(npc, db.actor)
end
function can_handle_all_items(a,b)
local npc = (a:id() ~= AC_ID) and a or b
-- actor inventory weight
local inv = get_inventory_table(db.actor, AC_ID, 3)
local inv_weight = get_inventory_weight(inv)
-- npc carry weight
local over_weight, tot_weight, max_weight = is_overweight(npc)
return (inv_weight <= (max_weight - tot_weight))
end
function can_not_handle_all_items(a,b)
return (not can_handle_all_items(a,b))
end
--------------------------------------------------
-- keybinding
--------------------------------------------------
axr_keybind.bind("kCUSTOM3",function(p)
move_to_point(p)
end)
function on_key_release(key)
local bind = dik_to_bind(key)
if (bind == key_bindings.kCUSTOM18) then
start_CW()
elseif (bind == key_bindings.kCUSTOM1) then
cycle_companions_combat_mode()
elseif (bind == key_bindings.kCUSTOM2) then
cycle_companions_move_mode()
elseif (bind == key_bindings.kCUSTOM3) then
--move_to_point(p)
elseif (bind == key_bindings.kCUSTOM4) then
cycle_companions_stealth_mode()
elseif (bind == key_bindings.kCUSTOM5) then
cycle_companions_loot_mode()
end
end
--------------------------------------------------
-- Companions Wheel
--------------------------------------------------
local max_trade_distance = 8 --[m]
local cw_cooldown = 0 --[ms]
local cw_commands = {}
function cw_prepare()
cw_commands["combat"] = { key= "DIK_1" ,on_state= 1 ,get= get_companion_combat_mode ,set_one= set_companion_squad_combat_mode ,set_all= cycle_companions_combat_mode ,on_str= "st_set_companion_to_attack_state" ,off_str= "st_set_companion_to_ignore_combat_state" }
cw_commands["movement"] = { key= "DIK_2" ,on_state= 2 ,get= get_companion_move_mode ,set_one= set_companion_squad_move_mode ,set_all= cycle_companions_move_mode ,on_str= "st_set_companion_to_follow_state" ,off_str= "st_set_companion_to_wait_state" }
cw_commands["stealth"] = { key= "DIK_3" ,on_state= 1 ,get= get_companion_stealth_mode ,set_one= set_companion_squad_stealth_mode ,set_all= cycle_companions_stealth_mode ,on_str= "st_set_companion_to_stealth_substate" ,off_str= "st_set_companion_to_default_substate" }
cw_commands["distance"] = { key= "DIK_4" ,on_state= 2 ,get= get_companion_distance_mode ,set_one= set_companion_squad_distance_mode ,set_all= cycle_companions_distance_mode ,on_str= "st_set_companion_to_stay_far" ,off_str= "st_set_companion_to_stay_close" }
cw_commands["loot"] = { key= "DIK_5" ,on_state= 1 ,get= get_companion_loot_mode ,set_one= set_companion_squad_loot_mode ,set_all= cycle_companions_loot_mode ,on_str= "st_set_companion_to_loot_items_and_corpses" ,off_str= "st_set_companion_to_loot_nothing" }
cw_commands["trade"] = { key= "DIK_6" ,on_state= 1 ,get= companion_inventory_mode ,set_one= start_trade ,off_str= "st_set_companion_to_trade" }
end
function get_nearby_companion()
local obj = level.get_target_obj()
if obj and obj:has_info("npcx_is_companion") and (distance_between(db.actor,obj) <= max_trade_distance) then
return obj
elseif is_not_empty(companion_squads) then
return true
end
return false
end
function companion_inventory_mode(npc)
return npc and 1 or 2
end
-------------------------------------------------------------------
GUI = nil -- instance, don't touch
function start_CW()
local obj = get_nearby_companion()
if (not obj) then
actor_menu.set_msg(1, game.translate_string("st_no_companions"),8)
return
end
hide_hud_inventory()
if (not GUI) then
GUI = UIWheelCompanion()
end
if (GUI) and (not GUI:IsShown()) then
GUI:ShowDialog(true)
if obj == true then
GUI:Reset()
else
GUI:Reset(obj)
end
cw_cooldown = time_global()
Register_UI("UIWheelCompanion","ui_wheel_companion")
end
end
class "UIWheelCompanion" (CUIScriptWnd)
function UIWheelCompanion:__init() super()
self.clr_on = GetARGB(255, 255, 255, 255)
self.clr_off = GetARGB(70, 255, 255, 255)
self.states = {}
if is_empty(cw_commands) then
cw_prepare()
end
self:InitControls()
self:InitCallBacks()
end
function UIWheelCompanion:__finalize()
end
function UIWheelCompanion:InitControls()
self:SetWndRect (Frect():set(0,0,1024,768))
self:SetAutoDelete(true)
self:AllowMovement(true)
self.xml = CScriptXmlInit()
local xml = self.xml
xml:ParseFile ("ui_wheel_companion.xml")
-- Main
self.dialog = xml:InitStatic("wheel", self)
xml:InitStatic("wheel:background", self.dialog)
-- Buttons
self.cmd_pic = {}
self.cmd_btn = {}
for cmd,v in pairs(cw_commands) do
self.cmd_pic[cmd] = xml:InitStatic("wheel:box_" .. cmd, self.dialog)
self.cmd_pic[cmd]:InitTexture( "ui_companion_" .. cmd )
self.cmd_pic[cmd]:SetStretchTexture(true)
--self.cmd_pic[cmd]:SetTextureColor( self.clr_off )
self.cmd_btn[cmd] = xml:Init3tButton("wheel:btn_" .. cmd, self.dialog)
self:Register(self.cmd_btn[cmd],"btn_" .. cmd)
end
-- Text
self.hint = xml:InitTextWnd("wheel:hint", self.dialog)
-- Extended
self.dialog_info = xml:InitStatic("info", self)
xml:InitStatic("info:background", self.dialog_info)
-- Icon and name
self.icon = xml:InitStatic("info:icon", self.dialog_info)
self.name = xml:InitTextWnd("info:name", self.dialog_info)
-- Health
self.dialog_health = xml:InitStatic("info:health", self.dialog_info)
xml:InitStatic("info:health:caption", self.dialog_health)
xml:InitStatic("info:health:prog_back", self.dialog_health)
self.prog_health = xml:InitProgressBar("info:health:value", self.dialog_health)
self.prog_health:Show(true)
self.prog_health:SetProgressPos(0.0)
-- Carry weight
self.dialog_weight = xml:InitStatic("info:weight", self.dialog_info)
xml:InitStatic("info:weight:caption", self.dialog_weight)
xml:InitStatic("info:weight:prog_back", self.dialog_weight)
self.prog_weight = xml:InitProgressBar("info:weight:value", self.dialog_weight)
self.prog_weight:Show(true)
self.prog_weight:SetProgressPos(0.0)
end
function UIWheelCompanion:InitCallBacks()
for cmd,v in pairs(cw_commands) do
local _wrapper = function(handler) -- we need wrapper in order to pass ctrl to method
self:Order(cmd)
end
self:AddCallback("btn_" .. cmd, ui_events.BUTTON_CLICKED, _wrapper, self)
end
end
function UIWheelCompanion:Reset(npc)
self.id = npc and npc:id() or false
for cmd,v in pairs(cw_commands) do
local state = v.get(npc)
self.cmd_pic[cmd]:SetTextureColor( state == v.on_state and self.clr_on or self.clr_off )
self.states[cmd] = (state == v.on_state)
end
self.dialog_info:Show(npc and true or false)
if (npc) then
local overweight, w_total, w_max = is_overweight(npc) --utils_item.is_overweight(npc)
self.icon:InitTexture( npc:character_icon() )
self.name:SetText( npc:character_name() )
self.prog_health:SetProgressPos( npc.health )
self.prog_weight:SetProgressPos( w_total / w_max )
end
end
function UIWheelCompanion:Update()
CUIScriptWnd.Update(self)
for cmd,element in pairs(self.cmd_btn) do
if element:IsCursorOverWindow() then
local state = self.states[cmd]
local str = state and cw_commands[cmd].off_str or cw_commands[cmd].on_str
self.hint:SetText(str and game.translate_string(str) or "")
return
end
end
self.hint:SetText("")
end
function UIWheelCompanion:Order(cmd)
--printf(cmd)
local obj
if self.id then
obj = db.storage[self.id] and db.storage[self.id].object or level.object_by_id(self.id)
end
local state = cw_commands[cmd].get(obj)
local next_state = (state == 2) and 1 or 2
if obj then
if cw_commands[cmd].set_one then
if cmd == "trade" then
cw_commands[cmd].set_one(obj)
else
cw_commands[cmd].set_one(next_state,obj)
end
end
elseif cw_commands[cmd].set_all then
cw_commands[cmd].set_all()
end
self:Close()
end
function UIWheelCompanion:OnKeyboard(dik, keyboard_action)
local res = CUIScriptWnd.OnKeyboard(self,dik,keyboard_action)
if (res == false) then
if keyboard_action == ui_events.WINDOW_KEY_RELEASED then
local bind = dik_to_bind(dik)
if (time_global() > cw_cooldown + 100) and (bind == key_bindings.kQUIT or bind == key_bindings.kUSE or bind == key_bindings.kCUSTOM18) then
self:Close()
return
end
for cmd,v in pairs(cw_commands) do
if v.key and (dik == DIK_keys[v.key]) then
self:Order(cmd)
pass = false
break
end
end
end
end
return res
end
function UIWheelCompanion:Close()
if self:IsShown() then
self:HideDialog()
self:Show(false)
Unregister_UI("UIWheelCompanion")
end
end
--------------------------------------------------
-- Companions List
--------------------------------------------------
HUD = nil
function activate_hud()
RegisterScriptCallback("actor_on_net_destroy",actor_on_net_destroy)
RegisterScriptCallback("on_console_execute",on_console_execute)
RegisterScriptCallback("GUI_on_show",update_hud)
RegisterScriptCallback("GUI_on_hide",update_hud)
if HUD == nil then
HUD = UICompanionList()
get_hud():AddDialogToRender(HUD)
end
end
function deactivate_hud()
if HUD ~= nil then
get_hud():RemoveDialogToRender(HUD)
HUD = nil
end
UnregisterScriptCallback("actor_on_net_destroy",actor_on_net_destroy)
UnregisterScriptCallback("on_console_execute",on_console_execute)
UnregisterScriptCallback("GUI_on_show",update_hud)
UnregisterScriptCallback("GUI_on_hide",update_hud)
end
function update_hud()
if HUD ~= nil then
HUD:Update(true)
end
end
function actor_on_net_destroy()
if HUD ~= nil then
get_hud():RemoveDialogToRender(HUD)
HUD = nil
end
end
function on_console_execute(name)
if name == "hud_draw" and HUD then
HUD:Update(true)
end
end
---------------------------------------
class "UICompanionList" (CUIScriptWnd)
function UICompanionList:__init() super()
self.companion_info = {}
self._tmr = time_global()
self.update_rate = 1000 --[ms]
self.clr_list = {
["def"] = GetARGB(255,255,255,255),
["stalker"] = GetARGB(255,255,255,100),
["bandit"] = GetARGB(255,120,201,79),
["ecolog"] = GetARGB(255,255,128,100),
["csky"] = GetARGB(255,100,200,255),
["dolg"] = GetARGB(255,255,100,100),
["freedom"] = GetARGB(255,100,255,100),
["killer"] = GetARGB(255,100,100,255),
["army"] = GetARGB(255,100,128,255),
["monolith"]= GetARGB(255,120,201,79),
}
self:InitControls()
end
function UICompanionList:__finalize()
end
function UICompanionList:InitControls()
local xml = utils_xml.get_hud_xml()
self.dialog = xml:InitStatic("companion_list", self)
--utils_xml.correct_ratio(self.dialog, true)
self.companion_info = {}
for i=1,8 do
self.companion_info[i] = {}
self.companion_info[i].base = xml:InitStatic("companion_list:slot", self.dialog)
self.companion_info[i].background = xml:InitStatic("companion_list:slot:background", self.companion_info[i].base)
self.companion_info[i].icon = xml:InitStatic("companion_list:slot:icon", self.companion_info[i].base)
self.companion_info[i].danger_indicator = xml:InitStatic("companion_list:slot:danger_indicator", self.companion_info[i].base)
self.companion_info[i].team_role_shadow = xml:InitStatic("companion_list:slot:team_role", self.companion_info[i].base)
self.companion_info[i].team_role = xml:InitStatic("companion_list:slot:team_role", self.companion_info[i].base)
self.companion_info[i].distance = xml:InitTextWnd("companion_list:slot:distance", self.companion_info[i].base)
self.companion_info[i].prog_health = xml:InitProgressBar("companion_list:slot:health", self.companion_info[i].base)
for _,ele in pairs(self.companion_info[i]) do
utils_xml.correct_ratio(ele)
end
local h = self.companion_info[i].background:GetHeight()
self.companion_info[i].distance:SetFont(GetFontSmall())
self.companion_info[i].base:SetWndPos( vector2():set( 0 , (8-i)*(h+10) ) )
local pos = self.companion_info[i].team_role:GetWndPos()
self.companion_info[i].team_role_shadow:SetWndPos( vector2():set( pos.x + 1 , pos.y + 2 ) )
self.companion_info[i].team_role_shadow:SetTextureColor( GetARGB(255, 0, 0, 0) )
end
-- HG
local hud = false
if (hg_companion) then
hud = hg_companion:return_hud()
end
if (self.companion_info[1] and hg_companion and hud == true) then
xml = hg_companion:get_hg_xml()
self.dialog = xml:InitStatic("hg_ui_companion", self)
self.companion_info[0] = {}
self.companion_info[0].base = xml:InitStatic("hg_ui_companion:slot", self.dialog)
self.companion_info[0].background = xml:InitStatic("hg_ui_companion:slot:background", self.companion_info[0].base)
self.companion_info[0].move = xml:InitStatic("hg_ui_companion:slot:move", self.companion_info[0].base)
self.companion_info[0].group = xml:InitStatic("hg_ui_companion:slot:group", self.companion_info[0].base)
for _,hg in pairs(self.companion_info[0]) do
utils_xml.correct_ratio(hg)
end
local h = self.companion_info[1].background:GetHeight()
self.companion_info[0].base:SetWndPos( vector2():set( 0 , (8)*(h+10) ) )
end
end
function UICompanionList:Update(force)
CUIScriptWnd.Update(self)
local tg = time_global()
if force then
self._tmr = tg - 1
end
if self._tmr >= tg then
return
end
self._tmr = tg + self.update_rate
local to_show = main_hud_shown()
local clist = list_actor_squad_by_id()
local count = 0
for i=1,8 do
local se_obj = clist[i] and alife_object(clist[i])
if to_show and (se_obj and IsStalker(nil,se_obj:clsid()) and se_obj:alive()) then
local st = db.storage[se_obj.id]
local npc = st and st.object
local ele = self.companion_info[i]
-- Icon
local icon_name = npc and npc:character_icon() or se_obj:character_icon()
icon_name = icon_name and icon_name ~= "" and icon_name or "ui\\ui_noise"
ele.icon:InitTexture(icon_name)
-- Distance
ele.distance:SetText(string.format("%.2f M", se_obj.position:distance_to(db.actor:position())))
-- Leader and relation status
local squad = se_obj.group_id and se_obj.group_id ~= 65535 and alife_object(se_obj.group_id)
if (squad and squad:commander_id() == se_obj.id) then
ele.team_role:InitTexture("ui_minimap_squad_leader")
ele.team_role_shadow:InitTexture("ui_minimap_squad_leader")
else
ele.team_role:InitTexture("ui_minimap_point")
ele.team_role_shadow:InitTexture("ui_minimap_point")
end
local community = npc and npc:character_community()
local clr = community and self.clr_list[community] or self.clr_list["def"]
ele.team_role:SetTextureColor(clr)
-- Health
if (npc) then
local health = clamp( round_idp(npc.health,1),0,1)
ele.prog_health:SetProgressPos( health )
end
-- Danger state
ele.danger_indicator:Show(npc and npc:best_enemy() and true or false)
self.companion_info[i].base:Show(true)
count = count + 1
else
self.companion_info[i].base:Show(false)
end
end
-- HG
local hud = false
if (hg_companion) then
hud = hg_companion:return_hud()
end
local hg = self.companion_info[0]
if (hg ~=nil) then
if (to_show and count > 0 and hud == true) then
local move = 0
local group = 0
if (hg_companion) then
move = hg_companion:return_movement()
group = hg_companion:return_group()
end
hg.move:InitTexture("hg_ui_move_"..move)
hg.group:InitTexture("hg_ui_group_"..group)
hg.base:Show(true)
else
hg.base:Show(false)
end
end
end