diff --git a/code/modules/flufftext/Hallucination.dm.rej b/code/modules/flufftext/Hallucination.dm.rej index 78b5d879f2..2b8fe4b064 100644 --- a/code/modules/flufftext/Hallucination.dm.rej +++ b/code/modules/flufftext/Hallucination.dm.rej @@ -1,209 +1,1151 @@ -diff a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm (rejected hunks) -@@ -60,9 +60,14 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - var/halpick = pick(current) - new halpick(src, FALSE) - -+/mob/living/carbon/proc/set_screwyhud(hud_type) -+ hal_screwyhud = hud_type -+ update_health_hud() -+ - /datum/hallucination - var/mob/living/carbon/target - var/cost = 5 //affects the amount of hallucination reduced, and cooldown until the next hallucination -+ var/feedback_details //extra info for investigate - - /datum/hallucination/New(mob/living/carbon/T, forced = TRUE) - set waitfor = 0 -@@ -72,9 +77,13 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - target.next_hallucination = world.time + (rand(cost * 0.5, cost * 3) * 10) - - /datum/hallucination/proc/wake_and_restore() -- target.hal_screwyhud = SCREWYHUD_NONE -+ target.set_screwyhud(SCREWYHUD_NONE) - target.SetSleeping(0) - -+/datum/hallucination/Destroy() -+ target.investigate_log("was afflicted with a hallucination of type [type]. [feedback_details]", INVESTIGATE_HALLUCINATIONS) -+ return ..() -+ - /obj/effect/hallucination - invisibility = INVISIBILITY_OBSERVER - var/mob/living/carbon/target = null -@@ -744,7 +771,9 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - for(var/mob/living/carbon/human/H in GLOB.living_mob_list) - humans += H - person = pick(humans) -- to_chat(target, target.compose_message(person,understood_language,pick(radio_messages),"1459",person.get_spans())) -+ var/message = target.compose_message(person,understood_language,pick(radio_messages),"1459",person.get_spans()) -+ feedback_details += "Type: Radio, Source: [person.real_name], Message: [message]" -+ to_chat(target, message) - qdel(src) - - /datum/hallucination/message -@@ -763,43 +792,48 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - "You're hallucinating.", \ - //Direct advice - "[pick_list_replacements(HAL_LINES_FILE, "advice")]") -+ feedback_details += "Message: [chosen]" - to_chat(target, chosen) - qdel(src) - - /datum/hallucination/sounds - cost = 15 - --/datum/hallucination/sounds/New(mob/living/carbon/T, forced = TRUE) -+/datum/hallucination/sounds/New(mob/living/carbon/T, forced = TRUE, sound_type) - ..() -+ if(!sound_type) -+ sound_type = pick("airlock","explosion","far_explosion","glass","phone","summon_guns","alarm","beepsky","hallelujah","creepy","ratvar","shuttle_dock", -+ "wall_decon","door_hack","esword","blob_alert","tesla","malf_ai") -+ feedback_details += "Type: [sound_type]" - //Strange audio -- switch(rand(1,20)) -- if(1) -+ switch(sound_type) -+ if("airlock") - target.playsound_local(null,'sound/machines/airlock.ogg', 15, 1) -- if(2) -+ if("explosion") - if(prob(50)) - target.playsound_local(null,'sound/effects/explosion1.ogg', 50, 1) - else - target.playsound_local(null, 'sound/effects/explosion2.ogg', 50, 1) -- if(3) -+ if("far_explosion") - target.playsound_local(null, 'sound/effects/explosionfar.ogg', 50, 1) -- if(4) -+ if("glass") - target.playsound_local(null, pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg'), 50, 1) -- if(5) -+ if("phone") - target.playsound_local(null, 'sound/weapons/ring.ogg', 35) - sleep(15) - target.playsound_local(null, 'sound/weapons/ring.ogg', 35) - sleep(15) - target.playsound_local(null, 'sound/weapons/ring.ogg', 35) -- if(6) -+ if("summon_guns") - target.playsound_local(null, 'sound/magic/summon_guns.ogg', 50, 1) -- if(7) -+ if("alarm") - target.playsound_local(null, 'sound/machines/alarm.ogg', 100, 0) -- if(8) -+ if("beepsky") - target.playsound_local(null, 'sound/voice/bfreeze.ogg', 35, 0) -- if(9) -+ if("hallelujah") - target.playsound_local(null, 'sound/effects/pray_chaplain.ogg', 50) - //Rare audio -- if(10) -+ if("creepy") - //These sounds are (mostly) taken from Hidden: Source - var/static/list/hallucinations_creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/screech.ogg',\ - 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ -@@ -807,42 +841,42 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\ - 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg') - target.playsound_local(null, pick(hallucinations_creepyasssounds), 50, 1) -- if(11) -+ if("ratvar") - target.playsound_local(null, 'sound/effects/ratvar_rises.ogg', 100) - sleep(150) - target.playsound_local(null, 'sound/effects/ratvar_reveal.ogg', 100) -- if(12) -+ if("shuttle_dock") - to_chat(target, "

Priority Announcement

") - to_chat(target, "

The Emergency Shuttle has docked with the station. You have 3 minutes to board the Emergency Shuttle.

") - target.playsound_local(null, 'sound/ai/shuttledock.ogg', 100) - //Deconstructing a wall -- if(13) -+ if("wall_decon") - target.playsound_local(null, 'sound/items/welder.ogg', 15, 1) - sleep(105) - target.playsound_local(null, 'sound/items/welder2.ogg', 15, 1) - sleep(15) - target.playsound_local(null, 'sound/items/ratchet.ogg', 15, 1) - //Hacking a door -- if(14) -+ if("door_hack") - target.playsound_local(null, 'sound/items/screwdriver.ogg', 15, 1) - sleep(rand(10,30)) - for(var/i = rand(1,3), i>0, i--) - target.playsound_local(null, 'sound/weapons/empty.ogg', 15, 1) - sleep(rand(10,30)) - target.playsound_local(null, 'sound/machines/airlockforced.ogg', 15, 1) -- if(15) -+ if("esword") - target.playsound_local(null, 'sound/weapons/saberon.ogg',35,1) -- if(16) -+ if("blob_alert") - to_chat(target, "

