-- ============================================================ -- -- gulag_general.script -- CoC 1.5b r4 - DoctorX Call of The Zone 1.0 -- -- Modified by: DoctorX -- Last revised: October 05, 2019 -- -- ============================================================ --[[ Dynamic Gulag Rewritten by Alundaio (original: ??? GSC) This script was re-written to overcome some serious performance issues in vanilla game. If you are wondering why CoC loads faster, this is one the reason. String concatenation is very costly in lua, so script was rewritten to avoid it. Another reason this was re-written is because it alters the job tables used by smart_terrain.script. GSC made the job system very inefficient by putting tables inside of tables which required deep recursion to find a job when you can simply put them all in one table and sort by priority. Indexing the jobs like I do here also allows to step through the table once per update for NPCs who already have a job and are just scanning for a higher priority job to switch to; This improves performance substantially. ---------- Notes: ---------- 1. Exclusive jobs share the same table as the auto-generated jobs. So priority is important. Here is the current prior list: ----------------------------------------------------- Job | Prior ----------------------------------------------------- animpoint 15 beh 30 beh_surge 60 camper 45 campfire_point 10 collector 25 cover 3 guard 25 patrol 25 (-1 per follower) sleep 20 sniper 30 surge 60 walker 15 So if you want an exclusive job to ignore the priority of all other jobs set prior at 61+ If you want an exclusive job to take priority over all other jobs except surge jobs, set prior at 50. Exclusive jobs should be used for jobs you want everyone else locked out of, such as simulation squads. They are also useful for filling up jobs with a specific character or squad type. Like a faction smart. 2. Never use the same logic section names (ex. 'logic@walker_1') between exclusive jobs and auto-generated jobs for each smart. There is a table that a smart_terrain uses called 'npc_by_job_section' which would require that every logic section is uniquely named. To avoid this simply give exclusive jobs a prefix, like 'logic@exclusive_walker_1_walk_1' 3. Squad smart max population should be based on sim squad count multiplied by 3. Do not count jobs for squads with target_smart as a field in squad descriptions. --]] -- Naming Conventions for dynamic gulag jobs --[[ animpoint - _animpoint_ (ex. yan_smart_terrain_4_6_animpoint_1) animpoint_surge - _surge_animpoint_ (ex. yan_smart_terrain_4_6_surge_animpoint_1) beh - _beh_ (ex. esc_smart_terrain_1_5_beh_1) beh_surge - _beh_surge_ (ex. esc_smart_terrain_1_5_beh_surge_1) beh_trader - _beh_trade_ (ex. esc_smart_terrain_1_5_beh_trade_1) camper - _camper__walk (ex. yan_smart_terrain_4_6_camper_1_walk) look path: yan_smart_terrain_4_6_camper_1_look campfire_point - _campfire_point_ (ex. yan_smart_terrain_4_6_campfire_point_1) cover - _point_ (ex. yan_smart_terrain_4_6_point_1) collector - _collector__walk (ex. yan_smart_terrain_4_6_collector_1_walk) look path: yan_smart_terrain_4_6_collector_1_look guard - _guard__walk (ex. yan_smart_terrain_4_6_guard_1_walk) look path: yan_smart_terrain_4_6_guard_1_look mob_home - _home_ (ex. yan_smart_terrain_4_6_home_1) patrol - _patrol__walk (ex. yan_smart_terrain_4_6_patrol_1_walk) look path: yan_smart_terrain_4_6_patrol_1_look point - _point_ (ex. yan_smart_terrain_4_6_point_1) sleeper - _sleep_ (ex. yan_smart_terrain_4_6_sleep_1) sniper - _sniper__walk (ex. yan_smart_terrain_4_6_sniper_1_walk) look path: yan_smart_terrain_4_6_sniper_1_look surge - _surge__walk (ex. yan_smart_terrain_4_6_surge_1_walk) look path: yan_smart_terrain_4_6_surge_1_look walker - _walker__walk (ex. yan_smart_terrain_4_6_walker_1_walk) look path: yan_smart_terrain_4_6_walker_1_look --]] -- HOW TO ADD HELI JOBS: --[[ 1. create a new path _heli__fly for each smart terrain available to heli (army bases in sim_board) In smart logic create a helicopter respawn section ie. ------------------------------------------------- respawn_params = respawn@mar_smart_terrain_11_11 respawn_idle = 600 [respawn@mar_smart_terrain_11_11] spawn_heli [spawn_heli] spawn_helicopter = helicopter spawn_num = 1 2. create _heli_hide waypoint under map near edge 3. create _goto_hide path above map that leads to map edge. Heli will teleport to heli hide from this path If there is a heli hide waypoint helicopter will spawn here instead of over smart terrain that spawned them in ------------------------------------------------- --]] local ltx_size = 0 local beh_ini local ids = 0 local job_type_by_scheme = { ["animpoint"] = "smartcover_job", ["beh"] = "point_job", ["camper"] = "path_job", ["companion"] = "point_job", ["cover"] = "point_job", ["heli_move"] = "heli_path_job", ["mob_home"] = "path_job", ["mob_jump"] = "point_job", ["mob_walker"] = "path_job", ["patrol"] = "path_job", ["remark"] = "point_job", ["sleeper"] = "path_job", ["smartcover"] = "smartcover_job", ["walker"] = "path_job" } -- Table of smart terrains where certain jobs are not generated. local campfire_point_smart_blacklist = { -- Outskirts ["pri_a16"] = true, ["pri_a18_smart_terrain"] = true, ["pri_b36_smart_terrain"] = true, ["pri_sim_1"] = true } local generic_point_smart_blacklist = { -- Outskirts ["pri_a16"] = true, ["pri_a18_smart_terrain"] = true, ["pri_b36_smart_terrain"] = true, ["pri_sim_1"] = true } local walker_smart_blacklist = { -- Outskirts ["pri_a18_smart_terrain"] = true, ["pri_b36_smart_terrain"] = true, ["pri_sim_1"] = true } function get_job_prior(job) if (job.exclusive) then return job.prior end return job_info_by_job_type_id[job.job_type_id].prior end function get_job_precondition(job) if (job.exclusive) then return job.precondition_function end return job_info_by_job_type_id[job.job_type_id].precondition_function end function get_job_type(job) if (job.exclusive) then return job.job_type end return job_info_by_job_type_id[job.job_type_id].job_type end function get_job_prefix_name(job) if (job.exclusive) then return job.prefix_name end return job_info_by_job_type_id[job.job_type_id].prefix_name end -------------------------------------------------------------------- -- Job Preconditions -------------------------------------------------------------------- local function precond_exclusive(se_obj,smart,job) local result = xr_logic.pick_section_from_condlist(db.actor, se_obj, job.precondition_params.condlist) if (result == "false" or result == nil) then return false end return true end local function precond_heli_hide(se_obj,smart,job) return xr_conditions.surge_started() or not utils.is_day() end local function precond_heli(se_obj,smart,job) return not xr_conditions.surge_started() and utils.is_day() end local function precond_surge(se_obj, smart, job) if se_obj:community() == "zombied" or se_obj:community() == "monolith" then return false end return xr_conditions.surge_started() end local function precond_safe_job_sleeper(se_obj, smart, job) if se_obj:community() == "zombied" then return false end if not in_time_interval(21,7) then return false end return smart.smart_alarm_time == nil end local function precond_safe_job(se_obj, smart, job) return smart.smart_alarm_time == nil end local function precond_safe_job_patrol(se_obj, smart, job) if not (se_obj.community) then return false end if se_obj:community() == "zombied" then return false end return smart.smart_alarm_time == nil end local function precond_collector(se_obj, smart, job) local st = db.storage[se_obj.id] local npc = st and st.object if not (npc) then return false end if se_obj:community() == "zombied" then return false end if (npc:object("detector_simple") or npc:object("detector_advanced") or npc:object("detector_elite") or npc:object("detector_scientific")) then return true end return false end local function precond_animpoint(se_obj, smart, job) if se_obj:community() == "zombied" then return false end return true end local function precond_safe_job_guard(se_obj, smart, job) return smart.smart_alarm_time == nil end -- ._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._. -- -- precond_guard_follower() -- -- Changelist -- ---------- -- 23/07/2024 4:38:56 PM [moonshroom] -- -> Disabled follower job. -- -- ._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._. local function precond_guard_follower(se_obj, smart, job, npc_info) -- return npc_info.need_job == job.precondition_params.changing_job return false end local function precond_sniper(se_obj, smart, job) if se_obj:community() == "zombied" then return false end --return combat_restrictor.accessible_job(se_obj, job.precondition_params.name) return true end local function precond_camper(se_obj, smart, job) --return combat_restrictor.accessible_job(se_obj, job.precondition_params.name) return true end local function precond_trade_job(se_obj,smart,job) if (se_obj:community() == "zombied") then return false end if (xr_conditions.surge_started()) then return false end if (job.idle and time_global() < job.idle) then return false end local st = db.storage[se_obj.id] return st and st.has_items_to_sell or false end -- lookup table for job info -- instead of keeping track of this data per job, to save memory a single lookup -- table was created for all constant data shared among job types job_info_by_job_type_id = { [0] = {job_type = "point_job", prior = 3}, -- generic_point [1] = {job_type = "point_job", prior = 10}, -- campfire_point [2] = {job_type = "path_job", prior = 60, precondition_function = precond_surge}, -- surge [3] = {job_type = "path_job", prior = 10, precondition_function = precond_safe_job_sleeper}, -- sleep [4] = {job_type = "path_job", prior = 25, precondition_function = precond_collector}, -- collector [5] = {job_type = "path_job", prior = 15, precondition_function = precond_safe_job}, -- walker [6] = {job_type = "path_job", prior = 25, precondition_function = precond_safe_job_patrol}, -- patrol [7] = {job_type = "smartcover_job", prior = 15, precondition_function = precond_animpoint}, -- animpoint [8] = {job_type = "smartcover_job", prior = 60, precondition_function = precond_surge}, -- animpoint_surge [9] = {job_type = "path_job", prior = 25, precondition_function = precond_safe_job_guard}, -- guard [10] = {job_type = "path_job", prior = 14, precondition_function = precond_safe_job_guard}, -- follower [11] = {job_type = "path_job", prior = 30, precondition_function = precond_sniper}, -- sniper [12] = {job_type = "path_job", prior = 45, precondition_function = precond_camper}, -- camper [13] = {job_type = "point_job", prior = 30}, -- beh [14] = {job_type = "point_job", prior = 60, precondition_function = precond_surge}, -- beh_surge [15] = {job_type = "point_job", prior = 59, precondition_function = precond_trade_job}, -- beh_trader [16] = {job_type = "point_job", prior = 40}, -- mob home [17] = {job_type = "heli_path_job", prior = 45}, -- heli_move [18] = {job_type = "heli_hide_job", prior = 100, precondition_function = precond_heli_hide, prefix_name = ""}, -- heli_hide [19] = {job_type = "point_job", prior = 3} -- heli point } ------------------------------------------------------------------------------------ -- Smart Job Automation functions ------------------------------------------------------------------------------------ local function init_campfire_point_jobs(smart,stalker_jobs,gname,ltx) -- Don't generate this job if current smart terrain is blacklisted. if ( campfire_point_smart_blacklist[gname] == true ) then return end local max_jobs = smart.max_population > 0 and smart.max_population*3 or 1 for i=1, max_jobs do local name = gname.."_campfire_point_"..i stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 1} local job_ltx = [[ [logic@%s] active = campfire_point@%s [campfire_point@%s] smart = %s meet = meet@generic_lager use_camp = {!npc_community(zombied)} true, false anim = {!npc_community(zombied)} sit_ass, guard ]] job_ltx = strformat(job_ltx,name,name,name,gname) if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx end end local function init_generic_point_jobs(smart,stalker_jobs,gname,ltx) -- Don't generate this job if current smart terrain is blacklisted. if ( generic_point_smart_blacklist[gname] == true ) then return end local max_jobs = smart.max_population > 0 and smart.max_population*3 or 1 for i=1, max_jobs do local name = gname.."_point_"..i stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 0} local job_ltx = [[ [logic@%s] active = cover@%s [cover@%s] meet = meet@generic_lager smart = %s radius_min = 3 radius_max = 8 use_attack_direction = false anim = {!npc_community(zombied)} sit_ass, guard ]] job_ltx = strformat(job_ltx,name,name,name,gname) if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx end end local function init_surge_jobs(smart,stalker_jobs,gname,ltx) local i = 1 while level.patrol_path_exists(gname.."_surge_"..i.."_walk") do local name = gname.."_surge_"..i.."_walk" stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 2} local job_ltx = [[ [logic@%s] active = walker@%s [walker@%s] sound_idle = state use_camp = true meet = meet@generic_lager path_walk = surge_%s_walk def_state_standing = guard def_state_moving = {=dist_to_job_point_ge(25)} sprint, {=dist_to_job_point_ge(5)} run, patrol ]] job_ltx = strformat(job_ltx,name,name,name,i) if (level.patrol_path_exists(gname.."_surge_"..i.."_look")) then job_ltx = job_ltx.."path_look = surge_"..i.."_look\n" end if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_sleep_jobs(smart,stalker_jobs,gname,ltx) local i = 1 while level.patrol_path_exists(gname.."_sleep_"..i) do local name = gname.."_sleep_"..i stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 3} local job_ltx = [[ [logic@%s] active = sleeper@%s [sleeper@%s] path_main = sleep_%s ]] job_ltx = strformat(job_ltx,name,name,name,i) if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_collector_jobs(smart,stalker_jobs,gname,ltx) local i = 1 while level.patrol_path_exists(gname.."_collector_"..i.."_walk") do local name = gname.."_collector_"..i.."_walk" stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 4} local job_ltx = [[ [logic@%s] active = walker@%s [walker@%s] sound_idle = state meet = meet@generic_lager path_walk = collector_%s_walk def_state_standing = guard def_state_moving = patrol ]] job_ltx = strformat(job_ltx,name,name,name,i) if (level.patrol_path_exists(gname.."_collector_"..i.."_look")) then job_ltx = job_ltx.."path_look = collector_"..i.."_look\n" end if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_walker_jobs(smart,stalker_jobs,gname,ltx) -- Don't generate this job if current smart terrain is blacklisted. if ( walker_smart_blacklist[gname] == true ) then return end local i = 1 while level.patrol_path_exists(gname.."_walker_"..i.."_walk") do local name = gname.."_walker_"..i.."_walk" stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 5} local job_ltx = [[ [logic@%s] active = walker@%s [walker@%s] sound_idle = state meet = meet@generic_lager path_walk = walker_%s_walk def_state_standing = guard def_state_moving = patrol ]] job_ltx = strformat(job_ltx,name,name,name,i) if (level.patrol_path_exists(gname.."_walker_"..i.."_look")) then job_ltx = job_ltx.."path_look = walker_"..i.."_look\n" end if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_patrol_jobs(smart,stalker_jobs,gname,ltx) local ptr, wp_prop, job_count local i = 1 while level.patrol_path_exists(gname.."_patrol_"..i.."_walk") do local name = gname.."_patrol_"..i.."_walk" ptr = patrol(name) wp_prop = utils.parse_waypoint_data(name, ptr:flags(0), ptr:name(0)) job_count = 3 if (wp_prop.count) then job_count = wp_prop.count end for n = 1, job_count do stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 6} end local job_ltx = [[ [logic@%s] active = patrol@%s [patrol@%s] meet = meet@generic_lager formation = back path_walk = patrol_%s_walk ]] --on_signal = end| %=search_gulag_job% job_ltx = strformat(job_ltx,name,name,name,i) if (level.patrol_path_exists(gname.."_patrol_"..i.."_look")) then job_ltx = job_ltx.."path_look = patrol_"..i.."_look\n" end if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_animpoint_jobs(smart,stalker_jobs,gname,ltx) local i = 1 while se_smart_cover.registered_smartcovers[gname.."_animpoint_"..i] ~= nil do local name = gname.."_animpoint_"..i stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 7} local job_ltx = [[ [logic@%s] active = animpoint@%s [animpoint@%s] meet = meet@generic_animpoint cover_name = %s ]] job_ltx = strformat(job_ltx,name,name,name,name) if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end -- surge i = 1 while se_smart_cover.registered_smartcovers[gname.."_surge_animpoint_"..i] ~= nil do local name = gname.."_surge_animpoint_"..i stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 8} local job_ltx = [[ [logic@%s] active = animpoint@%s [animpoint@%s] meet = meet@generic_animpoint cover_name = %s ]] job_ltx = strformat(job_ltx,name,name,name,name) if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end -- ._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._. -- -- init_guard_jobs() -- -- Changelist -- ---------- -- 23/07/2024 5:14:16 PM [moonshroom] -- -> Disabled follower job. -- -- ._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._. local function init_guard_jobs(smart,stalker_jobs,gname,ltx) local i = 1 local guard_timeout while level.patrol_path_exists(gname.."_guard_"..i.."_walk") do local name = gname.."_guard_"..i.."_walk" stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 9} -- stalker_jobs[#stalker_jobs+1] = {section = "logic@follower_"..name, job_type_id = 10} --, precondition_params = { changing_job = "logic@"..name }, precondition_function = precond_guard_follower}) local job_ltx = [[ [logic@%s] active = walker@%s [walker@%s] meet = meet@generic_lager path_walk = guard_%s_walk ]] job_ltx = strformat(job_ltx,name,name,name,i) if (level.patrol_path_exists(gname.."_guard_"..i.."_look")) then job_ltx = job_ltx.."path_look = guard_"..i.."_look\n" end if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx --on_info = {!is_obj_on_job(logic@follower_%s:3)} walker@%s local job_ltx2 = [[ [walker1@%s] meet = meet@generic_lager path_walk = guard_%s_walk def_state_standing = wait_na ;on_info2 = {=dist_to_obj_on_job_le(logic@follower_%s:4)} remark@%s ]] job_ltx2 = strformat(job_ltx2,name,i,name,name,name,name) if (level.patrol_path_exists(gname.."_guard_"..i.."_look")) then job_ltx2 = job_ltx2.."path_look = guard_"..i.."_look\n" end if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx2 = job_ltx2 .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx2 = job_ltx2 .."out_restr = "..smart.def_restr.."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx2 = job_ltx2 .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx2 local job_ltx3 = [[ [remark@%s] anim = wait_na target = job | logic@follower_%s, %s ]] job_ltx3 = strformat(job_ltx3,name,name,gname) if (smart.def_restr) then job_ltx3 = job_ltx3 .."out_restr = "..smart.def_restr.."\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx3 local job_ltx4 = [[ [logic@follower_%s] active = walker@follow_%s [walker@follow_%s] meet = meet@generic_lager path_walk = guard_%s_walk on_info = {=dist_to_obj_on_job_le(logic@%s:4)} remark@follower_%s ]] job_ltx4 = strformat(job_ltx4,name,name,name,i,name,name) if (level.patrol_path_exists(gname.."_guard_"..i.."_look")) then job_ltx4 = job_ltx4.."path_look = guard_"..i.."_look\n" end if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx4 = job_ltx4 .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then job_ltx4 = job_ltx4 .."out_restr = "..smart.def_restr.."\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx4 guard_timeout = tostring(math.random(20,21))*1000 local job_ltx5 = [[ [remark@follower_%s] anim = wait_na target = job | logic@%s, %s ;on_timer = %s | %=switch_to_desired_job% on_info = {!dist_to_obj_on_job_le(logic@%s:4)} walker@follow_%s ]] job_ltx5 = strformat(job_ltx5,name,name,gname,guard_timeout) if (smart.def_restr) then job_ltx5 = job_ltx5 .."out_restr = "..smart.def_restr.."\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx5 i = i + 1 end end local function init_sniper_jobs(smart,stalker_jobs,gname,ltx) local ptr, wp_prop, radius local state local i = 1 while level.patrol_path_exists(gname.."_sniper_"..i.."_walk") do local name = gname.."_sniper_"..i.."_walk" stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 11} ptr = patrol(name) wp_prop = utils.parse_waypoint_data(name, ptr:flags(0), ptr:name(0)) state = "guard" if wp_prop.state ~= nil then if wp_prop.state == "stand" then state = "threat" end end radius = 10 if wp_prop.radius ~= nil then radius = wp_prop.radius end local job_ltx = [[ [logic@%s] active = camper@%s [camper@%s] meet = meet@generic_lager path_walk = sniper_%s_walk sniper = true radius = %s def_state_campering = %s def_state_campering_fire = %s ]] job_ltx = strformat(job_ltx,name,name,name,i,radius,state,"hide_fire") if (level.patrol_path_exists(gname.."_sniper_"..i.."_look")) then job_ltx = job_ltx.."path_look = sniper_"..i.."_look\n" end --[[ if (xr_gulag.job_in_restrictor(smart,gname.."_sniper_"..i.."_restr",name)) then job_ltx = job_ltx.."out_restr = " .. gname.."_sniper_"..i.."_restr" end --]] if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then --job_ltx = job_ltx .."out_restr = "..smart.def_restr..","..combat_restrictor.get_job_restrictor(name).."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_camper_jobs(smart,stalker_jobs,gname,ltx) local ptr, wp_prop, radius, state local i = 1 while level.patrol_path_exists(gname.."_camper_"..i.."_walk") do local name = gname.."_camper_"..i.."_walk" stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 12} ptr = patrol(name) wp_prop = utils.parse_waypoint_data(name, ptr:flags(0), ptr:name(0)) state = "guard" radius = 10 if wp_prop.state ~= nil then if wp_prop.state == "stand" then state = "threat" end end if wp_prop.radius ~= nil then radius = wp_prop.radius end local job_ltx = [[ [logic@%s] active = camper@%s [camper@%s] meet = meet@generic_lager radius = %s path_walk = camper_%s_walk def_state_moving = {=has_enemy} raid, patrol def_state_campering = %s def_state_campering_fire = %s ]] job_ltx = strformat(job_ltx,name,name,name,radius,i,state,"hide_fire") if (level.patrol_path_exists(gname.."_camper_"..i.."_look")) then job_ltx = job_ltx.."path_look = camper_"..i.."_look\n" end if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end --[[ if (xr_gulag.job_in_restrictor(smart,gname.."_camper_"..i.."_restr",name)) then job_ltx = job_ltx.."out_restr = " .. gname.."_camper_"..i.."_restr" end --]] if (smart.def_restr) then --job_ltx = job_ltx .."out_restr = "..smart.def_restr..","..combat_restrictor.get_job_restrictor(name).."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_beh_jobs(smart,stalker_jobs,gname,ltx) if not (beh_ini) then return end local i = 1 while beh_ini:section_exist(gname.."_beh_"..i) do local name = gname.."_beh_"..i stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 13} local job_ltx = [[ [logic@%s] active = beh@%s [beh@%s] meet = meet@generic_lager ]] job_ltx = strformat(job_ltx,name,name,name) local _root = beh_ini:collect_section(name) for key,val in pairs (_root) do job_ltx = job_ltx .. key .. " = " .. val .. "\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end -- Beh Surge i = 1 while beh_ini:section_exist(gname.."_beh_surge_"..i) do local name = gname.."_beh_surge_"..i stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 14} local job_ltx = [[ [logic@%s] active = beh@%s [beh@%s] meet = meet@generic_lager ]] job_ltx = strformat(job_ltx,name,name,name) local _root = beh_ini:collect_section(name) for key,val in pairs (_root) do job_ltx = job_ltx .. key .. " = " .. val .. "\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end -- Beh Trader i = 1 while beh_ini:section_exist(gname.."_beh_trade_"..i) do local name = gname.."_beh_trade_"..i stalker_jobs[#stalker_jobs+1] = {section = "logic@"..name, job_type_id = 15} local job_ltx = [[ [logic@%s] active = beh@%s [beh@%s] meet = meet@generic_lager ]] job_ltx = strformat(job_ltx,name,name,name) local _root = beh_ini:collect_section(name) for key,val in pairs (_root) do job_ltx = job_ltx .. key .. " = " .. val .. "\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_monster_jobs(smart,monster_jobs,gname,ltx) local max_jobs = smart.max_population > 0 and smart.max_population*4 or 1 for i=1, max_jobs do local name = gname.."_home_"..i monster_jobs[#monster_jobs+1] = {section = "logic@"..name, job_type_id = 16} local job_ltx = [[ [logic@%s] active = mob_home@%s [mob_home@%s] gulag_point = true home_min_radius = 10 -- //////////////////////////////////////////////////////////////////////////////////////////////// -- -- Increased mutant home radius -- -- - Values used from Revo Lucas commit cb038c5 2017-10-18 -- -- Modified by DoctorX -- for DoctorX Call of The Zone 1.0 -- Last modified October 05, 2019 -- -- ------------------------------------------------------------------------------------------------ -- home_mid_radius = 20 -- home_max_radius = 30 home_mid_radius = 50 home_max_radius = 100 -- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ]] job_ltx = strformat(job_ltx,name,name,name) if (smart.def_restr) then job_ltx = job_ltx .."out_restr = "..smart.def_restr.."\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx end end local function init_heli_move_jobs(smart,heli_jobs,gname,ltx) local i = 1 while level.patrol_path_exists(gname.."_heli_"..i.."_fly") do local name = gname.."_heli_"..i.."_fly" heli_jobs[#heli_jobs+1] = {section = "logic@"..name, job_type_id = 17} local job_ltx = [[ [logic@%s] active = heli_move@%s [heli_move@%s] path_move = heli_%s_fly defend_job = true defend_job_radius = 50 engine_sound = true show_health = false fire_trail = false immortal = false mute = false ]] job_ltx = strformat(job_ltx,name,name,name,i) if (level.patrol_path_exists(gname.."_heli_"..i.."_fly_look")) then job_ltx = job_ltx.."path_look = heli_"..i.."_fly_look\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_heli_hide_jobs(smart,heli_jobs,gname,ltx) local i = 1 local gg = game_graph() local sim = alife() local level_name = sim:level_name(gg:vertex(smart.m_game_vertex_id):level_id()) local job_ltx = [[ [heli_move@hide] path_move = %s retreat_mode = 2 max_velocity = 60 max_mgun_dist = 100 max_rocket_dist = 100 min_mgun_dist = 20 min_rocket_dist = 50 use_rocket = false use_mgun = false engine_sound = false show_health = false fire_trail = false immortal = true mute = true ]] job_ltx = strformat(job_ltx,level_name.."_heli_hide") ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx -- increase if you want more then 1 heli hide job per smart for i=1, 1 do local name = gname.."_heli_"..i.."_hide" heli_jobs[#heli_jobs+1] = {section = "logic@"..name, job_type_id = 18} local job_ltx = [[ [logic@%s] active = heli_move@%s [heli_move@%s] path_move = %s retreat_mode = 1 max_velocity = 60 max_mgun_dist = 100 max_rocket_dist = 100 min_mgun_dist = 20 min_rocket_dist = 50 use_rocket = true use_mgun = true engine_sound = true show_health = false fire_trail = false on_info = {=heli_dist_to_max_bounding_le(3)} heli_move@hide ]] job_ltx = strformat(job_ltx,name,name,name,level_name.."_goto_hide") ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx i = i + 1 end end local function init_heli_move_point_jobs(smart,heli_jobs,gname,ltx) -- default heli job for all smarts local i = 1 local name = gname.."_heli_"..i.."_point" heli_jobs[#heli_jobs+1] = {section = "logic@"..name, job_type_id = 19} local job_ltx = [[ [logic@%s] active = heli_move@%s [heli_move@%s] max_velocity = 60 max_mgun_dist = 100 max_rocket_dist = 100 min_mgun_dist = 20 min_rocket_dist = 50 use_rocket = true use_mgun = true engine_sound = true show_health = false fire_trail = false immortal = false mute = false ]] job_ltx = strformat(job_ltx,name,name,name) if (level.patrol_path_exists(gname.."_heli_"..i.."_point_look")) then job_ltx = job_ltx.."path_look = heli_"..i.."_point_look\n" end if (smart.safe_restr and xr_gulag.job_in_restrictor(smart, smart.safe_restr, name)) then job_ltx = job_ltx .. "invulnerable = {=npc_in_zone("..smart.safe_restr..")} true\n" end if (smart.def_restr) then --job_ltx = job_ltx .."out_restr = "..smart.def_restr..","..combat_restrictor.get_job_restrictor(name).."\n" end if (smart.base_on_actor_control and smart.base_on_actor_control.ignore_zone and xr_gulag.job_in_restrictor(smart, smart.base_on_actor_control.ignore_zone, name)) then job_ltx = job_ltx .."combat_ignore_cond = {=npc_in_zone("..smart.base_on_actor_control.ignore_zone..")} true\ncombat_ignore_keep_when_attacked = true\n" end ltx_size = ltx_size + 1 ltx[ltx_size] = job_ltx end local function add_exclusive_job(smart,sect, work_field, smart_ini) local work = smart_ini:r_string_ex(sect,work_field) if not (work) then return end local ini_path = "scripts\\"..work local fs = getFS() if fs:exist("$game_config$",ini_path) == nil then printf("there is no configuration file [%s]", ini_path) return end local job_ini_file = ini_file(ini_path) local lsec = "logic@"..work_field local new_prior = job_ini_file:r_float_ex(lsec,"prior") or 100 local job_suitable = job_ini_file:r_string_ex(lsec,"suitable") or "true" local is_monster = job_ini_file:r_bool_ex(lsec,"monster_job",false) local is_heli = job_ini_file:r_bool_ex(lsec,"heli_job",false) local prefix = job_ini_file:r_string_ex(lsec,"prefix_name") local active_section = job_ini_file:r_string_ex(lsec,"active") or "nil" local scheme = utils.get_scheme_by_section(active_section) local job_type = job_type_by_scheme[scheme] if not (job_type) then printf("gulag_general.add_exclusive_job(): Invalid job_type! smart=%s job_type = %s scheme = %s job=%s sect=%s",smart:name(),job_type,scheme,work,sect) return end if scheme == "mob_home" then if (job_ini_file:r_bool_ex(active_section,"gulag_point",false)) then job_type = "point_job" end end if not (job_suitable) then local new_job = {section = lsec, ini_path = ini_path, ltx = job_ini_file, job_type = job_type, prior = new_prior, prefix_name = prefix, exclusive = true} if (is_monster) then rawset(smart.monster_jobs,#smart.monster_jobs+1,new_job) elseif (is_heli) then rawset(smart.heli_jobs,#smart.heli_jobs+1,new_job) else rawset(smart.stalker_jobs,#smart.stalker_jobs+1,new_job) end return end local condlist = xr_logic.parse_condlist(smart, lsec, "suitable", job_suitable) local new_job = {section = lsec, ini_path = ini_path, ltx = job_ini_file, job_type = job_type, prior = new_prior, precondition_params = { condlist = condlist }, precondition_function = precond_exclusive, prefix_name = prefix, exclusive = true} if (is_monster) then rawset(smart.monster_jobs,#smart.monster_jobs+1,new_job) elseif (is_heli) then rawset(smart.heli_jobs,#smart.heli_jobs+1,new_job) else rawset(smart.stalker_jobs,#smart.stalker_jobs+1,new_job) end end --------------------------------------------------------------------------------------- -- LOAD SMART JOBS -- called in smart_terrain.script --------------------------------------------------------------------------------------- local str_builder = {} function load_job(smart) -- Reset Unique ID counter for each smart_terrain ids = 0 if not (beh_ini) then beh_ini = ini_file_ex("beh_gulag_jobs.ltx") end local gname = smart:name() ltx_size = 1 str_builder[ltx_size] = [[ [meet@generic_lager] close_distance = {=is_wounded} 0, {!is_squad_commander} 0, {!after_first_meet !actor_friend =actor_has_weapon} 3, 3 close_anim = {=is_wounded} nil, {!is_squad_commander} nil, {=actor_has_weapon} nil, talk_default close_snd_hello = {=is_wounded} nil, {!is_squad_commander} nil, {=actor_enemy} nil, {!after_first_meet !actor_friend =actor_has_weapon} meet_stop, meet_hello close_snd_bye = nil close_victim = {=is_wounded} nil, {!is_squad_commander} nil, actor far_distance = {=is_wounded} 0, {!is_squad_commander} 0, {!after_first_meet !actor_friend =actor_has_weapon} 5, 5 far_anim = {=is_wounded} nil, {!is_squad_commander} nil, {!after_first_meet !actor_friend =actor_has_weapon} threat_na, nil far_snd = {=is_wounded} nil, {!is_squad_commander} nil, {=actor_enemy} nil, {!after_first_meet !actor_friend =actor_has_weapon} meet_hide_weapon, meet_wait far_victim = {=is_wounded} nil, {!is_squad_commander} nil, actor use = {=is_wounded} false, {!is_squad_commander} false, {=actor_enemy} false, {=has_enemy} false, {=dist_to_actor_le(3)} true, false snd_on_use = {=is_wounded} nil, {=actor_enemy} nil, {!is_squad_commander} meet_use_no_talk_leader, {=has_enemy} meet_use_no_fight, {=dist_to_actor_le(3)} meet_use_no_default, nil meet_dialog = nil abuse = {=has_enemy} false, true trade_enable = {=actor_enemy} false, true allow_break = true use_text = nil [meet@generic_animpoint] close_distance = 0 close_anim = {!is_squad_commander} nil, nil close_snd_hello = {!is_squad_commander} nil, nil close_snd_bye = {!is_squad_commander} nil, nil close_victim = {!is_squad_commander} nil, nil far_distance = 0 far_anim = nil far_snd = nil far_victim = nil use = {=is_wounded} false, {!is_squad_commander} false, {=actor_enemy} false, {=has_enemy} false, {=dist_to_actor_le(3)} true, false snd_on_use = {=is_wounded} nil, {=actor_enemy} nil, {!is_squad_commander} meet_use_no_talk_leader, {=has_enemy} meet_use_no_fight, {=dist_to_actor_le(3)} meet_use_no_default, nil meet_dialog = nil abuse = {=has_enemy} false, true trade_enable = true allow_break = true meet_on_talking = true use_text = nil ]] -- these tables store job information of the smart terrain local stalker_jobs = {} local monster_jobs = {} local heli_jobs = {} -- create heli jobs init_heli_move_jobs(smart,heli_jobs,gname,str_builder) init_heli_move_point_jobs(smart,heli_jobs,gname,str_builder) init_heli_hide_jobs(smart,heli_jobs,gname,str_builder) -- create stalker jobs init_campfire_point_jobs(smart,stalker_jobs,gname,str_builder) init_generic_point_jobs(smart,stalker_jobs,gname,str_builder) init_surge_jobs(smart,stalker_jobs,gname,str_builder) init_sleep_jobs(smart,stalker_jobs,gname,str_builder) init_collector_jobs(smart,stalker_jobs,gname,str_builder) init_walker_jobs(smart,stalker_jobs,gname,str_builder) init_patrol_jobs(smart,stalker_jobs,gname,str_builder) init_animpoint_jobs(smart,stalker_jobs,gname,str_builder) init_guard_jobs(smart,stalker_jobs,gname,str_builder) init_sniper_jobs(smart,stalker_jobs,gname,str_builder) init_camper_jobs(smart,stalker_jobs,gname,str_builder) init_beh_jobs(smart,stalker_jobs,gname,str_builder) -- create monster jobs init_monster_jobs(smart,monster_jobs,gname,str_builder) -- reference newly created job tables smart.stalker_jobs = stalker_jobs smart.monster_jobs = monster_jobs smart.heli_jobs = heli_jobs -- create exclusive jobs local smart_ini = smart.ini if (smart_ini:section_exist("smart_terrain")) then if smart_ini:section_exist("exclusive") then local n = smart_ini:line_count("exclusive") for i=0,n-1 do local result, id, value = smart_ini:r_line("exclusive",i,"","") add_exclusive_job(smart,"exclusive", id, smart_ini) end else local num = 1 while smart_ini:line_exist("smart_terrain", "work"..num) do add_exclusive_job(smart,"smart_terrain", "work"..num, smart_ini) num = num + 1 end end end -- sort jobs for fast iteration. Highest priority jobs will be at lower indices table.sort(smart.stalker_jobs, function(a,b) return get_job_prior(a) > get_job_prior(b) end) table.sort(smart.monster_jobs, function(a,b) return get_job_prior(a) > get_job_prior(b) end) table.sort(smart.heli_jobs, function(a,b) return get_job_prior(a) > get_job_prior(b) end) -- generate the INI file db.dynamic_ltx[gname] = create_ini_file(table.concat(str_builder,"\n")) smart.ltx = db.dynamic_ltx[gname] smart.ltx_name = "*"..gname iempty_table(str_builder) end