1454 lines
46 KiB
Plaintext
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
|