Biohazard Alert

") - to_chat(target, "

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

") - target.playsound_local(null, 'sound/ai/outbreak5.ogg', 100, 0) -- if(17) //Tesla loose! -+ if("tesla") //Tesla loose! - target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 35, 1) - sleep(20) - target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 65, 1) - sleep(20) - target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 100, 1) -- if(18) //AI is doomsdaying! -+ if("malf_ai") //AI is doomsdaying! - to_chat(target, "

Anomaly Alert

") - to_chat(target, "

Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.

") - target.playsound_local(null, 'sound/ai/aimalf.ogg', 100, 0) -@@ -854,9 +888,10 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - /datum/hallucination/hudscrew/New(mob/living/carbon/T, forced = TRUE) - ..() - //Screwy HUD -- target.hal_screwyhud = pick(SCREWYHUD_CRIT,SCREWYHUD_DEAD,SCREWYHUD_HEALTHY) -+ target.set_screwyhud(pick(SCREWYHUD_CRIT,SCREWYHUD_DEAD,SCREWYHUD_HEALTHY)) -+ feedback_details += "Type: [target.hal_screwyhud]" - sleep(rand(100,250)) -- target.hal_screwyhud = SCREWYHUD_NONE -+ target.set_screwyhud(SCREWYHUD_NONE) - qdel(src) - - /datum/hallucination/fake_alert -@@ -867,6 +902,7 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - var/alert_type = pick("not_enough_oxy","not_enough_tox","not_enough_co2","too_much_oxy","too_much_co2","too_much_tox","newlaw","nutrition","charge","weightless","fire","locked","hacked","temphot","tempcold","pressure") - if(specific) - alert_type = specific -+ feedback_details += "Type: [alert_type]" - switch(alert_type) - if("oxy") - target.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy, override = TRUE) -@@ -962,6 +998,7 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - target.halitem.icon = 'icons/obj/grenade.dmi' - target.halitem.icon_state = "flashbang1" - target.halitem.name = "Flashbang" -+ feedback_details += "Type: [target.halitem.name]" - if(target.client) target.client.screen += target.halitem - QDEL_IN(target.halitem, rand(150, 350)) - qdel(src) -@@ -1006,7 +1043,7 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - /datum/hallucination/death/New(mob/living/carbon/T, forced = TRUE) - set waitfor = 0 - ..() -- target.hal_screwyhud = SCREWYHUD_DEAD -+ target.set_screwyhud(SCREWYHUD_DEAD) - target.Knockdown(300) - target.silent += 10 - var/area/area = get_area(target) -@@ -1025,7 +1062,7 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - to_chat(target, "DEAD: [fakemob.name] says, \"[pick("rip","hey [target.first_name()]","you too?","is the AI rogue?",\ - "i[prob(50)?" fucking":""] hate [pick("blood cult", "clock cult", "revenants", "abductors","double agents","viruses","badmins","you")]")]\"") - sleep(rand(70,90)) -- target.hal_screwyhud = SCREWYHUD_NONE -+ target.set_screwyhud(SCREWYHUD_NONE) - target.SetKnockdown(0) - target.silent = 0 - qdel(src) -@@ -1100,6 +1137,7 @@ GLOBAL_LIST_INIT(hallucinations_major, list( - startlocs += T - var/turf/start = pick(startlocs) - var/proj_type = pick(subtypesof(/obj/item/projectile/hallucination)) -+ feedback_details += "Type: [proj_type]" - var/obj/item/projectile/hallucination/H = new proj_type(start) - target.playsound_local(start, H.hal_fire_sound, 60, 1) - H.hal_target = target +/* +Ideas for the subtle effects of hallucination: + +Light up oxygen/plasma indicators (done) +Cause health to look critical/dead, even when standing (done) +Characters silently watching you +Brief flashes of fire/space/bombs/c4/dangerous shit (done) +Items that are rare/traitorous/don't exist appearing in your inventory slots (done) +Strange audio (should be rare) (done) +Gunshots/explosions/opening doors/less rare audio (done) + +*/ + +#define HAL_LINES_FILE "hallucination.json" + +/mob/living/carbon + var/image/halimage + var/image/halbody + var/obj/halitem + var/hal_screwyhud = SCREWYHUD_NONE + var/next_hallucination = 0 + +GLOBAL_LIST_INIT(hallucinations_minor, list( + /datum/hallucination/sounds, + /datum/hallucination/bolts, + /datum/hallucination/whispers, + /datum/hallucination/message, + /datum/hallucination/hudscrew)) + +GLOBAL_LIST_INIT(hallucinations_medium, list( + /datum/hallucination/fake_alert, + /datum/hallucination/items, + /datum/hallucination/items_other, + /datum/hallucination/dangerflash, + /datum/hallucination/bolts, + /datum/hallucination/fake_flood, + /datum/hallucination/husks, + /datum/hallucination/battle, + /datum/hallucination/fire, + /datum/hallucination/self_delusion)) + +GLOBAL_LIST_INIT(hallucinations_major, list( + /datum/hallucination/fakeattacker, + /datum/hallucination/death, + /datum/hallucination/xeno_attack, + /datum/hallucination/singularity_scare, + /datum/hallucination/delusion, + /datum/hallucination/oh_yeah)) + +/mob/living/carbon/proc/handle_hallucinations() + if(world.time < next_hallucination) + return + + if(hallucination) + var/list/current = GLOB.hallucinations_minor + if(prob(25) && hallucination > 100) + current = GLOB.hallucinations_medium + else if(prob(10) && hallucination > 200) + current = GLOB.hallucinations_major + var/halpick = pick(current) + new halpick(src, FALSE) + +/mob/living/carbon/proc/set_screwyhud(hud_type) + hal_screwyhud = hud_type + update_health_hud() + +/datum/hallucination + var/mob/living/carbon/target + var/cost = 5 //affects the amount of hallucination reduced, and cooldown until the next hallucination + var/feedback_details //extra info for investigate + +/datum/hallucination/New(mob/living/carbon/T, forced = TRUE) + set waitfor = 0 + target = T + if(!forced) + target.hallucination = max(0, target.hallucination - cost) + target.next_hallucination = world.time + (rand(cost * 0.5, cost * 3) * 10) + +/datum/hallucination/proc/wake_and_restore() + target.set_screwyhud(SCREWYHUD_NONE) + target.SetSleeping(0) + +/datum/hallucination/Destroy() + target.investigate_log("was afflicted with a hallucination of type [type]. [feedback_details]", INVESTIGATE_HALLUCINATIONS) + return ..() + +/obj/effect/hallucination + invisibility = INVISIBILITY_OBSERVER + var/mob/living/carbon/target = null + +/obj/effect/hallucination/simple + var/image_icon = 'icons/mob/alien.dmi' + var/image_state = "alienh_pounce" + var/px = 0 + var/py = 0 + var/col_mod = null + var/image/current_image = null + var/image_layer = MOB_LAYER + var/active = TRUE //qdelery + +/obj/effect/hallucination/simple/Initialize(mapload, var/mob/living/carbon/T) + ..() + target = T + current_image = GetImage() + if(target.client) + target.client.images |= current_image + +/obj/effect/hallucination/simple/proc/GetImage() + var/image/I = image(image_icon,src,image_state,image_layer,dir=src.dir) + I.pixel_x = px + I.pixel_y = py + if(col_mod) + I.color = col_mod + return I + +/obj/effect/hallucination/simple/proc/Show(update=1) + if(active) + if(target.client) + target.client.images.Remove(current_image) + if(update) + current_image = GetImage() + if(target.client) + target.client.images |= current_image + +/obj/effect/hallucination/simple/update_icon(new_state,new_icon,new_px=0,new_py=0) + image_state = new_state + if(new_icon) + image_icon = new_icon + else + image_icon = initial(image_icon) + px = new_px + py = new_py + Show() + +/obj/effect/hallucination/simple/Moved(atom/OldLoc, Dir) + Show() + +/obj/effect/hallucination/simple/Destroy() + if(target.client) + target.client.images.Remove(current_image) + active = FALSE + return ..() + +#define FAKE_FLOOD_EXPAND_TIME 20 +#define FAKE_FLOOD_MAX_RADIUS 10 + +/datum/hallucination/fake_flood + //Plasma starts flooding from the nearby vent + var/turf/center + var/list/flood_images = list() + var/list/turf/flood_turfs = list() + var/image_icon = 'icons/effects/tile_effects.dmi' + var/image_state = "plasma" + var/radius = 0 + var/next_expand = 0 + cost = 25 + +/datum/hallucination/fake_flood/New(mob/living/carbon/T, forced = TRUE) + ..() + for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target)) + if(!U.welded) + center = get_turf(U) + break + feedback_details += "Vent Coords: [center.x],[center.y],[center.z]" + flood_images += image(image_icon,center,image_state,MOB_LAYER) + flood_turfs += center + if(target.client) target.client.images |= flood_images + next_expand = world.time + FAKE_FLOOD_EXPAND_TIME + START_PROCESSING(SSobj, src) + +/datum/hallucination/fake_flood/process() + if(next_expand <= world.time) + radius++ + if(radius > FAKE_FLOOD_MAX_RADIUS) + qdel(src) + return + Expand() + if((get_turf(target) in flood_turfs) && !target.internal) + new /datum/hallucination/fake_alert(target, TRUE, "tox_in_air") + next_expand = world.time + FAKE_FLOOD_EXPAND_TIME + +/datum/hallucination/fake_flood/proc/Expand() + for(var/turf/FT in flood_turfs) + for(var/dir in GLOB.cardinals) + var/turf/T = get_step(FT, dir) + if((T in flood_turfs) || !FT.CanAtmosPass(T)) + continue + flood_images += image(image_icon,T,image_state,MOB_LAYER) + flood_turfs += T + if(target.client) + target.client.images |= flood_images + +/datum/hallucination/fake_flood/Destroy() + STOP_PROCESSING(SSobj, src) + qdel(flood_turfs) + flood_turfs = list() + if(target.client) + target.client.images.Remove(flood_images) + target = null + qdel(flood_images) + flood_images = list() + return ..() + +/obj/effect/hallucination/simple/xeno + image_icon = 'icons/mob/alien.dmi' + image_state = "alienh_pounce" + +/obj/effect/hallucination/simple/xeno/Initialize(mapload, mob/living/carbon/T) + ..() + name = "alien hunter ([rand(1, 1000)])" + +/obj/effect/hallucination/simple/xeno/throw_impact(A) + update_icon("alienh_pounce") + if(A == target && target.stat!=DEAD) + target.Knockdown(100) + target.visible_message("[target] flails around wildly.","[name] pounces on you!") + +/datum/hallucination/xeno_attack + //Xeno crawls from nearby vent,jumps at you, and goes back in + var/obj/machinery/atmospherics/components/unary/vent_pump/pump = null + var/obj/effect/hallucination/simple/xeno/xeno = null + cost = 25 + +/datum/hallucination/xeno_attack/New(mob/living/carbon/T, forced = TRUE) + ..() + for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target)) + if(!U.welded) + pump = U + break + feedback_details += "Vent Coords: [pump.x],[pump.y],[pump.z]" + if(pump) + xeno = new(pump.loc,target) + sleep(10) + xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32) + xeno.throw_at(target,7,1, spin = 0, diagonals_first = 1) + sleep(10) + xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32) + xeno.throw_at(pump,7,1, spin = 0, diagonals_first = 1) + sleep(10) + var/xeno_name = xeno.name + to_chat(target, "[xeno_name] begins climbing into the ventilation system...") + sleep(30) + qdel(xeno) + to_chat(target, "[xeno_name] scrambles into the ventilation ducts!") + qdel(src) + +/obj/effect/hallucination/simple/clown + image_icon = 'icons/mob/animal.dmi' + image_state = "clown" + +/obj/effect/hallucination/simple/clown/Initialize(mapload, mob/living/carbon/T, duration) + ..(loc, T) + name = pick(GLOB.clown_names) + QDEL_IN(src,duration) + +/obj/effect/hallucination/simple/clown/scary + image_state = "scary_clown" + +/obj/effect/hallucination/simple/bubblegum + name = "Bubblegum" + image_icon = 'icons/mob/lavaland/96x96megafauna.dmi' + image_state = "bubblegum" + px = -32 + +/datum/hallucination/oh_yeah + var/obj/effect/hallucination/simple/bubblegum/bubblegum + var/image/fakebroken + var/image/fakerune + cost = 75 + +/datum/hallucination/oh_yeah/New(mob/living/carbon/T, forced = TRUE) + . = ..() + var/turf/closed/wall/wall + for(var/turf/closed/wall/W in range(7,target)) + wall = W + break + if(!wall) + return INITIALIZE_HINT_QDEL + feedback_details += "Source: [wall.x],[wall.y],[wall.z]" + + fakebroken = image('icons/turf/floors.dmi', wall, "plating", layer = TURF_LAYER) + var/turf/landing = get_turf(target) + var/turf/landing_image_turf = get_step(landing, SOUTHWEST) //the icon is 3x3 + fakerune = image('icons/effects/96x96.dmi', landing_image_turf, "landing", layer = ABOVE_OPEN_TURF_LAYER) + fakebroken.override = TRUE + if(target.client) + target.client.images |= fakebroken + target.client.images |= fakerune + target.playsound_local(wall,'sound/effects/meteorimpact.ogg', 150, 1) + bubblegum = new(wall, target) + addtimer(CALLBACK(src, .proc/bubble_attack, landing), 10) + +/datum/hallucination/oh_yeah/proc/bubble_attack(turf/landing) + var/charged = FALSE //only get hit once + while(get_turf(bubblegum) != landing && target && target.stat != DEAD) + bubblegum.forceMove(get_step_towards(bubblegum, landing)) + bubblegum.setDir(get_dir(bubblegum, landing)) + target.playsound_local(get_turf(bubblegum), 'sound/effects/meteorimpact.ogg', 150, 1) + shake_camera(target, 2, 1) + if(bubblegum.Adjacent(target) && !charged) + charged = TRUE + target.Knockdown(80) + target.adjustStaminaLoss(40) + step_away(target, bubblegum) + shake_camera(target, 4, 3) + target.visible_message("[target] jumps backwards, falling on the ground!","[bubblegum] slams into you!") + sleep(2) + sleep(30) + qdel(src) + +/datum/hallucination/oh_yeah/Destroy() + if(target.client) + target.client.images.Remove(fakebroken) + target.client.images.Remove(fakerune) + QDEL_NULL(fakebroken) + QDEL_NULL(fakerune) + QDEL_NULL(bubblegum) + return ..() + +/datum/hallucination/singularity_scare + //Singularity moving towards you. + //todo Hide where it moved with fake space images + var/obj/effect/hallucination/simple/singularity/s = null + cost = 75 + +/datum/hallucination/singularity_scare/New(mob/living/carbon/T, forced = TRUE) + ..() + var/turf/start = get_turf(T) + var/screen_border = pick(SOUTH,EAST,WEST,NORTH) + for(var/i in 1 to 13) + start = get_step(start,screen_border) + feedback_details += "Source: [start.x],[start.y],[start.z]" + s = new(start,target) + s.parent = src + for(var/i in 1 to 13) + sleep(10) + s.forceMove(get_step(get_turf(s),get_dir(s,target))) + s.Show() + s.Eat() + qdel(s) + +/obj/effect/hallucination/simple/singularity + image_icon = 'icons/effects/224x224.dmi' + image_state = "singularity_s7" + image_layer = MASSIVE_OBJ_LAYER + px = -96 + py = -96 + var/datum/hallucination/singularity_scare/parent + +/obj/effect/hallucination/simple/singularity/proc/Eat(atom/OldLoc, Dir) + var/target_dist = get_dist(src,target) + if(target_dist<=3) //"Eaten" + target.set_screwyhud(SCREWYHUD_DEAD) + target.SetUnconscious(160) + addtimer(CALLBACK(parent, /datum/hallucination/.proc/wake_and_restore), rand(30, 50)) + +/datum/hallucination/battle + cost = 15 + +/datum/hallucination/battle/New(mob/living/carbon/T, forced = TRUE, battle_type) + ..() + var/hits = rand(3,6) + if(!battle_type) + battle_type = pick("laser","esword","gun","stunprod","bomb") + feedback_details += "Type: [battle_type]" + switch(battle_type) + if("laser") //Laser fight + for(var/i in 1 to hits) + target.playsound_local(null, 'sound/weapons/laser.ogg', 25, 1) + if(prob(50)) + addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, 'sound/weapons/sear.ogg', 25, 1), rand(10,20)) + else + addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, 'sound/weapons/effects/searwall.ogg', 25, 1), rand(10,20)) + sleep(rand(CLICK_CD_RANGE, CLICK_CD_RANGE + 8)) + target.playsound_local(null, get_sfx("bodyfall"), 25, 1) + if("esword") //Esword fight + target.playsound_local(null, 'sound/weapons/saberon.ogg',15, 1) + for(var/i=0,i...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/fakeattacker/New(mob/living/carbon/T, forced = TRUE) + ..() + var/mob/living/carbon/human/clone = null + var/clone_weapon = null + + for(var/mob/living/carbon/human/H in GLOB.living_mob_list) + if(H.stat || H.lying) + continue + clone = H + feedback_details += "Clone of: [H.real_name]" + break + + if(!clone) + return + + var/static/list/non_fakeattack_weapons = list(/obj/item/weapon/gun/ballistic, /obj/item/ammo_box/a357,\ + /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow, /obj/item/weapon/melee/transforming/energy/sword/saber,\ + /obj/item/weapon/storage/box/syndicate, /obj/item/weapon/storage/box/emps,\ + /obj/item/weapon/cartridge/virus/syndicate, /obj/item/clothing/under/chameleon,\ + /obj/item/clothing/shoes/chameleon, /obj/item/weapon/card/id/syndicate,\ + /obj/item/clothing/mask/chameleon, /obj/item/clothing/glasses/thermal,\ + /obj/item/device/chameleon, /obj/item/weapon/card/emag, /obj/item/weapon/grenade/plastic/x4,\ + /obj/item/weapon/storage/toolbox/syndicate, /obj/item/weapon/aiModule,\ + /obj/item/device/radio/headset/syndicate, /obj/item/weapon/grenade/plastic/c4,\ + /obj/item/device/powersink, /obj/item/weapon/storage/box/syndie_kit,\ + /obj/item/toy/syndicateballoon, /obj/item/weapon/gun/energy/laser/captain,\ + /obj/item/weapon/hand_tele, /obj/item/weapon/construction/rcd, /obj/item/weapon/tank/jetpack,\ + /obj/item/clothing/under/rank/captain, /obj/item/device/aicard,\ + /obj/item/clothing/shoes/magboots, /obj/item/areaeditor/blueprints, /obj/item/weapon/disk/nuclear,\ + /obj/item/clothing/suit/space/nasavoid, /obj/item/weapon/tank) + + var/obj/effect/fake_attacker/F = new/obj/effect/fake_attacker(get_turf(target),target) + + for(var/obj/item/I in clone.held_items) + if(!(locate(I) in non_fakeattack_weapons)) + clone_weapon = I.name + F.weap = I + + F.name = clone.name + F.my_target = target + F.weapon_name = clone_weapon + + F.left = image(clone,dir = WEST) + F.right = image(clone,dir = EAST) + F.up = image(clone,dir = NORTH) + F.down = image(clone,dir = SOUTH) + + F.updateimage() + qdel(src) + +/obj/effect/fake_attacker + icon = null + icon_state = null + name = "" + desc = "" + density = FALSE + anchored = TRUE + opacity = 0 + var/mob/living/carbon/human/my_target = null + var/weapon_name = null + var/obj/item/weap = null + var/image/stand_icon = null + var/image/currentimage = null + var/icon/base = null + var/skin_tone + var/mob/living/clone = null + var/image/left + var/image/right + var/image/up + var/collapse + var/image/down + + max_integrity = 100 + +/obj/effect/fake_attacker/attackby(obj/item/weapon/P, mob/living/user, params) + step_away(src,my_target,2) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src) + my_target.playsound_local(src, P.hitsound, 1) + my_target.visible_message("[my_target] flails around wildly.", \ + "[my_target] has attacked [src]!") + + obj_integrity -= P.force + +/obj/effect/fake_attacker/Crossed(mob/M, somenumber) + if(M == my_target) + step_away(src,my_target,2) + if(prob(30)) + for(var/mob/O in oviewers(world.view , my_target)) + to_chat(O, "[my_target] stumbles around.") + +/obj/effect/fake_attacker/Initialize(mapload, mob/living/carbon/T) + ..() + my_target = T + QDEL_IN(src, 300) + step_away(src,my_target,2) + INVOKE_ASYNC(src, .proc/attack_loop) + + +/obj/effect/fake_attacker/proc/updateimage() +// del src.currentimage + if(src.dir == NORTH) + del src.currentimage + src.currentimage = new /image(up,src) + else if(src.dir == SOUTH) + del src.currentimage + src.currentimage = new /image(down,src) + else if(src.dir == EAST) + del src.currentimage + src.currentimage = new /image(right,src) + else if(src.dir == WEST) + del src.currentimage + src.currentimage = new /image(left,src) + SEND_IMAGE(my_target, currentimage) + + +/obj/effect/fake_attacker/proc/attack_loop() + while(1) + sleep(rand(5,10)) + if(obj_integrity < 0 || my_target.stat) + collapse() + continue + if(get_dist(src,my_target) > 1) + src.setDir(get_dir(src,my_target)) + step_towards(src,my_target) + updateimage() + else + if(prob(15)) + if(weapon_name) + my_target.playsound_local(my_target, weap.hitsound, weap.get_clamped_volume(), 1) + my_target.show_message("[src.name] has attacked [my_target] with [weapon_name]!", 1) + my_target.staminaloss += 30 + if(prob(20)) + my_target.blur_eyes(3) + if(prob(33)) + if(!locate(/obj/effect/overlay) in my_target.loc) + fake_blood(my_target) + else + my_target.playsound_local(my_target, pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg'), 25, 1) + my_target.show_message("[src.name] has punched [my_target]!", 1) + my_target.staminaloss += 30 + if(prob(33)) + if(!locate(/obj/effect/overlay) in my_target.loc) + fake_blood(my_target) + + if(prob(15)) + step_away(src,my_target,2) + +/obj/effect/fake_attacker/proc/collapse() + collapse = 1 + updateimage() + qdel(src) + +/obj/effect/fake_attacker/proc/fake_blood(mob/target) + var/obj/effect/overlay/O = new/obj/effect/overlay(target.loc) + O.name = "blood" + var/image/I = image('icons/effects/blood.dmi',O,"floor[rand(1,7)]",O.dir,1) + SEND_IMAGE(target, I) + QDEL_IN(O, 300) + + +/datum/hallucination/bolts + var/list/doors = list() + cost = 25 + +/datum/hallucination/bolts/New(mob/living/carbon/T, forced, door_number=-1) //-1 for severe, 1-2 for subtle + ..() + var/image/I = null + var/count = 0 + feedback_details += "Door amount: [door_number]" + for(var/obj/machinery/door/airlock/A in range(7, target)) + if(count>door_number && door_number>0) + break + count++ + I = image(A.overlays_file, get_turf(A), "lights_bolts",layer=A.layer+0.1) + doors += I + if(target.client) + target.client.images |= I + target.playsound_local(get_turf(A), 'sound/machines/boltsdown.ogg',30,0,3) + sleep(rand(6,12)) + sleep(100) + for(var/image/B in doors) + if(target.client) + target.client.images.Remove(B) + target.playsound_local(get_turf(B), 'sound/machines/boltsup.ogg',30,0,3) + sleep(rand(6,12)) + qdel(src) + +/datum/hallucination/whispers + cost = 15 + +/datum/hallucination/whispers/New(mob/living/carbon/T, forced = TRUE) + ..() + var/speak_messages = list("[pick_list_replacements(HAL_LINES_FILE, "suspicion")]",\ + "[pick_list_replacements(HAL_LINES_FILE, "greetings")][target.first_name()]!",\ + "[pick_list_replacements(HAL_LINES_FILE, "getout")]",\ + "[pick_list_replacements(HAL_LINES_FILE, "weird")]",\ + "[pick_list_replacements(HAL_LINES_FILE, "didyouhearthat")]",\ + "[pick_list_replacements(HAL_LINES_FILE, "imatraitor")]",\ + "[pick_list_replacements(HAL_LINES_FILE, "doubt")]",\ + "[pick_list_replacements(HAL_LINES_FILE, "aggressive")]",\ + "[pick_list_replacements(HAL_LINES_FILE, "help")]!!",\ + "[pick_list_replacements(HAL_LINES_FILE, "escape")]",\ + "I'm infected, [pick_list_replacements(HAL_LINES_FILE, "infection_advice")]!") + + var/radio_messages = list("Set [target.first_name()] to arrest!",\ + "[pick_list_replacements(HAL_LINES_FILE, "people")] is [pick_list_replacements(HAL_LINES_FILE, "accusations")]!",\ + "Help!",\ + "[pick_list_replacements(HAL_LINES_FILE, "threat")] in [pick_list_replacements(HAL_LINES_FILE, "location")][prob(50)?"!":"!!"]",\ + "Where's [target.first_name()]?"\ + ,"[pick("C","Ai, c","Someone c","Rec")]all the shuttle!"\ + ,"AI [pick("rogue", "is dead")]!!") + + var/list/mob/living/carbon/people = list() + var/list/mob/living/carbon/person = null + var/datum/language/understood_language = target.get_random_understood_language() + for(var/mob/living/carbon/H in view(target)) + if(H == target) + continue + if(!person) + person = H + else + if(get_dist(target,H)The light burns you!", \ + "You don't feel like yourself.", \ + "You hear something squeezing through the ducts...", \ + "You hear a distant scream.", \ + "You feel invincible, nothing can hurt you!", \ + "You feel a tiny prick!", \ + "[target] sneezes.", \ + //The truth, revealed + "You're hallucinating.", \ + //Direct advice + "[pick_list_replacements(HAL_LINES_FILE, "advice")]") + feedback_details += "Message: [chosen]" + to_chat(target, chosen) + qdel(src) + +/datum/hallucination/sounds + cost = 15 + +/datum/hallucination/sounds/New(mob/living/carbon/T, forced = TRUE, sound_type) + ..() + if(!sound_type) + sound_type = pick("airlock","explosion","far_explosion","glass","phone","summon_guns","alarm","beepsky","hallelujah","creepy","ratvar","shuttle_dock", + "wall_decon","door_hack","esword","blob_alert","tesla","malf_ai") + feedback_details += "Type: [sound_type]" + //Strange audio + switch(sound_type) + if("airlock") + target.playsound_local(null,'sound/machines/airlock.ogg', 15, 1) + if("explosion") + if(prob(50)) + target.playsound_local(null,'sound/effects/explosion1.ogg', 50, 1) + else + target.playsound_local(null, 'sound/effects/explosion2.ogg', 50, 1) + if("far_explosion") + target.playsound_local(null, 'sound/effects/explosionfar.ogg', 50, 1) + if("glass") + target.playsound_local(null, pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg'), 50, 1) + if("phone") + target.playsound_local(null, 'sound/weapons/ring.ogg', 35) + sleep(15) + target.playsound_local(null, 'sound/weapons/ring.ogg', 35) + sleep(15) + target.playsound_local(null, 'sound/weapons/ring.ogg', 35) + if("summon_guns") + target.playsound_local(null, 'sound/magic/summon_guns.ogg', 50, 1) + if("alarm") + target.playsound_local(null, 'sound/machines/alarm.ogg', 100, 0) + if("beepsky") + target.playsound_local(null, 'sound/voice/bfreeze.ogg', 35, 0) + if("hallelujah") + target.playsound_local(null, 'sound/effects/pray_chaplain.ogg', 50) + //Rare audio + if("creepy") + //These sounds are (mostly) taken from Hidden: Source + var/static/list/hallucinations_creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/screech.ogg',\ + 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ + 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\ + 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\ + 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg') + target.playsound_local(null, pick(hallucinations_creepyasssounds), 50, 1) + if("ratvar") + target.playsound_local(null, 'sound/effects/ratvar_rises.ogg', 100) + sleep(150) + target.playsound_local(null, 'sound/effects/ratvar_reveal.ogg', 100) + if("shuttle_dock") + to_chat(target, "

Priority Announcement

") + to_chat(target, "

The Emergency Shuttle has docked with the station. You have 3 minutes to board the Emergency Shuttle.

") + target.playsound_local(null, 'sound/ai/shuttledock.ogg', 100) + //Deconstructing a wall + if("wall_decon") + target.playsound_local(null, 'sound/items/welder.ogg', 15, 1) + sleep(105) + target.playsound_local(null, 'sound/items/welder2.ogg', 15, 1) + sleep(15) + target.playsound_local(null, 'sound/items/ratchet.ogg', 15, 1) + //Hacking a door + if("door_hack") + target.playsound_local(null, 'sound/items/screwdriver.ogg', 15, 1) + sleep(rand(10,30)) + for(var/i = rand(1,3), i>0, i--) + target.playsound_local(null, 'sound/weapons/empty.ogg', 15, 1) + sleep(rand(10,30)) + target.playsound_local(null, 'sound/machines/airlockforced.ogg', 15, 1) + if("esword") + target.playsound_local(null, 'sound/weapons/saberon.ogg',35,1) + if("blob_alert") + to_chat(target, "

Biohazard Alert

") + to_chat(target, "

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

") + target.playsound_local(null, 'sound/ai/outbreak5.ogg', 100, 0) + if("tesla") //Tesla loose! + target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 35, 1) + sleep(20) + target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 65, 1) + sleep(20) + target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 100, 1) + if("malf_ai") //AI is doomsdaying! + to_chat(target, "

Anomaly Alert

") + to_chat(target, "

Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.

") + target.playsound_local(null, 'sound/ai/aimalf.ogg', 100, 0) + qdel(src) + +/datum/hallucination/hudscrew + cost = 10 + +/datum/hallucination/hudscrew/New(mob/living/carbon/T, forced = TRUE) + ..() + //Screwy HUD + target.set_screwyhud(pick(SCREWYHUD_CRIT,SCREWYHUD_DEAD,SCREWYHUD_HEALTHY)) + feedback_details += "Type: [target.hal_screwyhud]" + sleep(rand(100,250)) + target.set_screwyhud(SCREWYHUD_NONE) + qdel(src) + +/datum/hallucination/fake_alert + cost = 15 + +/datum/hallucination/fake_alert/New(mob/living/carbon/T, forced = TRUE, specific, duration = 150) + ..() + var/alert_type = pick("not_enough_oxy","not_enough_tox","not_enough_co2","too_much_oxy","too_much_co2","too_much_tox","newlaw","nutrition","charge","weightless","fire","locked","hacked","temphot","tempcold","pressure") + if(specific) + alert_type = specific + feedback_details += "Type: [alert_type]" + switch(alert_type) + if("oxy") + target.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy, override = TRUE) + if("not_enough_tox") + target.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox, override = TRUE) + if("not_enough_co2") + target.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2, override = TRUE) + if("too_much_oxy") + target.throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy, override = TRUE) + if("too_much_co2") + target.throw_alert("too_much_co2", /obj/screen/alert/too_much_co2, override = TRUE) + if("tox_in_air") + target.throw_alert("too_much_tox", /obj/screen/alert/too_much_tox, override = TRUE) + if("nutrition") + if(prob(50)) + target.throw_alert("nutrition", /obj/screen/alert/fat, override = TRUE) + else + target.throw_alert("nutrition", /obj/screen/alert/starving, override = TRUE) + if("weightless") + target.throw_alert("weightless", /obj/screen/alert/weightless, override = TRUE) + if("fire") + target.throw_alert("fire", /obj/screen/alert/fire, override = TRUE) + if("temphot") + target.throw_alert("temp", /obj/screen/alert/hot, 3, override = TRUE) + if("tempcold") + target.throw_alert("temp", /obj/screen/alert/cold, 3, override = TRUE) + if("pressure") + if(prob(50)) + target.throw_alert("pressure", /obj/screen/alert/highpressure, 2, override = TRUE) + else + target.throw_alert("pressure", /obj/screen/alert/lowpressure, 2, override = TRUE) + //BEEP BOOP I AM A ROBOT + if("newlaw") + target.throw_alert("newlaw", /obj/screen/alert/newlaw, override = TRUE) + if("locked") + target.throw_alert("locked", /obj/screen/alert/locked, override = TRUE) + if("hacked") + target.throw_alert("hacked", /obj/screen/alert/hacked, override = TRUE) + if("charge") + target.throw_alert("charge",/obj/screen/alert/emptycell, override = TRUE) + sleep(duration) + target.clear_alert(alert_type, clear_override = TRUE) + qdel(src) + +/datum/hallucination/items + cost = 15 + +/datum/hallucination/items/New(mob/living/carbon/T, forced = TRUE) + ..() + //Strange items + if(!target.halitem) + target.halitem = new + var/obj/item/l_hand = target.get_item_for_held_index(1) + var/obj/item/r_hand = target.get_item_for_held_index(2) + var/l = ui_hand_position(target.get_held_index_of_item(l_hand)) + var/r = ui_hand_position(target.get_held_index_of_item(r_hand)) + var/list/slots_free = list(l,r) + if(l_hand) slots_free -= l + if(r_hand) slots_free -= r + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(!H.belt) slots_free += ui_belt + if(!H.l_store) slots_free += ui_storage1 + if(!H.r_store) slots_free += ui_storage2 + if(slots_free.len) + target.halitem.screen_loc = pick(slots_free) + target.halitem.layer = ABOVE_HUD_LAYER + target.halitem.plane = ABOVE_HUD_PLANE + switch(rand(1,6)) + if(1) //revolver + target.halitem.icon = 'icons/obj/guns/projectile.dmi' + target.halitem.icon_state = "revolver" + target.halitem.name = "Revolver" + if(2) //c4 + target.halitem.icon = 'icons/obj/grenade.dmi' + target.halitem.icon_state = "plastic-explosive0" + target.halitem.name = "C4" + if(prob(25)) + target.halitem.icon_state = "plasticx40" + if(3) //sword + target.halitem.icon = 'icons/obj/weapons.dmi' + target.halitem.icon_state = "sword0" + target.halitem.name = "Energy Sword" + if(4) //stun baton + target.halitem.icon = 'icons/obj/weapons.dmi' + target.halitem.icon_state = "stunbaton" + target.halitem.name = "Stun Baton" + if(5) //emag + target.halitem.icon = 'icons/obj/card.dmi' + target.halitem.icon_state = "emag" + target.halitem.name = "Cryptographic Sequencer" + if(6) //flashbang + target.halitem.icon = 'icons/obj/grenade.dmi' + target.halitem.icon_state = "flashbang1" + target.halitem.name = "Flashbang" + feedback_details += "Type: [target.halitem.name]" + if(target.client) target.client.screen += target.halitem + QDEL_IN(target.halitem, rand(150, 350)) + qdel(src) + +/datum/hallucination/dangerflash + cost = 15 + +/datum/hallucination/dangerflash/New(mob/living/carbon/T, forced = TRUE) + ..() + //Flashes of danger + if(!target.halimage) + var/list/possible_points = list() + for(var/turf/open/floor/F in view(target,world.view)) + possible_points += F + if(possible_points.len) + var/turf/open/floor/danger_point = pick(possible_points) + + switch(rand(1,5)) + if(1) + target.halimage = image('icons/turf/space.dmi',danger_point,"[rand(1,25)]",TURF_LAYER) + if(2) + target.halimage = image('icons/turf/floors/lava.dmi',danger_point,"smooth",TURF_LAYER) + if(3) + target.halimage = image('icons/turf/floors/Chasms.dmi',danger_point,"smooth",TURF_LAYER) + if(4) + target.halimage = image('icons/effects/effects.dmi',danger_point,"anom",OBJ_LAYER+0.01) + if(5) + target.halimage = image('icons/effects/effects.dmi',danger_point,"electricity2",OBJ_LAYER+0.01) + + + if(target.client) + target.client.images += target.halimage + sleep(rand(200,450)) + if(target.client) + target.client.images -= target.halimage + QDEL_NULL(target.halimage) + qdel(src) + +/datum/hallucination/death + cost = 40 + +/datum/hallucination/death/New(mob/living/carbon/T, forced = TRUE) + set waitfor = 0 + ..() + target.set_screwyhud(SCREWYHUD_DEAD) + target.Knockdown(300) + target.silent += 10 + var/area/area = get_area(target) + to_chat(target, "[target.mind.name] has died at [area.name].") + if(prob(50)) + var/mob/fakemob + var/list/dead_people = list() + for(var/mob/dead/observer/G in GLOB.player_list) + dead_people += G + if(LAZYLEN(dead_people)) + fakemob = pick(dead_people) + else + fakemob = target //ever been so lonely you had to haunt yourself? + if(fakemob) + sleep(rand(20, 50)) + to_chat(target, "DEAD: [fakemob.name] says, \"[pick("rip","hey [target.first_name()]","you too?","is the AI rogue?",\ + "i[prob(50)?" fucking":""] hate [pick("blood cult", "clock cult", "revenants", "abductors","double agents","viruses","badmins","you")]")]\"") + sleep(rand(70,90)) + target.set_screwyhud(SCREWYHUD_NONE) + target.SetKnockdown(0) + target.silent = 0 + qdel(src) + +/datum/hallucination/fire + cost = 25 + +/datum/hallucination/fire/New(mob/living/carbon/T, forced = TRUE) + ..() + var/image/fire_overlay = image('icons/mob/OnFire.dmi', target, "Standing", ABOVE_MOB_LAYER) + if(target.client) + target.client.images += fire_overlay + to_chat(target, "You're set on fire!") + target.throw_alert("fire", /obj/screen/alert/fire, override = TRUE) + sleep(20) + target.throw_alert("temp", /obj/screen/alert/hot, 1, override = TRUE) + sleep(30) + target.clear_alert("temp", clear_override = TRUE) + target.throw_alert("temp", /obj/screen/alert/hot, 2, override = TRUE) + sleep(30) + target.clear_alert("temp", clear_override = TRUE) + target.throw_alert("temp", /obj/screen/alert/hot, 3, override = TRUE) + for(var/i in 1 to rand(5, 10)) + target.adjustStaminaLoss(15) + sleep(25) + target.clear_alert("fire", clear_override = TRUE) + target.clear_alert("temp", clear_override = TRUE) + if(target.client) + target.client.images -= fire_overlay + QDEL_NULL(fire_overlay) + qdel(src) + +/datum/hallucination/husks + cost = 20 + +/datum/hallucination/husks/New(mob/living/carbon/T, forced = TRUE) + ..() + if(!target.halbody) + var/list/possible_points = list() + for(var/turf/open/floor/F in view(target,world.view)) + possible_points += F + if(possible_points.len) + var/turf/open/floor/husk_point = pick(possible_points) + switch(rand(1,4)) + if(1) + var/image/body = image('icons/mob/human.dmi',husk_point,"husk",TURF_LAYER) + var/matrix/M = matrix() + M.Turn(90) + body.transform = M + target.halbody = body + if(2,3) + target.halbody = image('icons/mob/human.dmi',husk_point,"husk",TURF_LAYER) + if(4) + target.halbody = image('icons/mob/alien.dmi',husk_point,"alienother",TURF_LAYER) + + if(target.client) + target.client.images += target.halbody + sleep(rand(30,50)) //Only seen for a brief moment. + if(target.client) + target.client.images -= target.halbody + QDEL_NULL(target.halbody) + qdel(src) + +//hallucination projectile code in code/modules/projectiles/projectile/special.dm +/datum/hallucination/stray_bullet + cost = 15 + +/datum/hallucination/stray_bullet/New(mob/living/carbon/C, forced = TRUE) + ..() + var/list/turf/startlocs = list() + for(var/turf/open/T in view(world.view+1,target)-view(world.view,target)) + startlocs += T + var/turf/start = pick(startlocs) + var/proj_type = pick(subtypesof(/obj/item/projectile/hallucination)) + feedback_details += "Type: [proj_type]" + var/obj/item/projectile/hallucination/H = new proj_type(start) + target.playsound_local(start, H.hal_fire_sound, 60, 1) + H.hal_target = target + H.current = start + H.starting = start + H.yo = target.y - start.y + H.xo = target.x - start.x + H.original = target + H.fire() + qdel(src) +