diff --git a/code/__DEFINES/research/anomalies.dm b/code/__DEFINES/research/anomalies.dm index 3389b9e0fe6..3b2687b5fcf 100644 --- a/code/__DEFINES/research/anomalies.dm +++ b/code/__DEFINES/research/anomalies.dm @@ -34,10 +34,13 @@ GLOBAL_LIST_INIT(bioscrambler_organs_blacklist, typecacheof(list ( /obj/item/organ/internal/brain, /obj/item/organ/internal/body_egg, /obj/item/organ/internal/cyberimp, + /obj/item/organ/internal/ears/dullahan, + /obj/item/organ/internal/eyes/dullahan, /obj/item/organ/internal/heart/cursed, /obj/item/organ/internal/heart/demon, /obj/item/organ/internal/lungs, /obj/item/organ/internal/monster_core, + /obj/item/organ/internal/tongue/dullahan, /obj/item/organ/internal/vocal_cords/colossus, /obj/item/organ/internal/zombie_infection, ))) diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index c61e1c2ef9c..f5ad67a8c94 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -813,6 +813,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Trait applied to turfs when an atmos holosign is placed on them. It will stop firedoors from closing. #define TRAIT_FIREDOOR_STOP "firedoor_stop" +///Trait applied to turf blocked by a containment field +#define TRAIT_CONTAINMENT_FIELD "containment_field" + /// Trait applied when the MMI component is added to an [/obj/item/integrated_circuit] #define TRAIT_COMPONENT_MMI "component_mmi" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 49ea9004198..9ee26893b5f 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -599,6 +599,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( ), /turf = list( "TRAIT_CHASM_STOPPED" = TRAIT_CHASM_STOPPED, + "TRAIT_CONTAINMENT_FIELD" = TRAIT_CONTAINMENT_FIELD, "TRAIT_FIREDOOR_STOP" = TRAIT_FIREDOOR_STOP, "TRAIT_HYPERSPACE_STOPPED" = TRAIT_HYPERSPACE_STOPPED, "TRAIT_IMMERSE_STOPPED" = TRAIT_IMMERSE_STOPPED, diff --git a/code/datums/components/singularity.dm b/code/datums/components/singularity.dm index c1323d01e2f..56a6723f21f 100644 --- a/code/datums/components/singularity.dm +++ b/code/datums/components/singularity.dm @@ -360,8 +360,9 @@ target = potentially_closer //if we lost that target get a new one if(!target || QDELETED(target)) - target = find_new_target() - foreboding_nosebleed(target) + var/mob/living/new_target = find_new_target() + new_target?.ominous_nosebleed() + target = new_target return ..() ///Searches the living list for the closest target, and begins chasing them down. @@ -380,22 +381,6 @@ closest_target = target return closest_target -/// gives a little fluff warning that someone is being hunted. -/datum/component/singularity/bloodthirsty/proc/foreboding_nosebleed(mob/living/target) - if(!iscarbon(target)) - to_chat(target, span_warning("You feel a bit nauseous for just a moment.")) - return - var/mob/living/carbon/carbon_target = target - var/obj/item/bodypart/head = carbon_target.get_bodypart(BODY_ZONE_HEAD) - if(head) - if(HAS_TRAIT(carbon_target, TRAIT_NOBLOOD)) - to_chat(carbon_target, span_notice("You get a headache.")) - return - head.adjustBleedStacks(5) - carbon_target.visible_message(span_notice("[carbon_target] gets a nosebleed."), span_warning("You get a nosebleed.")) - return - to_chat(target, span_warning("You feel a bit nauseous for just a moment.")) - #undef CHANCE_TO_MOVE_TO_TARGET #undef CHANCE_TO_MOVE_TO_TARGET_BLOODTHIRSTY #undef CHANCE_TO_CHANGE_TARGET_BLOODTHIRSTY diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index 0e2be91490c..4e7a80e1663 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -364,11 +364,8 @@ visible_message(span_danger("[src] shuts down due to lack of power!"), \ "If this message is ever seen, something is wrong.", span_hear("You hear heavy droning fade out.")) - active = FALSE + deactivate() log_game("[src] deactivated due to lack of power at [AREACOORD(src)]") - for(var/d in GLOB.cardinals) - cleanup_field(d) - update_appearance() else update_appearance() for(var/d in GLOB.cardinals) @@ -463,7 +460,6 @@ balloon_alert(user, "malfunctioning!") else balloon_alert(user, "no access!") - return add_fingerprint(user) @@ -493,13 +489,13 @@ user.visible_message(span_notice("[user] turned \the [src] off."), \ span_notice("You turn off \the [src]."), \ span_hear("You hear heavy droning fade out.")) - active = FALSE + deactivate() user.log_message("deactivated [src].", LOG_GAME) else user.visible_message(span_notice("[user] turned \the [src] on."), \ span_notice("You turn on \the [src]."), \ span_hear("You hear heavy droning.")) - active = ACTIVE_SETUPFIELDS + activate() user.log_message("activated [src].", LOG_GAME) add_fingerprint(user) @@ -513,6 +509,19 @@ balloon_alert(user, "access controller shorted") return TRUE +/// Turn the machine on with side effects +/obj/machinery/power/shieldwallgen/proc/activate() + active = ACTIVE_SETUPFIELDS + AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) + +/// Turn the machine off with side effects +/obj/machinery/power/shieldwallgen/proc/deactivate() + active = FALSE + for(var/d in GLOB.cardinals) + cleanup_field(d) + update_appearance() + RemoveElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) + //////////////Containment Field START /obj/machinery/shieldwall name = "shield wall" @@ -538,6 +547,7 @@ L.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS) L.gib(DROP_ALL_REMAINS) RegisterSignal(src, COMSIG_ATOM_SINGULARITY_TRY_MOVE, PROC_REF(block_singularity)) + AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) /obj/machinery/shieldwall/Destroy() gen_primary = null diff --git a/code/game/objects/effects/anomalies/_anomalies.dm b/code/game/objects/effects/anomalies/_anomalies.dm index 530da562827..8c34c48ee41 100644 --- a/code/game/objects/effects/anomalies/_anomalies.dm +++ b/code/game/objects/effects/anomalies/_anomalies.dm @@ -23,6 +23,8 @@ var/immortal = FALSE ///Do we stay in one place? var/immobile = FALSE + ///Chance per second that we will move + var/move_chance = ANOMALY_MOVECHANCE /obj/effect/anomaly/Initialize(mapload, new_lifespan, drops_core = TRUE) . = ..() @@ -76,8 +78,12 @@ return ..() /obj/effect/anomaly/proc/anomalyEffect(seconds_per_tick) - if(!immobile && SPT_PROB(ANOMALY_MOVECHANCE, seconds_per_tick)) - step(src,pick(GLOB.alldirs)) + if(SPT_PROB(move_chance, seconds_per_tick)) + move_anomaly() + +/// Move in a direction +/obj/effect/anomaly/proc/move_anomaly() + step(src, pick(GLOB.alldirs)) /obj/effect/anomaly/proc/detonate() return diff --git a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm index b4ee3713a25..214440b92c2 100644 --- a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm +++ b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm @@ -4,6 +4,10 @@ icon_state = "bioscrambler" aSignal = /obj/item/assembly/signaler/anomaly/bioscrambler immortal = TRUE + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSCLOSEDTURF | PASSMACHINE | PASSSTRUCTURE | PASSDOORS + layer = ABOVE_MOB_LAYER + /// Who are we moving towards? + var/datum/weakref/pursuit_target /// Cooldown for every anomaly pulse COOLDOWN_DECLARE(pulse_cooldown) /// How many seconds between each anomaly pulses @@ -11,11 +15,52 @@ /// Range of the anomaly pulse var/range = 5 +/obj/effect/anomaly/bioscrambler/Initialize(mapload, new_lifespan, drops_core) + . = ..() + pursuit_target = WEAKREF(find_nearest_target()) + /obj/effect/anomaly/bioscrambler/anomalyEffect(seconds_per_tick) . = ..() if(!COOLDOWN_FINISHED(src, pulse_cooldown)) return COOLDOWN_START(src, pulse_cooldown, pulse_delay) - for(var/mob/living/carbon/nearby in range(range, src)) + for(var/mob/living/carbon/nearby in hearers(range, src)) nearby.bioscramble(name) + +/obj/effect/anomaly/bioscrambler/move_anomaly() + var/mob/living/current_target = pursuit_target?.resolve() + if (QDELETED(current_target)) + pursuit_target = null + if (isnull(pursuit_target) || prob(20)) + var/mob/living/new_target = find_nearest_target() + if (isnull(new_target)) + pursuit_target = null + else if (new_target != current_target) + current_target = new_target + pursuit_target = WEAKREF(new_target) + new_target.ominous_nosebleed() + if (isnull(pursuit_target)) + return + var/turf/step_turf = get_step(src, get_dir(src, current_target)) + if (!HAS_TRAIT(step_turf, TRAIT_CONTAINMENT_FIELD)) + Move(step_turf) + +/// Returns the closest conscious carbon on our z level or null if there somehow isn't one +/obj/effect/anomaly/bioscrambler/proc/find_nearest_target() + var/closest_distance = INFINITY + var/mob/living/carbon/closest_target = null + for(var/mob/living/carbon/target in GLOB.player_list) + if (target.z != z) + continue + if (target.status_effects & GODMODE) + continue + if (target.stat >= UNCONSCIOUS) + continue // Don't just haunt a corpse + var/distance_from_target = get_dist(src, target) + if(distance_from_target >= closest_distance) + continue + closest_distance = distance_from_target + closest_target = target + + return closest_target diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 6f8f95c64d7..cd97eba3c9e 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1477,3 +1477,13 @@ if (!IS_ORGANIC_LIMB(target_part) || (target_part.bodypart_flags & BODYPART_PSEUDOPART)) return FALSE return ..() + +/mob/living/carbon/ominous_nosebleed() + var/obj/item/bodypart/head = get_bodypart(BODY_ZONE_HEAD) + if(isnull(head)) + return ..() + if(HAS_TRAIT(src, TRAIT_NOBLOOD)) + to_chat(src, span_notice("You get a headache.")) + return + head.adjustBleedStacks(5) + visible_message(span_notice("[src] gets a nosebleed."), span_warning("You get a nosebleed.")) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 0b25077d3d3..46d65b376bd 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2426,6 +2426,10 @@ GLOBAL_LIST_EMPTY(fire_appearances) apply_damage(rand(5,10), BRUTE, BODY_ZONE_CHEST) lattice.deconstruct(FALSE) +/// Prints an ominous message if something bad is going to happen to you +/mob/living/proc/ominous_nosebleed() + to_chat(src, span_warning("You feel a bit nauseous for just a moment.")) + /** * Proc used by different station pets such as Ian and Poly so that some of their data can persist between rounds. * This base definition only contains a trait and comsig to stop memory from being (over)written. diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index 0fd3c15a39a..22682471d13 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -29,6 +29,7 @@ COMSIG_ATOM_ENTERED = PROC_REF(on_entered), ) AddElement(/datum/element/connect_loc, loc_connections) + AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) /obj/machinery/field/containment/Destroy() if(field_gen_1) diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index c18e7f15a65..b46b29538c6 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -208,6 +208,7 @@ no power level overlay is currently in the overlays list. air_update_turf(TRUE, FALSE) INVOKE_ASYNC(src, PROC_REF(cleanup)) addtimer(CALLBACK(src, PROC_REF(cool_down)), 5 SECONDS) + RemoveElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) /obj/machinery/field/generator/proc/cool_down() if(active || warming_up <= 0) @@ -220,6 +221,7 @@ no power level overlay is currently in the overlays list. /obj/machinery/field/generator/proc/turn_on() active = FG_CHARGING addtimer(CALLBACK(src, PROC_REF(warm_up)), 5 SECONDS) + AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) /obj/machinery/field/generator/proc/warm_up() if(!active) diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index d808ea0b009..d47d7536094 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -386,16 +386,8 @@ /obj/singularity/proc/can_move(turf/considered_turf) if(!considered_turf) return FALSE - if((locate(/obj/machinery/field/containment) in considered_turf) || (locate(/obj/machinery/shieldwall) in considered_turf)) + if (HAS_TRAIT(considered_turf, TRAIT_CONTAINMENT_FIELD)) return FALSE - else if(locate(/obj/machinery/field/generator) in considered_turf) - var/obj/machinery/field/generator/check_generator = locate(/obj/machinery/field/generator) in considered_turf - if(check_generator?.active) - return FALSE - else if(locate(/obj/machinery/power/shieldwallgen) in considered_turf) - var/obj/machinery/power/shieldwallgen/check_shield = locate(/obj/machinery/power/shieldwallgen) in considered_turf - if(check_shield?.active) - return FALSE return TRUE /obj/singularity/proc/event()