From b0a54c9dd7cbe8cff251a267079fe062909f7ac0 Mon Sep 17 00:00:00 2001 From: Jared-Fogle <35135081+Jared-Fogle@users.noreply.github.com> Date: Mon, 4 Jan 2021 23:25:56 -0800 Subject: [PATCH] Remove sleep() from hallucinations (#55927) Changes all uses of sleep in hallucination to fast processing or timers. --- code/modules/flufftext/Hallucination.dm | 749 +++++++++++------- .../mob/living/carbon/carbon_defines.dm | 3 - 2 files changed, 482 insertions(+), 270 deletions(-) diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 5baac83f0ed..4c78e5be551 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -53,12 +53,24 @@ GLOBAL_LIST_INIT(hallucination_list, list( target = C natural = !forced + // Cancel early if the target is deleted + RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/target_deleting) + +/datum/hallucination/proc/target_deleting() + SIGNAL_HANDLER + + qdel(src) + /datum/hallucination/proc/wake_and_restore() target.set_screwyhud(SCREWYHUD_NONE) target.SetSleeping(0) /datum/hallucination/Destroy() target.investigate_log("was afflicted with a hallucination of type [type] by [natural?"hallucination status":"an external source"]. [feedback_details]", INVESTIGATE_HALLUCINATIONS) + + if (target) + UnregisterSignal(target, COMSIG_PARENT_QDELETING) + target = null return ..() @@ -165,7 +177,6 @@ GLOBAL_LIST_INIT(hallucination_list, list( var/next_expand = 0 /datum/hallucination/fake_flood/New(mob/living/carbon/C, forced = TRUE) - set waitfor = FALSE ..() for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target)) if(!U.welded) @@ -242,34 +253,64 @@ GLOBAL_LIST_INIT(hallucination_list, list( target.Paralyze(100) target.visible_message("[target] flails around wildly.","[name] pounces on you!") +// The numbers of seconds it takes to get to each stage of the xeno attack choreography +#define XENO_ATTACK_STAGE_LEAP_AT_TARGET 1 +#define XENO_ATTACK_STAGE_LEAP_AT_PUMP 2 +#define XENO_ATTACK_STAGE_CLIMB 3 +#define XENO_ATTACK_STAGE_FINISH 6 + +/// Xeno crawls from nearby vent,jumps at you, and goes back in /datum/hallucination/xeno_attack - //Xeno crawls from nearby vent,jumps at you, and goes back in - var/obj/machinery/atmospherics/components/unary/vent_pump/pump = null + var/turf/pump_location = null var/obj/effect/hallucination/simple/xeno/xeno = null + var/time_processing = 0 + var/stage = XENO_ATTACK_STAGE_LEAP_AT_TARGET /datum/hallucination/xeno_attack/New(mob/living/carbon/C, forced = TRUE) - set waitfor = FALSE ..() for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target)) if(!U.welded) - pump = U + pump_location = get_turf(U) break - if(pump) - feedback_details += "Vent Coords: [pump.x],[pump.y],[pump.z]" - xeno = new(pump.loc,target) - sleep(10) - xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32) - xeno.throw_at(target, 7, 1, spin = FALSE, diagonals_first = TRUE) - sleep(10) - xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32) - xeno.throw_at(pump, 7, 1, spin = FALSE, diagonals_first = TRUE) - sleep(10) - var/xeno_name = xeno.name - to_chat(target, "[xeno_name] begins climbing into the ventilation system...") - sleep(30) - qdel(xeno) - to_chat(target, "[xeno_name] scrambles into the ventilation ducts!") - qdel(src) + + if(pump_location) + feedback_details += "Vent Coords: [pump_location.x],[pump_location.y],[pump_location.z]" + xeno = new(pump_location, target) + START_PROCESSING(SSfastprocess, src) + else + qdel(src) + +/datum/hallucination/xeno_attack/process(delta_time) + time_processing += delta_time + + if (time_processing >= stage) + switch (time_processing) + if (XENO_ATTACK_STAGE_FINISH to INFINITY) + to_chat(target, "[xeno.name] scrambles into the ventilation ducts!") + qdel(src) + if (XENO_ATTACK_STAGE_CLIMB to XENO_ATTACK_STAGE_FINISH) + to_chat(target, "[xeno.name] begins climbing into the ventilation system...") + stage = XENO_ATTACK_STAGE_FINISH + if (XENO_ATTACK_STAGE_LEAP_AT_PUMP to XENO_ATTACK_STAGE_CLIMB) + xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi', -32, -32) + xeno.throw_at(pump_location, 7, 1, spin = FALSE, diagonals_first = TRUE) + stage = XENO_ATTACK_STAGE_CLIMB + if (XENO_ATTACK_STAGE_LEAP_AT_TARGET to XENO_ATTACK_STAGE_LEAP_AT_PUMP) + xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi', -32, -32) + xeno.throw_at(target, 7, 1, spin = FALSE, diagonals_first = TRUE) + stage = XENO_ATTACK_STAGE_LEAP_AT_PUMP + +/datum/hallucination/xeno_attack/Destroy() + . = ..() + + STOP_PROCESSING(SSfastprocess, src) + QDEL_NULL(xeno) + pump_location = null + +#undef XENO_ATTACK_STAGE_LEAP_AT_TARGET +#undef XENO_ATTACK_STAGE_LEAP_AT_PUMP +#undef XENO_ATTACK_STAGE_CLIMB +#undef XENO_ATTACK_STAGE_FINISH /obj/effect/hallucination/simple/clown image_icon = 'icons/mob/animal.dmi' @@ -293,6 +334,9 @@ GLOBAL_LIST_INIT(hallucination_list, list( var/obj/effect/hallucination/simple/bubblegum/bubblegum var/image/fakebroken var/image/fakerune + var/turf/landing + var/charged + var/next_action = 0 /datum/hallucination/oh_yeah/New(mob/living/carbon/C, forced = TRUE) set waitfor = FALSE @@ -306,7 +350,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( feedback_details += "Source: [wall.x],[wall.y],[wall.z]" fakebroken = image('icons/turf/floors.dmi', wall, "plating", layer = TURF_LAYER) - var/turf/landing = get_turf(target) + landing = get_turf(target) var/turf/landing_image_turf = get_step(landing, SOUTHWEST) //the icon is 3x3 fakerune = image('icons/effects/96x96.dmi', landing_image_turf, "landing", layer = ABOVE_OPEN_TURF_LAYER) fakebroken.override = TRUE @@ -315,15 +359,24 @@ GLOBAL_LIST_INIT(hallucination_list, list( target.client.images |= fakerune target.playsound_local(wall,'sound/effects/meteorimpact.ogg', 150, 1) bubblegum = new(wall, target) - addtimer(CALLBACK(src, .proc/bubble_attack, landing), 10) + addtimer(CALLBACK(src, .proc/start_processing), 10) -/datum/hallucination/oh_yeah/proc/bubble_attack(turf/landing) - var/charged = FALSE //only get hit once - while(get_turf(bubblegum) != landing && target?.stat != DEAD) - if(!landing) - break - if((get_turf(bubblegum)).loc.z != landing.loc.z) - break +/datum/hallucination/oh_yeah/proc/start_processing() + if (isnull(target)) + qdel(src) + return + START_PROCESSING(SSfastprocess, src) + +/datum/hallucination/oh_yeah/process(delta_time) + next_action -= delta_time + + if (next_action > 0) + return + + if (get_turf(bubblegum) != landing && target?.stat != DEAD) + if(!landing || (get_turf(bubblegum)).loc.z != landing.loc.z) + qdel(src) + return bubblegum.forceMove(get_step_towards(bubblegum, landing)) bubblegum.setDir(get_dir(bubblegum, landing)) target.playsound_local(get_turf(bubblegum), 'sound/effects/meteorimpact.ogg', 150, 1) @@ -335,9 +388,10 @@ GLOBAL_LIST_INIT(hallucination_list, list( step_away(target, bubblegum) shake_camera(target, 4, 3) target.visible_message("[target] jumps backwards, falling on the ground!","[bubblegum] slams into you!") - sleep(2) - sleep(30) - qdel(src) + next_action = 0.2 + else + STOP_PROCESSING(SSfastprocess, src) + QDEL_IN(src, 3 SECONDS) /datum/hallucination/oh_yeah/Destroy() if(target.client) @@ -346,82 +400,127 @@ GLOBAL_LIST_INIT(hallucination_list, list( QDEL_NULL(fakebroken) QDEL_NULL(fakerune) QDEL_NULL(bubblegum) + STOP_PROCESSING(SSfastprocess, src) return ..() /datum/hallucination/battle + var/battle_type + var/iterations_left + var/hits = 0 + var/next_action = 0 + var/turf/source -/datum/hallucination/battle/New(mob/living/carbon/C, forced = TRUE, battle_type) - set waitfor = FALSE +/datum/hallucination/battle/New(mob/living/carbon/C, forced = TRUE, new_battle_type) ..() - var/turf/source = random_far_turf() - if(!battle_type) - battle_type = pick("laser","disabler","esword","gun","stunprod","harmbaton","bomb") + + source = random_far_turf() + + battle_type = new_battle_type + if (isnull(battle_type)) + battle_type = pick("laser", "disabler", "esword", "gun", "stunprod", "harmbaton", "bomb") feedback_details += "Type: [battle_type]" + var/process = TRUE + switch(battle_type) - if("laser") - var/hits = 0 - for(var/i in 1 to rand(5, 10)) - target.playsound_local(source, 'sound/weapons/laser.ogg', 25, 1) - if(prob(50)) - addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/weapons/sear.ogg', 25, 1), rand(5,10)) - hits++ - else - addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/weapons/effects/searwall.ogg', 25, 1), rand(5,10)) - sleep(rand(CLICK_CD_RANGE, CLICK_CD_RANGE + 6)) - if(hits >= 4 && prob(70)) - target.playsound_local(source, get_sfx("bodyfall"), 25, 1) - break - if("disabler") - var/hits = 0 - for(var/i in 1 to rand(5, 10)) - target.playsound_local(source, 'sound/weapons/taser2.ogg', 25, 1) - if(prob(50)) - addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/weapons/tap.ogg', 25, 1), rand(5,10)) - hits++ - else - addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/weapons/effects/searwall.ogg', 25, 1), rand(5,10)) - sleep(rand(CLICK_CD_RANGE, CLICK_CD_RANGE + 6)) - if(hits >= 3 && prob(70)) - target.playsound_local(source, get_sfx("bodyfall"), 25, 1) - break + if("disabler", "laser") + iterations_left = rand(5, 10) if("esword") + iterations_left = rand(4, 8) target.playsound_local(source, 'sound/weapons/saberon.ogg',15, 1) - for(var/i in 1 to rand(4, 8)) - target.playsound_local(source, 'sound/weapons/blade1.ogg', 50, 1) - if(i == 4) - target.playsound_local(source, get_sfx("bodyfall"), 25, 1) - sleep(rand(CLICK_CD_MELEE, CLICK_CD_MELEE + 6)) - target.playsound_local(source, 'sound/weapons/saberoff.ogg', 15, 1) if("gun") - var/hits = 0 - for(var/i in 1 to rand(3, 6)) - target.playsound_local(source, 'sound/weapons/gun/shotgun/shot.ogg', 25, TRUE) - if(prob(60)) - addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/weapons/pierce.ogg', 25, 1), rand(5,10)) - hits++ - else - addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, "ricochet", 25, 1), rand(5,10)) - sleep(rand(CLICK_CD_RANGE, CLICK_CD_RANGE + 6)) - if(hits >= 2 && prob(80)) - target.playsound_local(source, get_sfx("bodyfall"), 25, 1) - break + iterations_left = rand(3, 6) if("stunprod") //Stunprod + cablecuff + process = FALSE target.playsound_local(source, 'sound/weapons/egloves.ogg', 40, 1) target.playsound_local(source, get_sfx("bodyfall"), 25, 1) - sleep(20) - target.playsound_local(source, 'sound/weapons/cablecuff.ogg', 15, 1) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/weapons/cablecuff.ogg', 15, 1), 20) if("harmbaton") //zap n slap + iterations_left = rand(5, 12) target.playsound_local(source, 'sound/weapons/egloves.ogg', 40, 1) target.playsound_local(source, get_sfx("bodyfall"), 25, 1) - sleep(20) - for(var/i in 1 to rand(5, 12)) - target.playsound_local(source, "swing_hit", 50, 1) - sleep(rand(CLICK_CD_MELEE, CLICK_CD_MELEE + 4)) + next_action = 2 SECONDS if("bomb") // Tick Tock - for(var/i in 1 to rand(3, 11)) - target.playsound_local(source, 'sound/items/timer.ogg', 25, 0) - sleep(15) - qdel(src) + iterations_left = rand(3, 11) + + if (process) + START_PROCESSING(SSfastprocess, src) + else + qdel(src) + +/datum/hallucination/battle/process(delta_time) + next_action -= (delta_time * 10) + + if (next_action > 0) + return + + switch (battle_type) + if ("disabler", "laser", "gun") + var/fire_sound + var/hit_person_sound + var/hit_wall_sound + var/number_of_hits + var/chance_to_fall + + switch (battle_type) + if ("disabler") + fire_sound = 'sound/weapons/taser2.ogg' + hit_person_sound = 'sound/weapons/tap.ogg' + hit_wall_sound = 'sound/weapons/effects/searwall.ogg' + number_of_hits = 3 + chance_to_fall = 70 + if ("laser") + fire_sound = 'sound/weapons/laser.ogg' + hit_person_sound = 'sound/weapons/sear.ogg' + hit_wall_sound = 'sound/weapons/effects/searwall.ogg' + number_of_hits = 4 + chance_to_fall = 70 + if ("gun") + fire_sound = 'sound/weapons/gun/shotgun/shot.ogg' + hit_person_sound = 'sound/weapons/pierce.ogg' + hit_wall_sound = "ricochet" + number_of_hits = 2 + chance_to_fall = 80 + + target.playsound_local(source, fire_sound, 25, 1) + + if(prob(50)) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, hit_person_sound, 25, 1), rand(5,10)) + hits += 1 + else + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, hit_wall_sound, 25, 1), rand(5,10)) + + next_action = rand(CLICK_CD_RANGE, CLICK_CD_RANGE + 6) + + if(hits >= number_of_hits && prob(chance_to_fall)) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, get_sfx("bodyfall"), 25, 1), next_action) + qdel(src) + return + if ("esword") + target.playsound_local(source, 'sound/weapons/blade1.ogg', 50, 1) + + if (hits == 4) + target.playsound_local(source, get_sfx("bodyfall"), 25, 1) + + next_action = rand(CLICK_CD_MELEE, CLICK_CD_MELEE + 6) + hits += 1 + + if (iterations_left == 1) + target.playsound_local(source, 'sound/weapons/saberoff.ogg', 15, 1) + if ("harmbaton") + target.playsound_local(source, "swing_hit", 50, 1) + next_action = rand(CLICK_CD_MELEE, CLICK_CD_MELEE + 4) + if ("bomb") + target.playsound_local(source, 'sound/items/timer.ogg', 25, 0) + next_action = 15 + + iterations_left -= 1 + if (iterations_left == 0) + qdel(src) + +/datum/hallucination/battle/Destroy() + . = ..() + source = null + STOP_PROCESSING(SSfastprocess, src) /datum/hallucination/items_other @@ -512,12 +611,19 @@ GLOBAL_LIST_INIT(hallucination_list, list( A = image(image_file,H,"arm_blade", layer=ABOVE_MOB_LAYER) if(target.client) target.client.images |= A - sleep(rand(150,250)) - if(item == "esword" || item == "dual_esword") - target.playsound_local(H, 'sound/weapons/saberoff.ogg',35,1) - if(item == "armblade") - target.playsound_local(H, 'sound/effects/blobattack.ogg',30,1) - target.client.images.Remove(A) + addtimer(CALLBACK(src, .proc/cleanup, item, A, H), rand(15 SECONDS, 25 SECONDS)) + return + qdel(src) + +/datum/hallucination/items_other/proc/cleanup(item, atom/image_used, has_the_item) + if (isnull(target)) + qdel(src) + return + if(item == "esword" || item == "dual_esword") + target.playsound_local(has_the_item, 'sound/weapons/saberoff.ogg',35,1) + if(item == "armblade") + target.playsound_local(has_the_item, 'sound/effects/blobattack.ogg',30,1) + target.client.images.Remove(image_used) qdel(src) /datum/hallucination/delusion @@ -617,7 +723,10 @@ GLOBAL_LIST_INIT(hallucination_list, list( return ..() /datum/hallucination/bolts - var/list/locks = list() + var/list/airlocks_to_hit + var/list/locks + var/next_action = 0 + var/locking = TRUE /datum/hallucination/bolts/New(mob/living/carbon/C, forced, door_number) set waitfor = FALSE @@ -626,24 +735,48 @@ GLOBAL_LIST_INIT(hallucination_list, list( door_number = rand(0,4) //if 0 bolts all visible doors var/count = 0 feedback_details += "Door amount: [door_number]" + for(var/obj/machinery/door/airlock/A in range(7, target)) if(count>door_number && door_number>0) break if(!A.density) continue count++ - var/obj/effect/hallucination/fake_door_lock/lock = new(get_turf(A)) - lock.target = target - lock.airlock = A - locks += lock - lock.lock() - sleep(rand(4,12)) - sleep(100) - for(var/obj/effect/hallucination/fake_door_lock/lock in locks) - locks -= lock - lock.unlock() - sleep(rand(4,12)) - qdel(src) + LAZYADD(airlocks_to_hit, A) + + START_PROCESSING(SSfastprocess, src) + +/datum/hallucination/bolts/process(delta_time) + next_action -= (delta_time * 10) + if (next_action > 0) + return + + if (locking) + var/atom/next_airlock = pop(airlocks_to_hit) + if (next_airlock) + var/obj/effect/hallucination/fake_door_lock/lock = new(get_turf(next_airlock)) + lock.target = target + lock.airlock = next_airlock + LAZYADD(locks, lock) + + if (!airlocks_to_hit.len) + locking = FALSE + next_action = 10 SECONDS + return + else + var/obj/effect/hallucination/fake_door_lock/next_unlock = popleft(locks) + if (next_unlock) + next_unlock.unlock() + else + qdel(src) + return + + next_action = rand(4, 12) + +/datum/hallucination/bolts/Destroy() + . = ..() + QDEL_LIST(locks) + STOP_PROCESSING(SSfastprocess, src) /obj/effect/hallucination/fake_door_lock layer = CLOSED_DOOR_LAYER + 1 //for Bump priority @@ -797,8 +930,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( target.playsound_local(source,'sound/machines/airlock.ogg', 30, 1) if("airlock pry") target.playsound_local(source,'sound/machines/airlock_alien_prying.ogg', 100, 1) - sleep(50) - target.playsound_local(source, 'sound/machines/airlockforced.ogg', 30, 1) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/machines/airlockforced.ogg', 30, 1), 50) if("console") target.playsound_local(source,'sound/machines/terminal_prompt.ogg', 25, 1) if("explosion") @@ -815,29 +947,53 @@ GLOBAL_LIST_INIT(hallucination_list, list( if("beepsky") target.playsound_local(source, 'sound/voice/beepsky/freeze.ogg', 35, 0) if("mech") - var/mech_dir = pick(GLOB.cardinals) - for(var/i in 1 to rand(4,9)) - if(prob(75)) - target.playsound_local(source, 'sound/mecha/mechstep.ogg', 40, 1) - source = get_step(source, mech_dir) - else - target.playsound_local(source, 'sound/mecha/mechturn.ogg', 40, 1) - mech_dir = pick(GLOB.cardinals) - sleep(10) + new /datum/hallucination/mech_sounds(C, forced, sound_type) //Deconstructing a wall if("wall decon") target.playsound_local(source, 'sound/items/welder.ogg', 50, 1) - sleep(105) - target.playsound_local(source, 'sound/items/welder2.ogg', 50, 1) - sleep(15) - target.playsound_local(source, 'sound/items/ratchet.ogg', 50, 1) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/items/welder2.ogg', 50, 1), 105) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/items/ratchet.ogg', 50, 1), 120) //Hacking a door if("door hack") target.playsound_local(source, 'sound/items/screwdriver.ogg', 50, 1) - sleep(rand(40,80)) - target.playsound_local(source, 'sound/machines/airlockforced.ogg', 30, 1) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/machines/airlockforced.ogg', 30, 1), rand(40, 80)) qdel(src) +/datum/hallucination/mech_sounds + var/mech_dir + var/steps_left + var/next_action = 0 + var/turf/source + +/datum/hallucination/mech_sounds/New() + . = ..() + mech_dir = pick(GLOB.cardinals) + steps_left = rand(4, 9) + source = random_far_turf() + START_PROCESSING(SSfastprocess, src) + +/datum/hallucination/mech_sounds/process(delta_time) + next_action -= delta_time + if (next_action > 0) + return + + if(prob(75)) + target.playsound_local(source, 'sound/mecha/mechstep.ogg', 40, 1) + source = get_step(source, mech_dir) + else + target.playsound_local(source, 'sound/mecha/mechturn.ogg', 40, 1) + mech_dir = pick(GLOB.cardinals) + + steps_left -= 1 + if (!steps_left) + qdel(src) + return + next_action = 1 + +/datum/hallucination/mech_sounds/Destroy() + . = ..() + STOP_PROCESSING(SSfastprocess, src) + /datum/hallucination/weird_sounds /datum/hallucination/weird_sounds/New(mob/living/carbon/C, forced = TRUE, sound_type) @@ -851,12 +1007,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( switch(sound_type) if("phone") target.playsound_local(source, 'sound/weapons/ring.ogg', 15) - sleep(25) - target.playsound_local(source, 'sound/weapons/ring.ogg', 15) - sleep(25) - target.playsound_local(source, 'sound/weapons/ring.ogg', 15) - sleep(25) - target.playsound_local(source, 'sound/weapons/ring.ogg', 15) + for (var/next_rings in 1 to 3) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/weapons/ring.ogg', 15), 25 * next_rings) if("hyperspace") target.playsound_local(null, 'sound/runtime/hyperspace/hyperspace_begin.ogg', 50) if("hallelujah") @@ -875,10 +1027,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( target.playsound_local(source, pick(CREEPY_SOUNDS), 50, 1) if("tesla") //Tesla loose! target.playsound_local(source, 'sound/magic/lightningbolt.ogg', 35, 1) - sleep(30) - target.playsound_local(source, 'sound/magic/lightningbolt.ogg', 65, 1) - sleep(30) - target.playsound_local(source, 'sound/magic/lightningbolt.ogg', 100, 1) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/magic/lightningbolt.ogg', 65, 1), 30) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, 'sound/magic/lightningbolt.ogg', 100, 1), 60) qdel(src) @@ -898,8 +1048,18 @@ GLOBAL_LIST_INIT(hallucination_list, list( if("ratvar") target.playsound_local(target, 'sound/machines/clockcult/ark_deathrattle.ogg', 50, FALSE, pressure_affected = FALSE) target.playsound_local(target, 'sound/effects/clockcult_gateway_disrupted.ogg', 50, FALSE, pressure_affected = FALSE) - sleep(27) - target.playsound_local(target, 'sound/effects/explosion_distant.ogg', 50, FALSE, pressure_affected = FALSE) + addtimer(CALLBACK( + target, + /mob/.proc/playsound_local, + target, + 'sound/effects/explosion_distant.ogg', + 50, + FALSE, + /* frequency = */ null, + /* falloff_exponential = */ null, + /* channel = */ null, + /* pressure_affected = */ FALSE + ), 27) if("shuttle dock") to_chat(target, "