Divergent/mods/Zone Customization Project/gamedata/scripts/sim_squad_scripted.script

1454 lines
46 KiB
Plaintext

--'******************************************************
-- Edited by Alundaio
--
-- edited 1/14/2016: Added ability to create squad npcs without specifying a smart
--'******************************************************
-- Current Action: 0 - Reach Target | 1 - Stay Point
local STAY_POINT_IDLE_MIN = 2*60*60
local STAY_POINT_IDLE_MAX = 8*60*60
dbg_map_hud = false
local excl_dist = ui_options.get("alife/general/excl_dist")
-- setup location types
local location_types = {}
do -- to localize below
local locations_ini = ini_file("misc\\smart_terrain_masks.ltx")
local function itr(section)
local a,key,val = locations_ini:r_line_ex(section,0,"","")
location_types[key] = key
return false
end
locations_ini:section_for_each(itr)
end
--------------------------------------------------------------
-- INITIALIZATION
--------------------------------------------------------------
class "sim_squad_scripted" (cse_alife_online_offline_group)
function sim_squad_scripted:__init(section) super (section)
self.smart_id = nil
self.current_spot_id = nil
self.current_action = nil
self.current_target_id = nil
self.assigned_target_id = nil
self.rush = nil
self.item_on_all = nil
self:init_squad()
--if (USE_MARSHAL) then
-- RegisterScriptCallback("save_state",self.save_state,self)
--end
self.first_update = false
end
function sim_squad_scripted:init_squad()
--utils_data.debug_write("sim_squad_scripted:init_squad")
local settings_id = self:section_name()
self.player_id = ini_sys:r_string_ex(settings_id,"faction") or "stalker"
self.common = ini_sys:r_bool_ex(settings_id,"common")
self.action_condlist = ini_sys:r_string_to_condlist(settings_id,"target_smart")
self.death_condlist = ini_sys:r_string_to_condlist(settings_id,"on_death")
self.invul_condlist = ini_sys:r_string_to_condlist(settings_id,"invulnerability")
self.relationship_condlist = ini_sys:r_string_to_condlist(settings_id,"relationship")
if (self.relationship_condlist) then
local new_relation = xr_logic.pick_section_from_condlist(db.actor, self, self.relationship_condlist)
if (new_relation) and (new_relation == "enemy" or new_relation == "friend" or new_relation == "neutral") then
self.relationship = new_relation
end
end
local s = ini_sys:r_string_ex(settings_id, "target_random_smart")
if (s) then
self.random_targets = str_explode(s,",")
end
s = ini_sys:r_string_ex(settings_id, "item_on_all")
if (s) then
self.item_on_all = str_explode(s,",")
end
self.rush = ini_sys:r_bool_ex(settings_id, "rush")
self.idle_time = ini_sys:r_float_ex(settings_id,"idle_time") or math.random(STAY_POINT_IDLE_MIN,STAY_POINT_IDLE_MAX)
self.sympathy = ini_sys:r_float_ex(settings_id,"sympathy")
self.always_arrived = ini_sys:r_bool_ex(settings_id,"always_arrived",false)
self:set_location_types_section("squad_terrain")
self:set_location_types_section("stalker_terrain")
self:set_squad_sympathy()
end
function sim_squad_scripted:init_squad_on_load()
--utils_data.debug_write(self:name().."init_squad_on_load start")
self:set_squad_sympathy()
sim_board.get_sim_board():assign_squad_to_smart(self, self.smart_id)
self.current_action = 0
self.need_to_reset_location_masks = true
--utils_data.debug_write(self:name().."init_squad_on_load end")
end
--------------------------------------------------------------
-- SCRIPT_TARGET_SELECTION
--------------------------------------------------------------
function sim_squad_scripted:get_script_target()
--utils_data.debug_write("sim_squad_scripted:get_script_target")
local new_target
if (self.scripted_target) then
new_target = self.scripted_target
elseif (axr_task_manager.hostages_by_id[self:commander_id()]) then
return axr_task_manager.hostages_by_id[self:commander_id()]
elseif (self.random_targets) then
if (self.current_action == 0 and self.assigned_target_id == nil) then
new_target = self.random_targets[math.random(#self.random_targets)]
else
return self.assigned_target_id
end
elseif (self.action_condlist) then
new_target = xr_logic.pick_section_from_condlist(db.actor, self, self.action_condlist)
end
-- prevent companions from moving to actor if they cannot teleport and actor is not on same level
if (axr_companions.companion_squads[self.id]) then
local se_obj = alife_object(self:commander_id())
if (self.online ~= true) and (se_obj and se_load_var(se_obj.id,se_obj:name(),"companion_cannot_teleport")) then
return self.id
end
return 0
end
if (new_target == "actor") then
return 0
end
local smart = new_target and SIMBOARD.smarts_by_names[new_target]
if (smart) then
return smart.id
end
local new_id = tonumber(new_target)
local se_target = new_id and alife_object(new_id)
if se_target and (obj:clsid() == clsid.online_offline_group_s or obj:clsid() == clsid.smart_terrain) then
return new_id
end
end
function sim_squad_scripted:update()
--utils_data.debug_write("sim_squad_scripted:update START")
cse_alife_online_offline_group.update(self)
if not (self.first_update) then
self.first_update = true
--[[
local skip = false
local sim = alife()
local se_actor = sim:actor()
local se_obj = self:commander_id() and alife_object(self:commander_id())
if (se_obj) then
if (se_load_var(se_obj.id,se_obj:name(),"companion")) then
skip = true
elseif (se_load_var(se_obj.id,se_obj:name(),"do_not_deactivate")) then
skip = true
end
end
--]]
if self.rush then
self.rush_to_target = true
end
if self.item_on_all then
local sim = alife()
for k in self:squad_members() do
for kk,vv in pairs(self.item_on_all) do
local itm = alife_create_item(vv, alife_object(k.id))
-- if itm then printf('spawned %s on %s',vv,k.id) end
end
end
end
--[[ down below here is needed only if the feature to disable squads on non linked levels is true, which breaks lots of stuff
if (skip ~= true) then
local gg = game_graph()
--printf("m_game_vertex_id=%s actor.m_game_vertex_id=%s",self.m_game_vertex_id,se_actor.m_game_vertex_id)
if (se_actor and gg:valid_vertex_id(self.m_game_vertex_id) and gg:valid_vertex_id(se_actor.m_game_vertex_id)) then
local lvl = sim:level_name(gg:vertex(self.m_game_vertex_id):level_id())
local actor_level = sim:level_name(gg:vertex(se_actor.m_game_vertex_id):level_id())
-- Disable any squad that is not on actor's level and not linked to actor's level through ai_tweaks\simulation_objects.ltx
if (lvl ~= actor_level) and (self:commander_id() and not sim:has_info(self:commander_id(),"npcx_is_companion")) then
if not (string.find(simulation_objects.config:r_value(actor_level,"target_maps",0,""),lvl)) then
if (DEACTIVATE_SIM_ON_NON_LINKED_LEVELS) then
SIMBOARD.squads[self.id] = nil
if (self.smart_id) then
--SIMBOARD.tmp_entered_squad[self.smart_id] = nil
SIMBOARD.tmp_assigned_squad[self.smart_id] = nil
end
--simulation_objects.unregister(self)
self.disabled = true
--printf("%s disabled",self:name())
--utils_data.debug_write("sim_squad_scripted:update END 3")
return
end
end
end
end
end
--]]
SendScriptCallback("squad_on_first_update",self)
end
self:refresh()
if (self.disabled) then
--utils_data.debug_write("sim_squad_scripted:update END 4")
return
end
if self.need_to_reset_location_masks then
self:set_location_types()
self.need_to_reset_location_masks = false
end
self:check_online_status()
SendScriptCallback("squad_on_update",self)
local sim = alife()
local se_actor = sim:actor()
self.dist_to_actor = self.position:distance_to(se_actor.position)
self:check_invulnerability()
local script_target_id = self:get_script_target()
if (script_target_id) then
self:specific_update(script_target_id)
else
self:generic_update()
end
local cur_lvl = sim:level_name(game_graph():vertex(self.m_game_vertex_id):level_id())
if not (self.last_gvid == self.m_game_vertex_id) then
SendScriptCallback("squad_on_after_game_vertex_change",self, self.last_gvid, self.m_game_vertex_id, not (cur_lvl == self.level_name))
self.last_gvid = self.m_game_vertex_id
end
if not (cur_lvl == self.level_name) then
SendScriptCallback("squad_on_after_level_change",self, self.level_name, cur_lvl)
self.level_name = cur_lvl
end
--utils_data.debug_write("sim_squad_scripted:update END 2")
end
--------------------------------------------------------------
-- SIMULATION_TARGET_SELECTION
--------------------------------------------------------------
local function can_help_actor(squad)
if is_empty(xr_combat_ignore.fighting_with_actor_npcs) then
return false
end
if game_graph():vertex(squad.m_game_vertex_id):level_id() ~= get_player_level_id() then
return false
end
return false
end
local function get_help_target_id(squad)
if (smr_amain_mcm.get_config("smr_enabled") and smr_zombies_mcm.get_config("seek_enabled") and squad.online and squad.dist_to_actor <= smr_zombies_mcm.get_config("seek_distance")) then
if (squad.player_id == "zombied" or is_squad_monster[squad.player_id]) then
return 0
end
end
return squad.task_target_id
end
function sim_squad_scripted:specific_update(script_target_id) -- This update is called for all squads with a scripted smart_terrain target
--utils_data.debug_write(strformat("%s:specific_update",self:name()))
local se_target = alife_object(script_target_id)
if not (se_target) then
return
end
self.assigned_target_id = script_target_id
if (self.assigned_target_id == self.id) then
return
end
if (self.smart_id ~= self.assigned_target_id and se_target:clsid() == clsid.smart_terrain) then
self:assign_smart(se_target,self.smart_id)
end
if (self.current_action) then
-- in such case assigned is changed (such as get_help_target_id) but current action is 1 (reached)
if (self.current_action == 1 and self.assigned_target_id ~= self.current_target_id) then
self.current_action = 0
end
-- If current action is not finished, then return
if (self.current_action == 0) then
-- if not (self.online) then
-- TeleportSquad(self,se_target.position,se_target.m_level_vertex_id,se_target.m_game_vertex_id)
-- end
if (se_target:am_i_reached(self)) then
se_target:on_after_reach(self)
self.current_target_id = self.assigned_target_id
self.current_action = 1
self.stay_time = game.get_game_time()
end
return
elseif (self.current_action == 1 and self.current_target_id == self.assigned_target_id) then
if (self.stay_time == nil or game.get_game_time():diffSec(self.stay_time) < self.idle_time) then
return
end
end
end
se_target:on_reach_target(self)
self.assigned_target_id = nil
self.current_target_id = nil
self.current_action = 0
for k in self:squad_members() do
local se_obj = alife_object(k.id)
if (se_obj) then
SIMBOARD:setup_squad_and_group(se_obj)
end
end
end
function sim_squad_scripted:generic_update() -- This update is called for all squads with no scripted assigned target
--utils_data.debug_write(strformat("%s:generic_update",self:name()))
if (self.__lock) then
return -- so that squad doesn't continously try to find target which lowers FPS
end
local forced_id = get_help_target_id(self)
self.assigned_target_id = forced_id or self.assigned_target_id or self.current_target_id
if (self.current_action) then
-- in such case assigned is changed (such as get_help_target_id) but current action is 1 (reached)
if (self.current_action == 1 and self.assigned_target_id ~= self.current_target_id) then
self.current_action = 0
end
local se_target = self.assigned_target_id == self.id and self or simulation_objects.get_server_entity(self.assigned_target_id)
-- Evaluate current assigned target; make sure it is still valid
if (se_target and se_target:target_precondition(self,true)) then
-- If current action is not finished, then return
if (self.smart_id ~= self.assigned_target_id and se_target:clsid() == clsid.smart_terrain) then
self:assign_smart(se_target,self.smart_id)
end
if (self.current_action == 0) then
if (se_target:am_i_reached(self)) then
se_target:on_after_reach(self)
self.current_target_id = self.assigned_target_id
self.current_action = 1
self.stay_time = game.get_game_time()
end
return
elseif (self.current_action == 1 and self.current_target_id == self.assigned_target_id) then
if (forced_id) then
return -- infinite stay time
end
if (se_target.locked == true) or (self.stay_time and game.get_game_time():diffSec(self.stay_time) < self.idle_time) then
return
end
end
end
end
-- Find a new target
local target = SIMBOARD:get_squad_target(self) or SIMBOARD:get_squad_target(self,false,true)
if not (target) then
--printf("squad %s has no target",self:name())
self:on_reach_target(self)
-- we have to clear AFTER on_reach_target, not BEFORE so we can get location mask of old smart
self.assigned_target_id = nil
self.current_target_id = nil
self.task_target_id = nil
self.current_action = nil
self.__lock = db.actor and db.actor.afterFirstUpdate -- keep trying until first update
return
end
target:on_reach_target(self)
self.assigned_target_id = target.id
self.current_target_id = nil
self.task_target_id = nil
self.current_action = 0
local sim = alife()
for k in self:squad_members() do
local se_obj = alife_object(k.id)
if (se_obj) then
SIMBOARD:setup_squad_and_group(se_obj)
end
end
end
--------------------------------------------------------------
-- MEMBERS_CONTROL
--------------------------------------------------------------
function sim_squad_scripted:remove_squad()
--utils_data.debug_write(strformat("START sim_squad_scripted:remove_squad %s",self:name()))
self.current_action = nil
SIMBOARD:assign_squad_to_smart(self, nil)
SIMBOARD.squads[self.id] = nil
local squad_npcs = {}
for k in self:squad_members() do
squad_npcs[k.id] = true
end
local sim = alife()
for id,v in pairs(squad_npcs) do
local se_obj = sim:object(id)
if (se_obj) then
local smart_id = se_obj.m_smart_terrain_id --se_obj:smart_terrain_id()
if (smart_id and smart_id ~= 65535) then
local smart = alife_object(smart_id)
if smart ~= nil then
smart:unregister_npc(self)
end
end
self:unregister_member(id)
safe_release_manager.release(se_obj)
end
end
self:hide()
safe_release_manager.release(self)
--utils_data.debug_write(strformat("END sim_squad_scripted:remove_squad %s",self:name()))
end
function sim_squad_scripted:remove_npc(npc_id, force)
--utils_data.debug_write(strformat("%s:remove_npc %s",self:name(),npc_id))
local se_obj = alife_object(npc_id)
if (se_obj) then
local smart_id = se_obj:smart_terrain_id()
if smart_id ~= 65535 then
local smart = alife_object(smart_id)
if smart ~= nil then
smart:unregister_npc(self)
end
end
self:unregister_member(npc_id)
if force or (not se_obj.online) then
safe_release_manager.release(se_obj)
end
end
if self:npc_count() > 0 then
self:refresh()
else
self.current_action = nil
if (self.death_condlist) then
xr_logic.pick_section_from_condlist(db.actor, self, self.death_condlist)
end
SIMBOARD:assign_squad_to_smart(self, nil)
SIMBOARD.squads[self.id] = nil
self:hide()
safe_release_manager.release(self)
end
end
function sim_squad_scripted:on_npc_death(se_npc,se_killer)
--utils_data.debug_write(strformat("%s:on_npc_death %s",self:name(),se_npc and se_npc:name()))
--printf("Squad %s. Killed member %s", tostring(self.id), se_npc.id)
--self.sound_manager:unregister_npc(npc.id)
local npc = db.storage[se_npc.id] and db.storage[se_npc.id].object
if (npc) then
local npc_binder = npc:binded_object()
if (npc_binder) then
npc_binder.squad = nil
end
end
SendScriptCallback("squad_on_npc_death",self,se_npc,se_killer)
self:remove_npc(se_npc.id)
end
function sim_squad_scripted:assign_squad_member_to_smart(member_id, smart, old_smart_id)
local sim = alife()
local obj = sim:object(member_id)
if obj ~= nil then
--printf(" npc [%s] smart [%s]", obj:name(), tostring(obj.m_smart_terrain_id))
--utils_data.debug_write(strformat("sim_squad_scripted:assign_squad_member_to_smart npc=%s smart=%s",obj and obj:name(),smart and smart:name()))
if obj.m_smart_terrain_id == self.smart_id then
return
end
if obj.m_smart_terrain_id ~= 65535 and old_smart_id ~= nil and (obj.m_smart_terrain_id == old_smart_id) and SIMBOARD.smarts[old_smart_id] ~= nil then
SIMBOARD.smarts[old_smart_id].smrt:unregister_npc(obj)
end
if smart ~= nil then
smart:register_npc(obj)
end
end
end
function sim_squad_scripted:assign_smart(smart,old_smart_id)
--utils_data.debug_write(strformat("%s:assign_smart %s",self:name(),smart and smart:name()))
local old_smart = old_smart_id and old_smart_id ~= 65535 and alife_object(old_smart_id)
if (smart == nil and old_smart == nil) then
return
end
if (smart) then
self.smart_id = smart.id
end
for k in self:squad_members() do
local se_obj = alife_object(k.id)
if (se_obj) then
if (old_smart) then
old_smart:unregister_npc(se_obj)
end
if (smart) then
smart:register_npc(se_obj)
end
end
end
end
function sim_squad_scripted:check_invulnerability()
if self.online ~= true then
return
end
local invulnerability = self.invul_condlist and xr_logic.pick_section_from_condlist(db.actor, self, self.invul_condlist) == "true"
if (invulnerability) then
for k in self:squad_members() do
local npc_st = db.storage[k.id]
local npc = npc_st and npc_st.object
if (npc) then
npc:invulnerable(invulnerability)
end
end
end
end
function sim_squad_scripted:set_location_types_section (section)
local location = location_types[section]
if (location) then
self:add_location_type(location)
end
end
function sim_squad_scripted:set_location_types(new_smart_name)
self:clear_location_types()
self:set_location_types_section("stalker_terrain")
self:set_location_types_section("squad_terrain")
local old_target = self.assigned_target_id and db.smart_terrain_by_id[self.assigned_target_id]
if (old_target) then
self:set_location_types_section(old_target:name())
end
if (new_smart_name) then
self:set_location_types_section(new_smart_name)
else
for id,se_obj in pairs(db.smart_terrain_by_id) do
local props_base = se_obj.props and se_obj.props["base"]
if (props_base and tonumber(props_base) ~= 0) then
self:set_location_types_section(se_obj:name())
end
end
end
end
function sim_squad_scripted:add_new_member_forced(section,pos,lvid,gvid)
local se_obj = alife_create(section,pos,lvid,gvid)
if (se_obj) then
self:register_member(se_obj.id)
local smart = self.smart_id and db.smart_terrain_by_id[self.smart_id]
if (smart) then
smart:register_npc(se_obj)
SIMBOARD:setup_squad_and_group(se_obj)
end
end
end
function sim_squad_scripted:add_squad_member(spawn_section, spawn_position, lv_id, gv_id)
--utils_data.debug_write("sim_squad_scripted:add_squad_member")
if not (ini_sys:section_exist(spawn_section)) then
printe("!ERROR: npc section %s does not exist!",spawn_section)
return
end
local custom_data = ini_sys:r_string_ex(spawn_section,"custom_data") or "default_custom_data.ltx"
if custom_data ~= "default_custom_data.ltx" then
printf("INCORRECT npc_spawn_section USED [%s]. You cannot use npc with custom_data in squads", spawn_section)
end
local sim = alife()
local obj = alife_create(spawn_section,spawn_position,lv_id,gv_id)
self:register_member(obj.id)
--self.sound_manager:register_npc(obj.id)
local actor = sim:actor()
if (simulation_objects.is_on_the_same_level(obj, actor) and spawn_position:distance_to_sqr(actor.position) <= sim:switch_distance()^2) then
db.spawned_vertex_by_id[obj.id] = lv_id
end
-- Alundaio
SendScriptCallback("squad_on_add_npc",self,obj,spawn_section,spawn_position,lv_id,gv_id)
-- End Alundaio
return obj.id
end
function sim_squad_scripted:create_npc(spawn_smart,pos,lvid,gvid)
--utils_data.debug_write("sim_squad_scripted:create_npc")
local settings_id = self:section_name()
local spawn_sections = parse_names(ini_sys:r_string_ex(settings_id, "npc") or "")
--smr_debug.get_log().info("simboard", "FOO: create_npc for %s", self:section_name())
local base_spawn_position, base_lvi, base_gvi, spawn_vid
if (spawn_smart) then
-- Try to look/parse spawn_vid in either squad logic or smart logic
local spawn_vid = ini_sys:r_string_to_condlist(settings_id,"spawn_vid") or spawn_smart.ini:r_string_to_condlist(settings_id,"spawn_vid")
if (spawn_vid) then
local str = xr_logic.pick_section_from_condlist(db.actor,self,spawn_vid)
if (str and str ~= "" and str ~= "nil") then
local vid = str_explode(str,":")
base_lvi = tonumber(vid[1])
base_spawn_position = level.vertex_position(base_lvi)
base_gvi = tonumber(vid[2])
end
end
-- Try to look/parse spawn_point in either squad logic or smart logic
if not (base_spawn_position and base_lvi and base_gvi) then
local spawn_point = ini_sys:r_string_to_condlist(settings_id,"spawn_point") or spawn_smart.ini:r_string_to_condlist(settings_id,"spawn_point")
if (spawn_point) then
local p_path = xr_logic.pick_section_from_condlist(db.actor, self, spawn_point)
if (p_path and p_path ~= "" and p_path ~= "nil") then
local pat = patrol(p_path)
if (pat) then
base_spawn_position = pat:point(0)
base_lvi = pat:level_vertex_id(0)
base_gvi = pat:game_vertex_id(0)
end
end
end
end
-- Try spawn_smart.spawn_point
if not (base_spawn_position and base_lvi and base_gvi) then
if (spawn_smart.spawn_point) then
local pat = patrol(spawn_smart.spawn_point)
if (pat) then
base_spawn_position = pat:point(0)
base_lvi = pat:level_vertex_id(0)
base_gvi = pat:game_vertex_id(0)
end
end
end
-- if all else fail, spawn on smart
if not (base_spawn_position and base_lvi and base_gvi) then
base_spawn_position = spawn_smart.position
base_lvi = spawn_smart.m_level_vertex_id
base_gvi = spawn_smart.m_game_vertex_id
end
self.smart_id = spawn_smart.id
else
printf('spawn smart: %s', spawn_smart)
base_spawn_position = pos or self.position
base_lvi = lvid or self.m_level_vertex_id
base_gvi = gvid or self.m_game_vertex_id
end
local npcs_list = {}
for ind=1,#spawn_sections do
local id = nil
if is_squad_monster[ini_sys:r_string_ex(settings_id, "faction")] and not smr_squad.is_mutant_type_enabled(spawn_sections[ind]) then
smr_debug.get_log().info("population/monsters", "not spawning disabled mutant %s in squad %s", spawn_sections[ind], self:section_name())
else
id = self:add_squad_member(smr_squad.replace_mutant_variant(spawn_sections[ind]), base_spawn_position, base_lvi, base_gvi,ind)
end
if (id) then
npcs_list[#npcs_list+1] = id
end
end
local random_spawn = ini_sys:r_string_ex(settings_id,"npc_random")
if random_spawn ~= nil then
random_spawn = parse_names(random_spawn)
local count = ini_sys:r_string_ex(settings_id,"npc_in_squad") or "1,2"
count = str_explode(count,",")
count[1] = count[1] and tonumber(count[1])
count[2] = count[2] and tonumber(count[2]) or count[1]
--[[if _G.WARFARE then
local faction = ini_sys:r_string_ex(settings_id, "faction")
if (warfare_options.options.factions[faction]) then
if (warfare_options.options.factions[faction].random_squad_count) then
printf("!!! GETTING RANDOM COUNT FOR "..faction.." | " .. tonumber(warfare_options.options.factions[faction].min_random_squad_count) .. " - " .. tonumber(warfare_options.options.factions[faction].max_random_squad_count) .. " !!!!")
count[1] = tonumber(warfare_options.options.factions[faction].min_random_squad_count)
count[2] = tonumber(warfare_options.options.factions[faction].max_random_squad_count)
end
end
end]]
-- check for random squad members for each faction here.
local random_count = count[1] and count[2] and math.random(count[1],count[2]) or math.random(1,2)
random_count = random_count - self:npc_count()
random_count = smr_squad.adjust_random_count(self, random_count)
local random_id
for i = 1,random_count do
local id = nil
random_id = math.random(1, #random_spawn)
if is_squad_monster[ini_sys:r_string_ex(settings_id, "faction")] and not smr_squad.is_mutant_type_enabled(random_spawn[random_id]) then
smr_debug.get_log().info("population/monsters", "not spawning disabled mutant %s in squad %s", random_spawn[random_id], self:section_name())
else
id = self:add_squad_member(smr_squad.replace_mutant_variant(random_spawn[random_id]), base_spawn_position, base_lvi, base_gvi)
end
if (id) then
npcs_list[#npcs_list+1] = id
end
end
elseif #spawn_sections == 0 then
printf("You are trying to spawn an empty squad [%s]!!!", settings_id)
end
if not (spawn_smart) then
local smart = self.smart_id and db.smart_terrain_by_id[self.smart_id]
if (smart) then
local id
for i=1, #npcs_list do
id = npcs_list[i]
local se_obj = id and alife_object(id)
if (se_obj) then
smart:register_npc(se_obj)
SIMBOARD:setup_squad_and_group(se_obj)
end
end
end
end
self:set_squad_relation()
self:refresh()
end
function sim_squad_scripted:set_squad_sympathy(sympathy)
--utils_data.debug_write("sim_squad_scripted:set_squad_sympathy")
local symp = sympathy or self.sympathy
if(symp~=nil) then
local npc
for k in self:squad_members() do
npc = db.storage[k.id] and db.storage[k.id].object
if(npc) then
game_relations.set_npc_sympathy(npc, symp)
else
if(db.goodwill.sympathy==nil) then
db.goodwill.sympathy = {}
end
db.goodwill.sympathy[k.id] = symp
end
end
end
end
function sim_squad_scripted:set_squad_relation(relation)
--utils_data.debug_write("sim_squad_scripted:set_squad_relation")
if (is_squad_monster[self.player_id]) then
return
end
local rel = relation or self.relationship
if (rel and (rel == "enemy" or rel == "friend" or rel == "neutral")) then
local sim = alife()
for k in self:squad_members() do
local npc = db.storage[k.id] and db.storage[k.id].object
if (npc) then
game_relations.set_npcs_relation(npc, db.actor, rel)
elseif (k.object) then
local goodwill = rel == "enemy" and -1000 or rel == "friend" and 1000 or 0
k.object:force_set_goodwill(goodwill, 0)
end
end
end
end
local function reset_animation(npc)
--utils_data.debug_write("sim_squad_scripted:reset_animation")
local state_mgr = db.storage[npc:id()].state_mgr
if state_mgr == nil then
return
end
local planner = npc:motivation_action_manager()
--if not planner or not planner:initialized() then return end
state_mgr.animation:set_state(nil, true)
state_mgr.animation:set_control()
state_mgr.animstate:set_state(nil, true)
state_mgr.animstate:set_control()
state_mgr:set_state("idle", nil, nil, nil, {fast_set = true})
state_mgr:update()
state_mgr:update()
state_mgr:update()
state_mgr:update()
state_mgr:update()
state_mgr:update()
state_mgr:update()
npc:set_body_state(move.standing)
npc:set_mental_state(anim.free)
end
function sim_squad_scripted:set_squad_position(position)
--utils_data.debug_write("sim_squad_scripted:set_squad_position")
if self.online == false then
self:force_change_position(position)
end
local cl_object
for k in self:squad_members() do
cl_object = db.storage[k.id] and db.storage[k.id].object
if not (db.offline_objects[k.id]) then
db.offline_objects[k.id] = {}
end
db.offline_objects[k.id].level_vertex_id = level.vertex_id(position)
if cl_object then
reset_animation(cl_object)
cl_object:set_npc_position(position)
else
k.object.position = position
end
end
end
function sim_squad_scripted:has_detector()
local sim = alife()
for k in self:squad_members() do
local se_obj = sim:object(k.id)
if se_obj and se_obj:has_detector() then
return true
end
end
return false
end
function sim_squad_scripted:get_squad_community()
local squad_community = squad_community_by_behaviour[self.player_id]
if squad_community == nil then
printf("squad community is 'nil' for player_id [%s]", self.player_id)
end
return squad_community
end
function sim_squad_scripted:has_items_to_sell()
if not (self.online) then
return false
end
local sim = alife()
local st
for k in self:squad_members() do
st = db.storage[k.id]
if (st and st.has_items_to_sell) then
return true
end
end
return false
end
function sim_squad_scripted:has_tech_items()
if not (self.online) then
return false
end
local sim = alife()
local st
for k in self:squad_members() do
st = db.storage[k.id]
if (st and st.has_tech_items) then
return true
end
end
return false
end
--------------------------------------------------------------
-- SAVE\LOAD
--------------------------------------------------------------
--[[
function sim_squad_scripted:save_state(m_data)
--utils_data.debug_write(strformat("%s:%s sim_squad_scripted:save_state BEFORE",self:name(),self.id))
m_data.sim_squad_scripted[self.id] = {}
m_data.sim_squad_scripted[self.id].name = self:name() -- debugging only
m_data.sim_squad_scripted[self.id].current_target_id = self.current_target_id
m_data.sim_squad_scripted[self.id].respawn_point_id = self.respawn_point_id
m_data.sim_squad_scripted[self.id].respawn_point_prop_section = self.respawn_point_prop_section
m_data.sim_squad_scripted[self.id].smart_id = self.smart_id
--utils_data.debug_write(strformat("%s:%s sim_squad_scripted:save_state AFTER",self:name(),self.id))
end
function sim_squad_scripted:load_state(m_data)
if not (m_data.sim_squad_scripted and m_data.sim_squad_scripted[self.id]) then
return
end
--utils_data.debug_write("sim_squad_scripted:load_state BEFORE")
self.current_target_id = m_data.sim_squad_scripted[self.id].current_target_id
self.respawn_point_id = m_data.sim_squad_scripted[self.id].respawn_point_id
self.respawn_point_prop_section = m_data.sim_squad_scripted[self.id].respawn_point_prop_section
self.smart_id = m_data.sim_squad_scripted[self.id].smart_id
m_data.sim_squad_scripted[self.id] = nil
--utils_data.debug_write("sim_squad_scripted:load_state AFTER")
end
--]]
function sim_squad_scripted:STATE_Write(packet)
--utils_data.debug_write("sim_squad_scripted:STATE_Write")
cse_alife_online_offline_group.STATE_Write (self, packet)
--if (USE_MARSHAL) then
--return
--end
set_save_marker(packet, "save", false, "sim_squad_scripted")
packet:w_stringZ(tostring(self.current_target_id))
packet:w_stringZ(tostring(self.respawn_point_id))
packet:w_stringZ(tostring(self.respawn_point_prop_section))
packet:w_stringZ(tostring(self.smart_id))
local scripted_target_tosave = self.scripted_target
if not scripted_target_tosave then scripted_target_tosave = "nil" end
packet:w_stringZ(tostring(scripted_target_tosave))
--packet:w_stringZ(tostring(self.online))
set_save_marker(packet, "save", true, "sim_squad_scripted")
end
function sim_squad_scripted:STATE_Read(packet, size)
--utils_data.debug_write( strformat("\n%s:STATE_Read start",self:name()) )
cse_alife_online_offline_group.STATE_Read (self, packet, size)
--if (USE_MARSHAL) then
--self:load_state(alife_storage_manager.get_state())
--self:init_squad_on_load()
--return
--end
set_save_marker(packet, "load", false, "sim_squad_scripted")
self.current_target_id = packet:r_stringZ()
if self.current_target_id == "nil" then
self.current_target_id = nil
else
self.current_target_id = tonumber(self.current_target_id)
end
self.respawn_point_id = packet:r_stringZ()
if self.respawn_point_id == "nil" then
self.respawn_point_id = nil
else
self.respawn_point_id = tonumber(self.respawn_point_id)
end
self.respawn_point_prop_section = packet:r_stringZ()
if self.respawn_point_prop_section == "nil" then
self.respawn_point_prop_section = nil
end
self.smart_id = packet:r_stringZ()
if self.smart_id == "nil" then
self.smart_id = nil
else
self.smart_id = tonumber(self.smart_id)
end
self.scripted_target = packet:r_stringZ()
if self.scripted_target == "nil" then
self.scripted_target = nil
end
--[[
local isOnline = packet:r_stringZ()
if isOnline == "false" then
self.online = false
end
--]]
self:init_squad_on_load()
set_save_marker(packet, "load", true, "sim_squad_scripted")
--utils_data.debug_write( strformat("%s:STATE_Read end",self:name()) )
end
--------------------------------------------------------------
-- SERVER_OBJECT
--------------------------------------------------------------
function sim_squad_scripted:on_register()
--utils_data.debug_write( strformat("%s:on_register start",self:name()) )
cse_alife_online_offline_group.on_register( self )
SendScriptCallback("server_entity_on_register",self,"sim_squad_scripted")
sim_board.get_sim_board().squads[self.id] = true
--simulation_objects.register(self)
--SendScriptCallback("squad_on_register",self)
--utils_data.debug_write( strformat("%s:on_register end",self:name()) )
end
function sim_squad_scripted:on_unregister()
--utils_data.debug_write("sim_squad_scripted:on_unregister")
--SendScriptCallback("squad_on_unregister",self)
SendScriptCallback("server_entity_on_unregister",self,"sim_squad_scripted")
SIMBOARD.squads[self.id] = nil
SIMBOARD:assign_squad_to_smart(self, nil)
--simulation_objects.unregister(self)
if self.respawn_point_id ~= nil then
local smart = alife_object(self.respawn_point_id)
if smart == nil then
return
end
if smart.already_spawned ~= nil and smart.already_spawned[self.respawn_point_prop_section] ~= nil then
smart.already_spawned[self.respawn_point_prop_section].num = smart.already_spawned[self.respawn_point_prop_section].num - 1
end
end
cse_alife_online_offline_group.on_unregister(self)
end
function sim_squad_scripted:check_online_status()
local b = nil
if (get_object_story_id(self.id)) then
-- don't enforce any rule
elseif (self.force_online) then -- for tasks
b = true
elseif (self.__lock) then -- couldn't find a target
b = false
elseif (IsSurvivalMode()) then
b = true
elseif (axr_companions.companion_squads[self.id]) then
b = true
elseif not (level_weathers.valid_levels[level.name()]) then -- underground levels
-- don't enforce any rule
else
if (self.online ~= true and is_squad_monster[self.player_id]) then -- monster behaviors
local hour = level.get_time_hours()
if (self.player_id == "monster_predatory_day") then
-- Monster will not come online during the hours of (10PM till 5AM)
if (hour <= 5 or hour >= 22) then
b = false
end
elseif (self.player_id == "monster_zombied_day") then
-- Monster will not come online during the hours of (10PM till 5AM)
if (hour <= 5 or hour >= 22) then
b = false
end
elseif (self.player_id == "monster_predatory_night") then
-- Monster will not come online during the hours of (6AM till 6PM)
if (hour >= 6 and hour <= 18) then
b = false
end
elseif (self.player_id == "monster_zombied_night") then
-- Monster will not come online during the hours of (5AM till 7PM)
if (hour >= 6 and hour <= 18) then
b = false
end
end
if (b == false) then
self.assigned_target_id = self.id
self.current_target_id = self.id
self.current_action = 0
end
end
if (b == nil and db.actor) then
if (db.actor:has_info("enforce_online_exclusion")) then -- For story needs
if (self.online) then -- exclude from coming online except if already online or storied
if (self.was_forced_offline or self.position:distance_to(db.actor:position()) >= 2500) then
self.was_forced_offline = false
b = false -- switch online to offline
end
else
b = false -- all simulation already offline, refuse to come online while actor has info
end
else
if not (self.online) then
local dist = excl_dist
if (dist > 0 and not db.actor:has_info("actor_is_sleeping")) then
if (self.position:distance_to(db.actor:position()) <= dist) then
b = false -- force offline for online exclusion
end
end
end
end
end
end
self.forced_online_status = b
end
function sim_squad_scripted:can_switch_offline()
--[[
if (self.current_action == 1) then
local current_target = self.current_target_id and alife_object(self.current_target_id)
if (current_target.online and current_target.faction_controlled and current_target.faction_war_in_progress == true) then
--printf("forced online %s",current_target:name())
return false
end
end
--]]
if not (cse_alife_online_offline_group.can_switch_offline(self)) then
return false
end
if (self.forced_online_status == true) then
return false
end
return true
end
function sim_squad_scripted:can_switch_online()
-- important to check with inherited method first if it's okay to switch online, otherwise this leads to
-- crash saying 'object with ID already exists' when returning false and object is already online
if not cse_alife_online_offline_group.can_switch_online(self) then
return false
end
if (self.forced_online_status == false) then
return false
end
return true
end
function sim_squad_scripted:switch_offline()
cse_alife_online_offline_group.switch_offline(self)
end
function sim_squad_scripted:switch_online()
-- if (DEV_DEBUG) then
-- printf("sim_squad_scripted:switch_online() [%s]",self:name())
-- end
cse_alife_online_offline_group.switch_online(self)
end
--------------------------------------------------------------
-- MAP LOCATION
--------------------------------------------------------------
function sim_squad_scripted:refresh()
if(self:commander_id()==nil) then
self:hide()
return
end
self:show()
end
function sim_squad_scripted:hide()
if(self.current_spot_id==nil) or (self.spot_section==nil) then
return
end
level.map_remove_object_spot(self.current_spot_id, self.spot_section)
self.current_spot_id = nil
self.spot_section = nil
end
local squad_icons = {
["stalker"] = "warfare_stalker_spot",
["dolg"] = "warfare_duty_spot",
["freedom"] = "warfare_freedom_spot",
["killer"] = "warfare_killer_spot",
["csky"] = "warfare_csky_spot",
["monolith"] = "warfare_monolith_spot",
["army"] = "warfare_army_spot",
["ecolog"] = "warfare_ecolog_spot",
["bandit"] = "warfare_bandit_spot",
["greh"] = "warfare_greh_spot",
["isg"] = "warfare_isg_spot",
["renegade"] = "warfare_renegade_spot",
["greh_npc"] = "warfare_greh_spot",
["army_npc"] = "warfare_army_spot",
["zombied"] = "warfare_zombied_spot",
["monster"] = "alife_presentation_squad_monster_1",
["friends"] = "alife_presentation_squad_friend_1",
["neutral"] = "alife_presentation_squad_neutral_1",
["enemy"] = "alife_presentation_squad_enemy_1"
--["companion"] = "companion_spot"
}
function sim_squad_scripted:show()
if self.show_disabled then
self:hide()
return
end
if(level.map_has_object_spot(self:commander_id(), "ui_pda2_trader_location")~=0) or
(level.map_has_object_spot(self:commander_id(), "ui_pda2_mechanic_location")~=0) or
(level.map_has_object_spot(self:commander_id(), "ui_pda2_scout_location")~=0) or
(level.map_has_object_spot(self:commander_id(), "ui_pda2_quest_npc_location")~=0) or
(level.map_has_object_spot(self:commander_id(), "ui_pda2_medic_location")~=0) then
self.show_disabled = true
return
end
if self.current_spot_id ~= self:commander_id() then
self:hide()
self.current_spot_id = self:commander_id()
self:show()
return
end
-- local param = squad_smart.get_params() or {}
local sim = alife()
local se_actor = sim:actor()
local spot = ""
-- 1. Debug mode + Debug HUD enabled
if (DEV_DEBUG and dbg_map_hud) then
spot = squad_icons[self.player_id] or "warfare_mutant_spot"
-- 2. Improved PDAs
elseif db.actor then
local pda = db.actor:item_in_slot(8)
if pda and simulation_objects.is_on_the_same_level(se_actor,self) and (not is_squad_monster[self.player_id]) then
if (pda:section() == "device_pda_2") and (self.player_id == character_community(db.actor):sub(7)) then
spot = squad_icons[self.player_id]
elseif (pda:section() == "device_pda_3") and (self:get_squad_relation() ~= "enemy") then
spot = squad_icons[self.player_id]
end
end
end
if (spot ~= "") then
if spot == self.spot_section then
level.map_change_spot_hint(self.current_spot_id, self.spot_section, self:get_squad_props())
return
end
if (self.spot_section == nil) then
level.map_add_object_spot(self.current_spot_id, spot, self:get_squad_props())
else
level.map_remove_object_spot(self.current_spot_id, self.spot_section)
level.map_add_object_spot(self.current_spot_id, spot, self:get_squad_props())
end
self.spot_section = spot
elseif (self.spot_section ~= nil) then
level.map_remove_object_spot(self.current_spot_id, self.spot_section)
self.spot_section = nil
end
end
function sim_squad_scripted:get_squad_props()
local t = ""
-- 1. Debug mode + Debug HUD enabled
if (DEV_DEBUG and dbg_map_hud) then
local assigned_target = self.assigned_target_id and alife_object(self.assigned_target_id)
local current_target = self.current_target_id and alife_object(self.current_target_id)
t = "%s\\n\\n%s\\n\\nnpcs=%s\\n\\ncurrent_target=%s\\nassigned_target=%s\\n\\n"
t = strformat(t,self.disabled and "[disabled]" or "",self:name(),self:npc_count(),self.current_target_id == AC_ID and "actor" or current_target and current_target:name(),self.assigned_target_id == AC_ID and "actor" or assigned_target and assigned_target:name())
if (self.current_action) then
if (self.current_action == 0) then
t = t .. "Trying to Reach Target"
elseif (self.current_action == 1) then
t = t .. "Stay On Target"
end
if (self.current_action == 1 and self.stay_time) then
t = strformat("%s = %s (%s)",t,tostring(self.idle_time - game.get_game_time():diffSec(self.stay_time)),self.idle_time)
end
end
return t
-- 2. Everything else
elseif (not is_squad_monster[self.player_id]) then
return game.translate_string(self.player_id)
end
local assigned_target = self.assigned_target_id and alife_object(self.assigned_target_id)
local assigned_target_name = (self.assigned_target_id == AC_ID and "actor") or (assigned_target and assigned_target:name())
local current_target = self.current_target_id and alife_object(self.current_target_id)
local current_target_name = (self.current_target_id == AC_ID and "actor") or (current_target and current_target:name())
if assigned_target_name and (assigned_target_name ~= current_target_name) then
assigned_target_name = (assigned_target_name == "actor" and alife():actor():character_name()) or game.translate_string("st_"..assigned_target_name.."_name")
t = t .. "\\n" .. game.translate_string("st_sq_target") .. " - \\n" .. assigned_target_name
end
return t
end
function sim_squad_scripted:get_squad_relation()
local squad = alife_object(self.id)
if (is_squad_monster[squad.player_id]) then
return "monster"
end
local goodwill = 0
local npc_count = 0
for k in squad:squad_members() do
local object = db.storage[k.id] and db.storage[k.id].object
if object and db.actor then
goodwill = goodwill + object:general_goodwill(db.actor)
npc_count = npc_count + 1
end
end
if npc_count ~= 0 then
local delta = goodwill/npc_count
if delta <= -1000 then
return "enemy"
elseif delta >= 1000 then
return "friends"
elseif delta < 1000 and delta > -1000 then
return "neutral"
end
end
local relation = 0
if db.actor then
relation = db.actor:community_goodwill(self.player_id)+game_relations.get_factions_community(self.player_id, alife():actor():community())
else
relation = game_relations.get_factions_community(self.player_id, alife():actor():community())
end
if relation >= 1000 then
return "friends"
elseif relation <= -1000 then
return "enemy"
else
return "neutral"
end
end
--------------------------------------------------------------
-- SIMULATION_TARGET_SQUAD
--------------------------------------------------------------
function sim_squad_scripted:get_location()
return self.position, self.m_level_vertex_id, self.m_game_vertex_id
end
function sim_squad_scripted:get_current_task() -- called from engine
--utils_data.debug_write("sim_self_scripted:get_current_task")
local se_target = self.assigned_target_id and simulation_objects.get_server_entity(self.assigned_target_id)
if (se_target) then
local npc_info = se_target.npc_info and se_target.npc_info[self:commander_id()]
if (npc_info and npc_info.job) then
return npc_info.job.alife_task or se_target:get_alife_task() or self:get_alife_task()
end
return se_target:get_alife_task()
end
return self:get_alife_task()
end
function sim_squad_scripted:am_i_reached(squad)
return self:npc_count() == 0 or self.id == squad.id
end
function sim_squad_scripted:on_after_reach(squad)
squad.current_action = nil
squad.current_target_id = nil
end
function sim_squad_scripted:on_reach_target(squad)
--utils_data.debug_write("sim_squad_scripted:on_reach_target")
squad:set_location_types()
for k in squad:squad_members() do
db.spawned_vertex_by_id[k.id] = nil
if db.offline_objects[k.id] ~= nil then
db.offline_objects[k.id] = db.offline_objects[k.id] and empty_table(db.offline_objects[k.id])
end
db.spawned_vertex_by_id[k.id] = nil
end
SIMBOARD:assign_squad_to_smart(squad, nil)
end
function sim_squad_scripted:get_alife_task()
return CALifeSmartTerrainTask(self.m_game_vertex_id, self.m_level_vertex_id)
end
local smarts_by_no_assault_zones = {
["zat_a2_sr_no_assault"] = "zat_stalker_base_smart",
["jup_a6_sr_no_assault"] = "jup_a6",
["jup_b41_sr_no_assault"] = "jup_b41"
}
function sim_squad_scripted:sim_available()
return false
--[[
if self.smart_id == nil then
return true
end
local smart = simulation_objects.get_server_entity(self.smart_id)
if not (smart) then
return false
end
-- Can't be targetted by other squads if at base or trade prop smart
if (smart.props and smart.props.base and smart.props.base > 0) then
return false
end
return true
--]]
end
function sim_squad_scripted:target_precondition(squad)
if (self.id == squad.id) then
return true
end
if (sim_board.general_squad_precondition(squad,self)) then
return true
end
return false
end
function sim_squad_scripted:evaluate_prior(squad)
return simulation_objects.evaluate_prior(self, squad)
end
-- function on_game_start()
-- RegisterScriptCallback("squad_on_after_game_vertex_change",ongv)
-- RegisterScriptCallback("squad_on_after_level_change",onlv)
-- end
-- function onlv(se, old_lvl, cur_lvl)
-- printf('[%s]: changed level: [%s] --> [%s]', se:name(), old_lvl, cur_lvl)
-- end
-- function ongv(se, last_gv, cur_gv, change)
-- printf('[%s]: changed game vertex: [%s] --> [%s] (level changed: %s)', se:name(), last_gv, cur_gv, change)
-- end