From 7f92d9a0de9fbf335fb36d68ec569cfa3fdc587b Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 11 Jun 2022 20:16:42 -0500 Subject: [PATCH] Move and split hallucination code to module (#67642) * Move hallucination code to module * Remove Hallucination.dm --- code/modules/flufftext/Hallucination.dm | 1634 ------------------ code/modules/hallucination/HUD.dm | 156 ++ code/modules/hallucination/_hallucination.dm | 162 ++ code/modules/hallucination/airlock.dm | 90 + code/modules/hallucination/chat.dm | 169 ++ code/modules/hallucination/death.dm | 34 + code/modules/hallucination/fire.dm | 89 + code/modules/hallucination/hazard.dm | 128 ++ code/modules/hallucination/hostile_mob.dm | 171 ++ code/modules/hallucination/husk.dm | 31 + code/modules/hallucination/item.dm | 172 ++ code/modules/hallucination/plasma_flood.dm | 83 + code/modules/hallucination/polymorph.dm | 101 ++ code/modules/hallucination/shock.dm | 31 + code/modules/hallucination/sound.dm | 243 +++ code/modules/hallucination/stray_bullet.dm | 21 + tgstation.dme | 16 +- 17 files changed, 1696 insertions(+), 1635 deletions(-) delete mode 100644 code/modules/flufftext/Hallucination.dm create mode 100644 code/modules/hallucination/HUD.dm create mode 100644 code/modules/hallucination/_hallucination.dm create mode 100644 code/modules/hallucination/airlock.dm create mode 100644 code/modules/hallucination/chat.dm create mode 100644 code/modules/hallucination/death.dm create mode 100644 code/modules/hallucination/fire.dm create mode 100644 code/modules/hallucination/hazard.dm create mode 100644 code/modules/hallucination/hostile_mob.dm create mode 100644 code/modules/hallucination/husk.dm create mode 100644 code/modules/hallucination/item.dm create mode 100644 code/modules/hallucination/plasma_flood.dm create mode 100644 code/modules/hallucination/polymorph.dm create mode 100644 code/modules/hallucination/shock.dm create mode 100644 code/modules/hallucination/sound.dm create mode 100644 code/modules/hallucination/stray_bullet.dm diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm deleted file mode 100644 index 07898f993e6..00000000000 --- a/code/modules/flufftext/Hallucination.dm +++ /dev/null @@ -1,1634 +0,0 @@ -#define HAL_LINES_FILE "hallucination.json" - -GLOBAL_LIST_INIT(hallucination_list, list( - /datum/hallucination/chat = 100, - /datum/hallucination/message = 60, - /datum/hallucination/sounds = 50, - /datum/hallucination/battle = 20, - /datum/hallucination/dangerflash = 15, - /datum/hallucination/hudscrew = 12, - /datum/hallucination/fake_health_doll = 12, - /datum/hallucination/fake_alert = 12, - /datum/hallucination/weird_sounds = 8, - /datum/hallucination/stationmessage = 7, - /datum/hallucination/fake_flood = 7, - /datum/hallucination/stray_bullet = 7, - /datum/hallucination/bolts = 7, - /datum/hallucination/items_other = 7, - /datum/hallucination/husks = 7, - /datum/hallucination/items = 4, - /datum/hallucination/fire = 3, - /datum/hallucination/self_delusion = 2, - /datum/hallucination/delusion = 2, - /datum/hallucination/shock = 1, - /datum/hallucination/death = 1, - /datum/hallucination/oh_yeah = 1 - )) - - -/mob/living/carbon/proc/handle_hallucinations(delta_time, times_fired) - if(!hallucination) - return - - hallucination = max(hallucination - (0.5 * delta_time), 0) - if(world.time < next_hallucination) - return - - var/halpick = pick_weight(GLOB.hallucination_list) - new halpick(src, FALSE) - - next_hallucination = world.time + rand(100, 600) - -/mob/living/carbon/proc/set_screwyhud(hud_type) - hal_screwyhud = hud_type - update_health_hud() - -/datum/hallucination - var/natural = TRUE - var/mob/living/carbon/target - var/feedback_details //extra info for investigate - -/datum/hallucination/New(mob/living/carbon/C, forced = TRUE) - set waitfor = FALSE - 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 ..() - -//Returns a random turf in a ring around the target mob, useful for sound hallucinations -/datum/hallucination/proc/random_far_turf() - var/x_based = prob(50) - var/first_offset = pick(-8,-7,-6,-5,5,6,7,8) - var/second_offset = rand(-8,8) - var/x_off - var/y_off - if(x_based) - x_off = first_offset - y_off = second_offset - else - y_off = first_offset - x_off = second_offset - var/turf/T = locate(target.x + x_off, target.y + y_off, target.z) - return T - -/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/image_plane = GAME_PLANE - var/active = TRUE //qdelery - -/obj/effect/hallucination/singularity_pull() - return - -/obj/effect/hallucination/singularity_act() - return - -/obj/effect/hallucination/simple/Initialize(mapload, mob/living/carbon/T) - . = ..() - if(!T) - stack_trace("A hallucination was created with no target") - return INITIALIZE_HINT_QDEL - 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.plane = image_plane - 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(updates=ALL, 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) - . = ..() - if(!loc) - return - 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 - -/obj/effect/plasma_image_holder - icon_state = "nothing" - anchored = TRUE - layer = FLY_LAYER - plane = ABOVE_GAME_PLANE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/datum/hallucination/fake_flood - //Plasma starts flooding from the nearby vent - var/turf/center - var/list/flood_images = list() - var/list/flood_image_holders = list() - var/list/turf/flood_turfs = list() - var/image_icon = 'icons/effects/atmospherics.dmi' - var/image_state = "plasma" - var/radius = 0 - var/next_expand = 0 - -/datum/hallucination/fake_flood/New(mob/living/carbon/C, forced = TRUE) - ..() - 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]" - var/obj/effect/plasma_image_holder/pih = new(center) - var/image/plasma_image = image(image_icon, pih, image_state, FLY_LAYER) - plasma_image.alpha = 50 - plasma_image.plane = ABOVE_GAME_PLANE - flood_images += plasma_image - flood_image_holders += pih - 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, ALERT_TOO_MUCH_PLASMA) - next_expand = world.time + FAKE_FLOOD_EXPAND_TIME - -/datum/hallucination/fake_flood/proc/Expand() - for(var/image/I in flood_images) - I.alpha = min(I.alpha + 50, 255) - 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) || !TURFS_CAN_SHARE(T, FT) || isspaceturf(T)) //If we've gottem already, or if they're not alright to spread with. - continue - var/obj/effect/plasma_image_holder/pih = new(T) - var/image/new_plasma = image(image_icon, pih, image_state, FLY_LAYER) - new_plasma.alpha = 50 - new_plasma.plane = ABOVE_GAME_PLANE - flood_images += new_plasma - flood_image_holders += pih - 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() - qdel(flood_image_holders) - flood_image_holders = 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(atom/hit_atom, datum/thrownthing/throwingdatum) - update_icon(ALL, "alienh_pounce") - if(hit_atom == target && target.stat!=DEAD) - target.Paralyze(100) - target.visible_message(span_danger("[target] flails around wildly."),span_userdanger("[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 - 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) - ..() - for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target)) - if(!U.welded) - pump_location = get_turf(U) - break - - 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, span_notice("[xeno.name] scrambles into the ventilation ducts!")) - qdel(src) - if (XENO_ATTACK_STAGE_CLIMB to XENO_ATTACK_STAGE_FINISH) - to_chat(target, span_notice("[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(ALL, "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(ALL, "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' - 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 - var/turf/landing - var/charged - var/next_action = 0 - -/datum/hallucination/oh_yeah/New(mob/living/carbon/C, 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) - 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/start_processing), 10) - -/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) - shake_camera(target, 2, 1) - if(bubblegum.Adjacent(target) && !charged) - charged = TRUE - target.Paralyze(80) - target.adjustStaminaLoss(40) - step_away(target, bubblegum) - shake_camera(target, 4, 3) - target.visible_message(span_warning("[target] jumps backwards, falling on the ground!"),span_userdanger("[bubblegum] slams into you!")) - next_action = 0.2 - else - STOP_PROCESSING(SSfastprocess, src) - QDEL_IN(src, 3 SECONDS) - -/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) - 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, new_battle_type) - ..() - - 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("disabler", "laser") - iterations_left = rand(5, 10) - if("esword") - iterations_left = rand(4, 8) - target.playsound_local(source, 'sound/weapons/saberon.ogg',15, 1) - if("gun") - 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(SFX_BODYFALL), 25, 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(SFX_BODYFALL), 25, 1) - next_action = 2 SECONDS - if("bomb") // Tick Tock - 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 = SFX_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(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(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, SFX_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 - -/datum/hallucination/items_other/New(mob/living/carbon/C, forced = TRUE, item_type) - set waitfor = FALSE - ..() - var/item - if(!item_type) - item = pick(list("esword","taser","ebow","baton","dual_esword","ttv","flash","armblade")) - else - item = item_type - feedback_details += "Item: [item]" - var/side - var/image_file - var/image/A = null - var/list/mob_pool = list() - - for(var/mob/living/carbon/human/M in view(7,target)) - if(M != target) - mob_pool += M - if(!mob_pool.len) - return - - var/mob/living/carbon/human/H = pick(mob_pool) - feedback_details += " Mob: [H.real_name]" - - var/free_hand = H.get_empty_held_index_for_side(LEFT_HANDS) - if(free_hand) - side = "left" - else - free_hand = H.get_empty_held_index_for_side(RIGHT_HANDS) - if(free_hand) - side = "right" - - if(side) - switch(item) - if("esword") - if(side == "right") - image_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - else - image_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - target.playsound_local(H, 'sound/weapons/saberon.ogg',35,1) - A = image(image_file,H,"e_sword_on_red", layer=ABOVE_MOB_LAYER) - if("dual_esword") - if(side == "right") - image_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - else - image_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - target.playsound_local(H, 'sound/weapons/saberon.ogg',35,1) - A = image(image_file,H,"dualsaberred1", layer=ABOVE_MOB_LAYER) - if("taser") - if(side == "right") - image_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' - else - image_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - A = image(image_file,H,"advtaserstun4", layer=ABOVE_MOB_LAYER) - if("ebow") - if(side == "right") - image_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' - else - image_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - A = image(image_file,H,"crossbow", layer=ABOVE_MOB_LAYER) - if("baton") - if(side == "right") - image_file = 'icons/mob/inhands/equipment/security_righthand.dmi' - else - image_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' - target.playsound_local(H, SFX_SPARKS,75,1,-1) - A = image(image_file,H,"baton", layer=ABOVE_MOB_LAYER) - if("ttv") - if(side == "right") - image_file = 'icons/mob/inhands/weapons/bombs_righthand.dmi' - else - image_file = 'icons/mob/inhands/weapons/bombs_lefthand.dmi' - A = image(image_file,H,"ttv", layer=ABOVE_MOB_LAYER) - if("flash") - if(side == "right") - image_file = 'icons/mob/inhands/equipment/security_righthand.dmi' - else - image_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' - A = image(image_file,H,"flashtool", layer=ABOVE_MOB_LAYER) - if("armblade") - if(side == "right") - image_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' - else - image_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi' - target.playsound_local(H, 'sound/effects/blobattack.ogg',30,1) - A = image(image_file,H,"arm_blade", layer=ABOVE_MOB_LAYER) - if(target.client) - target.client.images |= 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 - var/list/image/delusions = list() - -/datum/hallucination/delusion/New(mob/living/carbon/C, forced, force_kind = null , duration = 300,skip_nearby = TRUE, custom_icon = null, custom_icon_file = null, custom_name = null) - set waitfor = FALSE - . = ..() - var/image/A = null - var/kind = force_kind ? force_kind : pick("nothing","monkey","corgi","carp","skeleton","demon","zombie") - feedback_details += "Type: [kind]" - var/list/nearby - if(skip_nearby) - nearby = get_hearers_in_view(7, target) - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - if(H == target) - continue - if(skip_nearby && (H in nearby)) - continue - switch(kind) - if("nothing") - A = image('icons/effects/effects.dmi',H,"nothing") - A.name = "..." - if("monkey")//Monkey - A = image('icons/mob/human.dmi',H,"monkey") - A.name = "Monkey ([rand(1,999)])" - if("carp")//Carp - A = image('icons/mob/carp.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 - -/datum/hallucination/self_delusion/New(mob/living/carbon/C, 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/human.dmi',target,"monkey") - 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_hear("...wabbajack...wabbajack...")) - 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/bolts - 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 - ..() - if(!door_number) - 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++ - LAZYADD(airlocks_to_hit, A) - - if(!LAZYLEN(airlocks_to_hit)) //no valid airlocks in sight - qdel(src) - return - - 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 (!LAZYLEN(airlocks_to_hit)) - 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 - plane = GAME_PLANE - var/image/bolt_light - var/obj/machinery/door/airlock/airlock - -/obj/effect/hallucination/fake_door_lock/proc/lock() - bolt_light = image(airlock.overlays_file, get_turf(airlock), "lights_bolts",layer=airlock.layer+0.1) - if(target.client) - target.client.images |= bolt_light - target.playsound_local(get_turf(airlock), 'sound/machines/boltsdown.ogg',30,0,3) - -/obj/effect/hallucination/fake_door_lock/proc/unlock() - if(target.client) - target.client.images.Remove(bolt_light) - target.playsound_local(get_turf(airlock), 'sound/machines/boltsup.ogg',30,0,3) - qdel(src) - -/obj/effect/hallucination/fake_door_lock/CanAllowThrough(atom/movable/mover, border_dir) - . = ..() - if(mover == target && airlock.density) - return FALSE - -/datum/hallucination/chat - -/datum/hallucination/chat/New(mob/living/carbon/C, forced = TRUE, force_radio, specific_message) - 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, "conversation")]",\ - "[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, "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("[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)?"!":"!!"]",\ - "[pick("Where's [target.first_name()]?", "Set [target.first_name()] to arrest!")]",\ - "[pick("C","Ai, c","Someone c","Rec")]all the shuttle!",\ - "AI [pick("rogue", "is dead")]!!") - - var/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)[other] puts the [pick(\ - "revolver","energy sword","cryptographic sequencer","power sink","energy bow",\ - "hybrid taser","stun baton","flash","syringe gun","circular saw","tank transfer valve",\ - "ritual dagger","spellbook",\ - "Codex Cicatrix", "Living Heart",\ - "pulse rifle","captain's spare ID","hand teleporter","hypospray","antique laser gun","X-01 MultiPhase Energy Gun","station's blueprints"\ - )] into [equipped_backpack].") - - message_pool.Add("[other] [pick("sneezes","coughs")].") - - message_pool.Add(span_notice("You hear something squeezing through the ducts..."), \ - span_notice("Your [pick("arm", "leg", "back", "head")] itches."),\ - span_warning("You feel [pick("hot","cold","dry","wet","woozy","faint")]."), - span_warning("Your stomach rumbles."), - span_warning("Your head hurts."), - span_warning("You hear a faint buzz in your head."), - "[target] sneezes.") - if(prob(10)) - message_pool.Add(span_warning("Behind you."),\ - span_warning("You hear a faint laughter."), - span_warning("You see something move."), - span_warning("You hear skittering on the ceiling."), - span_warning("You see an inhumanly tall silhouette moving in the distance.")) - if(prob(10)) - message_pool.Add("[pick_list_replacements(HAL_LINES_FILE, "advice")]") - var/chosen = pick(message_pool) - feedback_details += "Message: [chosen]" - to_chat(target, chosen) - qdel(src) - -/datum/hallucination/sounds - -/datum/hallucination/sounds/New(mob/living/carbon/C, forced = TRUE, sound_type) - set waitfor = FALSE - ..() - var/turf/source = random_far_turf() - if(!sound_type) - sound_type = pick("airlock","airlock pry","console","explosion","far explosion","mech","glass","alarm","beepsky","mech","wall decon","door hack") - feedback_details += "Type: [sound_type]" - //Strange audio - switch(sound_type) - if("airlock") - 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) - 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") - if(prob(50)) - target.playsound_local(source,'sound/effects/explosion1.ogg', 50, 1) - else - target.playsound_local(source, 'sound/effects/explosion2.ogg', 50, 1) - if("far explosion") - target.playsound_local(source, 'sound/effects/explosionfar.ogg', 50, 1) - if("glass") - target.playsound_local(source, pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg'), 50, 1) - if("alarm") - target.playsound_local(source, 'sound/machines/alarm.ogg', 100, 0) - if("beepsky") - target.playsound_local(source, 'sound/voice/beepsky/freeze.ogg', 35, 0) - if("mech") - 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) - 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) - 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) - set waitfor = FALSE - ..() - var/turf/source = random_far_turf() - if(!sound_type) - sound_type = pick("phone","hallelujah","highlander","laughter","hyperspace","game over","creepy","tesla") - feedback_details += "Type: [sound_type]" - //Strange audio - switch(sound_type) - if("phone") - 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") - target.playsound_local(source, 'sound/effects/pray_chaplain.ogg', 50) - if("highlander") - target.playsound_local(null, 'sound/misc/highlander.ogg', 50) - if("game over") - target.playsound_local(source, 'sound/misc/compiler-failure.ogg', 50) - if("laughter") - if(prob(50)) - target.playsound_local(source, 'sound/voice/human/womanlaugh.ogg', 50, 1) - else - target.playsound_local(source, pick('sound/voice/human/manlaugh1.ogg', 'sound/voice/human/manlaugh2.ogg'), 50, 1) - if("creepy") - //These sounds are (mostly) taken from Hidden: Source - target.playsound_local(source, pick(GLOB.creepy_ambience), 50, 1) - if("tesla") //Tesla loose! - target.playsound_local(source, 'sound/magic/lightningbolt.ogg', 35, 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) - -/datum/hallucination/stationmessage - -/datum/hallucination/stationmessage/New(mob/living/carbon/C, forced = TRUE, message) - set waitfor = FALSE - ..() - if(!message) - message = pick("ratvar","shuttle dock","blob alert","malf ai","meteors","supermatter") - feedback_details += "Type: [message]" - switch(message) - if("blob alert") - to_chat(target, "

Biohazard Alert

") - to_chat(target, "

[span_alert("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.")]

") - SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_OUTBREAK5]) - 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) - 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, "

Priority Announcement

") - to_chat(target, "

[span_alert("The Emergency Shuttle has docked with the station. You have 3 minutes to board the Emergency Shuttle.")]

") - SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_SHUTTLEDOCK]) - if("malf ai") //AI is doomsdaying! - to_chat(target, "

Anomaly Alert

") - to_chat(target, "

[span_alert("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.")]

") - SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_AIMALF]) - if("meteors") //Meteors inbound! - to_chat(target, "

Meteor Alert

") - to_chat(target, "

[span_alert("Meteors have been detected on collision course with the station.")]

") - SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_METEORS]) - if("supermatter") - SEND_SOUND(target, 'sound/magic/charge.ogg') - to_chat(target, span_boldannounce("You feel reality distort for a moment...")) - -/datum/hallucination/hudscrew - -/datum/hallucination/hudscrew/New(mob/living/carbon/C, forced = TRUE, screwyhud_type) - set waitfor = FALSE - ..() - //Screwy HUD - var/chosen_screwyhud = screwyhud_type - if(!chosen_screwyhud) - chosen_screwyhud = pick(SCREWYHUD_CRIT,SCREWYHUD_DEAD,SCREWYHUD_HEALTHY) - target.set_screwyhud(chosen_screwyhud) - feedback_details += "Type: [target.hal_screwyhud]" - QDEL_IN(src, rand(100, 250)) - -/datum/hallucination/hudscrew/Destroy() - target?.set_screwyhud(SCREWYHUD_NONE) - return ..() - -/datum/hallucination/fake_alert - var/alert_type - -/datum/hallucination/fake_alert/New(mob/living/carbon/C, forced = TRUE, specific, duration = 150) - set waitfor = FALSE - ..() - alert_type = pick( - ALERT_NOT_ENOUGH_OXYGEN, - ALERT_NOT_ENOUGH_PLASMA, - ALERT_NOT_ENOUGH_CO2, - ALERT_TOO_MUCH_OXYGEN, - ALERT_TOO_MUCH_CO2, - ALERT_TOO_MUCH_PLASMA, - ALERT_NUTRITION, - ALERT_GRAVITY, - ALERT_FIRE, - ALERT_TEMPERATURE_HOT, - ALERT_TEMPERATURE_COLD, - ALERT_PRESSURE, - ALERT_NEW_LAW, - ALERT_LOCKED, - ALERT_HACKED, - ALERT_CHARGE, - ) - - if(specific) - alert_type = specific - feedback_details += "Type: [alert_type]" - switch(alert_type) - if(ALERT_NOT_ENOUGH_OXYGEN) - target.throw_alert(alert_type, /atom/movable/screen/alert/not_enough_oxy, override = TRUE) - if(ALERT_NOT_ENOUGH_PLASMA) - target.throw_alert(alert_type, /atom/movable/screen/alert/not_enough_plas, override = TRUE) - if(ALERT_NOT_ENOUGH_CO2) - target.throw_alert(alert_type, /atom/movable/screen/alert/not_enough_co2, override = TRUE) - if(ALERT_TOO_MUCH_OXYGEN) - target.throw_alert(alert_type, /atom/movable/screen/alert/too_much_oxy, override = TRUE) - if(ALERT_TOO_MUCH_CO2) - target.throw_alert(alert_type, /atom/movable/screen/alert/too_much_co2, override = TRUE) - if(ALERT_TOO_MUCH_PLASMA) - target.throw_alert(alert_type, /atom/movable/screen/alert/too_much_plas, override = TRUE) - if(ALERT_NUTRITION) - if(prob(50)) - target.throw_alert(alert_type, /atom/movable/screen/alert/fat, override = TRUE) - else - target.throw_alert(alert_type, /atom/movable/screen/alert/starving, override = TRUE) - if(ALERT_GRAVITY) - target.throw_alert(alert_type, /atom/movable/screen/alert/weightless, override = TRUE) - if(ALERT_FIRE) - target.throw_alert(alert_type, /atom/movable/screen/alert/fire, override = TRUE) - if(ALERT_TEMPERATURE_HOT) - alert_type = "temp" - target.throw_alert(alert_type, /atom/movable/screen/alert/hot, 3, override = TRUE) - if(ALERT_TEMPERATURE_COLD) - alert_type = "temp" - target.throw_alert(alert_type, /atom/movable/screen/alert/cold, 3, override = TRUE) - if(ALERT_PRESSURE) - if(prob(50)) - target.throw_alert(alert_type, /atom/movable/screen/alert/highpressure, 2, override = TRUE) - else - target.throw_alert(alert_type, /atom/movable/screen/alert/lowpressure, 2, override = TRUE) - //BEEP BOOP I AM A ROBOT - if(ALERT_NEW_LAW) - target.throw_alert(alert_type, /atom/movable/screen/alert/newlaw, override = TRUE) - if(ALERT_LOCKED) - target.throw_alert(alert_type, /atom/movable/screen/alert/locked, override = TRUE) - if(ALERT_HACKED) - target.throw_alert(alert_type, /atom/movable/screen/alert/hacked, override = TRUE) - if(ALERT_CHARGE) - target.throw_alert(alert_type, /atom/movable/screen/alert/emptycell, override = TRUE) - - addtimer(CALLBACK(src, .proc/cleanup), duration) - -/datum/hallucination/fake_alert/proc/cleanup() - target.clear_alert(alert_type, clear_override = TRUE) - qdel(src) - -///Causes the target to see incorrect health damages on the healthdoll -/datum/hallucination/fake_health_doll - var/timer_id = null - -///Creates a specified doll hallucination, or picks one randomly -/datum/hallucination/fake_health_doll/New(mob/living/carbon/human/human_mob, forced = TRUE, specific_limb, severity, duration = 500) - . = ..() - if(!specific_limb) - specific_limb = pick(list(SCREWYDOLL_HEAD, SCREWYDOLL_CHEST, SCREWYDOLL_L_ARM, SCREWYDOLL_R_ARM, SCREWYDOLL_L_LEG, SCREWYDOLL_R_LEG)) - if(!severity) - severity = rand(1, 5) - LAZYSET(human_mob.hal_screwydoll, specific_limb, severity) - human_mob.update_health_hud() - - timer_id = addtimer(CALLBACK(src, .proc/cleanup), duration, TIMER_STOPPABLE) - -///Increments the severity of the damage seen on the doll -/datum/hallucination/fake_health_doll/proc/increment_fake_damage() - if(!ishuman(target)) - stack_trace("Somehow [target] managed to get a fake health doll hallucination, while not being a human mob.") - var/mob/living/carbon/human/human_mob = target - for(var/entry in human_mob.hal_screwydoll) - human_mob.hal_screwydoll[entry] = clamp(human_mob.hal_screwydoll[entry]+1, 1, 5) - human_mob.update_health_hud() - -///Adds a fake limb to the hallucination datum effect -/datum/hallucination/fake_health_doll/proc/add_fake_limb(specific_limb, severity) - if(!specific_limb) - specific_limb = pick(list(SCREWYDOLL_HEAD, SCREWYDOLL_CHEST, SCREWYDOLL_L_ARM, SCREWYDOLL_R_ARM, SCREWYDOLL_L_LEG, SCREWYDOLL_R_LEG)) - if(!severity) - severity = rand(1, 5) - var/mob/living/carbon/human/human_mob = target - LAZYSET(human_mob.hal_screwydoll, specific_limb, severity) - target.update_health_hud() - -/datum/hallucination/fake_health_doll/target_deleting() - if(isnull(timer_id)) - return - deltimer(timer_id) - timer_id = null - ..() - -///Cleans up the hallucinations - this deletes any overlap, but that shouldn't happen. -/datum/hallucination/fake_health_doll/proc/cleanup() - qdel(src) - -//So that the associated addition proc cleans it up correctly -/datum/hallucination/fake_health_doll/Destroy() - if(!ishuman(target)) - stack_trace("Somehow [target] managed to get a fake health doll hallucination, while not being a human mob.") - var/mob/living/carbon/human/human_mob = target - LAZYNULL(human_mob.hal_screwydoll) - human_mob.update_health_hud() - return ..() - - -/datum/hallucination/items/New(mob/living/carbon/C, forced = TRUE) - set waitfor = FALSE - ..() - //Strange items - - var/obj/halitem = new - - 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) - halitem.screen_loc = pick(slots_free) - halitem.plane = ABOVE_HUD_PLANE - switch(rand(1,6)) - if(1) //revolver - halitem.icon = 'icons/obj/guns/ballistic.dmi' - halitem.icon_state = "revolver" - halitem.name = "Revolver" - if(2) //c4 - halitem.icon = 'icons/obj/grenade.dmi' - halitem.icon_state = "plastic-explosive0" - halitem.name = "C4" - if(prob(25)) - halitem.icon_state = "plasticx40" - if(3) //sword - halitem.icon = 'icons/obj/transforming_energy.dmi' - halitem.icon_state = "e_sword" - halitem.name = "energy sword" - if(4) //stun baton - halitem.icon = 'icons/obj/items_and_weapons.dmi' - halitem.icon_state = "stunbaton" - halitem.name = "Stun Baton" - if(5) //emag - halitem.icon = 'icons/obj/card.dmi' - halitem.icon_state = "emag" - halitem.name = "Cryptographic Sequencer" - if(6) //flashbang - halitem.icon = 'icons/obj/grenade.dmi' - halitem.icon_state = "flashbang1" - halitem.name = "Flashbang" - feedback_details += "Type: [halitem.name]" - if(target.client) - target.client.screen += halitem - QDEL_IN(halitem, rand(150, 350)) - - qdel(src) - -/datum/hallucination/dangerflash - -/datum/hallucination/dangerflash/New(mob/living/carbon/C, forced = TRUE, danger_type) - set waitfor = FALSE - ..() - //Flashes of danger - - 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) - if(!danger_type) - danger_type = pick("lava","chasm","anomaly") - switch(danger_type) - if("lava") - new /obj/effect/hallucination/danger/lava(danger_point, target) - if("chasm") - new /obj/effect/hallucination/danger/chasm(danger_point, target) - if("anomaly") - new /obj/effect/hallucination/danger/anomaly(danger_point, target) - - qdel(src) - -/obj/effect/hallucination/danger - var/image/image - -/obj/effect/hallucination/danger/proc/show_icon() - return - -/obj/effect/hallucination/danger/proc/clear_icon() - if(image && target.client) - target.client.images -= image - -/obj/effect/hallucination/danger/Initialize(mapload, _target) - . = ..() - target = _target - show_icon() - QDEL_IN(src, rand(200, 450)) - -/obj/effect/hallucination/danger/Destroy() - clear_icon() - . = ..() - -/obj/effect/hallucination/danger/lava - name = "lava" - -/obj/effect/hallucination/danger/lava/Initialize(mapload, _target) - . = ..() - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = .proc/on_entered, - ) - AddElement(/datum/element/connect_loc, loc_connections) - -/obj/effect/hallucination/danger/lava/show_icon() - var/turf/danger_turf = get_turf(src) - image = image('icons/turf/floors/lava.dmi', src, "lava-[danger_turf.smoothing_junction || 0]", TURF_LAYER) - if(target.client) - target.client.images += image - -/obj/effect/hallucination/danger/lava/proc/on_entered(datum/source, atom/movable/AM) - SIGNAL_HANDLER - if(AM == target) - target.adjustStaminaLoss(20) - new /datum/hallucination/fire(target) - -/obj/effect/hallucination/danger/chasm - name = "chasm" - -/obj/effect/hallucination/danger/chasm/Initialize(mapload, _target) - . = ..() - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = .proc/on_entered, - ) - AddElement(/datum/element/connect_loc, loc_connections) - -/obj/effect/hallucination/danger/chasm/show_icon() - var/turf/danger_turf = get_turf(src) - image = image('icons/turf/floors/chasms.dmi', src, "chasms-[danger_turf.smoothing_junction || 0]", TURF_LAYER) - if(target.client) - target.client.images += image - -/obj/effect/hallucination/danger/chasm/proc/on_entered(datum/source, atom/movable/AM) - SIGNAL_HANDLER - if(AM == target) - if(istype(target, /obj/effect/dummy/phased_mob)) - return - to_chat(target, span_userdanger("You fall into the chasm!")) - target.Paralyze(40) - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, target, span_notice("It's surprisingly shallow.")), 15) - QDEL_IN(src, 30) - -/obj/effect/hallucination/danger/anomaly - name = "flux wave anomaly" - -/obj/effect/hallucination/danger/anomaly/Initialize(mapload) - . = ..() - START_PROCESSING(SSobj, src) - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = .proc/on_entered, - ) - AddElement(/datum/element/connect_loc, loc_connections) - -/obj/effect/hallucination/danger/anomaly/process(delta_time) - if(DT_PROB(45, delta_time)) - step(src,pick(GLOB.alldirs)) - -/obj/effect/hallucination/danger/anomaly/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/effect/hallucination/danger/anomaly/show_icon() - image = image('icons/effects/effects.dmi',src,"electricity2",OBJ_LAYER+0.01) - if(target.client) - target.client.images += image - -/obj/effect/hallucination/danger/anomaly/proc/on_entered(datum/source, atom/movable/AM) - SIGNAL_HANDLER - if(AM == target) - new /datum/hallucination/shock(target) - -/datum/hallucination/death - -/datum/hallucination/death/New(mob/living/carbon/C, forced = TRUE) - set waitfor = FALSE - ..() - target.set_screwyhud(SCREWYHUD_DEAD) - target.Paralyze(300) - target.silent += 10 - to_chat(target, span_deadsay("[target.real_name] has died at [get_area_name(target)].")) - - var/delay = 0 - - 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) - delay = rand(20, 50) - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, target, "DEAD: [fakemob.name] says, \"[pick("rip","why did i just drop dead?","hey [target.first_name()]","git gud","you too?","is the AI rogue?",\ - "i[prob(50)?" fucking":""] hate [pick("blood cult", "clock cult", "revenants", "this round","this","myself","admins","you")]")]\""), delay) - - addtimer(CALLBACK(src, .proc/cleanup), delay + rand(70, 90)) - -/datum/hallucination/death/proc/cleanup() - if (target) - target.set_screwyhud(SCREWYHUD_NONE) - target.SetParalyzed(0) - target.silent = FALSE - qdel(src) - -#define RAISE_FIRE_COUNT 3 -#define RAISE_FIRE_TIME 3 - -/datum/hallucination/fire - var/active = TRUE - var/stage = 0 - var/image/fire_overlay - - var/next_action = 0 - var/times_to_lower_stamina - var/fire_clearing = FALSE - var/increasing_stages = TRUE - var/time_spent = 0 - -/datum/hallucination/fire/New(mob/living/carbon/C, forced = TRUE) - set waitfor = FALSE - ..() - target.set_fire_stacks(max(target.fire_stacks, 0.1)) //Placebo flammability - fire_overlay = image('icons/mob/onfire.dmi', target, "human_big_fire", ABOVE_MOB_LAYER) - if(target.client) - target.client.images += fire_overlay - to_chat(target, span_userdanger("You're set on fire!")) - target.throw_alert(ALERT_FIRE, /atom/movable/screen/alert/fire, override = TRUE) - times_to_lower_stamina = rand(5, 10) - addtimer(CALLBACK(src, .proc/start_expanding), 20) - -/datum/hallucination/fire/Destroy() - . = ..() - STOP_PROCESSING(SSfastprocess, src) - -/datum/hallucination/fire/proc/start_expanding() - if (isnull(target)) - qdel(src) - return - START_PROCESSING(SSfastprocess, src) - -/datum/hallucination/fire/process(delta_time) - if (isnull(target)) - qdel(src) - return - - if(target.fire_stacks <= 0) - clear_fire() - - time_spent += delta_time - - if (fire_clearing) - next_action -= delta_time - if (next_action < 0) - stage -= 1 - update_temp() - next_action += 3 - else if (increasing_stages) - var/new_stage = min(round(time_spent / RAISE_FIRE_TIME), RAISE_FIRE_COUNT) - if (stage != new_stage) - stage = new_stage - update_temp() - - if (stage == RAISE_FIRE_COUNT) - increasing_stages = FALSE - else if (times_to_lower_stamina) - next_action -= delta_time - if (next_action < 0) - target.adjustStaminaLoss(15) - next_action += 2 - times_to_lower_stamina -= 1 - else - clear_fire() - -/datum/hallucination/fire/proc/update_temp() - if(stage <= 0) - target.clear_alert(ALERT_TEMPERATURE, clear_override = TRUE) - else - target.clear_alert(ALERT_TEMPERATURE, clear_override = TRUE) - target.throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/hot, stage, override = TRUE) - -/datum/hallucination/fire/proc/clear_fire() - if(!active) - return - active = FALSE - target.clear_alert(ALERT_FIRE, clear_override = TRUE) - if(target.client) - target.client.images -= fire_overlay - QDEL_NULL(fire_overlay) - fire_clearing = TRUE - next_action = 0 - -#undef RAISE_FIRE_COUNT -#undef RAISE_FIRE_TIME - -/datum/hallucination/shock - var/image/shock_image - var/image/electrocution_skeleton_anim - -/datum/hallucination/shock/New(mob/living/carbon/C, forced = TRUE) - set waitfor = FALSE - ..() - shock_image = image(target, target, dir = target.dir) - shock_image.appearance_flags |= KEEP_APART - shock_image.color = rgb(0,0,0) - shock_image.override = TRUE - electrocution_skeleton_anim = image('icons/mob/human.dmi', target, icon_state = "electrocuted_base", layer=ABOVE_MOB_LAYER) - electrocution_skeleton_anim.appearance_flags |= RESET_COLOR|KEEP_APART - to_chat(target, span_userdanger("You feel a powerful shock course through your body!")) - if(target.client) - target.client.images |= shock_image - target.client.images |= electrocution_skeleton_anim - addtimer(CALLBACK(src, .proc/reset_shock_animation), 40) - target.playsound_local(get_turf(src), SFX_SPARKS, 100, 1) - target.staminaloss += 50 - target.Stun(4 SECONDS) - target.do_jitter_animation(300) // Maximum jitter - target.adjust_timed_status_effect(20 SECONDS, /datum/status_effect/jitter) - addtimer(CALLBACK(src, .proc/shock_drop), 2 SECONDS) - -/datum/hallucination/shock/proc/reset_shock_animation() - target.client?.images.Remove(shock_image) - target.client?.images.Remove(electrocution_skeleton_anim) - -/datum/hallucination/shock/proc/shock_drop() - target.Paralyze(6 SECONDS) - -/datum/hallucination/husks - var/image/halbody - -/datum/hallucination/husks/New(mob/living/carbon/C, forced = TRUE) - set waitfor = FALSE - ..() - 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 - halbody = body - if(2,3) - halbody = image('icons/mob/human.dmi',husk_point,"husk",TURF_LAYER) - if(4) - halbody = image('icons/mob/alien.dmi',husk_point,"alienother",TURF_LAYER) - - if(target.client) - target.client.images += halbody - QDEL_IN(src, rand(30,50)) //Only seen for a brief moment. - -/datum/hallucination/husks/Destroy() - target?.client?.images -= halbody - QDEL_NULL(halbody) - return ..() - -//hallucination projectile code in code/modules/projectiles/projectile/special.dm -/datum/hallucination/stray_bullet - -/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 - if(!startlocs.len) - qdel(src) - return - var/turf/start = pick(startlocs) - var/proj_type = pick(subtypesof(/obj/projectile/hallucination)) - feedback_details += "Type: [proj_type]" - var/obj/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) diff --git a/code/modules/hallucination/HUD.dm b/code/modules/hallucination/HUD.dm new file mode 100644 index 00000000000..8e7bf445d70 --- /dev/null +++ b/code/modules/hallucination/HUD.dm @@ -0,0 +1,156 @@ +/* HUD Hallucinations + * + * Contains: + * Fake Alerts + * Health Alerts + * Health Doll Damage + */ + +/datum/hallucination/hudscrew + +/datum/hallucination/hudscrew/New(mob/living/carbon/C, forced = TRUE, screwyhud_type) + set waitfor = FALSE + ..() + //Screwy HUD + var/chosen_screwyhud = screwyhud_type + if(!chosen_screwyhud) + chosen_screwyhud = pick(SCREWYHUD_CRIT,SCREWYHUD_DEAD,SCREWYHUD_HEALTHY) + target.set_screwyhud(chosen_screwyhud) + feedback_details += "Type: [target.hal_screwyhud]" + QDEL_IN(src, rand(100, 250)) + +/datum/hallucination/hudscrew/Destroy() + target?.set_screwyhud(SCREWYHUD_NONE) + return ..() + +/datum/hallucination/fake_alert + var/alert_type + +/datum/hallucination/fake_alert/New(mob/living/carbon/C, forced = TRUE, specific, duration = 150) + set waitfor = FALSE + ..() + alert_type = pick( + ALERT_NOT_ENOUGH_OXYGEN, + ALERT_NOT_ENOUGH_PLASMA, + ALERT_NOT_ENOUGH_CO2, + ALERT_TOO_MUCH_OXYGEN, + ALERT_TOO_MUCH_CO2, + ALERT_TOO_MUCH_PLASMA, + ALERT_NUTRITION, + ALERT_GRAVITY, + ALERT_FIRE, + ALERT_TEMPERATURE_HOT, + ALERT_TEMPERATURE_COLD, + ALERT_PRESSURE, + ALERT_NEW_LAW, + ALERT_LOCKED, + ALERT_HACKED, + ALERT_CHARGE, + ) + + if(specific) + alert_type = specific + feedback_details += "Type: [alert_type]" + switch(alert_type) + if(ALERT_NOT_ENOUGH_OXYGEN) + target.throw_alert(alert_type, /atom/movable/screen/alert/not_enough_oxy, override = TRUE) + if(ALERT_NOT_ENOUGH_PLASMA) + target.throw_alert(alert_type, /atom/movable/screen/alert/not_enough_plas, override = TRUE) + if(ALERT_NOT_ENOUGH_CO2) + target.throw_alert(alert_type, /atom/movable/screen/alert/not_enough_co2, override = TRUE) + if(ALERT_TOO_MUCH_OXYGEN) + target.throw_alert(alert_type, /atom/movable/screen/alert/too_much_oxy, override = TRUE) + if(ALERT_TOO_MUCH_CO2) + target.throw_alert(alert_type, /atom/movable/screen/alert/too_much_co2, override = TRUE) + if(ALERT_TOO_MUCH_PLASMA) + target.throw_alert(alert_type, /atom/movable/screen/alert/too_much_plas, override = TRUE) + if(ALERT_NUTRITION) + if(prob(50)) + target.throw_alert(alert_type, /atom/movable/screen/alert/fat, override = TRUE) + else + target.throw_alert(alert_type, /atom/movable/screen/alert/starving, override = TRUE) + if(ALERT_GRAVITY) + target.throw_alert(alert_type, /atom/movable/screen/alert/weightless, override = TRUE) + if(ALERT_FIRE) + target.throw_alert(alert_type, /atom/movable/screen/alert/fire, override = TRUE) + if(ALERT_TEMPERATURE_HOT) + alert_type = "temp" + target.throw_alert(alert_type, /atom/movable/screen/alert/hot, 3, override = TRUE) + if(ALERT_TEMPERATURE_COLD) + alert_type = "temp" + target.throw_alert(alert_type, /atom/movable/screen/alert/cold, 3, override = TRUE) + if(ALERT_PRESSURE) + if(prob(50)) + target.throw_alert(alert_type, /atom/movable/screen/alert/highpressure, 2, override = TRUE) + else + target.throw_alert(alert_type, /atom/movable/screen/alert/lowpressure, 2, override = TRUE) + //BEEP BOOP I AM A ROBOT + if(ALERT_NEW_LAW) + target.throw_alert(alert_type, /atom/movable/screen/alert/newlaw, override = TRUE) + if(ALERT_LOCKED) + target.throw_alert(alert_type, /atom/movable/screen/alert/locked, override = TRUE) + if(ALERT_HACKED) + target.throw_alert(alert_type, /atom/movable/screen/alert/hacked, override = TRUE) + if(ALERT_CHARGE) + target.throw_alert(alert_type, /atom/movable/screen/alert/emptycell, override = TRUE) + + addtimer(CALLBACK(src, .proc/cleanup), duration) + +/datum/hallucination/fake_alert/proc/cleanup() + target.clear_alert(alert_type, clear_override = TRUE) + qdel(src) + +///Causes the target to see incorrect health damages on the healthdoll +/datum/hallucination/fake_health_doll + var/timer_id = null + +///Creates a specified doll hallucination, or picks one randomly +/datum/hallucination/fake_health_doll/New(mob/living/carbon/human/human_mob, forced = TRUE, specific_limb, severity, duration = 500) + . = ..() + if(!specific_limb) + specific_limb = pick(list(SCREWYDOLL_HEAD, SCREWYDOLL_CHEST, SCREWYDOLL_L_ARM, SCREWYDOLL_R_ARM, SCREWYDOLL_L_LEG, SCREWYDOLL_R_LEG)) + if(!severity) + severity = rand(1, 5) + LAZYSET(human_mob.hal_screwydoll, specific_limb, severity) + human_mob.update_health_hud() + + timer_id = addtimer(CALLBACK(src, .proc/cleanup), duration, TIMER_STOPPABLE) + +///Increments the severity of the damage seen on the doll +/datum/hallucination/fake_health_doll/proc/increment_fake_damage() + if(!ishuman(target)) + stack_trace("Somehow [target] managed to get a fake health doll hallucination, while not being a human mob.") + var/mob/living/carbon/human/human_mob = target + for(var/entry in human_mob.hal_screwydoll) + human_mob.hal_screwydoll[entry] = clamp(human_mob.hal_screwydoll[entry]+1, 1, 5) + human_mob.update_health_hud() + +///Adds a fake limb to the hallucination datum effect +/datum/hallucination/fake_health_doll/proc/add_fake_limb(specific_limb, severity) + if(!specific_limb) + specific_limb = pick(list(SCREWYDOLL_HEAD, SCREWYDOLL_CHEST, SCREWYDOLL_L_ARM, SCREWYDOLL_R_ARM, SCREWYDOLL_L_LEG, SCREWYDOLL_R_LEG)) + if(!severity) + severity = rand(1, 5) + var/mob/living/carbon/human/human_mob = target + LAZYSET(human_mob.hal_screwydoll, specific_limb, severity) + target.update_health_hud() + +/datum/hallucination/fake_health_doll/target_deleting() + if(isnull(timer_id)) + return + deltimer(timer_id) + timer_id = null + ..() + +///Cleans up the hallucinations - this deletes any overlap, but that shouldn't happen. +/datum/hallucination/fake_health_doll/proc/cleanup() + qdel(src) + +//So that the associated addition proc cleans it up correctly +/datum/hallucination/fake_health_doll/Destroy() + if(!ishuman(target)) + stack_trace("Somehow [target] managed to get a fake health doll hallucination, while not being a human mob.") + var/mob/living/carbon/human/human_mob = target + LAZYNULL(human_mob.hal_screwydoll) + human_mob.update_health_hud() + return ..() diff --git a/code/modules/hallucination/_hallucination.dm b/code/modules/hallucination/_hallucination.dm new file mode 100644 index 00000000000..d3835c8825d --- /dev/null +++ b/code/modules/hallucination/_hallucination.dm @@ -0,0 +1,162 @@ +GLOBAL_LIST_INIT(hallucination_list, list( + /datum/hallucination/chat = 100, + /datum/hallucination/message = 60, + /datum/hallucination/sounds = 50, + /datum/hallucination/battle = 20, + /datum/hallucination/dangerflash = 15, + /datum/hallucination/hudscrew = 12, + /datum/hallucination/fake_health_doll = 12, + /datum/hallucination/fake_alert = 12, + /datum/hallucination/weird_sounds = 8, + /datum/hallucination/stationmessage = 7, + /datum/hallucination/fake_flood = 7, + /datum/hallucination/stray_bullet = 7, + /datum/hallucination/bolts = 7, + /datum/hallucination/items_other = 7, + /datum/hallucination/husks = 7, + /datum/hallucination/items = 4, + /datum/hallucination/fire = 3, + /datum/hallucination/self_delusion = 2, + /datum/hallucination/delusion = 2, + /datum/hallucination/shock = 1, + /datum/hallucination/death = 1, + /datum/hallucination/oh_yeah = 1 + )) + + +/mob/living/carbon/proc/handle_hallucinations(delta_time, times_fired) + if(!hallucination) + return + + hallucination = max(hallucination - (0.5 * delta_time), 0) + if(world.time < next_hallucination) + return + + var/halpick = pick_weight(GLOB.hallucination_list) + new halpick(src, FALSE) + + next_hallucination = world.time + rand(100, 600) + +/mob/living/carbon/proc/set_screwyhud(hud_type) + hal_screwyhud = hud_type + update_health_hud() + +/datum/hallucination + var/natural = TRUE + var/mob/living/carbon/target + var/feedback_details //extra info for investigate + +/datum/hallucination/New(mob/living/carbon/C, forced = TRUE) + set waitfor = FALSE + 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 ..() + +//Returns a random turf in a ring around the target mob, useful for sound hallucinations +/datum/hallucination/proc/random_far_turf() + var/x_based = prob(50) + var/first_offset = pick(-8,-7,-6,-5,5,6,7,8) + var/second_offset = rand(-8,8) + var/x_off + var/y_off + if(x_based) + x_off = first_offset + y_off = second_offset + else + y_off = first_offset + x_off = second_offset + var/turf/T = locate(target.x + x_off, target.y + y_off, target.z) + return T + +/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/image_plane = GAME_PLANE + var/active = TRUE //qdelery + +/obj/effect/hallucination/singularity_pull() + return + +/obj/effect/hallucination/singularity_act() + return + +/obj/effect/hallucination/simple/Initialize(mapload, mob/living/carbon/T) + . = ..() + if(!T) + stack_trace("A hallucination was created with no target") + return INITIALIZE_HINT_QDEL + 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.plane = image_plane + 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(updates=ALL, 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) + . = ..() + if(!loc) + return + Show() + +/obj/effect/hallucination/simple/Destroy() + if(target.client) + target.client.images.Remove(current_image) + active = FALSE + return ..() diff --git a/code/modules/hallucination/airlock.dm b/code/modules/hallucination/airlock.dm new file mode 100644 index 00000000000..950a267f75c --- /dev/null +++ b/code/modules/hallucination/airlock.dm @@ -0,0 +1,90 @@ +/* Airlock Hallucinations + * + * Contains: + * Nearby airlocks being bolted + * Nearby airlocks being unbolted + */ + +/datum/hallucination/bolts + 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 + ..() + if(!door_number) + 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++ + LAZYADD(airlocks_to_hit, A) + + if(!LAZYLEN(airlocks_to_hit)) //no valid airlocks in sight + qdel(src) + return + + 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 (!LAZYLEN(airlocks_to_hit)) + 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 + plane = GAME_PLANE + var/image/bolt_light + var/obj/machinery/door/airlock/airlock + +/obj/effect/hallucination/fake_door_lock/proc/lock() + bolt_light = image(airlock.overlays_file, get_turf(airlock), "lights_bolts",layer=airlock.layer+0.1) + if(target.client) + target.client.images |= bolt_light + target.playsound_local(get_turf(airlock), 'sound/machines/boltsdown.ogg',30,0,3) + +/obj/effect/hallucination/fake_door_lock/proc/unlock() + if(target.client) + target.client.images.Remove(bolt_light) + target.playsound_local(get_turf(airlock), 'sound/machines/boltsup.ogg',30,0,3) + qdel(src) + +/obj/effect/hallucination/fake_door_lock/CanAllowThrough(atom/movable/mover, border_dir) + . = ..() + if(mover == target && airlock.density) + return FALSE + diff --git a/code/modules/hallucination/chat.dm b/code/modules/hallucination/chat.dm new file mode 100644 index 00000000000..4c9912e5113 --- /dev/null +++ b/code/modules/hallucination/chat.dm @@ -0,0 +1,169 @@ +/* Chat Hallucinations + * + * Contains: + * Radio messages (";Someone recall the shuttle!", ";Set John Doe to arrest!") + * Speak messages ("Get out!", "Did you hear that?") + * Action messages ("You feel a tiny prick!", "John Doe puts the cryptographic sequencer into their backpack.") + * Station messages ("The Emergency Shuttle has docked with the station.", "Hostile runtimes detected in all station systems") + */ + +#define HAL_LINES_FILE "hallucination.json" + +/datum/hallucination/chat + +/datum/hallucination/chat/New(mob/living/carbon/C, forced = TRUE, force_radio, specific_message) + 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, "conversation")]",\ + "[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, "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("[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)?"!":"!!"]",\ + "[pick("Where's [target.first_name()]?", "Set [target.first_name()] to arrest!")]",\ + "[pick("C","Ai, c","Someone c","Rec")]all the shuttle!",\ + "AI [pick("rogue", "is dead")]!!") + + var/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)[other] puts the [pick(\ + "revolver","energy sword","cryptographic sequencer","power sink","energy bow",\ + "hybrid taser","stun baton","flash","syringe gun","circular saw","tank transfer valve",\ + "ritual dagger","spellbook",\ + "Codex Cicatrix", "Living Heart",\ + "pulse rifle","captain's spare ID","hand teleporter","hypospray","antique laser gun","X-01 MultiPhase Energy Gun","station's blueprints"\ + )] into [equipped_backpack].") + + message_pool.Add("[other] [pick("sneezes","coughs")].") + + message_pool.Add(span_notice("You hear something squeezing through the ducts..."), \ + span_notice("Your [pick("arm", "leg", "back", "head")] itches."),\ + span_warning("You feel [pick("hot","cold","dry","wet","woozy","faint")]."), + span_warning("Your stomach rumbles."), + span_warning("Your head hurts."), + span_warning("You hear a faint buzz in your head."), + "[target] sneezes.") + if(prob(10)) + message_pool.Add(span_warning("Behind you."),\ + span_warning("You hear a faint laughter."), + span_warning("You see something move."), + span_warning("You hear skittering on the ceiling."), + span_warning("You see an inhumanly tall silhouette moving in the distance.")) + if(prob(10)) + message_pool.Add("[pick_list_replacements(HAL_LINES_FILE, "advice")]") + var/chosen = pick(message_pool) + feedback_details += "Message: [chosen]" + to_chat(target, chosen) + qdel(src) + +/datum/hallucination/stationmessage + +/datum/hallucination/stationmessage/New(mob/living/carbon/C, forced = TRUE, message) + set waitfor = FALSE + ..() + if(!message) + message = pick("ratvar","shuttle dock","blob alert","malf ai","meteors","supermatter") + feedback_details += "Type: [message]" + switch(message) + if("blob alert") + to_chat(target, "

Biohazard Alert

") + to_chat(target, "

[span_alert("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.")]

") + SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_OUTBREAK5]) + 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) + 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, "

Priority Announcement

") + to_chat(target, "

[span_alert("The Emergency Shuttle has docked with the station. You have 3 minutes to board the Emergency Shuttle.")]

") + SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_SHUTTLEDOCK]) + if("malf ai") //AI is doomsdaying! + to_chat(target, "

Anomaly Alert

") + to_chat(target, "

[span_alert("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.")]

") + SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_AIMALF]) + if("meteors") //Meteors inbound! + to_chat(target, "

Meteor Alert

") + to_chat(target, "

[span_alert("Meteors have been detected on collision course with the station.")]

") + SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_METEORS]) + if("supermatter") + SEND_SOUND(target, 'sound/magic/charge.ogg') + to_chat(target, span_boldannounce("You feel reality distort for a moment...")) diff --git a/code/modules/hallucination/death.dm b/code/modules/hallucination/death.dm new file mode 100644 index 00000000000..744ff878a56 --- /dev/null +++ b/code/modules/hallucination/death.dm @@ -0,0 +1,34 @@ +/datum/hallucination/death + +/datum/hallucination/death/New(mob/living/carbon/C, forced = TRUE) + set waitfor = FALSE + ..() + target.set_screwyhud(SCREWYHUD_DEAD) + target.Paralyze(300) + target.silent += 10 + to_chat(target, span_deadsay("[target.real_name] has died at [get_area_name(target)].")) + + var/delay = 0 + + 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) + delay = rand(20, 50) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, target, "DEAD: [fakemob.name] says, \"[pick("rip","why did i just drop dead?","hey [target.first_name()]","git gud","you too?","is the AI rogue?",\ + "i[prob(50)?" fucking":""] hate [pick("blood cult", "clock cult", "revenants", "this round","this","myself","admins","you")]")]\""), delay) + + addtimer(CALLBACK(src, .proc/cleanup), delay + rand(70, 90)) + +/datum/hallucination/death/proc/cleanup() + if (target) + target.set_screwyhud(SCREWYHUD_NONE) + target.SetParalyzed(0) + target.silent = FALSE + qdel(src) diff --git a/code/modules/hallucination/fire.dm b/code/modules/hallucination/fire.dm new file mode 100644 index 00000000000..81396caa45c --- /dev/null +++ b/code/modules/hallucination/fire.dm @@ -0,0 +1,89 @@ +#define RAISE_FIRE_COUNT 3 +#define RAISE_FIRE_TIME 3 + +/datum/hallucination/fire + var/active = TRUE + var/stage = 0 + var/image/fire_overlay + + var/next_action = 0 + var/times_to_lower_stamina + var/fire_clearing = FALSE + var/increasing_stages = TRUE + var/time_spent = 0 + +/datum/hallucination/fire/New(mob/living/carbon/C, forced = TRUE) + set waitfor = FALSE + ..() + target.set_fire_stacks(max(target.fire_stacks, 0.1)) //Placebo flammability + fire_overlay = image('icons/mob/onfire.dmi', target, "human_burning", ABOVE_MOB_LAYER) + if(target.client) + target.client.images += fire_overlay + to_chat(target, span_userdanger("You're set on fire!")) + target.throw_alert(ALERT_FIRE, /atom/movable/screen/alert/fire, override = TRUE) + times_to_lower_stamina = rand(5, 10) + addtimer(CALLBACK(src, .proc/start_expanding), 20) + +/datum/hallucination/fire/Destroy() + . = ..() + STOP_PROCESSING(SSfastprocess, src) + +/datum/hallucination/fire/proc/start_expanding() + if (isnull(target)) + qdel(src) + return + START_PROCESSING(SSfastprocess, src) + +/datum/hallucination/fire/process(delta_time) + if (isnull(target)) + qdel(src) + return + + if(target.fire_stacks <= 0) + clear_fire() + + time_spent += delta_time + + if (fire_clearing) + next_action -= delta_time + if (next_action < 0) + stage -= 1 + update_temp() + next_action += 3 + else if (increasing_stages) + var/new_stage = min(round(time_spent / RAISE_FIRE_TIME), RAISE_FIRE_COUNT) + if (stage != new_stage) + stage = new_stage + update_temp() + + if (stage == RAISE_FIRE_COUNT) + increasing_stages = FALSE + else if (times_to_lower_stamina) + next_action -= delta_time + if (next_action < 0) + target.adjustStaminaLoss(15) + next_action += 2 + times_to_lower_stamina -= 1 + else + clear_fire() + +/datum/hallucination/fire/proc/update_temp() + if(stage <= 0) + target.clear_alert(ALERT_TEMPERATURE, clear_override = TRUE) + else + target.clear_alert(ALERT_TEMPERATURE, clear_override = TRUE) + target.throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/hot, stage, override = TRUE) + +/datum/hallucination/fire/proc/clear_fire() + if(!active) + return + active = FALSE + target.clear_alert(ALERT_FIRE, clear_override = TRUE) + if(target.client) + target.client.images -= fire_overlay + QDEL_NULL(fire_overlay) + fire_clearing = TRUE + next_action = 0 + +#undef RAISE_FIRE_COUNT +#undef RAISE_FIRE_TIME diff --git a/code/modules/hallucination/hazard.dm b/code/modules/hallucination/hazard.dm new file mode 100644 index 00000000000..5278c9d7e8a --- /dev/null +++ b/code/modules/hallucination/hazard.dm @@ -0,0 +1,128 @@ +/* Hazard Hallucinations + * + * Contains: + * Lava + * Chasm + * Anomaly + */ + +/datum/hallucination/dangerflash + +/datum/hallucination/dangerflash/New(mob/living/carbon/C, forced = TRUE, danger_type) + set waitfor = FALSE + ..() + //Flashes of danger + + 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) + if(!danger_type) + danger_type = pick("lava","chasm","anomaly") + switch(danger_type) + if("lava") + new /obj/effect/hallucination/danger/lava(danger_point, target) + if("chasm") + new /obj/effect/hallucination/danger/chasm(danger_point, target) + if("anomaly") + new /obj/effect/hallucination/danger/anomaly(danger_point, target) + + qdel(src) + +/obj/effect/hallucination/danger + var/image/image + +/obj/effect/hallucination/danger/proc/show_icon() + return + +/obj/effect/hallucination/danger/proc/clear_icon() + if(image && target.client) + target.client.images -= image + +/obj/effect/hallucination/danger/Initialize(mapload, _target) + . = ..() + target = _target + show_icon() + QDEL_IN(src, rand(200, 450)) + +/obj/effect/hallucination/danger/Destroy() + clear_icon() + . = ..() + +/obj/effect/hallucination/danger/lava + name = "lava" + +/obj/effect/hallucination/danger/lava/Initialize(mapload, _target) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = .proc/on_entered, + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/effect/hallucination/danger/lava/show_icon() + var/turf/danger_turf = get_turf(src) + image = image('icons/turf/floors/lava.dmi', src, "lava-[danger_turf.smoothing_junction || 0]", TURF_LAYER) + if(target.client) + target.client.images += image + +/obj/effect/hallucination/danger/lava/proc/on_entered(datum/source, atom/movable/AM) + SIGNAL_HANDLER + if(AM == target) + target.adjustStaminaLoss(20) + new /datum/hallucination/fire(target) + +/obj/effect/hallucination/danger/chasm + name = "chasm" + +/obj/effect/hallucination/danger/chasm/Initialize(mapload, _target) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = .proc/on_entered, + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/effect/hallucination/danger/chasm/show_icon() + var/turf/danger_turf = get_turf(src) + image = image('icons/turf/floors/chasms.dmi', src, "chasms-[danger_turf.smoothing_junction || 0]", TURF_LAYER) + if(target.client) + target.client.images += image + +/obj/effect/hallucination/danger/chasm/proc/on_entered(datum/source, atom/movable/AM) + SIGNAL_HANDLER + if(AM == target) + if(istype(target, /obj/effect/dummy/phased_mob)) + return + to_chat(target, span_userdanger("You fall into the chasm!")) + target.Paralyze(40) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, target, span_notice("It's surprisingly shallow.")), 15) + QDEL_IN(src, 30) + +/obj/effect/hallucination/danger/anomaly + name = "flux wave anomaly" + +/obj/effect/hallucination/danger/anomaly/Initialize(mapload) + . = ..() + START_PROCESSING(SSobj, src) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = .proc/on_entered, + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/effect/hallucination/danger/anomaly/process(delta_time) + if(DT_PROB(45, delta_time)) + step(src,pick(GLOB.alldirs)) + +/obj/effect/hallucination/danger/anomaly/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/hallucination/danger/anomaly/show_icon() + image = image('icons/effects/effects.dmi',src,"electricity2",OBJ_LAYER+0.01) + if(target.client) + target.client.images += image + +/obj/effect/hallucination/danger/anomaly/proc/on_entered(datum/source, atom/movable/AM) + SIGNAL_HANDLER + if(AM == target) + new /datum/hallucination/shock(target) diff --git a/code/modules/hallucination/hostile_mob.dm b/code/modules/hallucination/hostile_mob.dm new file mode 100644 index 00000000000..b6ccf581838 --- /dev/null +++ b/code/modules/hallucination/hostile_mob.dm @@ -0,0 +1,171 @@ +/* Hostile Mob Hallucinations + * + * Contains: + * Xeno + * Clown + * Bubblegum + */ + +/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(atom/hit_atom, datum/thrownthing/throwingdatum) + update_icon(ALL, "alienh_pounce") + if(hit_atom == target && target.stat!=DEAD) + target.Paralyze(100) + target.visible_message(span_danger("[target] flails around wildly."),span_userdanger("[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 + 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) + ..() + for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target)) + if(!U.welded) + pump_location = get_turf(U) + break + + 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, span_notice("[xeno.name] scrambles into the ventilation ducts!")) + qdel(src) + if (XENO_ATTACK_STAGE_CLIMB to XENO_ATTACK_STAGE_FINISH) + to_chat(target, span_notice("[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(ALL, "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(ALL, "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' + 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 + var/turf/landing + var/charged + var/next_action = 0 + +/datum/hallucination/oh_yeah/New(mob/living/carbon/C, 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) + 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/start_processing), 10) + +/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) + shake_camera(target, 2, 1) + if(bubblegum.Adjacent(target) && !charged) + charged = TRUE + target.Paralyze(80) + target.adjustStaminaLoss(40) + step_away(target, bubblegum) + shake_camera(target, 4, 3) + target.visible_message(span_warning("[target] jumps backwards, falling on the ground!"),span_userdanger("[bubblegum] slams into you!")) + next_action = 0.2 + else + STOP_PROCESSING(SSfastprocess, src) + QDEL_IN(src, 3 SECONDS) + +/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) + STOP_PROCESSING(SSfastprocess, src) + return ..() diff --git a/code/modules/hallucination/husk.dm b/code/modules/hallucination/husk.dm new file mode 100644 index 00000000000..5bbb94af5b6 --- /dev/null +++ b/code/modules/hallucination/husk.dm @@ -0,0 +1,31 @@ +/datum/hallucination/husks + var/image/halbody + +/datum/hallucination/husks/New(mob/living/carbon/C, forced = TRUE) + set waitfor = FALSE + ..() + 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 + halbody = body + if(2,3) + halbody = image('icons/mob/human.dmi',husk_point,"husk",TURF_LAYER) + if(4) + halbody = image('icons/mob/alien.dmi',husk_point,"alienother",TURF_LAYER) + + if(target.client) + target.client.images += halbody + QDEL_IN(src, rand(30,50)) //Only seen for a brief moment. + +/datum/hallucination/husks/Destroy() + target?.client?.images -= halbody + QDEL_NULL(halbody) + return ..() diff --git a/code/modules/hallucination/item.dm b/code/modules/hallucination/item.dm new file mode 100644 index 00000000000..dd462f2b15a --- /dev/null +++ b/code/modules/hallucination/item.dm @@ -0,0 +1,172 @@ +/* Item Hallucinations + * + * Contains: + * Putting items in other nearby peoples hands + * Putting items in your hands + */ + +/datum/hallucination/items_other + +/datum/hallucination/items_other/New(mob/living/carbon/C, forced = TRUE, item_type) + set waitfor = FALSE + ..() + var/item + if(!item_type) + item = pick(list("esword","taser","ebow","baton","dual_esword","ttv","flash","armblade")) + else + item = item_type + feedback_details += "Item: [item]" + var/side + var/image_file + var/image/A = null + var/list/mob_pool = list() + + for(var/mob/living/carbon/human/M in view(7,target)) + if(M != target) + mob_pool += M + if(!mob_pool.len) + return + + var/mob/living/carbon/human/H = pick(mob_pool) + feedback_details += " Mob: [H.real_name]" + + var/free_hand = H.get_empty_held_index_for_side(LEFT_HANDS) + if(free_hand) + side = "left" + else + free_hand = H.get_empty_held_index_for_side(RIGHT_HANDS) + if(free_hand) + side = "right" + + if(side) + switch(item) + if("esword") + if(side == "right") + image_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + else + image_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + target.playsound_local(H, 'sound/weapons/saberon.ogg',35,1) + A = image(image_file,H,"e_sword_on_red", layer=ABOVE_MOB_LAYER) + if("dual_esword") + if(side == "right") + image_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + else + image_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + target.playsound_local(H, 'sound/weapons/saberon.ogg',35,1) + A = image(image_file,H,"dualsaberred1", layer=ABOVE_MOB_LAYER) + if("taser") + if(side == "right") + image_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + else + image_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' + A = image(image_file,H,"advtaserstun4", layer=ABOVE_MOB_LAYER) + if("ebow") + if(side == "right") + image_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + else + image_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' + A = image(image_file,H,"crossbow", layer=ABOVE_MOB_LAYER) + if("baton") + if(side == "right") + image_file = 'icons/mob/inhands/equipment/security_righthand.dmi' + else + image_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' + target.playsound_local(H, SFX_SPARKS,75,1,-1) + A = image(image_file,H,"baton", layer=ABOVE_MOB_LAYER) + if("ttv") + if(side == "right") + image_file = 'icons/mob/inhands/weapons/bombs_righthand.dmi' + else + image_file = 'icons/mob/inhands/weapons/bombs_lefthand.dmi' + A = image(image_file,H,"ttv", layer=ABOVE_MOB_LAYER) + if("flash") + if(side == "right") + image_file = 'icons/mob/inhands/equipment/security_righthand.dmi' + else + image_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' + A = image(image_file,H,"flashtool", layer=ABOVE_MOB_LAYER) + if("armblade") + if(side == "right") + image_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' + else + image_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi' + target.playsound_local(H, 'sound/effects/blobattack.ogg',30,1) + A = image(image_file,H,"arm_blade", layer=ABOVE_MOB_LAYER) + if(target.client) + target.client.images |= 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/items/New(mob/living/carbon/C, forced = TRUE) + set waitfor = FALSE + ..() + //Strange items + + var/obj/halitem = new + + 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) + halitem.screen_loc = pick(slots_free) + halitem.plane = ABOVE_HUD_PLANE + switch(rand(1,6)) + if(1) //revolver + halitem.icon = 'icons/obj/guns/ballistic.dmi' + halitem.icon_state = "revolver" + halitem.name = "Revolver" + if(2) //c4 + halitem.icon = 'icons/obj/grenade.dmi' + halitem.icon_state = "plastic-explosive0" + halitem.name = "C4" + if(prob(25)) + halitem.icon_state = "plasticx40" + if(3) //sword + halitem.icon = 'icons/obj/transforming_energy.dmi' + halitem.icon_state = "e_sword" + halitem.name = "energy sword" + if(4) //stun baton + halitem.icon = 'icons/obj/items_and_weapons.dmi' + halitem.icon_state = "stunbaton" + halitem.name = "Stun Baton" + if(5) //emag + halitem.icon = 'icons/obj/card.dmi' + halitem.icon_state = "emag" + halitem.name = "Cryptographic Sequencer" + if(6) //flashbang + halitem.icon = 'icons/obj/grenade.dmi' + halitem.icon_state = "flashbang1" + halitem.name = "Flashbang" + feedback_details += "Type: [halitem.name]" + if(target.client) + target.client.screen += halitem + QDEL_IN(halitem, rand(150, 350)) + + qdel(src) diff --git a/code/modules/hallucination/plasma_flood.dm b/code/modules/hallucination/plasma_flood.dm new file mode 100644 index 00000000000..ea76106231c --- /dev/null +++ b/code/modules/hallucination/plasma_flood.dm @@ -0,0 +1,83 @@ +#define FAKE_FLOOD_EXPAND_TIME 20 +#define FAKE_FLOOD_MAX_RADIUS 10 + +/obj/effect/plasma_image_holder + icon_state = "nothing" + anchored = TRUE + layer = FLY_LAYER + plane = ABOVE_GAME_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/datum/hallucination/fake_flood + //Plasma starts flooding from the nearby vent + var/turf/center + var/list/flood_images = list() + var/list/flood_image_holders = list() + var/list/turf/flood_turfs = list() + var/image_icon = 'icons/effects/atmospherics.dmi' + var/image_state = "plasma" + var/radius = 0 + var/next_expand = 0 + +/datum/hallucination/fake_flood/New(mob/living/carbon/C, forced = TRUE) + ..() + 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]" + var/obj/effect/plasma_image_holder/pih = new(center) + var/image/plasma_image = image(image_icon, pih, image_state, FLY_LAYER) + plasma_image.alpha = 50 + plasma_image.plane = ABOVE_GAME_PLANE + flood_images += plasma_image + flood_image_holders += pih + 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, ALERT_TOO_MUCH_PLASMA) + next_expand = world.time + FAKE_FLOOD_EXPAND_TIME + +/datum/hallucination/fake_flood/proc/Expand() + for(var/image/I in flood_images) + I.alpha = min(I.alpha + 50, 255) + 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) || !TURFS_CAN_SHARE(T, FT) || isspaceturf(T)) //If we've gottem already, or if they're not alright to spread with. + continue + var/obj/effect/plasma_image_holder/pih = new(T) + var/image/new_plasma = image(image_icon, pih, image_state, FLY_LAYER) + new_plasma.alpha = 50 + new_plasma.plane = ABOVE_GAME_PLANE + flood_images += new_plasma + flood_image_holders += pih + 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() + qdel(flood_image_holders) + flood_image_holders = list() + return ..() diff --git a/code/modules/hallucination/polymorph.dm b/code/modules/hallucination/polymorph.dm new file mode 100644 index 00000000000..2ad4471f119 --- /dev/null +++ b/code/modules/hallucination/polymorph.dm @@ -0,0 +1,101 @@ +/* Polymorph Hallucinations + * + * Contains: + * Nearby mobs are polymorphed into other creatures + * Polymorphing yourself into other creatures + */ +/datum/hallucination/delusion + var/list/image/delusions = list() + +/datum/hallucination/delusion/New(mob/living/carbon/C, forced, force_kind = null , duration = 300,skip_nearby = TRUE, custom_icon = null, custom_icon_file = null, custom_name = null) + set waitfor = FALSE + . = ..() + var/image/A = null + var/kind = force_kind ? force_kind : pick("nothing","monkey","corgi","carp","skeleton","demon","zombie") + feedback_details += "Type: [kind]" + var/list/nearby + if(skip_nearby) + nearby = get_hearers_in_view(7, target) + for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) + if(H == target) + continue + if(skip_nearby && (H in nearby)) + continue + switch(kind) + if("nothing") + A = image('icons/effects/effects.dmi',H,"nothing") + A.name = "..." + if("monkey")//Monkey + A = image('icons/mob/human.dmi',H,"monkey") + A.name = "Monkey ([rand(1,999)])" + if("carp")//Carp + A = image('icons/mob/carp.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 + +/datum/hallucination/self_delusion/New(mob/living/carbon/C, 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/human.dmi',target,"monkey") + 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_hear("...wabbajack...wabbajack...")) + 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 ..() diff --git a/code/modules/hallucination/shock.dm b/code/modules/hallucination/shock.dm new file mode 100644 index 00000000000..eb8a4257366 --- /dev/null +++ b/code/modules/hallucination/shock.dm @@ -0,0 +1,31 @@ +/datum/hallucination/shock + var/image/shock_image + var/image/electrocution_skeleton_anim + +/datum/hallucination/shock/New(mob/living/carbon/C, forced = TRUE) + set waitfor = FALSE + ..() + shock_image = image(target, target, dir = target.dir) + shock_image.appearance_flags |= KEEP_APART + shock_image.color = rgb(0,0,0) + shock_image.override = TRUE + electrocution_skeleton_anim = image('icons/mob/human.dmi', target, icon_state = "electrocuted_base", layer=ABOVE_MOB_LAYER) + electrocution_skeleton_anim.appearance_flags |= RESET_COLOR|KEEP_APART + to_chat(target, span_userdanger("You feel a powerful shock course through your body!")) + if(target.client) + target.client.images |= shock_image + target.client.images |= electrocution_skeleton_anim + addtimer(CALLBACK(src, .proc/reset_shock_animation), 40) + target.playsound_local(get_turf(src), SFX_SPARKS, 100, 1) + target.staminaloss += 50 + target.Stun(4 SECONDS) + target.do_jitter_animation(300) // Maximum jitter + target.adjust_timed_status_effect(20 SECONDS, /datum/status_effect/jitter) + addtimer(CALLBACK(src, .proc/shock_drop), 2 SECONDS) + +/datum/hallucination/shock/proc/reset_shock_animation() + target.client?.images.Remove(shock_image) + target.client?.images.Remove(electrocution_skeleton_anim) + +/datum/hallucination/shock/proc/shock_drop() + target.Paralyze(6 SECONDS) diff --git a/code/modules/hallucination/sound.dm b/code/modules/hallucination/sound.dm new file mode 100644 index 00000000000..00dc752a191 --- /dev/null +++ b/code/modules/hallucination/sound.dm @@ -0,0 +1,243 @@ +/* Sound Hallucinations + * + * Contains: + * Fighting sounds + * Machinery sounds + * Special effects sounds + */ + +/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, new_battle_type) + ..() + + 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("disabler", "laser") + iterations_left = rand(5, 10) + if("esword") + iterations_left = rand(4, 8) + target.playsound_local(source, 'sound/weapons/saberon.ogg',15, 1) + if("gun") + 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(SFX_BODYFALL), 25, 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(SFX_BODYFALL), 25, 1) + next_action = 2 SECONDS + if("bomb") // Tick Tock + 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 = SFX_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(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(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, SFX_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/sounds + +/datum/hallucination/sounds/New(mob/living/carbon/C, forced = TRUE, sound_type) + set waitfor = FALSE + ..() + var/turf/source = random_far_turf() + if(!sound_type) + sound_type = pick("airlock","airlock pry","console","explosion","far explosion","mech","glass","alarm","beepsky","mech","wall decon","door hack") + feedback_details += "Type: [sound_type]" + //Strange audio + switch(sound_type) + if("airlock") + 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) + 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") + if(prob(50)) + target.playsound_local(source,'sound/effects/explosion1.ogg', 50, 1) + else + target.playsound_local(source, 'sound/effects/explosion2.ogg', 50, 1) + if("far explosion") + target.playsound_local(source, 'sound/effects/explosionfar.ogg', 50, 1) + if("glass") + target.playsound_local(source, pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg'), 50, 1) + if("alarm") + target.playsound_local(source, 'sound/machines/alarm.ogg', 100, 0) + if("beepsky") + target.playsound_local(source, 'sound/voice/beepsky/freeze.ogg', 35, 0) + if("mech") + 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) + 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) + 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) + set waitfor = FALSE + ..() + var/turf/source = random_far_turf() + if(!sound_type) + sound_type = pick("phone","hallelujah","highlander","laughter","hyperspace","game over","creepy","tesla") + feedback_details += "Type: [sound_type]" + //Strange audio + switch(sound_type) + if("phone") + 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") + target.playsound_local(source, 'sound/effects/pray_chaplain.ogg', 50) + if("highlander") + target.playsound_local(null, 'sound/misc/highlander.ogg', 50) + if("game over") + target.playsound_local(source, 'sound/misc/compiler-failure.ogg', 50) + if("laughter") + if(prob(50)) + target.playsound_local(source, 'sound/voice/human/womanlaugh.ogg', 50, 1) + else + target.playsound_local(source, pick('sound/voice/human/manlaugh1.ogg', 'sound/voice/human/manlaugh2.ogg'), 50, 1) + if("creepy") + //These sounds are (mostly) taken from Hidden: Source + target.playsound_local(source, pick(GLOB.creepy_ambience), 50, 1) + if("tesla") //Tesla loose! + target.playsound_local(source, 'sound/magic/lightningbolt.ogg', 35, 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) diff --git a/code/modules/hallucination/stray_bullet.dm b/code/modules/hallucination/stray_bullet.dm new file mode 100644 index 00000000000..b0fba6d724a --- /dev/null +++ b/code/modules/hallucination/stray_bullet.dm @@ -0,0 +1,21 @@ +//hallucination projectile code in code/modules/projectiles/projectile/special.dm +/datum/hallucination/stray_bullet + +/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 + if(!startlocs.len) + qdel(src) + return + var/turf/start = pick(startlocs) + var/proj_type = pick(subtypesof(/obj/projectile/hallucination)) + feedback_details += "Type: [proj_type]" + var/obj/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) diff --git a/tgstation.dme b/tgstation.dme index 409c907ec18..98f0246f47e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2871,7 +2871,6 @@ #include "code\modules\explorer_drone\exploration_events\resource.dm" #include "code\modules\explorer_drone\exploration_events\trader.dm" #include "code\modules\flufftext\Dreaming.dm" -#include "code\modules\flufftext\Hallucination.dm" #include "code\modules\food_and_drinks\food.dm" #include "code\modules\food_and_drinks\pizzabox.dm" #include "code\modules\food_and_drinks\plate.dm" @@ -2918,6 +2917,21 @@ #include "code\modules\food_and_drinks\restaurant\customers\_customer.dm" #include "code\modules\forensics\_forensics.dm" #include "code\modules\forensics\forensics_helpers.dm" +#include "code\modules\hallucination\_hallucination.dm" +#include "code\modules\hallucination\airlock.dm" +#include "code\modules\hallucination\chat.dm" +#include "code\modules\hallucination\death.dm" +#include "code\modules\hallucination\fire.dm" +#include "code\modules\hallucination\hazard.dm" +#include "code\modules\hallucination\hostile_mob.dm" +#include "code\modules\hallucination\HUD.dm" +#include "code\modules\hallucination\husk.dm" +#include "code\modules\hallucination\item.dm" +#include "code\modules\hallucination\plasma_flood.dm" +#include "code\modules\hallucination\polymorph.dm" +#include "code\modules\hallucination\shock.dm" +#include "code\modules\hallucination\sound.dm" +#include "code\modules\hallucination\stray_bullet.dm" #include "code\modules\holiday\easter.dm" #include "code\modules\holiday\foreign_calendar.dm" #include "code\modules\holiday\holidays.dm"