From 74a5c0d0afe620cd5abd8742fd9d723b445d01a0 Mon Sep 17 00:00:00 2001 From: asciodev <81930475+asciodev@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:38:50 -0500 Subject: [PATCH] [PTBF] Adds blob hallucinations in dark areas (#27722) * [PTBF] Adds blob hallucinations in dark areas This hallucination looks for dark, unobstructed tiles within view of the hallucinating target, and gradually expands a blob from one of those tiles. It also spawns a blob zombie nearby. If the blob zombie knocks the target down, the target will hallucinate becoming a blob zombie. * Blob hallucination icon fix/change, add vomit sfx Blob zombies now appropriately spawn their heads, and as requested in PR review, the first blob spawned will have a core icon, and will be accompanied by a vomit sound effect, like actual blobs. Additionally, the blob zombie will no longer attack the target after already once knocking them down. * Apply suggestions from code review Remove unnecessary nulls Change return signature of blob_zombie#think to match convention Set blob_zombie's name in typedef instead of initializer Declare source_turf in loop body in blob#expand Add nullchecks and safety check for deleted targets in blob#Destroy Format blob hallucination colors list Return qdel hint in blob#Initialize instead of qdeling self Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: asciodev <81930475+asciodev@users.noreply.github.com> * Prevent blob zombie hallucination name from being overwritten when created Signed-off-by: asciodev <81930475+asciodev@users.noreply.github.com> * DM doesn't like parentheses on their own lines apparently * Enable dynamic blob hallucination expansion count Co-authored-by: Drsmail <60036448+Drsmail@users.noreply.github.com> --------- Signed-off-by: asciodev <81930475+asciodev@users.noreply.github.com> Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Co-authored-by: Drsmail <60036448+Drsmail@users.noreply.github.com> --- code/modules/hallucinations/effects/major.dm | 147 ++++++++++++++++++ code/modules/hallucinations/hallucinations.dm | 1 + 2 files changed, 148 insertions(+) diff --git a/code/modules/hallucinations/effects/major.dm b/code/modules/hallucinations/effects/major.dm index 547b0b1e33e..c17b03e7667 100644 --- a/code/modules/hallucinations/effects/major.dm +++ b/code/modules/hallucinations/effects/major.dm @@ -329,3 +329,150 @@ target.visible_message("[target] recoils as if hit by something, before suddenly collapsing!", "[name] has [attack_verb] [target]!") QDEL_IN(src, 3 SECONDS) + +/obj/effect/hallucination/blob + duration = 30 SECONDS + /// The blob zombie hallucination. + var/obj/effect/hallucination/chaser/attacker/blob_zombie/zombie + /// The self delusion blob zombie hallucination, triggers on knockdown. + var/obj/effect/hallucination/blob_zombify/player_zombie + /// List of turfs that need expanding from. + var/list/turf/expand_queue = list() + /// Associative list of turfs that have already been processed. + var/list/turf/processed = list() + /// The image for the chaser zombie's blob head + var/image/chaser_blob_head + /// The image for the player zombie's blob head + var/image/target_blob_head + /// The delay at which the blob expands in deciseconds. Shouldn't be too low to prevent lag. + var/expand_delay + /// The amount of time in deciseconds the hallucination should continue after final expansion. + var/conclude_delay = 2 SECONDS + /// Expand timer handle. + var/expand_timer + +/obj/effect/hallucination/blob/Initialize(mapload, mob/living/carbon/target, expansions = 4) + . = ..() + expand_delay = (duration - conclude_delay) / max(1, expansions) + var/list/locs = list() + for(var/turf/T in oview(world.view / 2, target)) + var/light_amount = T.get_lumcount() + if(!is_blocked_turf(T) && light_amount <= 0.5) + locs += T + if(!length(locs)) + return INITIALIZE_HINT_QDEL + var/turf/T = get_turf(pick(locs)) + color = pick(COLOR_BLACK, + COLOR_RIPPING_TENDRILS, + COLOR_BOILING_OIL, + COLOR_ENVENOMED_FILAMENTS, + COLOR_LEXORIN_JELLY, + COLOR_KINETIC_GELATIN, + COLOR_CRYOGENIC_LIQUID, + COLOR_SORIUM, + COLOR_TESLIUM_PASTE) + create_blob(T, core = TRUE) + target.playsound_local(T, 'sound/effects/splat.ogg', 50, 1) + create_zombie(T) + expand_queue += T + processed[T] = TRUE + expand_timer = addtimer(CALLBACK(src, PROC_REF(expand)), expand_delay, TIMER_LOOP | TIMER_STOPPABLE) + +/obj/effect/hallucination/blob/Destroy() + deltimer(expand_timer) + expand_queue.Cut() + processed.Cut() + QDEL_NULL(zombie) + QDEL_NULL(player_zombie) + if(!QDELETED(target)) + target.client?.images -= chaser_blob_head + target.client?.images -= target_blob_head + chaser_blob_head = null + target_blob_head = null + return ..() + +/** + * Called regularly in a timer to process the blob expanding. + */ +/obj/effect/hallucination/blob/proc/expand() + // Brace for potentially expensive proc + for(var/turf/source_turf as anything in expand_queue) + expand_queue -= source_turf + //Expand to each dir + for(var/direction in GLOB.cardinal) + var/turf/target_turf = get_step(source_turf, direction) + if(processed[target_turf] || !source_turf.CanAtmosPass(direction) || !target_turf.CanAtmosPass(turn(direction, 180))) + continue + create_blob(target_turf) + expand_queue += target_turf + processed[target_turf] = TRUE + +/** + * Creates a fake blob overlay on the given turf. + * + * Arguments: + * * T - The turf to create a fake blob overlay on. + */ +/obj/effect/hallucination/blob/proc/create_blob(turf/T, core = FALSE) + var/blob_icon_state = "blob" + if(core) + blob_icon_state = "blob_core" + var/image/I = image('icons/mob/blob.dmi', T, blob_icon_state, layer = FLY_LAYER) + I.plane = GAME_PLANE + I.color = color + add_icon(I) + +/obj/effect/hallucination/blob/proc/create_zombie(turf/T) + zombie = new(T, target, src) + +/obj/effect/hallucination/blob/proc/zombify(turf/T) + player_zombie = new(T, target, src) + +/obj/effect/hallucination/chaser/attacker/blob_zombie + name = "blob zombie" + hallucination_icon = 'icons/mob/human.dmi' + hallucination_icon_state = "zombie2_s" + duration = 45 SECONDS + damage = 25 + /// The hallucination that spawned us. + var/obj/effect/hallucination/blob/owning_hallucination = null + /// Whether or not the target has been zombified already. + var/has_zombified = FALSE + +/obj/effect/hallucination/chaser/attacker/blob_zombie/Initialize(mapload, mob/living/carbon/target, obj/effect/hallucination/blob/blob) + . = ..() + name = "blob zombie" + var/image/I = image('icons/mob/blob.dmi', src, "blob_head") + I.color = blob.color + target.client.images += I + owning_hallucination = blob + blob.chaser_blob_head = I + +/obj/effect/hallucination/chaser/attacker/blob_zombie/attack_effects() + do_attack_animation(target) + target.playsound_local(get_turf(src), 'sound/weapons/genhit1.ogg', 50, TRUE) + to_chat(target, "[name] has hit [target]!") + +/obj/effect/hallucination/chaser/attacker/blob_zombie/on_knockdown() + if(!QDELETED(owning_hallucination)) + target.visible_message("[target] recoils as if hit by something, before suddenly collapsing!", + "The corpse of [target.name] suddenly rises!") + owning_hallucination.zombify(target) + has_zombified = TRUE + else + qdel(src) + +/obj/effect/hallucination/chaser/attacker/blob_zombie/think() + if(has_zombified) + return + return ..() + +/obj/effect/hallucination/blob_zombify + duration = 20 SECONDS + +/obj/effect/hallucination/blob_zombify/Initialize(mapload, mob/living/carbon/target, obj/effect/hallucination/blob/blob) + . = ..() + var/image/I = image('icons/mob/blob.dmi', target, icon_state = "blob_head") + I.color = blob.color + target.client.images += I + blob.target_blob_head = I diff --git a/code/modules/hallucinations/hallucinations.dm b/code/modules/hallucinations/hallucinations.dm index 4bf7b41d60a..bd9f5a9dc62 100644 --- a/code/modules/hallucinations/hallucinations.dm +++ b/code/modules/hallucinations/hallucinations.dm @@ -32,6 +32,7 @@ GLOBAL_LIST_INIT(hallucinations, list( /obj/effect/hallucination/fake_grenade/spawner = 10, /obj/effect/hallucination/terror_infestation = 10, /obj/effect/hallucination/loose_energy_ball = 10, + /obj/effect/hallucination/blob = 10, /datum/hallucination_manager/xeno_pounce = 10 ) ))