diff --git a/aurorastation.dme b/aurorastation.dme
index 193c2625d64..8caaaf1b679 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -176,6 +176,7 @@
#include "code\controllers\subsystems\garbage.dm"
#include "code\controllers\subsystems\ghostroles.dm"
#include "code\controllers\subsystems\global_listener.dm"
+#include "code\controllers\subsystems\hallucinations.dm"
#include "code\controllers\subsystems\http.dm"
#include "code\controllers\subsystems\icon_cache.dm"
#include "code\controllers\subsystems\icon_smooth.dm"
@@ -1532,7 +1533,6 @@
#include "code\modules\examine\descriptions\weapons.dm"
#include "code\modules\ext_scripts\python.dm"
#include "code\modules\flufftext\Dreaming.dm"
-#include "code\modules\flufftext\Hallucination.dm"
#include "code\modules\flufftext\TextFilters.dm"
#include "code\modules\food\recipe.dm"
#include "code\modules\food\recipes_fryer.dm"
@@ -1569,6 +1569,12 @@
#include "code\modules\ghosttrap\trap.dm"
#include "code\modules\global_listener\devices.dm"
#include "code\modules\global_listener\interfaces.dm"
+#include "code\modules\hallucinations\hallucinations.dm"
+#include "code\modules\hallucinations\mob.dm"
+#include "code\modules\hallucinations\types\basic.dm"
+#include "code\modules\hallucinations\types\mirage.dm"
+#include "code\modules\hallucinations\types\powers.dm"
+#include "code\modules\hallucinations\types\sound.dm"
#include "code\modules\heavy_vehicle\_mech_setup.dm"
#include "code\modules\heavy_vehicle\mech_construction.dm"
#include "code\modules\heavy_vehicle\mech_damage.dm"
diff --git a/code/__defines/_macros.dm b/code/__defines/_macros.dm
index 99a4065b237..e97ef70ad61 100644
--- a/code/__defines/_macros.dm
+++ b/code/__defines/_macros.dm
@@ -7,7 +7,9 @@
#define SPAN_DANGER(X) "[X]"
#define SPAN_CULT(X) "[X]"
#define SPAN_GOOD(X) "[X]"
+#define SPAN_BAD(X) "[X]"
#define SPAN_ALIEN(X) "[X]"
+#define SPAN_ALERT(X) "[X]"
#define SPAN_ITALIC(X) "[X]"
#define SPAN_BOLD(X) "[X]"
#define SPAN_SUBTLE(X) "[X]"
diff --git a/code/controllers/subsystems/hallucinations.dm b/code/controllers/subsystems/hallucinations.dm
new file mode 100644
index 00000000000..8fb27220a5a
--- /dev/null
+++ b/code/controllers/subsystems/hallucinations.dm
@@ -0,0 +1,36 @@
+/var/datum/controller/subsystem/hallucinations/SShallucinations
+
+/datum/controller/subsystem/hallucinations
+ name = "Hallucinations"
+ flags = SS_NO_FIRE
+
+ var/list/hallucinated_phrases = list()
+ var/list/hallucinated_actions = list()
+ var/list/hallucinated_thoughts = list()
+ var/static/list/hal_emote = list("mutters quietly.", "stares.", "grunts.", "looks around.", "twitches.", "shivers.", "swats at the air.", "wobbles.", "gasps!", "blinks rapidly.", "murmurs.",
+ "dry heaves!", "twitches violently.", "giggles.", "drools.", "scratches all over.", "grinds their teeth.", "whispers something quietly.")
+ var/static/list/message_sender = list("Mom", "Dad", "Captain", "Captain(as Captain)", "help", "Home", "MaxBet Online Casino", "IDrist Corp", "Dr. Maxman",
+ "www.wetskrell.nt", "You are our lucky grand prize winner!", "Officer Beepsky", "Ginny", "Ian",
+ "what have you DONE?", "Miranda Trasen", "Central Command", "AI", "maintenance drone", "Unknown", "I don't want to die")
+ var/list/all_hallucinations = list()
+
+/datum/controller/subsystem/hallucinations/New()
+ NEW_SS_GLOBAL(SShallucinations)
+
+/datum/controller/subsystem/hallucinations/Initialize()
+ . = ..()
+ for(var/T in subtypesof(/datum/hallucination/))
+ all_hallucinations += T
+ hallucinated_phrases = file2list("code/modules/hallucinations/text_lists/hallucinated_phrases.txt")
+ hallucinated_actions = file2list("code/modules/hallucinations/text_lists/hallucinated_actions.txt") //important note when adding to this file: "you" will always be replaced by the hallucinator's name
+ hallucinated_thoughts = file2list("code/modules/hallucinations/text_lists/hallucinated_thoughts.txt")
+
+/datum/controller/subsystem/hallucinations/proc/get_hallucination(var/mob/living/carbon/C)
+ var/list/candidates = list()
+ for(var/T in all_hallucinations)
+ var/datum/hallucination/H = new T
+ if(H.can_affect(C))
+ candidates += H
+ if(candidates.len)
+ var/datum/hallucination/D = pick(candidates)
+ return D
\ No newline at end of file
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 2e45b2ac64e..a6b7b72dc41 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -472,11 +472,6 @@ About the new airlock wires panel:
return
else /*if(src.justzap)*/
return
- else if(user.hallucination > 50 && prob(10) && src.operating == 0 && !user.is_diona() && !user.isSynthetic())
- to_chat(user, span("danger", "You feel a powerful shock course through your body!"))
- user.adjustHalLoss(10)
- user.stunned += 10
- return
..(user)
/obj/machinery/door/airlock/proc/isElectrified()
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 6095c5089c6..b367e22200e 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -1116,6 +1116,12 @@ var/global/list/obj/item/device/pda/PDAs = list()
t = sanitize(t)
//t = readd_quotes(t)
t = replace_characters(t, list(""" = "\""))
+ if(iscarbon(U) && t)
+ var/mob/living/carbon/C = U
+ if(C.hallucination >= 50 && prob(C.hallucination / 10)) //If you're really hallucinating, you might not be typing what you think you are
+ var/t_orig = t
+ t = pick(SShallucinations.hallucinated_phrases) //see carbon/hallucination.dm
+ log_pda("[usr] (PDA: [C.name]) typed \"[t_orig]\" then hallucination changed it to \"[t]\". Recipient was [P.owner]",ckey=key_name(C),ckey_target=key_name(P.owner))
if (!t || !istype(P))
return
if (!in_range(src, U) && loc != U)
diff --git a/code/game/sound.dm b/code/game/sound.dm
index a54dc244bb9..cb33f61ea0a 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -24,7 +24,7 @@
#define UNDERWATER 22
#define DRUGGED 23
#define DIZZY 24
-#define PSYCHOTIC 25
+
#define STANDARD_STATION STONEROOM
#define LARGE_ENCLOSED HANGAR
@@ -35,6 +35,7 @@
#define SMALL_SOFTFLOOR ROOM
#define ASTEROID CAVE
#define SPACE UNDERWATER
+#define PSYCHOTIC PARKING_LOT
//footsteps
var/list/blank_footstep = list('sound/effects/footstep/blank.ogg')
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
deleted file mode 100644
index 2df1bba6d45..00000000000
--- a/code/modules/flufftext/Hallucination.dm
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
-Ideas for the subtle effects of hallucination:
-
-Light up oxygen/phoron 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)
-
-*/
-
-mob/living/carbon/var
- image/halimage
- image/halbody
- obj/halitem
- hal_screwyhud = 0 //1 - critical, 2 - dead, 3 - oxygen indicator, 4 - toxin indicator
- handling_hal = 0
- hal_crit = 0
-
-mob/living/carbon/proc/handle_hallucinations()
- if(handling_hal) return
- handling_hal = 1
- while(client && hallucination > 20)
- sleep(rand(200,500)/(hallucination/25))
- var/halpick = rand(1,100)
- switch(halpick)
- if(0 to 15)
- //Screwy HUD
- hal_screwyhud = pick(1,2,3,3,4,4)
- spawn(rand(100,250))
- hal_screwyhud = 0
- if(16 to 25)
- //Strange items
- if(!halitem)
- halitem = new
- var/list/slots_free = list(ui_lhand,ui_rhand)
- if(l_hand) slots_free -= ui_lhand
- if(r_hand) slots_free -= ui_rhand
- if(istype(src,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = src
- 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.layer = 50
- switch(rand(1,6))
- if(1) //revolver
- halitem.icon = 'icons/obj/guns/revolver.dmi'
- halitem.icon_state = "revolver"
- halitem.name = "Revolver"
- if(2) //c4
- halitem.icon = 'icons/obj/assemblies.dmi'
- halitem.icon_state = "plastic-explosive0"
- halitem.name = "Mysterious Package"
- if(prob(25))
- halitem.icon_state = "c4small_1"
- if(3) //sword
- halitem.icon = 'icons/obj/weapons.dmi'
- halitem.icon_state = "sword1"
- halitem.name = "Sword"
- if(4) //stun baton
- halitem.icon = 'icons/obj/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"
- if(client) client.screen += halitem
- spawn(rand(100,250))
- if(client)
- client.screen -= halitem
- halitem = null
- if(26 to 40)
- //Flashes of danger
- if(!halimage)
- var/list/possible_points = list()
- for(var/turf/simulated/floor/F in view(src,world.view))
- possible_points += F
- if(possible_points.len)
- var/turf/simulated/floor/target = pick(possible_points)
-
- switch(rand(1,3))
- if(1)
- halimage = image('icons/turf/space.dmi',target,"[rand(1,25)]",TURF_LAYER)
- if(2)
- halimage = image('icons/effects/fire.dmi',target,"1",TURF_LAYER)
- if(3)
- halimage = image('icons/obj/assemblies.dmi',target,"plastic-explosive2",OBJ_LAYER+0.01)
-
-
- if(client) client.images += halimage
- spawn(rand(10,50)) //Only seen for a brief moment.
- if(client) client.images -= halimage
- halimage = null
-
-
- if(41 to 65)
- //Strange audio
- switch(rand(1,12))
- if(1) sound_to(src, 'sound/machines/airlock.ogg')
- if(2)
- if(prob(50)) sound_to(src, 'sound/effects/Explosion1.ogg')
- else sound_to(src, 'sound/effects/Explosion2.ogg')
- if(3) sound_to(src, 'sound/effects/explosionfar.ogg')
- if(4) sound_to(src, 'sound/effects/glass_break1.ogg')
- if(5) sound_to(src, 'sound/effects/glass_break2.ogg')
- if(6) sound_to(src, 'sound/effects/glass_break3.ogg')
- if(7) sound_to(src, 'sound/machines/twobeep.ogg')
- if(8) sound_to(src, 'sound/machines/windowdoor.ogg')
- if(9)
- //To make it more realistic, I added two gunshots (enough to kill)
- sound_to(src, 'sound/weapons/gunshot/gunshot1.ogg')
- spawn(rand(10,30))
- sound_to(src, 'sound/weapons/gunshot/gunshot1.ogg')
- if(10) sound_to(src, 'sound/weapons/smash.ogg')
- if(11)
- //Same as above, but with tasers.
- sound_to(src, 'sound/weapons/Taser.ogg')
- spawn(rand(10,30))
- sound_to(src, 'sound/weapons/Taser.ogg')
- //Rare audio
- if(12)
-//These sounds are (mostly) taken from Hidden: Source
- var/list/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')
- sound_to(src, pick(creepyasssounds))
- if(66 to 70)
- //Flashes of danger
- if(!halbody)
- var/list/possible_points = list()
- for(var/turf/simulated/floor/F in view(src,world.view))
- possible_points += F
- if(possible_points.len)
- var/turf/simulated/floor/target = pick(possible_points)
- switch(rand(1,4))
- if(1)
- halbody = image('icons/mob/human.dmi',target,"husk_l",TURF_LAYER)
- if(2,3)
- halbody = image('icons/mob/human.dmi',target,"husk_s",TURF_LAYER)
- if(4)
- halbody = image('icons/mob/npc/alien.dmi',target,"alienother",TURF_LAYER)
- // if(5)
- // halbody = image('xcomalien.dmi',target,"chryssalid",TURF_LAYER)
-
- if(client) client.images += halbody
- spawn(rand(50,80)) //Only seen for a brief moment.
- if(client) client.images -= halbody
- halbody = null
- if(71 to 72)
- //Fake death
-// src.sleeping_willingly = 1
- src.sleeping = 20
- hal_crit = 1
- hal_screwyhud = 1
- spawn(rand(50,100))
-// src.sleeping_willingly = 0
- src.sleeping = 0
- hal_crit = 0
- hal_screwyhud = 0
- handling_hal = 0
-
-
-
-
-/*obj/machinery/proc/mockpanel(list/buttons,start_txt,end_txt,list/mid_txts)
-
- if(!mocktxt)
-
- mocktxt = ""
-
- var/possible_txt = list("Launch Escape Pods","Self-Destruct Sequence","\[Swipe ID\]","De-Monkify",\
- "Reticulate Splines","Plasma","Open Valve","Lockdown","Nerf Airflow","Kill Traitor","Nihilism",\
- "OBJECTION!","Arrest Stephen Bowman","Engage Anti-Trenna Defenses","Increase Captain IQ","Retrieve Arms",\
- "Play Charades","Oxygen","Inject BeAcOs","Ninja Lizards","Limit Break","Build Sentry")
-
- if(mid_txts)
- while(mid_txts.len)
- var/mid_txt = pick(mid_txts)
- mocktxt += mid_txt
- mid_txts -= mid_txt
-
- while(buttons.len)
-
- var/button = pick(buttons)
-
- var/button_txt = pick(possible_txt)
-
- mocktxt += "[button_txt]
"
-
- buttons -= button
- possible_txt -= button_txt
-
- return start_txt + mocktxt + end_txt + "