diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 74f3149610..41b742cfe7 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -55,3 +55,14 @@ GLOBAL_VAR(obfs_x) GLOBAL_VAR(obfs_y) GLOBAL_VAR_INIT(ai_xeno_weeding, TRUE) + +GLOBAL_VAR_INIT(xenosurge_spawner_limit, 30) + +GLOBAL_VAR_INIT(xenosurge_wave_max, 3) +GLOBAL_VAR_INIT(xenosurge_wave_current, 0) +GLOBAL_VAR_INIT(xenosurge_wave_delay, 60) + +GLOBAL_VAR_INIT(xenosurge_wave_xenos_max, 100) +GLOBAL_VAR_INIT(xenosurge_wave_xenos_current, 0) + +GLOBAL_LIST_EMPTY(xenosurge_configured_spawners) diff --git a/code/modules/admin/PvE/spawner_control.dm b/code/modules/admin/PvE/spawner_control.dm new file mode 100644 index 0000000000..f0e0ac94a4 --- /dev/null +++ b/code/modules/admin/PvE/spawner_control.dm @@ -0,0 +1,72 @@ +/client/proc/create_spawner() + set category = "Xenosurge.Spawners" + set name = "Create Spawner" + set desc = "Creates and launches configuration of a spawner at current location." + + if(!check_rights(R_ADMIN)) + return + + var/max_to_pass = tgui_input_number(usr, "How many xenos total from this spawner","Spawner Setup",5, timeout = 0) + if(max_to_pass == null) return 0 + var/delay_to_pass = tgui_input_number(usr, "Dealy, in ticks (~10 a second) between spawn checks","Spawner Setup",100, timeout = 0) + if(delay_to_pass == null) return 0 + var/type_to_pass = tgui_input_list(usr, "Xeno Type","Spawner Setup",list(XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_LURKER, XENO_CASTE_CRUSHER, XENO_CASTE_FACEHUGGER),timeout = 0, default = XENO_CASTE_DRONE) + if(type_to_pass == null) return 0 + var/spawner_cycle + spawner_cycle = tgui_alert(usr, "Move your ghost to the postion of the spawner and press OK. Cancel to cancel.","SPAWNER",list("Cancel","OK"), timeout = 0) + while(spawner_cycle == "OK") + var/turf/spawner_turf = mob.loc + var/obj/structure/xenosurge_spawner/spawner = new(spawner_turf) + if(spawner.setup_spawner(max = max_to_pass, delay = delay_to_pass, type = type_to_pass) == 0) + to_chat(usr, SPAN_WARNING("Spawner not configured. Discarding.")) + qdel(spawner) + spawner_cycle = tgui_alert(usr, "Move your ghost to the postion of the spawner and press OK. Cancel to cancel.","SPAWNER",list("Cancel","OK"), timeout = 0) + return + +/client/proc/setup_surge() + set category = "Xenosurge.Spawners" + set name = "Xenosurge - Setup" + set desc = "Sets parameters for next wave surge." + + if(!check_rights(R_ADMIN)) + return + var/surge_setup_value + switch(tgui_input_list(usr, "Max:[GLOB.xenosurge_spawner_limit]\nWaves:[GLOB.xenosurge_wave_max]\nDelay:[GLOB.xenosurge_wave_delay]\nXenos:[GLOB.xenosurge_wave_xenos_max]", "SURGE", list("Max","Waves","Delay","Xenos"))) + if(null) + return + if("Max") + surge_setup_value = tgui_input_number(usr, "Pick maximum xenos at once. This is a global control to prevent lag. Generally suggest leaving this alone.", "SURGE",GLOB.xenosurge_spawner_limit,timeout = 0) + if(surge_setup_value == null) return + GLOB.xenosurge_spawner_limit = surge_setup_value + if("Waves") + surge_setup_value = tgui_input_number(usr, "Select ammount of WAVES", "SURGE",GLOB.xenosurge_wave_max,timeout = 0) + if(surge_setup_value == null) return + GLOB.xenosurge_wave_max = surge_setup_value + if("Delay") + surge_setup_value = tgui_input_number(usr, "Pick delay, in ticks (~10 per second), between waves.", "SURGE",GLOB.xenosurge_wave_delay,timeout = 0) + if(surge_setup_value == null) return + GLOB.xenosurge_wave_delay = surge_setup_value + if("Xenos") + surge_setup_value = tgui_input_number(usr, "Xenos to spawn per wave", "SURGE",GLOB.xenosurge_wave_xenos_max,timeout = 0) + if(surge_setup_value == null) return + GLOB.xenosurge_wave_xenos_max = surge_setup_value + return + +/client/proc/start_surge() + set category = "Xenosurge.Spawners" + set name = "Xenosurge - Start" + set desc = "Checks critcial params, starts surge." + + if(!check_rights(R_ADMIN)) + return + if(tgui_alert(usr, "Confirm: Start Xenosurge?","START",list("Cancel","OK"), timeout = 0) == "OK") + var/spawner_count = 0 + for (var/obj/structure/xenosurge_spawner/spawner in GLOB.xenosurge_configured_spawners) + if(spawner == null) + to_chat(usr, SPAN_WARNING("No spawner found. Aborted.")) + return + if(spawner.spawner_initiated == TRUE) + spawner.start_spawning() + spawner_count += 1 + to_chat(usr, SPAN_INFO("Spawner activation complete. Spawners activated: [spawner_count].")) + log_admin("[usr] has activated a [spawner_count] spawner Xenosurge. Parameters: Max:[GLOB.xenosurge_spawner_limit], Waves:[GLOB.xenosurge_wave_max], Delay:[GLOB.xenosurge_wave_delay], Xenos:[GLOB.xenosurge_wave_xenos_max]") diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 4d1f4ba083..4475f26261 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -141,6 +141,9 @@ var/list/admin_verbs_minor_event = list( /client/proc/admin_biohazard_alert, /client/proc/toggle_hardcore_perma, /client/proc/toggle_bypass_joe_restriction, + /client/proc/create_spawner, + /client/proc/setup_surge, + /client/proc/start_surge, ) var/list/admin_verbs_major_event = list( diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index f27616508d..a19397a69f 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -342,6 +342,8 @@ var/atom/movable/vis_obj/xeno_wounds/wound_icon_holder var/atom/movable/vis_obj/xeno_pack/backpack_icon_holder + //ai spawner fun + var/spawner_id /mob/living/carbon/xenomorph/Initialize(mapload, mob/living/carbon/xenomorph/oldXeno, h_number, ai_hard_off = FALSE) var/area/A = get_area(src) diff --git a/code/modules/pve/spawner.dm b/code/modules/pve/spawner.dm new file mode 100644 index 0000000000..278580553b --- /dev/null +++ b/code/modules/pve/spawner.dm @@ -0,0 +1,103 @@ +#define AI_XENOS list(XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_LURKER, XENO_CASTE_CRUSHER, XENO_CASTE_FACEHUGGER) +#define XENO_BEHAVIORS list("Attack", "Capture", "Hive", "Build") +#define XENO_BEHAVIORS_ASSOC list("Attack" = /datum/component/ai_behavior_override/attack, "Capture" = /datum/component/ai_behavior_override/capture, "Hive" = /datum/component/ai_behavior_override/hive, "Build" = /datum/component/ai_behavior_override/build) +GLOBAL_VAR_INIT(spawner_number, 1) + + +/obj/structure/xenosurge_spawner + name = "AI spawner" + desc = "just spawnin' shit" + opacity = FALSE + mouse_opacity = FALSE + density = FALSE + invisibility = INVISIBILITY_OBSERVER + icon_state = "brazier" + indestructible = TRUE + unacidable = TRUE + unslashable = TRUE + var/xenos_to_spawn_max = 5 + var/xenos_to_spawn_type = XENO_CASTE_DRONE + var/xenos_to_spawn_delay = 100 + var/spawner_initiated = FALSE + var/spawner_id + +/obj/structure/xenosurge_spawner/proc/spawner_limit_reached() + log_admin("Wave limit of [GLOB.xenosurge_wave_xenos_max] reached. Disabling spawners.") + for (var/obj/structure/xenosurge_spawner/spawner in GLOB.xenosurge_configured_spawners) + spawner.spawner_initiated = FALSE + GLOB.xenosurge_wave_xenos_current = 0 + +/obj/structure/xenosurge_spawner/proc/spawner_loop() + sleep(xenos_to_spawn_delay) + if(spawner_initiated == FALSE) + return + else + spawner_spawn() + +/obj/structure/xenosurge_spawner/proc/spawner_spawn() + var/global_xeno_count = 0 + var/ai_count = 0 + for (var/mob/living/carbon/xenomorph/xeno in GLOB.living_xeno_list) + if(xeno.loc != null) + global_xeno_count += 1 + if(xeno.spawner_id == spawner_id) + ai_count += 1 + if(global_xeno_count > GLOB.xenosurge_spawner_limit) + log_admin("Spawner [spawner_id] returns [global_xeno_count] global xenos, over the [GLOB.xenosurge_spawner_limit], skipping.") + if(ai_count >= xenos_to_spawn_max) + log_admin("Spawner [spawner_id] returns [ai_count] out of [xenos_to_spawn_max], skipping.") + else + var/xenos_to_spawn = xenos_to_spawn_max - ai_count + log_admin("Spawner [spawner_id] returns [ai_count] out of [xenos_to_spawn_max], generating [xenos_to_spawn].") + while(xenos_to_spawn > 0) + var/turf/spawner_xeno_turf = get_random_turf_in_range(src, 2, 0) + var/spawner_xeno_typepath = RoleAuthority.get_caste_by_text(xenos_to_spawn_type) + var/mob/living/carbon/xenomorph/drone/spawned_xeno = new spawner_xeno_typepath(spawner_xeno_turf, null, "xeno_hive_normal") + spawned_xeno.spawner_id = spawner_id + xenos_to_spawn -= 1 + global_xeno_count += 1 + GLOB.xenosurge_wave_xenos_current += 1 + if(global_xeno_count >= GLOB.xenosurge_spawner_limit) + log_admin("Spawner [spawner_id] has reached [xenos_to_spawn_max] spawned xenos, skipping rest.") + break + if(GLOB.xenosurge_wave_xenos_current >= GLOB.xenosurge_wave_xenos_max) + spawner_limit_reached() + else + INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/structure/xenosurge_spawner/, spawner_loop)) + +/obj/structure/xenosurge_spawner/proc/setup_spawner(max = null, delay = null, type = null) + if(max == null) + xenos_to_spawn_max = tgui_input_number(usr, "How many xenos total from this spawner","Spawner Setup",xenos_to_spawn_max, timeout = 0) + if(xenos_to_spawn_max == null) return 0 + else + xenos_to_spawn_max = max + if(delay == null) + xenos_to_spawn_delay = tgui_input_number(usr, "Dealy, in ticks (~10 a second) between spawn checks","Spawner Setup",xenos_to_spawn_delay, timeout = 0) + if(xenos_to_spawn_delay == null) return 0 + else + xenos_to_spawn_delay = delay + if(type == null) + xenos_to_spawn_type = tgui_input_list(usr, "Xeno Type","Spawner Setup",AI_XENOS,timeout = 0, default = xenos_to_spawn_type) + if(xenos_to_spawn_type == null) return 0 + else + xenos_to_spawn_type = type + if(!spawner_id) + spawner_id = GLOB.spawner_number + GLOB.spawner_number += 1 + spawner_initiated = TRUE + to_chat(usr, SPAN_INFO("Spawner number [spawner_id] set.")) + GLOB.xenosurge_configured_spawners.Add(src) + return 1 + +/obj/structure/xenosurge_spawner/proc/start_spawning() + if(spawner_initiated == FALSE) + to_chat(usr, SPAN_WARNING("Failed. Spawner not initiated.")) + return + else + log_admin("Spawner [spawner_id] starting.") + spawner_spawn() + return + +#undef AI_XENOS +#undef XENO_BEHAVIORS +#undef XENO_BEHAVIORS_ASSOC diff --git a/colonialmarines.dme b/colonialmarines.dme index d814088a41..267d37df31 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -1424,6 +1424,7 @@ #include "code\modules\admin\player_panel\actions\physical.dm" #include "code\modules\admin\player_panel\actions\punish.dm" #include "code\modules\admin\player_panel\actions\transform.dm" +#include "code\modules\admin\PvE\spawner_control.dm" #include "code\modules\admin\tabs\admin_tab.dm" #include "code\modules\admin\tabs\debug_tab.dm" #include "code\modules\admin\tabs\event_tab.dm" @@ -2256,6 +2257,7 @@ #include "code\modules\projectiles\magazines\shotguns.dm" #include "code\modules\projectiles\magazines\smgs.dm" #include "code\modules\projectiles\magazines\specialist.dm" +#include "code\modules\pve\spawner.dm" #include "code\modules\reagents\Chemistry-Colours.dm" #include "code\modules\reagents\Chemistry-Generator.dm" #include "code\modules\reagents\Chemistry-Holder.dm"