Files
Bubberstation/code/modules/flufftext/Hallucination.dm
Qustinnus 4911991f70 [READY] Several fixes/changes to mood, longterm mood effects, beauty component (#36344)
cl Floyd / Qustinnus
del: Removes short-term effects of mood
add; Adds long-term effects of mood by implementing sanity which goes up with good mood, down with bad mood, but takes time to change. Your sanity can be seen as your average mood in the recent past. All effects of moods are now covered by this system
add: Beauty component, currently only attached to cleanables, but you could attach it to any atom/movable and make them pretty/ugly, affecting mood of anyone in the room.
refactor: Removes the original way of adding mood events, uses signals properly instead.
fix: Cleanables "giving" area's free beauty during initialization
fix: Fixes some events not clearing properly
/cl

Fixes #36444

From now on mood no longer affects you directly, instead it decides whether your sanity goes up or down, when your sanity gets too low you will get the effects of what mood did before.

This means getting hit with bad moods due to being attacked while not mean you are doomed anymore, and you get a large timeframe to get away and just fix your mood later.

I also added the beauty component, you could add this to any object and it would either make a room prettier or uglier, comparable to DF or Rimworld. You could add traits to make certain people ugly, for example.
2018-03-18 19:48:50 +13:00

1220 lines
42 KiB
Plaintext

/*
Ideas for the subtle effects of hallucination:
Light up oxygen/plasma indicators (done)
Cause health to look critical/dead, even when standing (done)
Characters silently watching you
Brief flashes of fire/space/bombs/c4/dangerous shit (done)
Items that are rare/traitorous/don't exist appearing in your inventory slots (done)
Strange audio (should be rare) (done)
Gunshots/explosions/opening doors/less rare audio (done)
*/
#define HAL_LINES_FILE "hallucination.json"
GLOBAL_LIST_INIT(hallucinations_minor, list(
/datum/hallucination/sounds,
/datum/hallucination/bolts,
/datum/hallucination/whispers,
/datum/hallucination/message,
/datum/hallucination/hudscrew))
GLOBAL_LIST_INIT(hallucinations_medium, list(
/datum/hallucination/fake_alert,
/datum/hallucination/items,
/datum/hallucination/items_other,
/datum/hallucination/dangerflash,
/datum/hallucination/bolts,
/datum/hallucination/fake_flood,
/datum/hallucination/husks,
/datum/hallucination/battle,
/datum/hallucination/fire,
/datum/hallucination/self_delusion,
/datum/hallucination/stray_bullet))
GLOBAL_LIST_INIT(hallucinations_major, list(
/datum/hallucination/fakeattacker,
/datum/hallucination/death,
/datum/hallucination/xeno_attack,
/datum/hallucination/singularity_scare,
/datum/hallucination/delusion,
/datum/hallucination/oh_yeah))
/mob/living/carbon/proc/handle_hallucinations()
if(world.time < next_hallucination)
return
if(hallucination)
var/list/current = GLOB.hallucinations_minor
if(prob(25) && hallucination > 100)
current = GLOB.hallucinations_medium
else if(prob(10) && hallucination > 200)
current = GLOB.hallucinations_major
var/halpick = pick(current)
new halpick(src, FALSE)
/mob/living/carbon/proc/set_screwyhud(hud_type)
hal_screwyhud = hud_type
update_health_hud()
/datum/hallucination
var/mob/living/carbon/target
var/cost = 5 //affects the amount of hallucination reduced, and cooldown until the next hallucination
var/feedback_details //extra info for investigate
/datum/hallucination/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
target = T
if(!forced)
target.hallucination = max(0, target.hallucination - cost)
target.next_hallucination = world.time + (rand(cost * 0.5, cost * 3) * 10)
/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]. [feedback_details]", INVESTIGATE_HALLUCINATIONS)
target = null
return ..()
/obj/effect/hallucination
invisibility = INVISIBILITY_OBSERVER
anchored = TRUE
var/mob/living/carbon/target = null
/obj/effect/hallucination/simple
var/image_icon = 'icons/mob/alien.dmi'
var/image_state = "alienh_pounce"
var/px = 0
var/py = 0
var/col_mod = null
var/image/current_image = null
var/image_layer = MOB_LAYER
var/active = TRUE //qdelery
/obj/effect/hallucination/singularity_pull()
return
/obj/effect/hallucination/singularity_act()
return
/obj/effect/hallucination/simple/Initialize(mapload, var/mob/living/carbon/T)
. = ..()
target = T
current_image = GetImage()
if(target.client)
target.client.images |= current_image
/obj/effect/hallucination/simple/proc/GetImage()
var/image/I = image(image_icon,src,image_state,image_layer,dir=src.dir)
I.pixel_x = px
I.pixel_y = py
if(col_mod)
I.color = col_mod
return I
/obj/effect/hallucination/simple/proc/Show(update=1)
if(active)
if(target.client)
target.client.images.Remove(current_image)
if(update)
current_image = GetImage()
if(target.client)
target.client.images |= current_image
/obj/effect/hallucination/simple/update_icon(new_state,new_icon,new_px=0,new_py=0)
image_state = new_state
if(new_icon)
image_icon = new_icon
else
image_icon = initial(image_icon)
px = new_px
py = new_py
Show()
/obj/effect/hallucination/simple/Moved(atom/OldLoc, Dir)
Show()
/obj/effect/hallucination/simple/Destroy()
if(target.client)
target.client.images.Remove(current_image)
active = FALSE
return ..()
#define FAKE_FLOOD_EXPAND_TIME 20
#define FAKE_FLOOD_MAX_RADIUS 10
/datum/hallucination/fake_flood
//Plasma starts flooding from the nearby vent
var/turf/center
var/list/flood_images = list()
var/list/turf/flood_turfs = list()
var/image_icon = 'icons/effects/tile_effects.dmi'
var/image_state = "plasma"
var/radius = 0
var/next_expand = 0
cost = 25
/datum/hallucination/fake_flood/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target))
if(!U.welded)
center = get_turf(U)
break
if(!center)
qdel(src)
return
feedback_details += "Vent Coords: [center.x],[center.y],[center.z]"
flood_images += image(image_icon,center,image_state,MOB_LAYER)
flood_turfs += center
if(target.client)
target.client.images |= flood_images
next_expand = world.time + FAKE_FLOOD_EXPAND_TIME
START_PROCESSING(SSobj, src)
/datum/hallucination/fake_flood/process()
if(next_expand <= world.time)
radius++
if(radius > FAKE_FLOOD_MAX_RADIUS)
qdel(src)
return
Expand()
if((get_turf(target) in flood_turfs) && !target.internal)
new /datum/hallucination/fake_alert(target, TRUE, "too_much_tox")
next_expand = world.time + FAKE_FLOOD_EXPAND_TIME
/datum/hallucination/fake_flood/proc/Expand()
for(var/turf/FT in flood_turfs)
for(var/dir in GLOB.cardinals)
var/turf/T = get_step(FT, dir)
if((T in flood_turfs) || !FT.CanAtmosPass(T))
continue
flood_images += image(image_icon,T,image_state,MOB_LAYER)
flood_turfs += T
if(target.client)
target.client.images |= flood_images
/datum/hallucination/fake_flood/Destroy()
STOP_PROCESSING(SSobj, src)
qdel(flood_turfs)
flood_turfs = list()
if(target.client)
target.client.images.Remove(flood_images)
qdel(flood_images)
flood_images = list()
return ..()
/obj/effect/hallucination/simple/xeno
image_icon = 'icons/mob/alien.dmi'
image_state = "alienh_pounce"
/obj/effect/hallucination/simple/xeno/Initialize(mapload, mob/living/carbon/T)
. = ..()
name = "alien hunter ([rand(1, 1000)])"
/obj/effect/hallucination/simple/xeno/throw_impact(A)
update_icon("alienh_pounce")
if(A == target && target.stat!=DEAD)
target.Knockdown(100)
target.visible_message("<span class='danger'>[target] flails around wildly.</span>","<span class ='userdanger'>[name] pounces on you!</span>")
/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/obj/effect/hallucination/simple/xeno/xeno = null
cost = 25
/datum/hallucination/xeno_attack/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target))
if(!U.welded)
pump = 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, xeno, FALSE, TRUE)
sleep(10)
xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32)
xeno.throw_at(pump,7,1, xeno, FALSE, TRUE)
sleep(10)
var/xeno_name = xeno.name
to_chat(target, "<span class='notice'>[xeno_name] begins climbing into the ventilation system...</span>")
sleep(30)
qdel(xeno)
to_chat(target, "<span class='notice'>[xeno_name] scrambles into the ventilation ducts!</span>")
qdel(src)
/obj/effect/hallucination/simple/clown
image_icon = 'icons/mob/animal.dmi'
image_state = "clown"
/obj/effect/hallucination/simple/clown/Initialize(mapload, mob/living/carbon/T, duration)
..(loc, T)
name = pick(GLOB.clown_names)
QDEL_IN(src,duration)
/obj/effect/hallucination/simple/clown/scary
image_state = "scary_clown"
/obj/effect/hallucination/simple/bubblegum
name = "Bubblegum"
image_icon = 'icons/mob/lavaland/96x96megafauna.dmi'
image_state = "bubblegum"
px = -32
/datum/hallucination/oh_yeah
var/obj/effect/hallucination/simple/bubblegum/bubblegum
var/image/fakebroken
var/image/fakerune
cost = 75
/datum/hallucination/oh_yeah/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
. = ..()
var/turf/closed/wall/wall
for(var/turf/closed/wall/W in range(7,target))
wall = W
break
if(!wall)
return INITIALIZE_HINT_QDEL
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)
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
if(target.client)
target.client.images |= fakebroken
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)
/datum/hallucination/oh_yeah/proc/bubble_attack(turf/landing)
var/charged = FALSE //only get hit once
while(get_turf(bubblegum) != landing && target && target.stat != DEAD)
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)
shake_camera(target, 2, 1)
if(bubblegum.Adjacent(target) && !charged)
charged = TRUE
target.Knockdown(80)
target.adjustStaminaLoss(40)
step_away(target, bubblegum)
shake_camera(target, 4, 3)
target.visible_message("<span class='warning'>[target] jumps backwards, falling on the ground!</span>","<span class='userdanger'>[bubblegum] slams into you!</span>")
sleep(2)
sleep(30)
qdel(src)
/datum/hallucination/oh_yeah/Destroy()
if(target.client)
target.client.images.Remove(fakebroken)
target.client.images.Remove(fakerune)
QDEL_NULL(fakebroken)
QDEL_NULL(fakerune)
QDEL_NULL(bubblegum)
return ..()
/datum/hallucination/singularity_scare
//Singularity moving towards you.
//todo Hide where it moved with fake space images
var/obj/effect/hallucination/simple/singularity/s = null
cost = 75
/datum/hallucination/singularity_scare/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
var/turf/start = get_turf(T)
var/screen_border = pick(SOUTH,EAST,WEST,NORTH)
for(var/i in 1 to 13)
start = get_step(start,screen_border)
feedback_details += "Source: [start.x],[start.y],[start.z]"
s = new(start,target)
s.parent = src
for(var/i in 1 to 13)
sleep(10)
s.forceMove(get_step(get_turf(s),get_dir(s,target)))
s.Show()
s.Eat()
qdel(s)
/obj/effect/hallucination/simple/singularity
image_icon = 'icons/effects/224x224.dmi'
image_state = "singularity_s7"
image_layer = MASSIVE_OBJ_LAYER
px = -96
py = -96
var/datum/hallucination/singularity_scare/parent
/obj/effect/hallucination/simple/singularity/proc/Eat(atom/OldLoc, Dir)
var/target_dist = get_dist(src,target)
if(target_dist<=3) //"Eaten"
target.set_screwyhud(SCREWYHUD_DEAD)
target.SetUnconscious(160)
addtimer(CALLBACK(parent, /datum/hallucination/.proc/wake_and_restore), rand(30, 50))
/datum/hallucination/battle
cost = 15
/datum/hallucination/battle/New(mob/living/carbon/T, forced = TRUE, battle_type)
set waitfor = FALSE
..()
var/hits = rand(3,6)
if(!battle_type)
battle_type = pick("laser","esword","gun","stunprod","bomb")
feedback_details += "Type: [battle_type]"
switch(battle_type)
if("laser") //Laser fight
for(var/i in 1 to hits)
target.playsound_local(null, 'sound/weapons/laser.ogg', 25, 1)
if(prob(50))
addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, 'sound/weapons/sear.ogg', 25, 1), rand(10,20))
else
addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, 'sound/weapons/effects/searwall.ogg', 25, 1), rand(10,20))
sleep(rand(CLICK_CD_RANGE, CLICK_CD_RANGE + 8))
target.playsound_local(null, get_sfx("bodyfall"), 25, 1)
if("esword") //Esword fight
target.playsound_local(null, 'sound/weapons/saberon.ogg',15, 1)
for(var/i=0,i<hits,i++)
target.playsound_local(null, 'sound/weapons/blade1.ogg', 25, 1)
sleep(rand(CLICK_CD_MELEE, CLICK_CD_MELEE + 8))
target.playsound_local(null, get_sfx("bodyfall"), 25, 1)
target.playsound_local(null, 'sound/weapons/saberoff.ogg', 15, 1)
if("gun") //Gun fight
for(var/i in 1 to hits)
target.playsound_local(null, get_sfx("gunshot"), 25)
if(prob(60))
addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, 'sound/weapons/pierce.ogg', 25, 1), rand(10,20))
else
addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, "ricochet", 25, 1), rand(10,20))
sleep(rand(CLICK_CD_RANGE, CLICK_CD_RANGE + 8))
target.playsound_local(null, get_sfx("bodyfall"), 25, 1)
if("stunprod") //Stunprod + cablecuff
target.playsound_local(null, 'sound/weapons/egloves.ogg', 40, 1)
target.playsound_local(null, get_sfx("bodyfall"), 25, 1)
sleep(20)
target.playsound_local(null, 'sound/weapons/cablecuff.ogg', 15, 1)
if("bomb") // Tick Tock
for(var/i in 1 to hits)
target.playsound_local(null, 'sound/items/timer.ogg', 25, 0)
sleep(15)
qdel(src)
/datum/hallucination/items_other
cost = 10
/datum/hallucination/items_other/New(mob/living/carbon/T, forced = TRUE, item_type)
set waitfor = FALSE
..()
var/item
if(!item_type)
item = pick(list("esword","dual_esword","stunpaper","clockspear","ttv","flash","armblade"))
else
item = item_type
feedback_details += "Item: [item]"
var/image_file
var/image/A = null
for(var/mob/living/carbon/human/H in view(7,target))
if(H != target)
var/free_hand = H.get_empty_held_index_for_side(side = "left")
if(free_hand)
image_file = 'icons/mob/inhands/items_lefthand.dmi'
else
free_hand = H.get_empty_held_index_for_side(side = "right")
if(free_hand)
image_file = 'icons/mob/inhands/items_righthand.dmi'
if(image_file)
switch(item)
if("esword")
target.playsound_local(H, 'sound/weapons/saberon.ogg',35,1)
A = image(image_file,H,"swordred", layer=ABOVE_MOB_LAYER)
if("dual_esword")
target.playsound_local(H, 'sound/weapons/saberon.ogg',35,1)
A = image(image_file,H,"dualsaberred1", layer=ABOVE_MOB_LAYER)
if("stunpaper")
A = image(image_file,H,"paper", layer=ABOVE_MOB_LAYER)
A.color = rgb(255,0,0)
if("clockspear")
A = image(image_file,H,"ratvarian_spear", layer=ABOVE_MOB_LAYER)
if("ttv")
A = image(image_file,H,"ttv", layer=ABOVE_MOB_LAYER)
if("flash")
A = image(image_file,H,"flashtool", layer=ABOVE_MOB_LAYER)
if("armblade")
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)
target.client.images.Remove(A)
break
qdel(src)
/datum/hallucination/delusion
var/list/image/delusions = list()
cost = 50
/datum/hallucination/delusion/New(mob/living/carbon/T, forced, force_kind = null , duration = 300,skip_nearby = 1, custom_icon = null, custom_icon_file = null, custom_name = null)
set waitfor = FALSE
. = ..()
var/image/A = null
var/kind = force_kind ? force_kind : pick("monkey","corgi","carp","skeleton","demon","zombie")
feedback_details += "Type: [kind]"
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(H == target)
continue
if(skip_nearby && (H in view(target)))
continue
switch(kind)
if("monkey")//Monkey
A = image('icons/mob/monkey.dmi',H,"monkey1")
A.name = "Monkey ([rand(1,999)])"
if("carp")//Carp
A = image('icons/mob/animal.dmi',H,"carp")
A.name = "Space Carp"
if("corgi")//Corgi
A = image('icons/mob/pets.dmi',H,"corgi")
A.name = "Corgi"
if("skeleton")//Skeletons
A = image('icons/mob/human.dmi',H,"skeleton")
A.name = "Skeleton"
if("zombie")//Zombies
A = image('icons/mob/human.dmi',H,"zombie")
A.name = "Zombie"
if("demon")//Demon
A = image('icons/mob/mob.dmi',H,"daemon")
A.name = "Demon"
if("custom")
A = image(custom_icon_file, H, custom_icon)
A.name = custom_name
A.override = 1
if(target.client)
delusions |= A
target.client.images |= A
if(duration)
QDEL_IN(src, duration)
/datum/hallucination/delusion/Destroy()
for(var/image/I in delusions)
if(target.client)
target.client.images.Remove(I)
return ..()
/datum/hallucination/self_delusion
var/image/delusion
cost = 40
/datum/hallucination/self_delusion/New(mob/living/carbon/T, forced, force_kind = null , duration = 300, custom_icon = null, custom_icon_file = null, wabbajack = TRUE) //set wabbajack to false if you want to use another fake source
set waitfor = FALSE
..()
var/image/A = null
var/kind = force_kind ? force_kind : pick("monkey","corgi","carp","skeleton","demon","zombie","robot")
feedback_details += "Type: [kind]"
switch(kind)
if("monkey")//Monkey
A = image('icons/mob/monkey.dmi',target,"monkey1")
if("carp")//Carp
A = image('icons/mob/animal.dmi',target,"carp")
if("corgi")//Corgi
A = image('icons/mob/pets.dmi',target,"corgi")
if("skeleton")//Skeletons
A = image('icons/mob/human.dmi',target,"skeleton")
if("zombie")//Zombies
A = image('icons/mob/human.dmi',target,"zombie")
if("demon")//Demon
A = image('icons/mob/mob.dmi',target,"daemon")
if("robot")//Cyborg
A = image('icons/mob/robots.dmi',target,"robot")
target.playsound_local(target,'sound/voice/liveagain.ogg', 75, 1)
if("custom")
A = image(custom_icon_file, target, custom_icon)
A.override = 1
if(target.client)
if(wabbajack)
to_chat(target, "<span class='italics'>...wabbajack...wabbajack...</span>")
target.playsound_local(target,'sound/magic/staff_change.ogg', 50, 1)
delusion = A
target.client.images |= A
QDEL_IN(src, duration)
/datum/hallucination/self_delusion/Destroy()
if(target.client)
target.client.images.Remove(delusion)
return ..()
/datum/hallucination/fakeattacker/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
var/mob/living/carbon/human/clone = null
var/clone_weapon = null
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(H.stat || H.lying)
continue
clone = H
feedback_details += "Clone of: [H.real_name]"
break
if(!clone)
return
var/static/list/non_fakeattack_weapons = list(/obj/item/gun/ballistic, /obj/item/ammo_box/a357,\
/obj/item/gun/energy/kinetic_accelerator/crossbow, /obj/item/melee/transforming/energy/sword/saber,\
/obj/item/storage/box/syndicate, /obj/item/storage/box/emps,\
/obj/item/cartridge/virus/syndicate, /obj/item/clothing/under/chameleon,\
/obj/item/clothing/shoes/chameleon/noslip, /obj/item/card/id/syndicate,\
/obj/item/clothing/mask/chameleon, /obj/item/clothing/glasses/thermal,\
/obj/item/device/chameleon, /obj/item/card/emag, /obj/item/grenade/plastic/x4,\
/obj/item/storage/toolbox/syndicate, /obj/item/aiModule,\
/obj/item/device/radio/headset/syndicate, /obj/item/grenade/plastic/c4,\
/obj/item/device/powersink, /obj/item/storage/box/syndie_kit,\
/obj/item/toy/syndicateballoon, /obj/item/gun/energy/laser/captain,\
/obj/item/hand_tele, /obj/item/construction/rcd, /obj/item/tank/jetpack,\
/obj/item/clothing/under/rank/captain, /obj/item/device/aicard,\
/obj/item/clothing/shoes/magboots, /obj/item/areaeditor/blueprints, /obj/item/disk/nuclear,\
/obj/item/clothing/suit/space/nasavoid, /obj/item/tank)
var/obj/effect/fake_attacker/F = new/obj/effect/fake_attacker(get_turf(target),target)
for(var/obj/item/I in clone.held_items)
if(!(locate(I) in non_fakeattack_weapons))
clone_weapon = I.name
F.weap = I
F.name = clone.name
F.my_target = target
F.weapon_name = clone_weapon
F.left = image(clone,dir = WEST)
F.right = image(clone,dir = EAST)
F.up = image(clone,dir = NORTH)
F.down = image(clone,dir = SOUTH)
F.updateimage()
qdel(src)
/obj/effect/fake_attacker
icon = null
icon_state = null
name = ""
desc = ""
density = FALSE
anchored = TRUE
opacity = 0
var/mob/living/carbon/human/my_target = null
var/weapon_name = null
var/obj/item/weap = null
var/image/stand_icon = null
var/image/currentimage = null
var/icon/base = null
var/skin_tone
var/mob/living/clone = null
var/image/left
var/image/right
var/image/up
var/collapse
var/image/down
max_integrity = 100
/obj/effect/fake_attacker/attackby(obj/item/P, mob/living/user, params)
step_away(src,my_target,2)
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src)
my_target.playsound_local(src, P.hitsound, 1)
my_target.visible_message("<span class='danger'>[my_target] flails around wildly.</span>", \
"<span class='danger'>[my_target] has attacked [src]!</span>")
obj_integrity -= P.force
/obj/effect/fake_attacker/Crossed(mob/M, somenumber)
if(M == my_target)
step_away(src,my_target,2)
if(prob(30))
for(var/mob/O in oviewers(world.view , my_target))
to_chat(O, "<span class='danger'>[my_target] stumbles around.</span>")
/obj/effect/fake_attacker/Initialize(mapload, mob/living/carbon/T)
..()
my_target = T
QDEL_IN(src, 300)
step_away(src,my_target,2)
INVOKE_ASYNC(src, .proc/attack_loop)
/obj/effect/fake_attacker/proc/updateimage()
// del src.currentimage
if(src.dir == NORTH)
del src.currentimage
src.currentimage = new /image(up,src)
else if(src.dir == SOUTH)
del src.currentimage
src.currentimage = new /image(down,src)
else if(src.dir == EAST)
del src.currentimage
src.currentimage = new /image(right,src)
else if(src.dir == WEST)
del src.currentimage
src.currentimage = new /image(left,src)
SEND_IMAGE(my_target, currentimage)
/obj/effect/fake_attacker/proc/attack_loop()
while(1)
sleep(rand(5,10))
if(obj_integrity < 0 || my_target.stat)
collapse()
continue
if(get_dist(src,my_target) > 1)
src.setDir(get_dir(src,my_target))
step_towards(src,my_target)
updateimage()
else
if(prob(15))
if(weapon_name)
my_target.playsound_local(my_target, weap.hitsound, weap.get_clamped_volume(), 1)
my_target.show_message("<span class='danger'>[src.name] has attacked [my_target] with [weapon_name]!</span>", 1)
my_target.staminaloss += 30
if(prob(20))
my_target.blur_eyes(3)
if(prob(33))
if(!locate(/obj/effect/overlay) in my_target.loc)
fake_blood(my_target)
else
my_target.playsound_local(my_target, pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg'), 25, 1)
my_target.show_message("<span class='userdanger'>[src.name] has punched [my_target]!</span>", 1)
my_target.staminaloss += 30
if(prob(33))
if(!locate(/obj/effect/overlay) in my_target.loc)
fake_blood(my_target)
if(prob(15))
step_away(src,my_target,2)
/obj/effect/fake_attacker/proc/collapse()
collapse = 1
updateimage()
qdel(src)
/obj/effect/fake_attacker/proc/fake_blood(mob/target)
var/obj/effect/overlay/O = new/obj/effect/overlay(target.loc)
O.name = "blood"
var/image/I = image('icons/effects/blood.dmi',O,"floor[rand(1,7)]",O.dir,1)
SEND_IMAGE(target, I)
QDEL_IN(O, 300)
/datum/hallucination/bolts
var/list/doors = list()
cost = 25
/datum/hallucination/bolts/New(mob/living/carbon/T, forced, door_number=-1) //-1 for severe, 1-2 for subtle
set waitfor = FALSE
..()
var/image/I = null
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++
I = image(A.overlays_file, get_turf(A), "lights_bolts",layer=A.layer+0.1)
doors += I
if(target.client)
target.client.images |= I
target.playsound_local(get_turf(A), 'sound/machines/boltsdown.ogg',30,0,3)
sleep(rand(6,12))
sleep(100)
for(var/image/B in doors)
if(target.client)
target.client.images.Remove(B)
target.playsound_local(get_turf(B), 'sound/machines/boltsup.ogg',30,0,3)
sleep(rand(6,12))
qdel(src)
/datum/hallucination/whispers
cost = 15
/datum/hallucination/whispers/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
var/target_name = target.first_name()
var/speak_messages = list("[pick_list_replacements(HAL_LINES_FILE, "suspicion")]",\
"[pick_list_replacements(HAL_LINES_FILE, "greetings")][target.first_name()]!",\
"[pick_list_replacements(HAL_LINES_FILE, "getout")]",\
"[pick_list_replacements(HAL_LINES_FILE, "weird")]",\
"[pick_list_replacements(HAL_LINES_FILE, "didyouhearthat")]",\
"[pick_list_replacements(HAL_LINES_FILE, "imatraitor")]",\
"[pick_list_replacements(HAL_LINES_FILE, "doubt")]",\
"[pick_list_replacements(HAL_LINES_FILE, "aggressive")]",\
"[pick_list_replacements(HAL_LINES_FILE, "help")]!!",\
"[pick_list_replacements(HAL_LINES_FILE, "escape")]",\
"I'm infected, [pick_list_replacements(HAL_LINES_FILE, "infection_advice")]!")
var/radio_messages = list("Set [target.first_name()] to arrest!",\
"[pick_list_replacements(HAL_LINES_FILE, "people")] is [pick_list_replacements(HAL_LINES_FILE, "accusations")]!",\
"Help!",\
"[pick_list_replacements(HAL_LINES_FILE, "threat")] in [pick_list_replacements(HAL_LINES_FILE, "location")][prob(50)?"!":"!!"]",\
"Where's [target.first_name()]?"\
,"[pick("C","Ai, c","Someone c","Rec")]all the shuttle!"\
,"AI [pick("rogue", "is dead")]!!")
var/list/mob/living/carbon/people = list()
var/list/mob/living/carbon/person = null
var/datum/language/understood_language = target.get_random_understood_language()
for(var/mob/living/carbon/H in view(target))
if(H == target)
continue
if(!person)
person = H
else
if(get_dist(target,H)<get_dist(target,person))
person = H
people += H
if(person) //Basic talk
var/chosen = pick(speak_messages)
chosen = replacetext(chosen, "%TARGETNAME%", target_name)
var/image/speech_overlay = image('icons/mob/talk.dmi', person, "default0", layer = ABOVE_MOB_LAYER)
var/message = target.compose_message(person,understood_language,chosen,null,person.get_spans(),face_name = TRUE)
feedback_details += "Type: Talk, Source: [person.real_name], Message: [message]"
to_chat(target, message)
if(target.client)
target.client.images |= speech_overlay
sleep(30)
target.client.images.Remove(speech_overlay)
else // Radio talk
var/chosen = pick(radio_messages)
chosen = replacetext(chosen, "%TARGETNAME%", target_name)
var/list/humans = list()
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
humans += H
person = pick(humans)
var/message = target.compose_message(person,understood_language,chosen,"[FREQ_COMMON]",person.get_spans(),face_name = TRUE)
feedback_details += "Type: Radio, Source: [person.real_name], Message: [message]"
to_chat(target, message)
qdel(src)
/datum/hallucination/message
cost = 15
/datum/hallucination/message/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
var/chosen = pick("<span class='userdanger'>The light burns you!</span>", \
"<span class='danger'>You don't feel like yourself.</span>", \
"<span class='notice'>You hear something squeezing through the ducts...</span>", \
"<span class='notice'>You hear a distant scream.</span>", \
"<span class='notice'>You feel invincible, nothing can hurt you!</span>", \
"<span class='warning'>You feel a tiny prick!</span>", \
"<B>[target]</B> sneezes.", \
//The truth, revealed
"<span class='warning'>You're hallucinating.</span>", \
//Direct advice
"[pick_list_replacements(HAL_LINES_FILE, "advice")]")
feedback_details += "Message: [chosen]"
to_chat(target, chosen)
qdel(src)
/datum/hallucination/sounds
cost = 15
/datum/hallucination/sounds/New(mob/living/carbon/T, forced = TRUE, sound_type)
set waitfor = FALSE
..()
if(!sound_type)
sound_type = pick("airlock","explosion","far_explosion","glass","phone","summon_guns","alarm","beepsky","hallelujah","creepy","ratvar","shuttle_dock",
"wall_decon","door_hack","esword","blob_alert","tesla","malf_ai","meteors")
feedback_details += "Type: [sound_type]"
//Strange audio
switch(sound_type)
if("airlock")
target.playsound_local(null,'sound/machines/airlock.ogg', 15, 1)
if("explosion")
if(prob(50))
target.playsound_local(null,'sound/effects/explosion1.ogg', 50, 1)
else
target.playsound_local(null, 'sound/effects/explosion2.ogg', 50, 1)
if("far_explosion")
target.playsound_local(null, 'sound/effects/explosionfar.ogg', 50, 1)
if("glass")
target.playsound_local(null, pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg'), 50, 1)
if("phone")
target.playsound_local(null, 'sound/weapons/ring.ogg', 15)
sleep(25)
target.playsound_local(null, 'sound/weapons/ring.ogg', 25)
sleep(25)
target.playsound_local(null, 'sound/weapons/ring.ogg', 35)
sleep(25)
target.playsound_local(null, 'sound/weapons/ring.ogg', 45)
if("summon_guns")
target.playsound_local(null, 'sound/magic/summon_guns.ogg', 50, 1)
if("alarm")
target.playsound_local(null, 'sound/machines/alarm.ogg', 100, 0)
if("beepsky")
target.playsound_local(null, 'sound/voice/bfreeze.ogg', 35, 0)
if("hallelujah")
target.playsound_local(null, 'sound/effects/pray_chaplain.ogg', 50)
//Rare audio
if("creepy")
//These sounds are (mostly) taken from Hidden: Source
target.playsound_local(null, pick(CREEPY_SOUNDS), 50, 1)
if("ratvar")
target.playsound_local(null, 'sound/effects/ratvar_rises.ogg', 100)
sleep(150)
target.playsound_local(null, 'sound/effects/ratvar_reveal.ogg', 100)
if("shuttle_dock")
to_chat(target, "<h1 class='alert'>Priority Announcement</h1>")
to_chat(target, "<br><br><span class='alert'>The Emergency Shuttle has docked with the station. You have 3 minutes to board the Emergency Shuttle.</span><br><br>")
target.playsound_local(null, 'sound/ai/shuttledock.ogg', 100)
//Deconstructing a wall
if("wall_decon")
target.playsound_local(null, 'sound/items/welder.ogg', 15, 1)
sleep(105)
target.playsound_local(null, 'sound/items/welder2.ogg', 15, 1)
sleep(15)
target.playsound_local(null, 'sound/items/ratchet.ogg', 15, 1)
//Hacking a door
if("door_hack")
target.playsound_local(null, 'sound/items/screwdriver.ogg', 15, 1)
sleep(rand(10,30))
for(var/i = rand(1,3), i>0, i--)
target.playsound_local(null, 'sound/weapons/empty.ogg', 15, 1)
sleep(rand(10,30))
target.playsound_local(null, 'sound/machines/airlockforced.ogg', 15, 1)
if("esword")
target.playsound_local(null, 'sound/weapons/saberon.ogg',35,1)
if("blob_alert")
to_chat(target, "<h1 class='alert'>Biohazard Alert</h1>")
to_chat(target, "<br><br><span class='alert'>Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.</span><br><br>")
target.playsound_local(null, 'sound/ai/outbreak5.ogg', 100, 0)
if("tesla") //Tesla loose!
target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 35, 1)
sleep(30)
target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 65, 1)
sleep(30)
target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 100, 1)
if("malf_ai") //AI is doomsdaying!
to_chat(target, "<h1 class='alert'>Anomaly Alert</h1>")
to_chat(target, "<br><br><span class='alert'>Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.</span><br><br>")
target.playsound_local(null, 'sound/ai/aimalf.ogg', 100, 0)
if("meteors") //Meteors inbound!
to_chat(target, "<h1 class='alert'>Meteor Alert</h1>")
to_chat(target, "<br><br><span class='alert'>Meteors have been detected on collision course with the station.</span><br><br>")
target.playsound_local(null, 'sound/ai/meteors.ogg', 100, 0)
qdel(src)
/datum/hallucination/hudscrew
cost = 10
/datum/hallucination/hudscrew/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
//Screwy HUD
target.set_screwyhud(pick(SCREWYHUD_CRIT,SCREWYHUD_DEAD,SCREWYHUD_HEALTHY))
feedback_details += "Type: [target.hal_screwyhud]"
sleep(rand(100,250))
target.set_screwyhud(SCREWYHUD_NONE)
qdel(src)
/datum/hallucination/fake_alert
cost = 15
/datum/hallucination/fake_alert/New(mob/living/carbon/T, forced = TRUE, specific, duration = 150)
set waitfor = FALSE
..()
var/alert_type = pick("not_enough_oxy","not_enough_tox","not_enough_co2","too_much_oxy","too_much_co2","too_much_tox","newlaw","nutrition","charge","weightless","fire","locked","hacked","temphot","tempcold","pressure")
if(specific)
alert_type = specific
feedback_details += "Type: [alert_type]"
switch(alert_type)
if("not_enough_oxy")
target.throw_alert(alert_type, /obj/screen/alert/not_enough_oxy, override = TRUE)
if("not_enough_tox")
target.throw_alert(alert_type, /obj/screen/alert/not_enough_tox, override = TRUE)
if("not_enough_co2")
target.throw_alert(alert_type, /obj/screen/alert/not_enough_co2, override = TRUE)
if("too_much_oxy")
target.throw_alert(alert_type, /obj/screen/alert/too_much_oxy, override = TRUE)
if("too_much_co2")
target.throw_alert(alert_type, /obj/screen/alert/too_much_co2, override = TRUE)
if("too_much_tox")
target.throw_alert(alert_type, /obj/screen/alert/too_much_tox, override = TRUE)
if("nutrition")
if(prob(50))
target.throw_alert(alert_type, /obj/screen/alert/fat, override = TRUE)
else
target.throw_alert(alert_type, /obj/screen/alert/starving, override = TRUE)
if("weightless")
target.throw_alert(alert_type, /obj/screen/alert/weightless, override = TRUE)
if("fire")
target.throw_alert(alert_type, /obj/screen/alert/fire, override = TRUE)
if("temphot")
alert_type = "temp"
target.throw_alert(alert_type, /obj/screen/alert/hot, 3, override = TRUE)
if("tempcold")
alert_type = "temp"
target.throw_alert(alert_type, /obj/screen/alert/cold, 3, override = TRUE)
if("pressure")
if(prob(50))
target.throw_alert(alert_type, /obj/screen/alert/highpressure, 2, override = TRUE)
else
target.throw_alert(alert_type, /obj/screen/alert/lowpressure, 2, override = TRUE)
//BEEP BOOP I AM A ROBOT
if("newlaw")
target.throw_alert(alert_type, /obj/screen/alert/newlaw, override = TRUE)
if("locked")
target.throw_alert(alert_type, /obj/screen/alert/locked, override = TRUE)
if("hacked")
target.throw_alert(alert_type, /obj/screen/alert/hacked, override = TRUE)
if("charge")
target.throw_alert(alert_type,/obj/screen/alert/emptycell, override = TRUE)
sleep(duration)
target.clear_alert(alert_type, clear_override = TRUE)
qdel(src)
/datum/hallucination/items
cost = 15
/datum/hallucination/items/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
//Strange items
if(!target.halitem)
target.halitem = new
var/obj/item/l_hand = target.get_item_for_held_index(1)
var/obj/item/r_hand = target.get_item_for_held_index(2)
var/l = ui_hand_position(target.get_held_index_of_item(l_hand))
var/r = ui_hand_position(target.get_held_index_of_item(r_hand))
var/list/slots_free = list(l,r)
if(l_hand)
slots_free -= l
if(r_hand)
slots_free -= r
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(!H.belt)
slots_free += ui_belt
if(!H.l_store)
slots_free += ui_storage1
if(!H.r_store)
slots_free += ui_storage2
if(slots_free.len)
target.halitem.screen_loc = pick(slots_free)
target.halitem.layer = ABOVE_HUD_LAYER
target.halitem.plane = ABOVE_HUD_PLANE
switch(rand(1,6))
if(1) //revolver
target.halitem.icon = 'icons/obj/guns/projectile.dmi'
target.halitem.icon_state = "revolver"
target.halitem.name = "Revolver"
if(2) //c4
target.halitem.icon = 'icons/obj/grenade.dmi'
target.halitem.icon_state = "plastic-explosive0"
target.halitem.name = "C4"
if(prob(25))
target.halitem.icon_state = "plasticx40"
if(3) //sword
target.halitem.icon = 'icons/obj/items_and_weapons.dmi'
target.halitem.icon_state = "sword0"
target.halitem.name = "Energy Sword"
if(4) //stun baton
target.halitem.icon = 'icons/obj/items_and_weapons.dmi'
target.halitem.icon_state = "stunbaton"
target.halitem.name = "Stun Baton"
if(5) //emag
target.halitem.icon = 'icons/obj/card.dmi'
target.halitem.icon_state = "emag"
target.halitem.name = "Cryptographic Sequencer"
if(6) //flashbang
target.halitem.icon = 'icons/obj/grenade.dmi'
target.halitem.icon_state = "flashbang1"
target.halitem.name = "Flashbang"
feedback_details += "Type: [target.halitem.name]"
if(target.client)
target.client.screen += target.halitem
QDEL_IN(target.halitem, rand(150, 350))
qdel(src)
/datum/hallucination/dangerflash
cost = 15
/datum/hallucination/dangerflash/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
//Flashes of danger
if(!target.halimage)
var/list/possible_points = list()
for(var/turf/open/floor/F in view(target,world.view))
possible_points += F
if(possible_points.len)
var/turf/open/floor/danger_point = pick(possible_points)
switch(rand(1,5))
if(1)
target.halimage = image('icons/turf/space.dmi',danger_point,"[rand(1,25)]",TURF_LAYER)
if(2)
target.halimage = image('icons/turf/floors/lava.dmi',danger_point,"smooth",TURF_LAYER)
if(3)
target.halimage = image('icons/turf/floors/Chasms.dmi',danger_point,"smooth",TURF_LAYER)
if(4)
target.halimage = image('icons/effects/effects.dmi',danger_point,"anom",OBJ_LAYER+0.01)
if(5)
target.halimage = image('icons/effects/effects.dmi',danger_point,"electricity2",OBJ_LAYER+0.01)
if(target.client)
target.client.images += target.halimage
sleep(rand(200,450))
if(target.client)
target.client.images -= target.halimage
QDEL_NULL(target.halimage)
qdel(src)
/datum/hallucination/death
cost = 40
/datum/hallucination/death/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
target.set_screwyhud(SCREWYHUD_DEAD)
target.Knockdown(300)
target.silent += 10
to_chat(target, "<span class='deadsay'><b>[target.real_name]</b> has died at <b>[get_area_name(target)]</b>.</span>")
if(prob(50))
var/mob/fakemob
var/list/dead_people = list()
for(var/mob/dead/observer/G in GLOB.player_list)
dead_people += G
if(LAZYLEN(dead_people))
fakemob = pick(dead_people)
else
fakemob = target //ever been so lonely you had to haunt yourself?
if(fakemob)
sleep(rand(20, 50))
to_chat(target, "<span class='deadsay'><b>DEAD: [fakemob.name]</b> says, \"[pick("rip","hey [target.first_name()]","you too?","is the AI rogue?",\
"i[prob(50)?" fucking":""] hate [pick("blood cult", "clock cult", "revenants", "abductors","double agents","viruses","badmins","you")]")]\"</span>")
sleep(rand(70,90))
target.set_screwyhud(SCREWYHUD_NONE)
target.SetKnockdown(0)
target.silent = FALSE
qdel(src)
/datum/hallucination/fire
cost = 25
/datum/hallucination/fire/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
var/image/fire_overlay = image('icons/mob/OnFire.dmi', target, "Standing", ABOVE_MOB_LAYER)
if(target.client)
target.client.images += fire_overlay
to_chat(target, "<span class='userdanger'>You're set on fire!</span>")
target.throw_alert("fire", /obj/screen/alert/fire, override = TRUE)
sleep(20)
target.throw_alert("temp", /obj/screen/alert/hot, 1, override = TRUE)
sleep(30)
target.clear_alert("temp", clear_override = TRUE)
target.throw_alert("temp", /obj/screen/alert/hot, 2, override = TRUE)
sleep(30)
target.clear_alert("temp", clear_override = TRUE)
target.throw_alert("temp", /obj/screen/alert/hot, 3, override = TRUE)
for(var/i in 1 to rand(5, 10))
target.adjustStaminaLoss(15)
sleep(25)
target.clear_alert("fire", clear_override = TRUE)
target.clear_alert("temp", clear_override = TRUE)
if(target.client)
target.client.images -= fire_overlay
QDEL_NULL(fire_overlay)
qdel(src)
/datum/hallucination/husks
cost = 20
/datum/hallucination/husks/New(mob/living/carbon/T, forced = TRUE)
set waitfor = FALSE
..()
if(!target.halbody)
var/list/possible_points = list()
for(var/turf/open/floor/F in view(target,world.view))
possible_points += F
if(possible_points.len)
var/turf/open/floor/husk_point = pick(possible_points)
switch(rand(1,4))
if(1)
var/image/body = image('icons/mob/human.dmi',husk_point,"husk",TURF_LAYER)
var/matrix/M = matrix()
M.Turn(90)
body.transform = M
target.halbody = body
if(2,3)
target.halbody = image('icons/mob/human.dmi',husk_point,"husk",TURF_LAYER)
if(4)
target.halbody = image('icons/mob/alien.dmi',husk_point,"alienother",TURF_LAYER)
if(target.client)
target.client.images += target.halbody
sleep(rand(30,50)) //Only seen for a brief moment.
if(target.client)
target.client.images -= target.halbody
QDEL_NULL(target.halbody)
qdel(src)
//hallucination projectile code in code/modules/projectiles/projectile/special.dm
/datum/hallucination/stray_bullet
cost = 15
/datum/hallucination/stray_bullet/New(mob/living/carbon/C, forced = TRUE)
set waitfor = FALSE
..()
var/list/turf/startlocs = list()
for(var/turf/open/T in view(world.view+1,target)-view(world.view,target))
startlocs += T
var/turf/start = pick(startlocs)
var/proj_type = pick(subtypesof(/obj/item/projectile/hallucination))
feedback_details += "Type: [proj_type]"
var/obj/item/projectile/hallucination/H = new proj_type(start)
target.playsound_local(start, H.hal_fire_sound, 60, 1)
H.hal_target = target
H.preparePixelProjectile(target, start)
H.fire()
qdel(src)
//Reality Dissociation Syndrome hallucinations only trigger in special cases and have no cost
/datum/hallucination/rds
cost = 0
/datum/hallucination/rds/fourth_wall/New(mob/living/carbon/C, forced = TRUE)
..()
to_chat(C, "<span class='userdanger extremelybig'>[pick("Leave the server" , "Close the game window")] [pick("immediately", "right now")].</span>")
/datum/hallucination/rds/supermatter/New(mob/living/carbon/C, forced = TRUE)
..()
SEND_SOUND(C, 'sound/magic/charge.ogg')
to_chat(C, "<span class='boldannounce'>You feel reality distort for a moment...</span>")
/datum/hallucination/rds/narsie/New(mob/living/carbon/C, forced = TRUE)
C.playsound_local(C, 'sound/creatures/narsie_rises.ogg', 50, FALSE, pressure_affected = FALSE)
to_chat(C, "<span class='narsie'>NAR-SIE HAS RISEN</span>")
/datum/hallucination/rds/ark/New(mob/living/carbon/C, forced = TRUE)
set waitfor = FALSE
..()
C.playsound_local(C, 'sound/machines/clockcult/ark_deathrattle.ogg', 50, FALSE, pressure_affected = FALSE)
C.playsound_local(C, 'sound/effects/clockcult_gateway_disrupted.ogg', 50, FALSE, pressure_affected = FALSE)
sleep(27)
C.playsound_local(C, 'sound/effects/explosion_distant.ogg', 50, FALSE, pressure_affected = FALSE)