diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm index 82237cd793b2..e9eb4ca4c1ed 100644 --- a/code/__DEFINES/xeno.dm +++ b/code/__DEFINES/xeno.dm @@ -165,6 +165,12 @@ /// The amount of time after round start before buried larva spawns are disallowed #define XENO_BURIED_LARVA_TIME_LIMIT (30 MINUTES) +/// The time when xenos can start taking over comm towers +#define XENO_COMM_ACQUISITION_TIME (90 MINUTES) + +/// The time it takes for a pylon to give one larva while activated +#define XENO_PYLON_ACTIVATION_COOLDOWN (5 MINUTES) + /// The time against away_timer when an AFK xeno larva can be replaced #define XENO_LEAVE_TIMER_LARVA 80 //80 seconds /// The time against away_timer when an AFK xeno (not larva) can be replaced diff --git a/code/game/machinery/telecomms/presets.dm b/code/game/machinery/telecomms/presets.dm index a25293aebbd3..68d67b6e702f 100644 --- a/code/game/machinery/telecomms/presets.dm +++ b/code/game/machinery/telecomms/presets.dm @@ -212,12 +212,27 @@ GLOBAL_LIST_EMPTY(all_static_telecomms_towers) freq_listening = list(COLONY_FREQ) var/toggle_cooldown = 0 + /// Tower has been taken over by xenos, is not usable + var/corrupted = FALSE + + /// Held image for the current overlay on the tower from xeno corruption + var/image/corruption_image + +/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/Initialize() + . = ..() + + RegisterSignal(src, COMSIG_ATOM_TURF_CHANGE, PROC_REF(register_with_turf)) + register_with_turf() + /obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/attack_hand(mob/user) if(user.action_busy) return if(toggle_cooldown > world.time) //cooldown only to prevent spam toggling to_chat(user, SPAN_WARNING("\The [src]'s processors are still cooling! Wait before trying to flip the switch again.")) return + if(corrupted) + to_chat(user, SPAN_WARNING("[src] is entangled in resin. Impossible to interact with.")) + return var/current_state = on if(!do_after(user, 20, INTERRUPT_NO_NEEDHAND|BEHAVIOR_IMMOBILE, BUSY_ICON_FRIENDLY, src)) return @@ -282,6 +297,84 @@ GLOBAL_LIST_EMPTY(all_static_telecomms_towers) else update_icon() +/// Handles xenos corrupting the tower when weeds touch the turf it is located on +/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/proc/handle_xeno_acquisition(turf/weeded_turf) + SIGNAL_HANDLER + + if(corrupted) + return + + if(!weeded_turf.weeds) + return + + if(weeded_turf.weeds.weed_strength < WEED_LEVEL_HIVE) + return + + if(!weeded_turf.weeds.parent) + return + + if(!istype(weeded_turf.weeds.parent, /obj/effect/alien/weeds/node/pylon/cluster)) + return + + if(SSticker.mode.is_in_endgame) + return + + if(ROUND_TIME < XENO_COMM_ACQUISITION_TIME) + addtimer(CALLBACK(src, PROC_REF(handle_xeno_acquisition), weeded_turf), (XENO_COMM_ACQUISITION_TIME - ROUND_TIME)) + return + + var/obj/effect/alien/weeds/node/pylon/cluster/parent_node = weeded_turf.weeds.parent + + var/obj/effect/alien/resin/special/cluster/cluster_parent = parent_node.resin_parent + + var/list/held_children_weeds = parent_node.children + var/cluster_loc = cluster_parent.loc + var/linked_hive = cluster_parent.linked_hive + + parent_node.children = list() + + qdel(cluster_parent) + + var/obj/effect/alien/resin/special/pylon/endgame/new_pylon = new(cluster_loc, linked_hive) + new_pylon.node.children = held_children_weeds + + for(var/obj/effect/alien/weeds/weed in new_pylon.node.children) + weed.parent = new_pylon.node + + RegisterSignal(new_pylon, COMSIG_PARENT_QDELETING, PROC_REF(uncorrupt)) + + corrupted = TRUE + + corruption_image = image(icon, icon_state = "resin_growing") + + flick_overlay(src, corruption_image, (2 SECONDS)) + addtimer(CALLBACK(src, PROC_REF(switch_to_idle_corruption)), (2 SECONDS)) + + new_pylon.comms_relay_connection() + +/// Handles removing corruption effects from the comms relay +/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/proc/uncorrupt(datum/deleting_datum) + SIGNAL_HANDLER + + corrupted = FALSE + + overlays -= corruption_image + +/// Handles moving the overlay from growing to idle +/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/proc/switch_to_idle_corruption() + if(!corrupted) + return + + corruption_image = image(icon, icon_state = "resin_idle") + + overlays += corruption_image + +/// Handles re-registering signals on new turfs if changed +/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/proc/register_with_turf() + SIGNAL_HANDLER + + RegisterSignal(get_turf(src), COMSIG_WEEDNODE_GROWTH, PROC_REF(handle_xeno_acquisition)) + /obj/structure/machinery/telecomms/relay/preset/telecomms id = "Telecomms Relay" autolinkers = list("relay") diff --git a/code/modules/cm_aliens/structures/special/pylon_core.dm b/code/modules/cm_aliens/structures/special/pylon_core.dm index 993d4f833fa6..068ffeb659eb 100644 --- a/code/modules/cm_aliens/structures/special/pylon_core.dm +++ b/code/modules/cm_aliens/structures/special/pylon_core.dm @@ -12,6 +12,7 @@ block_range = 0 var/cover_range = WEED_RANGE_PYLON var/node_type = /obj/effect/alien/weeds/node/pylon + var/obj/effect/alien/weeds/node/node var/linked_turfs = list() var/damaged = FALSE @@ -25,7 +26,7 @@ /obj/effect/alien/resin/special/pylon/Initialize(mapload, hive_ref) . = ..() - place_node() + node = place_node() for(var/turf/A in range(round(cover_range*PYLON_COVERAGE_MULT), loc)) LAZYADD(A.linked_pylons, src) linked_turfs += A @@ -34,9 +35,8 @@ for(var/turf/A as anything in linked_turfs) LAZYREMOVE(A.linked_pylons, src) - var/obj/effect/alien/weeds/node/pylon/W = locate() in loc - if(W) - qdel(W) + if(node) + QDEL_NULL(node) . = ..() /obj/effect/alien/resin/special/pylon/attack_alien(mob/living/carbon/xenomorph/M) @@ -87,8 +87,78 @@ playsound(loc, "alien_resin_build", 25) /obj/effect/alien/resin/special/pylon/proc/place_node() - var/obj/effect/alien/weeds/node/pylon/W = new node_type(loc, null, null, linked_hive) - W.resin_parent = src + var/obj/effect/alien/weeds/node/pylon/pylon_node = new node_type(loc, null, null, linked_hive) + pylon_node.resin_parent = src + return pylon_node + +/obj/effect/alien/resin/special/pylon/endgame + cover_range = WEED_RANGE_CORE + var/activated = FALSE + +/obj/effect/alien/resin/special/pylon/endgame/Destroy() + if(activated) + activated = FALSE + + if(hijack_delete) + return ..() + + marine_announcement("ALERT.\n\nEnergy build up around communication relay at [get_area(src)] halted.", "[MAIN_AI_SYSTEM] Biological Scanner") + + for(var/hivenumber in GLOB.hive_datum) + var/datum/hive_status/checked_hive = GLOB.hive_datum[hivenumber] + if(!length(checked_hive.totalXenos)) + continue + + if(checked_hive == linked_hive) + xeno_announcement(SPAN_XENOANNOUNCE("We have lost our control of the tall's communication relay at [get_area(src)]."), hivenumber, XENO_GENERAL_ANNOUNCE) + else + xeno_announcement(SPAN_XENOANNOUNCE("Another hive has lost control of the tall's communication relay at [get_area(src)]."), hivenumber, XENO_GENERAL_ANNOUNCE) + + return ..() + +/// Checks if all comms towers are connected and then starts end game content on all pylons if they are +/obj/effect/alien/resin/special/pylon/endgame/proc/comms_relay_connection() + marine_announcement("ALERT.\n\nIrregular build up of energy around communication relays at [get_area(src)].", "[MAIN_AI_SYSTEM] Biological Scanner") + + for(var/hivenumber in GLOB.hive_datum) + var/datum/hive_status/checked_hive = GLOB.hive_datum[hivenumber] + if(!length(checked_hive.totalXenos)) + continue + + if(checked_hive == linked_hive) + xeno_announcement(SPAN_XENOANNOUNCE("We have harnessed the tall's communication relay at [get_area(src)]. Hold it!"), hivenumber, XENO_GENERAL_ANNOUNCE) + else + xeno_announcement(SPAN_XENOANNOUNCE("Another hive has harnessed the tall's communication relay at [get_area(src)].[linked_hive.faction_is_ally(checked_hive.name) ? "" : " Stop them!"]"), hivenumber, XENO_GENERAL_ANNOUNCE) + + activated = TRUE + addtimer(CALLBACK(src, PROC_REF(give_larva)), XENO_PYLON_ACTIVATION_COOLDOWN, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_LOOP|TIMER_DELETE_ME) + +#define ENDGAME_LARVA_CAP_MULTIPLIER 0.4 +#define LARVA_ADDITION_MULTIPLIER 0.10 + +/// Looped proc via timer to give larva after time +/obj/effect/alien/resin/special/pylon/endgame/proc/give_larva() + if(!activated) + return + + if(!linked_hive.hive_location || !linked_hive.living_xeno_queen) + return + + var/list/hive_xenos = linked_hive.totalXenos + + for(var/mob/living/carbon/xenomorph/xeno in hive_xenos) + if(!xeno.counts_for_slots) + hive_xenos -= xeno + + if(length(hive_xenos) > (length(GLOB.alive_human_list) * ENDGAME_LARVA_CAP_MULTIPLIER)) + return + + linked_hive.partial_larva += length(hive_xenos) * LARVA_ADDITION_MULTIPLIER + linked_hive.convert_partial_larva_to_full_larva() + linked_hive.hive_ui.update_burrowed_larva() + +#undef ENDGAME_LARVA_CAP_MULTIPLIER +#undef LARVA_ADDITION_MULTIPLIER //Hive Core - Generates strong weeds, supports other buildings /obj/effect/alien/resin/special/pylon/core diff --git a/code/modules/cm_aliens/structures/special_structure.dm b/code/modules/cm_aliens/structures/special_structure.dm index caa729f1df87..69bdcc2438ac 100644 --- a/code/modules/cm_aliens/structures/special_structure.dm +++ b/code/modules/cm_aliens/structures/special_structure.dm @@ -39,6 +39,9 @@ plane = FLOOR_PLANE + /// Tells the structure if they are being deleted because of hijack + var/hijack_delete = FALSE + /obj/effect/alien/resin/special/Initialize(mapload, hive_ref) . = ..() maxhealth = health diff --git a/code/modules/cm_aliens/weeds.dm b/code/modules/cm_aliens/weeds.dm index f20fa842e446..01140beae304 100644 --- a/code/modules/cm_aliens/weeds.dm +++ b/code/modules/cm_aliens/weeds.dm @@ -55,7 +55,7 @@ linked_hive = GLOB.hive_datum[hivenumber] set_hive_data(src, hivenumber) - if(spread_on_semiweedable) + if(spread_on_semiweedable && weed_strength < WEED_LEVEL_HIVE) if(color) var/list/RGB = ReadRGB(color) RGB[1] = Clamp(RGB[1] + 35, 0, 255) @@ -588,9 +588,13 @@ weed_strength = WEED_LEVEL_HIVE node_range = WEED_RANGE_PYLON overlay_node = FALSE + spread_on_semiweedable = TRUE var/obj/effect/alien/resin/special/resin_parent /obj/effect/alien/weeds/node/pylon/proc/set_parent_damaged() + if(!resin_parent) + return + var/obj/effect/alien/resin/special/pylon/parent_pylon = resin_parent parent_pylon.damaged = TRUE @@ -616,7 +620,13 @@ /obj/effect/alien/weeds/node/pylon/acid_spray_act() return +/obj/effect/alien/weeds/node/pylon/cluster + spread_on_semiweedable = FALSE + /obj/effect/alien/weeds/node/pylon/cluster/set_parent_damaged() + if(!resin_parent) + return + var/obj/effect/alien/resin/special/cluster/parent_cluster = resin_parent parent_cluster.damaged = TRUE diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm index eaff5a66309e..fbf75f16ca8d 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm @@ -316,6 +316,7 @@ XENO_STRUCTURE_EGGMORPH = 6, XENO_STRUCTURE_EVOPOD = 2, XENO_STRUCTURE_RECOVERY = 6, + XENO_STRUCTURE_PYLON = 2, ) var/global/list/hive_structure_types = list( @@ -367,17 +368,25 @@ if(hivenumber != XENO_HIVE_NORMAL) return - RegisterSignal(SSdcs, COMSIG_GLOB_POST_SETUP, PROC_REF(setup_evolution_announcements)) + RegisterSignal(SSdcs, COMSIG_GLOB_POST_SETUP, PROC_REF(post_setup)) -/datum/hive_status/proc/setup_evolution_announcements() +/datum/hive_status/proc/post_setup() SIGNAL_HANDLER + setup_evolution_announcements() + setup_pylon_limits() + +/datum/hive_status/proc/setup_evolution_announcements() for(var/time in GLOB.xeno_evolve_times) if(time == "0") continue addtimer(CALLBACK(src, PROC_REF(announce_evolve_available), GLOB.xeno_evolve_times[time]), text2num(time)) +/// Sets up limits on pylons in New() for potential futureproofing with more static comms +/datum/hive_status/proc/setup_pylon_limits() + hive_structures_limit[XENO_STRUCTURE_PYLON] = length(GLOB.all_static_telecomms_towers) || 2 + /datum/hive_status/proc/announce_evolve_available(list/datum/caste_datum/available_castes) var/list/castes_available = list() @@ -890,6 +899,7 @@ for(var/obj/effect/alien/resin/special/S in hive_structures[name_ref]) if(get_area(S) == hijacked_dropship) continue + S.hijack_delete = TRUE hive_structures[name_ref] -= S qdel(S) for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) @@ -1106,6 +1116,10 @@ /datum/hive_status/proc/increase_larva_after_burst() var/extra_per_burst = CONFIG_GET(number/extra_larva_per_burst) partial_larva += extra_per_burst + convert_partial_larva_to_full_larva() + +///Called after times when partial larva are added to process them to stored larva +/datum/hive_status/proc/convert_partial_larva_to_full_larva() for(var/i = 1 to partial_larva) partial_larva-- stored_larva++ diff --git a/icons/obj/structures/machinery/comm_tower3.dmi b/icons/obj/structures/machinery/comm_tower3.dmi index 931d6f77514a..8a5aa1f68fe3 100644 Binary files a/icons/obj/structures/machinery/comm_tower3.dmi and b/icons/obj/structures/machinery/comm_tower3.dmi differ