--'****************************************************** -- 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