diff --git a/SQL/database_changelog.txt.rej b/SQL/database_changelog.txt.rej
new file mode 100644
index 0000000000..1b201ec4aa
--- /dev/null
+++ b/SQL/database_changelog.txt.rej
@@ -0,0 +1,15 @@
+diff a/SQL/database_changelog.txt b/SQL/database_changelog.txt (rejected hunks)
+@@ -1,10 +1,10 @@
+ Any time you make a change to the schema files, remember to increment the database schema version. Generally increment the minor number, major should be reserved for significant changes to the schema. Both values go up to 255.
+
+-The latest database version is 3.1; The query to update the schema revision table is:
++The latest database version is 3.0; The query to update the schema revision table is:
+
+-UPDATE `schema_revision` SET major = 3, minor = 1 LIMIT 1;
++INSERT INTO `schema_revision` (`major`, `minor`) VALUES (3, 0);
+ or
+-UPDATE `SS13_schema_revision` SET major = 3, minor = 1 LIMIT 1;
++INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (3, 0);
+
+ ----------------------------------------------------
+
diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm
index 4cc0b5c158..b4b78673a1 100644
--- a/_maps/map_files/PubbyStation/PubbyStation.dmm
+++ b/_maps/map_files/PubbyStation/PubbyStation.dmm
@@ -49675,7 +49675,7 @@
/turf/open/floor/plating,
/area/maintenance/department/engine)
"cdF" = (
-/obj/structure/closet/secure_closet/miner,
+/obj/structure/closet/secure_closet/miner/unlocked,
/turf/open/floor/plating,
/area/shuttle/auxillary_base)
"cdG" = (
diff --git a/apc_repair.dmi b/apc_repair.dmi
new file mode 100644
index 0000000000..6e861eef61
Binary files /dev/null and b/apc_repair.dmi differ
diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm
index 82d030bf08..24e10a24d1 100644
--- a/code/__DEFINES/logging.dm
+++ b/code/__DEFINES/logging.dm
@@ -1,15 +1,16 @@
//Investigate logging defines
-#define INVESTIGATE_ATMOS "atmos"
-#define INVESTIGATE_BOTANY "botany"
-#define INVESTIGATE_CARGO "cargo"
-#define INVESTIGATE_EXPERIMENTOR "experimentor"
-#define INVESTIGATE_GRAVITY "gravity"
-#define INVESTIGATE_RECORDS "records"
-#define INVESTIGATE_SINGULO "singulo"
-#define INVESTIGATE_SUPERMATTER "supermatter"
-#define INVESTIGATE_TELESCI "telesci"
-#define INVESTIGATE_WIRES "wires"
+#define INVESTIGATE_ATMOS "atmos"
+#define INVESTIGATE_BOTANY "botany"
+#define INVESTIGATE_CARGO "cargo"
+#define INVESTIGATE_EXPERIMENTOR "experimentor"
+#define INVESTIGATE_GRAVITY "gravity"
+#define INVESTIGATE_RECORDS "records"
+#define INVESTIGATE_SINGULO "singulo"
+#define INVESTIGATE_SUPERMATTER "supermatter"
+#define INVESTIGATE_TELESCI "telesci"
+#define INVESTIGATE_WIRES "wires"
#define INVESTIGATE_PORTAL "portals"
+#define INVESTIGATE_HALLUCINATIONS "hallucinations"
//Individual logging defines
#define INDIVIDUAL_ATTACK_LOG "Attack log"
diff --git a/code/__DEFINES/logging.dm.rej b/code/__DEFINES/logging.dm.rej
new file mode 100644
index 0000000000..6a7a358a9c
--- /dev/null
+++ b/code/__DEFINES/logging.dm.rej
@@ -0,0 +1,9 @@
+diff a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm (rejected hunks)
+@@ -9,6 +9,7 @@
+ #define INVESTIGATE_SUPERMATTER "supermatter"
+ #define INVESTIGATE_TELESCI "telesci"
+ #define INVESTIGATE_WIRES "wires"
++#define INVESTIGATE_HALLUCINATIONS "hallucinations"
+
+ //Individual logging defines
+ #define INDIVIDUAL_ATTACK_LOG "Attack log"
diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm
index afa77b0207..3344afcaaa 100644
--- a/code/controllers/subsystem/blackbox.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -250,7 +250,7 @@ SUBSYSTEM_DEF(blackbox)
return 0
return value
-/datum/feedback_variable/proc/get_variable()
+/datum/feedback_variable/proc/get_variable()
return variable
/datum/feedback_variable/proc/set_details(text)
@@ -260,12 +260,12 @@ SUBSYSTEM_DEF(blackbox)
/datum/feedback_variable/proc/add_details(text)
if (istext(text))
if (!details)
- details = text
+ details = "\"[text]\""
else
- details += " [text]"
+ details += " | \"[text]\""
-/datum/feedback_variable/proc/get_details()
+/datum/feedback_variable/proc/get_details()
return details
/datum/feedback_variable/proc/get_parsed()
- return list(variable,value,details)
+ return list(variable,value,details)
\ No newline at end of file
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 16fbeb8462..ecaa70387b 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -169,7 +169,7 @@
/datum/action/item_action/toggle_firemode
name = "Toggle Firemode"
-
+
/datum/action/item_action/rcl
name = "Change Cable Color"
button_icon_state = "rcl_rainbow"
@@ -490,7 +490,8 @@
var/obj/effect/proc_holder/spell/S = target
S.action = src
name = S.name
- icon_icon = S.action_icon
+ desc = S.desc
+ button_icon = S.action_icon
button_icon_state = S.action_icon_state
background_icon_state = S.action_background_icon_state
button.name = name
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index e519cf6af3..87c6ad7334 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -929,6 +929,26 @@
manipulate_organs(C)
href_list["datumrefresh"] = href_list["editorgans"]
+ else if(href_list["hallucinate"])
+ if(!check_rights(0))
+ return
+
+ var/mob/living/carbon/C = locate(href_list["hallucinate"]) in GLOB.mob_list
+ if(!istype(C))
+ to_chat(usr, "This can only be done to instances of type /mob/living/carbon")
+ return
+
+ var/list/hallucinations = subtypesof(/datum/hallucination)
+ var/result = input(usr, "Choose the hallucination to apply","Send Hallucination") as null|anything in hallucinations
+ if(!usr)
+ return
+ if(QDELETED(C))
+ to_chat(usr, "Mob doesn't exist anymore")
+ return
+
+ if(result)
+ new result(C, TRUE)
+
else if(href_list["makehuman"])
if(!check_rights(R_SPAWN))
return
diff --git a/code/datums/diseases/advance/symptoms/hallucigen.dm b/code/datums/diseases/advance/symptoms/hallucigen.dm
index f913d49573..9bfd5b0baf 100644
--- a/code/datums/diseases/advance/symptoms/hallucigen.dm
+++ b/code/datums/diseases/advance/symptoms/hallucigen.dm
@@ -59,4 +59,4 @@ Bonus
else
if(prob(base_message_chance))
to_chat(M, "[pick("Oh, your head...", "Your head pounds.", "They're everywhere! Run!", "Something in the shadows...")]")
- M.hallucination += (15 * power)
+ M.hallucination += (45 * power)
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 69b730d574..4c80f53b94 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -538,7 +538,7 @@ structure_check() searches for nearby cultist structures required for the invoca
mob_to_revive = input(user, "Choose a cultist to revive.", "Cultist to Revive") as null|anything in potential_revive_mobs
else
mob_to_revive = potential_revive_mobs[1]
- if(!src || QDELETED(src) || rune_in_use || !validness_checks(mob_to_revive, user))
+ if(QDELETED(src) || !validness_checks(mob_to_revive, user))
rune_in_use = FALSE
return
if(user.name == "Herbert West")
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 2a0cd230d5..790319e362 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -120,7 +120,7 @@
var/list/datum/game_mode/runnable_modes = config.get_runnable_midround_modes(living_crew.len)
var/list/datum/game_mode/usable_modes = list()
for(var/datum/game_mode/G in runnable_modes)
- if(G.reroll_friendly)
+ if(G.reroll_friendly && living_crew >= G.required_players)
usable_modes += G
else
qdel(G)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 624d853808..94047727bc 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -233,9 +233,13 @@
return
else /*if(src.justzap)*/
return
- else if(user.hallucination > 50 && ishuman(user) && prob(10) && src.operating == FALSE)
- hallucinate_shock(user)
- return
+ else if(user.hallucinating() && ishuman(user) && prob(4) && !operating)
+ var/mob/living/carbon/human/H = user
+ if(H.gloves)
+ var/obj/item/clothing/gloves/G = H.gloves
+ if(G.siemens_coefficient)//not insulated
+ hallucinate_shock(H)
+ return
if (cyclelinkedairlock)
if (!shuttledocked && !emergency && !cyclelinkedairlock.shuttledocked && !cyclelinkedairlock.emergency && allowed(user))
if(cyclelinkedairlock.operating)
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index c9b524836f..005d9c7595 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -127,7 +127,7 @@
var/pure_red = list(0,0,0,0,0,0,0,0,0,1,0,0)
spawn(0)
- new /obj/effect/hallucination/delusion(victim.loc,victim,"demon",duration,0)
+ new /datum/hallucination/delusion(victim, TRUE, "demon",duration,0)
var/obj/item/weapon/twohanded/required/chainsaw/doomslayer/chainsaw = new(victim.loc)
chainsaw.flags |= NODROP
diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm
index 0e6d6cc66f..1829538a01 100755
--- a/code/game/objects/items/weapons/flamethrower.dm
+++ b/code/game/objects/items/weapons/flamethrower.dm
@@ -26,6 +26,7 @@
var/create_full = FALSE
var/create_with_tank = FALSE
var/igniter_type = /obj/item/device/assembly/igniter
+ trigger_guard = TRIGGER_GUARD_NORMAL
/obj/item/weapon/flamethrower/Destroy()
if(weldtool)
@@ -69,12 +70,7 @@
if(flag)
return // too close
if(ishuman(user))
- var/mob/living/carbon/human/H = user
- if(H.dna.check_mutation(HULK))
- to_chat(user, "Your meaty finger is much too large for the trigger guard!")
- return
- if(NOGUNS in H.dna.species.species_traits)
- to_chat(user, "Your fingers don't fit in the trigger guard!")
+ if(!can_trigger_gun(user))
return
if(user && user.get_active_held_item() == src) // Make sure our user is still holding us
var/turf/target_turf = get_turf(target)
diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm
index 238022d209..8e1cf3301e 100644
--- a/code/game/objects/items/weapons/holy_weapons.dm
+++ b/code/game/objects/items/weapons/holy_weapons.dm
@@ -320,6 +320,8 @@
desc = "Particularly twisted dieties grant gifts of dubious value."
icon_state = "arm_blade"
item_state = "arm_blade"
+ lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
flags = ABSTRACT | NODROP
w_class = WEIGHT_CLASS_HUGE
sharpness = IS_SHARP
diff --git a/code/game/objects/items/weapons/pneumaticCannon.dm b/code/game/objects/items/weapons/pneumaticCannon.dm
index d2f5714d5e..90c0eceaad 100644
--- a/code/game/objects/items/weapons/pneumaticCannon.dm
+++ b/code/game/objects/items/weapons/pneumaticCannon.dm
@@ -26,6 +26,7 @@
var/fire_mode = PCANNON_FIREALL
var/automatic = FALSE
var/clumsyCheck = TRUE
+ trigger_guard = TRIGGER_GUARD_NORMAL
/obj/item/weapon/pneumatic_cannon/CanItemAutoclick()
return automatic
@@ -108,11 +109,7 @@
if(!istype(user) && !target)
return
var/discharge = 0
- if(user.dna.check_mutation(HULK))
- to_chat(user, "Your meaty finger is much too large for the trigger guard!")
- return
- if(NOGUNS in user.dna.species.species_traits)
- to_chat(user, "Your fingers don't fit in the trigger guard!")
+ if(!can_trigger_gun(user))
return
if(!loadedItems || !loadedWeightClass)
to_chat(user, "\The [src] has nothing loaded.")
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index bb723ffb33..53bfe5d64c 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -1,4 +1,5 @@
/obj/item/weapon
+ var/trigger_guard = TRIGGER_GUARD_NONE
/obj/item/weapon/banhammer
desc = "A banhammer"
@@ -584,3 +585,8 @@
throwforce = 0
flags = DROPDEL | ABSTRACT
attack_verb = list("bopped")
+
+/obj/item/weapon/proc/can_trigger_gun(mob/living/user)
+ if(!user.can_use_guns(src))
+ return FALSE
+ return TRUE
\ No newline at end of file
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index e478ff5d92..d141f71b7a 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -1,12 +1,13 @@
/obj/structure/flora
resistance_flags = FLAMMABLE
+ obj_integrity = 150
max_integrity = 150
- anchored = TRUE
+ anchored = 1
//trees
/obj/structure/flora/tree
name = "tree"
- density = TRUE
+ density = 1
pixel_x = -16
layer = FLY_LAYER
var/cut = FALSE
@@ -25,7 +26,7 @@
playsound(get_turf(src), 'sound/effects/meteorimpact.ogg', 100 , 0, 0)
icon = 'icons/obj/flora/pinetrees.dmi'
icon_state = "tree_stump"
- density = FALSE
+ density = 0
pixel_x = -16
name += " stump"
cut = TRUE
@@ -45,14 +46,14 @@
/obj/structure/flora/tree/pine/Initialize()
icon_state = "pine_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/tree/pine/xmas
name = "xmas tree"
icon_state = "pine_c"
/obj/structure/flora/tree/pine/xmas/Initialize()
- ..()
+ . = ..()
icon_state = "pine_c"
/obj/structure/flora/tree/dead
@@ -64,7 +65,7 @@
icon_state = "palm1"
/obj/structure/flora/tree/palm/Initialize()
- ..()
+ . = ..()
icon_state = pick("palm1","palm2")
pixel_x = 0
@@ -76,7 +77,7 @@
/obj/structure/flora/tree/dead/Initialize()
icon_state = "tree_[rand(1, 6)]"
- ..()
+ . = ..()
/obj/structure/flora/tree/jungle
name = "tree"
@@ -88,12 +89,7 @@
/obj/structure/flora/tree/jungle/Initialize()
icon_state = "[icon_state][rand(1, 6)]"
- ..()
-
-/obj/structure/flora/tree/jungle/small
- pixel_y = 0
- pixel_x = -32
- icon = 'icons/obj/flora/jungletreesmall.dmi'
+ . = ..()
//grass
/obj/structure/flora/grass
@@ -106,7 +102,7 @@
/obj/structure/flora/grass/brown/Initialize()
icon_state = "snowgrass[rand(1, 3)]bb"
- ..()
+ . = ..()
/obj/structure/flora/grass/green
@@ -114,14 +110,14 @@
/obj/structure/flora/grass/green/Initialize()
icon_state = "snowgrass[rand(1, 3)]gb"
- ..()
+ . = ..()
/obj/structure/flora/grass/both
icon_state = "snowgrassall1"
/obj/structure/flora/grass/both/Initialize()
icon_state = "snowgrassall[rand(1, 3)]"
- ..()
+ . = ..()
//bushes
@@ -129,11 +125,11 @@
name = "bush"
icon = 'icons/obj/flora/snowflora.dmi'
icon_state = "snowbush1"
- anchored = TRUE
+ anchored = 1
/obj/structure/flora/bush/Initialize()
icon_state = "snowbush[rand(1, 6)]"
- ..()
+ . = ..()
//newbushes
@@ -145,112 +141,112 @@
/obj/structure/flora/ausbushes/Initialize()
if(icon_state == "firstbush_1")
icon_state = "firstbush_[rand(1, 4)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/reedbush
icon_state = "reedbush_1"
/obj/structure/flora/ausbushes/reedbush/Initialize()
icon_state = "reedbush_[rand(1, 4)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/leafybush
icon_state = "leafybush_1"
/obj/structure/flora/ausbushes/leafybush/Initialize()
icon_state = "leafybush_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/palebush
icon_state = "palebush_1"
/obj/structure/flora/ausbushes/palebush/Initialize()
icon_state = "palebush_[rand(1, 4)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/stalkybush
icon_state = "stalkybush_1"
/obj/structure/flora/ausbushes/stalkybush/Initialize()
icon_state = "stalkybush_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/grassybush
icon_state = "grassybush_1"
/obj/structure/flora/ausbushes/grassybush/Initialize()
icon_state = "grassybush_[rand(1, 4)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/fernybush
icon_state = "fernybush_1"
/obj/structure/flora/ausbushes/fernybush/Initialize()
icon_state = "fernybush_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/sunnybush
icon_state = "sunnybush_1"
/obj/structure/flora/ausbushes/sunnybush/Initialize()
icon_state = "sunnybush_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/genericbush
icon_state = "genericbush_1"
/obj/structure/flora/ausbushes/genericbush/Initialize()
icon_state = "genericbush_[rand(1, 4)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/pointybush
icon_state = "pointybush_1"
/obj/structure/flora/ausbushes/pointybush/Initialize()
icon_state = "pointybush_[rand(1, 4)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/lavendergrass
icon_state = "lavendergrass_1"
/obj/structure/flora/ausbushes/lavendergrass/Initialize()
icon_state = "lavendergrass_[rand(1, 4)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/ywflowers
icon_state = "ywflowers_1"
/obj/structure/flora/ausbushes/ywflowers/Initialize()
icon_state = "ywflowers_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/brflowers
icon_state = "brflowers_1"
/obj/structure/flora/ausbushes/brflowers/Initialize()
icon_state = "brflowers_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/ppflowers
icon_state = "ppflowers_1"
/obj/structure/flora/ausbushes/ppflowers/Initialize()
icon_state = "ppflowers_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/sparsegrass
icon_state = "sparsegrass_1"
/obj/structure/flora/ausbushes/sparsegrass/Initialize()
icon_state = "sparsegrass_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/ausbushes/fullgrass
icon_state = "fullgrass_1"
/obj/structure/flora/ausbushes/fullgrass/Initialize()
icon_state = "fullgrass_[rand(1, 3)]"
- ..()
+ . = ..()
/obj/item/weapon/twohanded/required/kirbyplants
name = "potted plant"
@@ -308,10 +304,10 @@
desc = "A volcanic rock"
icon = 'icons/obj/flora/rocks.dmi'
resistance_flags = FIRE_PROOF
- density = TRUE
+ density = 1
/obj/structure/flora/rock/Initialize()
- ..()
+ . = ..()
icon_state = "[icon_state][rand(1,3)]"
/obj/structure/flora/rock/pile
@@ -319,7 +315,7 @@
desc = "A pile of rocks"
/obj/structure/flora/rock/pile/Initialize()
- ..()
+ . = ..()
icon_state = "[icon_state][rand(1,3)]"
//Jungle grass
@@ -333,7 +329,7 @@
/obj/structure/flora/grass/jungle/Initialize()
icon_state = "[icon_state][rand(1, 5)]"
- ..()
+ . = ..()
/obj/structure/flora/grass/jungle/b
icon_state = "grassb"
@@ -348,7 +344,7 @@
density = FALSE
/obj/structure/flora/rock/jungle/Initialize()
- ..()
+ . = ..()
icon_state = "[initial(icon_state)][rand(1,5)]"
@@ -361,7 +357,7 @@
/obj/structure/flora/junglebush/Initialize()
icon_state = "[icon_state][rand(1, 3)]"
- ..()
+ . = ..()
/obj/structure/flora/junglebush/b
icon_state = "bushb"
diff --git a/code/game/objects/structures/flora.dm.rej b/code/game/objects/structures/flora.dm.rej
new file mode 100644
index 0000000000..8257ec2122
--- /dev/null
+++ b/code/game/objects/structures/flora.dm.rej
@@ -0,0 +1,28 @@
+diff a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm (rejected hunks)
+@@ -89,7 +89,7 @@
+
+ /obj/structure/flora/tree/jungle/Initialize()
+ icon_state = "[icon_state][rand(1, 6)]"
+- ..()
++ . = ..()
+
+ //grass
+ /obj/structure/flora/grass
+@@ -307,7 +307,7 @@
+ density = 1
+
+ /obj/structure/flora/rock/Initialize()
+- ..()
++ . = ..()
+ icon_state = "[icon_state][rand(1,3)]"
+
+ /obj/structure/flora/rock/pile
+@@ -381,5 +381,5 @@
+ pixel_y = -16
+
+ /obj/structure/flora/rock/pile/largejungle/Initialize()
+- ..()
+- icon_state = "[initial(icon_state)][rand(1,3)]"
+\ No newline at end of file
++ . = ..()
++ icon_state = "[initial(icon_state)][rand(1,3)]"
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 82ff907a04..a990530056 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -18,6 +18,7 @@
var/sound
/turf/open/indestructible/sound/Entered(var/mob/AM)
+ ..()
if(istype(AM))
playsound(src,sound,50,1)
diff --git a/code/game/turfs/simulated/chasm.dm b/code/game/turfs/simulated/chasm.dm
index 968e782984..a51240c969 100644
--- a/code/game/turfs/simulated/chasm.dm
+++ b/code/game/turfs/simulated/chasm.dm
@@ -22,6 +22,7 @@
return
/turf/open/chasm/Entered(atom/movable/AM)
+ ..()
START_PROCESSING(SSobj, src)
drop_stuff(AM)
diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm
index c7e6973c4d..b877fa9eac 100644
--- a/code/game/turfs/space/transit.dm
+++ b/code/game/turfs/space/transit.dm
@@ -25,6 +25,7 @@
dir = EAST
/turf/open/space/transit/Entered(atom/movable/AM, atom/OldLoc)
+ ..()
if(!locate(/obj/structure/lattice) in src)
throw_atom(AM)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index b204406d05..4342d12714 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -160,6 +160,7 @@
return TRUE //Nothing found to block so return success!
/turf/Entered(atom/movable/AM)
+ ..()
if(explosion_level && AM.ex_check(explosion_id))
AM.ex_act(explosion_level)
diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm
index 54feb11532..2ca0593e8d 100644
--- a/code/modules/admin/admin_investigate.dm
+++ b/code/modules/admin/admin_investigate.dm
@@ -5,7 +5,7 @@
F << "[time_stamp()] \ref[src] ([x],[y],[z]) || [src] [message]
"
-/client/proc/investigate_show(subject in list("hrefs","notes, memos, watchlist", INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY) )
+/client/proc/investigate_show(subject in list("hrefs","notes, memos, watchlist", INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS) )
set name = "Investigate"
set category = "Admin"
if(!holder)
diff --git a/code/modules/admin/admin_investigate.dm.rej b/code/modules/admin/admin_investigate.dm.rej
new file mode 100644
index 0000000000..4f8c142c61
--- /dev/null
+++ b/code/modules/admin/admin_investigate.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm (rejected hunks)
+@@ -5,7 +5,7 @@
+ F << "[time_stamp()] \ref[src] ([x],[y],[z]) || [src] [message]
"
+
+
+-/client/proc/investigate_show(subject in list("hrefs","notes, memos, watchlist", INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY) )
++/client/proc/investigate_show(subject in list("hrefs","notes, memos, watchlist", INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS) )
+ set name = "Investigate"
+ set category = "Admin"
+ if(!holder)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 818b0d6f09..81925756fb 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -6,7 +6,7 @@
log_admin("[key_name(usr)] tried to use the admin panel without authorization.")
return
if(href_list["ahelp"])
- if(!check_rights(R_ADMIN))
+ if(!check_rights(R_ADMIN, TRUE))
return
var/ahelp_ref = href_list["ahelp"]
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index d733dfc3fb..a45536430a 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -189,8 +189,9 @@
#define CONNECTED 2
#define EMPTY 4
#define LOW 8
-#define FULL 16
-#define DANGER 32
+#define MEDIUM 16
+#define FULL 32
+#define DANGER 64
/obj/machinery/portable_atmospherics/canister/update_icon()
if(stat & BROKEN)
cut_overlays()
@@ -207,9 +208,11 @@
var/pressure = air_contents.return_pressure()
if(pressure < 10)
update |= EMPTY
- else if(pressure < ONE_ATMOSPHERE)
+ else if(pressure < 5 * ONE_ATMOSPHERE)
update |= LOW
- else if(pressure < 15 * ONE_ATMOSPHERE)
+ else if(pressure < 10 * ONE_ATMOSPHERE)
+ update |= MEDIUM
+ else if(pressure < 40 * ONE_ATMOSPHERE)
update |= FULL
else
update |= DANGER
@@ -222,9 +225,9 @@
add_overlay("can-open")
if(update & CONNECTED)
add_overlay("can-connector")
- if(update & EMPTY)
+ if(update & LOW)
add_overlay("can-o0")
- else if(update & LOW)
+ else if(update & MEDIUM)
add_overlay("can-o1")
else if(update & FULL)
add_overlay("can-o2")
@@ -234,6 +237,7 @@
#undef CONNECTED
#undef EMPTY
#undef LOW
+#undef MEDIUM
#undef FULL
#undef DANGER
diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm
index 1cf215b9f2..7926cfc34d 100644
--- a/code/modules/cargo/console.dm
+++ b/code/modules/cargo/console.dm
@@ -145,7 +145,7 @@
var/reason = ""
if(requestonly)
- reason = stripped_input("Reason:", name, "") as text|null
+ reason = stripped_input("Reason:", name, "")
if(isnull(reason) || ..())
return
diff --git a/code/modules/cargo/console.dm.rej b/code/modules/cargo/console.dm.rej
new file mode 100644
index 0000000000..e8798f179a
--- /dev/null
+++ b/code/modules/cargo/console.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm (rejected hunks)
+@@ -145,7 +145,7 @@
+
+ var/reason = ""
+ if(requestonly)
+- reason = stripped_input("Reason:", name, "") as text|null
++ reason = stripped_input("Reason:", name, "")
+ if(isnull(reason) || ..())
+ return
+
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index c1b0a387e4..2b8fe4b064 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -11,51 +11,83 @@ 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/handling_hal = 0
+ 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(handling_hal)
+ if(world.time < next_hallucination)
return
- //Least obvious
- var/list/minor = list("sounds"=25,"bolts_minor"=5,"whispers"=15,"message"=10,"hudscrew"=15)
- //Something's wrong here
- var/list/medium = list("fake_alert"=15,"items"=10,"items_other"=10,"dangerflash"=10,"bolts"=5,"flood"=5,"husks"=10,"battle"=15,"self_delusion"=10)
- //AAAAH
- var/list/major = list("fake"=20,"death"=10,"xeno"=10,"singulo"=10,"borer"=10,"delusion"=20,"koolaid"=10)
+ 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)
- handling_hal = 1
- while(hallucination > 20)
- sleep(rand(200,500)/(hallucination/25))
- if(prob(20))
- continue
- var/list/current = list()
- switch(rand(100))
- if(1 to 50)
- current = minor
- if(51 to 85)
- current = medium
- if(86 to 100)
- current = major
+/mob/living/carbon/proc/set_screwyhud(hud_type)
+ hal_screwyhud = hud_type
+ update_health_hud()
- var/halpick = pickweight(current)
+/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
- hallucinate(halpick)
- handling_hal = 0
+/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/proc/wake_and_restore()
- target.hal_screwyhud = SCREWYHUD_NONE
- target.SetSleeping(0)
-
/obj/effect/hallucination/simple
var/image_icon = 'icons/mob/alien.dmi'
var/image_state = "alienh_pounce"
@@ -64,13 +96,14 @@ Gunshots/explosions/opening doors/less rare audio (done)
var/col_mod = null
var/image/current_image = null
var/image_layer = MOB_LAYER
- var/active = 1 //qdelery
+ 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
+ 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)
@@ -82,10 +115,12 @@ Gunshots/explosions/opening doors/less rare audio (done)
/obj/effect/hallucination/simple/proc/Show(update=1)
if(active)
- if(target.client) target.client.images.Remove(current_image)
+ if(target.client)
+ target.client.images.Remove(current_image)
if(update)
current_image = GetImage()
- if(target.client) target.client.images |= current_image
+ 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
@@ -101,36 +136,39 @@ Gunshots/explosions/opening doors/less rare audio (done)
Show()
/obj/effect/hallucination/simple/Destroy()
- if(target.client) target.client.images.Remove(current_image)
- active = 0
+ 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/hallucination/fake_flood
+/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
-/obj/effect/hallucination/fake_flood/Initialize(mapload, var/mob/living/carbon/T)
+/datum/hallucination/fake_flood/New(mob/living/carbon/T, forced = TRUE)
..()
- target = T
for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target))
if(!U.welded)
- src.loc = U.loc
+ center = get_turf(U)
break
- flood_images += image(image_icon,src,image_state,MOB_LAYER)
- flood_turfs += get_turf(src.loc)
+ 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)
-/obj/effect/hallucination/fake_flood/process()
+/datum/hallucination/fake_flood/process()
if(next_expand <= world.time)
radius++
if(radius > FAKE_FLOOD_MAX_RADIUS)
@@ -138,10 +176,10 @@ Gunshots/explosions/opening doors/less rare audio (done)
return
Expand()
if((get_turf(target) in flood_turfs) && !target.internal)
- target.hallucinate("fake_alert", "tox_in_air")
+ new /datum/hallucination/fake_alert(target, TRUE, "tox_in_air")
next_expand = world.time + FAKE_FLOOD_EXPAND_TIME
-/obj/effect/hallucination/fake_flood/proc/Expand()
+/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)
@@ -152,7 +190,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
if(target.client)
target.client.images |= flood_images
-/obj/effect/hallucination/fake_flood/Destroy()
+/datum/hallucination/fake_flood/Destroy()
STOP_PROCESSING(SSobj, src)
qdel(flood_turfs)
flood_turfs = list()
@@ -167,7 +205,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
image_icon = 'icons/mob/alien.dmi'
image_state = "alienh_pounce"
-/obj/effect/hallucination/simple/xeno/Initialize(mapload, var/mob/living/carbon/T)
+/obj/effect/hallucination/simple/xeno/Initialize(mapload, mob/living/carbon/T)
..()
name = "alien hunter ([rand(1, 1000)])"
@@ -177,18 +215,19 @@ Gunshots/explosions/opening doors/less rare audio (done)
target.Knockdown(100)
target.visible_message("[target] flails around wildly.","[name] pounces on you!")
-/obj/effect/hallucination/xeno_attack
+/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
-/obj/effect/hallucination/xeno_attack/Initialize(mapload, var/mob/living/carbon/T)
+/datum/hallucination/xeno_attack/New(mob/living/carbon/T, forced = TRUE)
..()
- target = T
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)
@@ -200,7 +239,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
sleep(10)
var/xeno_name = xeno.name
to_chat(target, "[xeno_name] begins climbing into the ventilation system...")
- sleep(10)
+ sleep(30)
qdel(xeno)
to_chat(target, "[xeno_name] scrambles into the ventilation ducts!")
qdel(src)
@@ -209,7 +248,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
image_icon = 'icons/mob/animal.dmi'
image_state = "clown"
-/obj/effect/hallucination/simple/clown/Initialize(mapload, var/mob/living/carbon/T,duration)
+/obj/effect/hallucination/simple/clown/Initialize(mapload, mob/living/carbon/T, duration)
..(loc, T)
name = pick(GLOB.clown_names)
QDEL_IN(src,duration)
@@ -217,59 +256,27 @@ Gunshots/explosions/opening doors/less rare audio (done)
/obj/effect/hallucination/simple/clown/scary
image_state = "scary_clown"
-/obj/effect/hallucination/simple/borer
- image_icon = 'icons/mob/borer.dmi'
- image_state = "brainslug"
-
-/obj/effect/hallucination/borer
- //A borer unconsciouss you and crawls in your ear
- var/obj/machinery/atmospherics/components/unary/vent_pump/pump = null
- var/obj/effect/hallucination/simple/borer/borer = null
-
-/obj/effect/hallucination/borer/Initialize(mapload, var/mob/living/carbon/T)
- ..()
- target = T
- for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target))
- if(!U.welded)
- pump = U
- break
- if(pump)
- borer = new(pump.loc,target)
- for(var/i=0, i<11, i++)
- walk_to(borer, get_step(borer, get_cardinal_dir(borer, T)))
- if(borer.Adjacent(T))
- to_chat(T, "You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.")
- T.Stun(80)
- sleep(50)
- qdel(borer)
- sleep(rand(60, 90))
- to_chat(T, "Primary [rand(1000,9999)] states: [pick("Hello","Hi","You're my slave now!","Don't try to get rid of me...")]")
- break
- sleep(4)
- if(!QDELETED(borer))
- qdel(borer)
- qdel(src)
-
/obj/effect/hallucination/simple/bubblegum
name = "Bubblegum"
image_icon = 'icons/mob/lavaland/96x96megafauna.dmi'
image_state = "bubblegum"
px = -32
-/obj/effect/hallucination/oh_yeah
+/datum/hallucination/oh_yeah
var/obj/effect/hallucination/simple/bubblegum/bubblegum
var/image/fakebroken
var/image/fakerune
+ cost = 75
-/obj/effect/hallucination/oh_yeah/Initialize(mapload, var/mob/living/carbon/T)
+/datum/hallucination/oh_yeah/New(mob/living/carbon/T, forced = TRUE)
. = ..()
- target = T
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)
@@ -283,7 +290,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
bubblegum = new(wall, target)
addtimer(CALLBACK(src, .proc/bubble_attack, landing), 10)
-/obj/effect/hallucination/oh_yeah/proc/bubble_attack(turf/landing)
+/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))
@@ -301,7 +308,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
sleep(30)
qdel(src)
-/obj/effect/hallucination/oh_yeah/Destroy()
+/datum/hallucination/oh_yeah/Destroy()
if(target.client)
target.client.images.Remove(fakebroken)
target.client.images.Remove(fakerune)
@@ -310,23 +317,24 @@ Gunshots/explosions/opening doors/less rare audio (done)
QDEL_NULL(bubblegum)
return ..()
-/obj/effect/hallucination/singularity_scare
+/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
-/obj/effect/hallucination/singularity_scare/Initialize(mapload, var/mob/living/carbon/T)
+/datum/hallucination/singularity_scare/New(mob/living/carbon/T, forced = TRUE)
..()
- target = T
var/turf/start = get_turf(T)
var/screen_border = pick(SOUTH,EAST,WEST,NORTH)
- for(var/i = 0,i<11,i++)
+ 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 = 0,i<11,i++)
- sleep(5)
- s.loc = get_step(get_turf(s),get_dir(s,target))
+ 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)
@@ -337,64 +345,72 @@ Gunshots/explosions/opening doors/less rare audio (done)
image_layer = MASSIVE_OBJ_LAYER
px = -96
py = -96
- var/obj/effect/hallucination/singularity_scare/parent
+ 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.hal_screwyhud = SCREWYHUD_CRIT
+ target.set_screwyhud(SCREWYHUD_DEAD)
target.SetUnconscious(160)
- addtimer(CALLBACK(parent, .proc/wake_and_restore), rand(30, 50))
+ addtimer(CALLBACK(parent, /datum/hallucination/.proc/wake_and_restore), rand(30, 50))
-/obj/effect/hallucination/battle
+/datum/hallucination/battle
+ cost = 15
-/obj/effect/hallucination/battle/Initialize(mapload, var/mob/living/carbon/T)
+/datum/hallucination/battle/New(mob/living/carbon/T, forced = TRUE, battle_type)
..()
- target = T
var/hits = rand(3,6)
- switch(rand(1,5))
- if(1) //Laser fight
- for(var/i=0,i...wabbajack...wabbajack...")
- target.playsound_local(target,'sound/magic/staff_change.ogg', 50, 1, -1)
+ if(wabbajack)
+ to_chat(target, "...wabbajack...wabbajack...")
+ target.playsound_local(target,'sound/magic/staff_change.ogg', 50, 1)
delusion = A
target.client.images |= A
QDEL_IN(src, duration)
-/obj/effect/hallucination/self_delusion/Destroy()
+/datum/hallucination/self_delusion/Destroy()
if(target.client)
target.client.images.Remove(delusion)
return ..()
-/obj/effect/hallucination/fakeattacker/Initialize(mapload, var/mob/living/carbon/T)
+/datum/hallucination/fakeattacker/New(mob/living/carbon/T, forced = TRUE)
..()
- target = T
var/mob/living/carbon/human/clone = null
var/clone_weapon = null
@@ -523,6 +541,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
if(H.stat || H.lying)
continue
clone = H
+ feedback_details += "Clone of: [H.real_name]"
break
if(!clone)
@@ -604,7 +623,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
for(var/mob/O in oviewers(world.view , my_target))
to_chat(O, "[my_target] stumbles around.")
-/obj/effect/fake_attacker/Initialize(mapload, var/mob/living/carbon/T)
+/obj/effect/fake_attacker/Initialize(mapload, mob/living/carbon/T)
..()
my_target = T
QDEL_IN(src, 300)
@@ -651,7 +670,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
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, -1)
+ 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))
@@ -674,14 +693,15 @@ Gunshots/explosions/opening doors/less rare audio (done)
QDEL_IN(O, 300)
-/obj/effect/hallucination/bolts
+/datum/hallucination/bolts
var/list/doors = list()
+ cost = 25
-/obj/effect/hallucination/bolts/Initialize(mapload, var/mob/living/carbon/T,var/door_number=-1) //-1 for severe, 1-2 for subtle
+/datum/hallucination/bolts/New(mob/living/carbon/T, forced, door_number=-1) //-1 for severe, 1-2 for subtle
..()
- target = T
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
@@ -700,13 +720,30 @@ Gunshots/explosions/opening doors/less rare audio (done)
sleep(rand(6,12))
qdel(src)
-/obj/effect/hallucination/whispers
+/datum/hallucination/whispers
+ cost = 15
-/obj/effect/hallucination/whispers/Initialize(mapload, var/mob/living/carbon/T)
+/datum/hallucination/whispers/New(mob/living/carbon/T, forced = TRUE)
..()
- target = T
- var/speak_messages = list("I'm watching you...","[target.first_name()]!","Get out!","Kchck-Chkck? Kchchck!","Did you hear that?","What did you do ?","Why?","Give me that!","HELP!!","EI NATH!!", "RUN!!", "Kill me!","O bidai nabora se'sma!", "[text2ratvar("Divinity, grant...")]")
- var/radio_messages = list("Xenos!","Singularity loose!","Comms down!","They are arming the nuke!","They butchered Ian!","H-help!","[pick("Cult", "Wizard", "Ling", "Ops", "Revenant", "Murderer", "Harm", "I hear flashing", "Help")] in [pick(GLOB.teleportlocs)][prob(50)?"!":"!!"]","Where's [target.first_name()]?","Call the shuttle!","AI rogue!!")
+ 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
@@ -722,7 +759,9 @@ Gunshots/explosions/opening doors/less rare audio (done)
people += H
if(person) //Basic talk
var/image/speech_overlay = image('icons/mob/talk.dmi', person, "default0", layer = ABOVE_MOB_LAYER)
- to_chat(target, target.compose_message(person,understood_language,pick(speak_messages),null,person.get_spans()))
+ var/message = target.compose_message(person,understood_language,pick(speak_messages),null,person.get_spans())
+ feedback_details += "Type: Talk, Source: [person.real_name], Message: [message]"
+ to_chat(target, message)
if(target.client)
target.client.images |= speech_overlay
sleep(30)
@@ -732,312 +771,381 @@ Gunshots/explosions/opening doors/less rare audio (done)
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)
-/obj/effect/hallucination/message
+/datum/hallucination/message
+ cost = 15
-/obj/effect/hallucination/message/Initialize(mapload, var/mob/living/carbon/T)
+/datum/hallucination/message/New(mob/living/carbon/T, forced = TRUE)
..()
- target = T
var/chosen = pick("The light burns you!", \
"You don't feel like yourself.", \
- "Unknown has punched [target]!", \
"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.", \
- "You feel faint.", \
- "You hear a strange, alien voice in your head...[pick("Hiss","Ssss")]", \
- "You can see...everything!")
+ //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)
-/mob/living/carbon/proc/hallucinate(hal_type, specific) // specific is used to specify a particular hallucination
- set waitfor = 0
- switch(hal_type)
- if("xeno")
- new /obj/effect/hallucination/xeno_attack(src.loc,src)
- if("borer")
- new /obj/effect/hallucination/borer(src.loc,src)
- if("singulo")
- new /obj/effect/hallucination/singularity_scare(src.loc,src)
- if("koolaid")
- new /obj/effect/hallucination/oh_yeah(src.loc,src)
- if("battle")
- new /obj/effect/hallucination/battle(src.loc,src)
- if("flood")
- new /obj/effect/hallucination/fake_flood(src.loc,src)
- if("delusion")
- new /obj/effect/hallucination/delusion(src.loc,src)
- if("self_delusion")
- new /obj/effect/hallucination/self_delusion(src.loc,src)
- if("fake")
- new /obj/effect/hallucination/fakeattacker(src.loc,src)
- if("bolts")
- new /obj/effect/hallucination/bolts(src.loc,src)
- if("bolts_minor")
- new /obj/effect/hallucination/bolts(src.loc,src,rand(1,2))
- if("whispers")
- new /obj/effect/hallucination/whispers(src.loc,src)
- if("message")
- new /obj/effect/hallucination/message(src.loc,src)
- if("items_other")
- new /obj/effect/hallucination/items_other(src.loc,src)
- if("sounds")
- //Strange audio
- switch(rand(1,20))
- if(1) playsound_local(null,'sound/machines/airlock.ogg', 15, 1)
- if(2)
- if(prob(50)) playsound_local(null,'sound/effects/explosion1.ogg', 50, 1)
- else playsound_local(null, 'sound/effects/explosion2.ogg', 50, 1)
- if(3) playsound_local(null, 'sound/effects/explosionfar.ogg', 50, 1)
- if(4) playsound_local(null, pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg'), 50, 1)
- if(5)
- playsound_local(null, 'sound/weapons/ring.ogg', 35)
- sleep(15)
- playsound_local(null, 'sound/weapons/ring.ogg', 35)
- sleep(15)
- playsound_local(null, 'sound/weapons/ring.ogg', 35)
- if(6) playsound_local(null, 'sound/magic/summon_guns.ogg', 50, 1)
- if(7) playsound_local(null, 'sound/machines/alarm.ogg', 100, 0)
- if(8) playsound_local(null, 'sound/voice/bfreeze.ogg', 35, 0)
- if(9)
- //To make it more realistic, I added two gunshots (enough to kill)
- playsound_local(null, 'sound/weapons/gunshot.ogg', 25, 1)
- spawn(rand(10,30))
- playsound_local(null, 'sound/weapons/gunshot.ogg', 25, 1)
- sleep(rand(5,10))
- playsound_local(null, sound(get_sfx("bodyfall"), 25), 25, 1)
- if(10) playsound_local(null, 'sound/effects/pray_chaplain.ogg', 50)
- if(11)
- //Same as above, but with tasers.
- playsound_local(null, 'sound/weapons/taser.ogg', 25, 1)
- spawn(rand(10,30))
- playsound_local(null, 'sound/weapons/taser.ogg', 25, 1)
- sleep(rand(5,10))
- playsound_local(null, sound(get_sfx("bodyfall"), 25), 25, 1)
- //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')
- playsound_local(null, pick(creepyasssounds), 50, 1)
- if(13)
- playsound_local(null, 'sound/effects/ratvar_rises.ogg', 100)
- sleep(150)
- playsound_local(null, 'sound/effects/ratvar_reveal.ogg', 100)
- if(14)
- to_chat(src, "Priority Announcement
")
- to_chat(src, "
The Emergency Shuttle has docked with the station. You have 3 minutes to board the Emergency Shuttle.
")
- playsound_local(null, 'sound/ai/shuttledock.ogg', 100)
- //Deconstructing a wall
- if(15)
- playsound_local(null, 'sound/items/welder.ogg', 15, 1)
- sleep(105)
- playsound_local(null, 'sound/items/welder2.ogg', 15, 1)
- sleep(15)
- playsound_local(null, 'sound/items/ratchet.ogg', 15, 1)
- //Hacking a door
- if(16)
- playsound_local(null, 'sound/items/screwdriver.ogg', 15, 1)
- sleep(rand(10,30))
- for(var/i = rand(1,3), i>0, i--)
- playsound_local(null, 'sound/weapons/empty.ogg', 15, 1)
- sleep(rand(10,30))
- playsound_local(null, 'sound/machines/airlockforced.ogg', 15, 1)
- if(17)
- playsound_local(null, 'sound/weapons/saberon.ogg',35,1)
- if(18)
- to_chat(src, "Biohazard Alert
")
- to_chat(src, "
Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.
")
- playsound_local(null, 'sound/ai/outbreak5.ogg')
- if(19) //Tesla loose!
- playsound_local(null, 'sound/magic/lightningbolt.ogg', 35, 1)
- sleep(20)
- playsound_local(null, 'sound/magic/lightningbolt.ogg', 65, 1)
- sleep(20)
- playsound_local(null, 'sound/magic/lightningbolt.ogg', 100, 1)
- if(20) //AI is doomsdaying!
- to_chat(src, "Anomaly Alert
")
- to_chat(src, "
Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.
")
- playsound_local(null, 'sound/ai/aimalf.ogg', 100)
- if("hudscrew")
- //Screwy HUD
- //to_chat(src, "Screwy HUD")
- hal_screwyhud = pick(SCREWYHUD_NONE,SCREWYHUD_CRIT,SCREWYHUD_DEAD,SCREWYHUD_HEALTHY)
- sleep(rand(100,250))
- hal_screwyhud = 0
+/datum/hallucination/sounds
+ cost = 15
- if("fake_alert")
- 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","temp","pressure")
- if(specific)
- alert_type = specific
- switch(alert_type)
- if("not_enough_oxy")
- throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy, override = TRUE)
- if("not_enough_tox")
- throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox, override = TRUE)
- if("not_enough_co2")
- throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2, override = TRUE)
- if("too_much_oxy")
- throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy, override = TRUE)
- if("too_much_co2")
- throw_alert("too_much_co2", /obj/screen/alert/too_much_co2, override = TRUE)
- if("too_much_tox")
- throw_alert("too_much_tox", /obj/screen/alert/too_much_tox, override = TRUE)
- if("nutrition")
- if(prob(50))
- throw_alert("nutrition", /obj/screen/alert/fat, override = TRUE)
- else
- throw_alert("nutrition", /obj/screen/alert/starving, override = TRUE)
- if("weightless")
- throw_alert("weightless", /obj/screen/alert/weightless, override = TRUE)
- if("fire")
- throw_alert("fire", /obj/screen/alert/fire, override = TRUE)
- if("temp")
- if(prob(50))
- throw_alert("temp", /obj/screen/alert/hot, 3, override = TRUE)
- else
- throw_alert("temp", /obj/screen/alert/cold, 3, override = TRUE)
- if("pressure")
- if(prob(50))
- throw_alert("pressure", /obj/screen/alert/highpressure, 2, override = TRUE)
- else
- throw_alert("pressure", /obj/screen/alert/lowpressure, 2, override = TRUE)
- //BEEP BOOP I AM A ROBOT
- if("newlaw")
- throw_alert("newlaw", /obj/screen/alert/newlaw, override = TRUE)
- if("locked")
- throw_alert("locked", /obj/screen/alert/locked, override = TRUE)
- if("hacked")
- throw_alert("hacked", /obj/screen/alert/hacked, override = TRUE)
- if("charge")
- throw_alert("charge", /obj/screen/alert/emptycell, override = TRUE)
- sleep(rand(100,200))
- clear_alert(alert_type, clear_override = TRUE)
-
- if("items")
- //Strange items
- //to_chat(src, "Traitor Items")
- if(!halitem)
- halitem = new
- var/obj/item/l_hand = get_item_for_held_index(1)
- var/obj/item/r_hand = get_item_for_held_index(2)
- var/l = ui_hand_position(get_held_index_of_item(l_hand))
- var/r = ui_hand_position(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(src))
- 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 = ABOVE_HUD_LAYER
- halitem.plane = ABOVE_HUD_PLANE
- switch(rand(1,6))
- if(1) //revolver
- halitem.icon = 'icons/obj/guns/projectile.dmi'
- halitem.icon_state = "revolver"
- halitem.name = "Revolver"
- if(2) //c4
- halitem.icon = 'icons/obj/grenade.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
- QDEL_IN(halitem, rand(100, 250))
- if("dangerflash")
- //Flashes of danger
- //to_chat(src, "Danger Flash")
- if(!halimage)
- var/list/possible_points = list()
- for(var/turf/open/floor/F in view(src,world.view))
- possible_points += F
- if(possible_points.len)
- var/turf/open/floor/target = pick(possible_points)
-
- switch(rand(1,4))
- if(1)
- //to_chat(src, "Space")
- halimage = image('icons/turf/space.dmi',target,"[rand(1,25)]",TURF_LAYER)
- if(2)
- //to_chat(src, "Lava")
- halimage = image('icons/turf/floors/lava.dmi',target,"smooth",TURF_LAYER)
- if(3)
- //to_chat(src, "Chasm")
- halimage = image('icons/turf/floors/Chasms.dmi',target,"smooth",TURF_LAYER)
- if(4)
- //to_chat(src, "C4")
- halimage = image('icons/obj/grenade.dmi',target,"plastic-explosive2",OBJ_LAYER+0.01)
-
-
- if(client) client.images += halimage
- sleep(rand(40,60)) //Only seen for a brief moment.
- if(client) client.images -= halimage
- halimage = null
- if("death")
- //Fake death
- hal_screwyhud = SCREWYHUD_DEAD
- SetUnconscious(400)
- var/area/area = get_area(src)
- to_chat(src, "[mind.name] has died at [area.name].")
+/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))
- var/list/dead_people = list()
- for(var/mob/dead/observer/G in GLOB.player_list)
- dead_people += G
- var/mob/dead/observer/fakemob = pick(dead_people)
- if(fakemob)
- sleep(rand(30, 60))
- to_chat(src, "DEAD: [fakemob.name] says, \"[pick("rip","welcome [first_name()]","you too?","is the AI malf?",\
- "i[prob(50)?" fucking":""] hate [pick("blood cult", "clock cult", "revenants", "abductors","double agents","viruses","badmins","you")]")]\"")
- sleep(rand(50,70))
- hal_screwyhud = SCREWYHUD_NONE
- SetSleeping(0)
- if("husks")
- if(!halbody)
- var/list/possible_points = list()
- for(var/turf/open/floor/F in view(src,world.view))
- possible_points += F
- if(possible_points.len)
- var/turf/open/floor/target = pick(possible_points)
- switch(rand(1,4))
- if(1)
- var/image/body = image('icons/mob/human.dmi',target,"husk",TURF_LAYER)
- var/matrix/M = matrix()
- M.Turn(90)
- body.transform = M
- halbody = body
- if(2,3)
- halbody = image('icons/mob/human.dmi',target,"husk",TURF_LAYER)
- if(4)
- halbody = image('icons/mob/alien.dmi',target,"alienother",TURF_LAYER)
+ 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)
- if(client) client.images += halbody
- spawn(rand(30,50)) //Only seen for a brief moment.
- if(client) client.images -= halbody
- halbody = null
diff --git a/code/modules/flufftext/Hallucination.dm.rej b/code/modules/flufftext/Hallucination.dm.rej
new file mode 100644
index 0000000000..2b8fe4b064
--- /dev/null
+++ b/code/modules/flufftext/Hallucination.dm.rej
@@ -0,0 +1,1151 @@
+/*
+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)
+
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index a1251381b4..06845ba0cb 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -284,7 +284,7 @@
L.adjustBruteLoss(bonus_value)
/obj/item/crusher_trophy/tail_spike/proc/pushback(mob/living/target, mob/living/user)
- if(!target.anchored || ismegafauna(target)) //megafauna will always be pushed
+ if(!QDELETED(target) && !QDELETED(user) && (!target.anchored || ismegafauna(target))) //megafauna will always be pushed
step(target, get_dir(user, target))
//bubblegum
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 91a178ca36..9179221137 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -240,6 +240,12 @@
/mob/living/carbon/is_muzzled()
return(istype(src.wear_mask, /obj/item/clothing/mask/muzzle))
+/mob/living/carbon/hallucinating()
+ if(hallucination)
+ return TRUE
+ else
+ return FALSE
+
/mob/living/carbon/resist_buckle()
if(restrained())
changeNext_move(CLICK_CD_BREAKOUT)
@@ -812,3 +818,4 @@
.["Make AI"] = "?_src_=vars;makeai=\ref[src]"
.["Modify bodypart"] = "?_src_=vars;editbodypart=\ref[src]"
.["Modify organs"] = "?_src_=vars;editorgans=\ref[src]"
+ .["Hallucinate"] = "?_src_=vars;hallucinate=\ref[src]"
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 69b581995b..3dacc2f444 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -143,7 +143,7 @@
var/protection = (prot["head"] + prot["arms"] + prot["feet"] + prot["legs"] + prot["groin"] + prot["chest"] + prot["hands"])/7
return protection
-/mob/living/carbon/human/can_use_guns(var/obj/item/weapon/gun/G)
+/mob/living/carbon/human/can_use_guns(var/obj/item/weapon/G)
. = ..()
if(G.trigger_guard == TRIGGER_GUARD_NORMAL)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 72c65ae471..c52d9862ad 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -378,8 +378,7 @@
adjust_drugginess(-1)
if(hallucination)
- spawn handle_hallucinations()
- hallucination = max(hallucination-2,0)
+ handle_hallucinations()
//used in human and monkey handle_environment()
/mob/living/carbon/proc/natural_bodytemperature_stabilization()
diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm
index f57a48b5c6..9ed0a74d31 100644
--- a/code/modules/mob/living/carbon/monkey/monkey.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey.dm
@@ -140,7 +140,7 @@
return 0
return 1
-/mob/living/carbon/monkey/can_use_guns(var/obj/item/weapon/gun/G)
+/mob/living/carbon/monkey/can_use_guns(var/obj/item/weapon/G)
return 1
/mob/living/carbon/monkey/angry
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index b58fb0ec13..edf54e547a 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -795,7 +795,7 @@
else
to_chat(src, "You don't have the dexterity to do this!")
return
-/mob/living/proc/can_use_guns(var/obj/item/weapon/gun/G)
+/mob/living/proc/can_use_guns(var/obj/item/weapon/G)
if (G.trigger_guard != TRIGGER_GUARD_ALLOW_ALL && !IsAdvancedToolUser())
to_chat(src, "You don't have the dexterity to do this!")
return 0
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
index c984194805..51d28df31b 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
@@ -176,7 +176,7 @@
else
..()
-/mob/living/simple_animal/drone/cogscarab/can_use_guns(obj/item/weapon/gun/G)
+/mob/living/simple_animal/drone/cogscarab/can_use_guns(obj/item/weapon/G)
return GLOB.ratvar_awakens
/mob/living/simple_animal/drone/cogscarab/get_armor_effectiveness()
diff --git a/code/modules/mob/living/simple_animal/guardian/types/fire.dm b/code/modules/mob/living/simple_animal/guardian/types/fire.dm
index b0a8ba5215..8f456abc53 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/fire.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/fire.dm
@@ -21,7 +21,7 @@
/mob/living/simple_animal/hostile/guardian/fire/AttackingTarget()
. = ..()
if(. && ishuman(target) && target != summoner)
- new /obj/effect/hallucination/delusion(target.loc,target,"custom",200,0, icon_state,icon)
+ new /datum/hallucination/delusion(target,TRUE,"custom",200,0, icon_state,icon)
/mob/living/simple_animal/hostile/guardian/fire/Crossed(AM as mob|obj)
..()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
index 11c59a2d28..0ca966df35 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
@@ -57,6 +57,7 @@ Difficulty: Hard
ranged_cooldown_time = 40
aggro_vision_range = 21 //so it can see to one side of the arena to the other
loot = list(/obj/item/weapon/hierophant_club)
+ crusher_loot = list(/obj/item/weapon/hierophant_club)
wander = FALSE
var/burst_range = 3 //range on burst aoe
var/beam_range = 5 //range on cross blast beams
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index a704c2e43f..46f5db920b 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -325,6 +325,9 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
return B.eye_blind
return 0
+/mob/proc/hallucinating()
+ return FALSE
+
/proc/is_special_character(mob/M) // returns 1 for special characters and 2 for heroes of gamemode //moved out of admins.dm because things other than admin procs were calling this.
if(!SSticker.HasRoundStarted())
return 0
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index e36c639eb9..aec4bb82ea 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -120,8 +120,8 @@
if(auto_name)
name = "\improper [get_area(src)] APC"
- pixel_x = (src.tdir & 3)? 0 : (src.tdir == 4 ? 24 : -24)
- pixel_y = (src.tdir & 3)? (src.tdir ==1 ? 24 : -24) : 0
+ pixel_x = (src.tdir & 3)? 0 : (src.tdir == 4 ? 24 : -25)
+ pixel_y = (src.tdir & 3)? (src.tdir ==1 ? 23 : -24) : 0
if (building)
area = get_area(src)
opened = 1
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 156a5ee81e..d5109a2c0c 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -27,7 +27,7 @@
var/recoil = 0 //boom boom shake the room
var/clumsy_check = 1
var/obj/item/ammo_casing/chambered = null
- var/trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers
+ trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers
var/sawn_desc = null //description change if weapon is sawn-off
var/sawn_state = SAWN_INTACT
var/burst_size = 1 //how large a burst is
@@ -188,12 +188,10 @@
-/obj/item/weapon/gun/proc/can_trigger_gun(var/mob/living/user)
-
- if(!handle_pins(user) || !user.can_use_guns(src))
- return 0
-
- return 1
+/obj/item/weapon/gun/can_trigger_gun(mob/living/user)
+ . = ..()
+ if(!handle_pins(user))
+ return FALSE
/obj/item/weapon/gun/proc/handle_pins(mob/living/user)
if(pin)
diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm
index 4b1d5af6c4..5a8f49f45a 100644
--- a/code/modules/projectiles/projectile/special.dm
+++ b/code/modules/projectiles/projectile/special.dm
@@ -387,3 +387,232 @@
var/obj/effect/ebeam/B = b
animate(B, alpha = 0, time = 32)
return ..()
+
+/obj/item/projectile/hallucination
+ name = "bullet"
+ icon = null
+ icon_state = null
+ hitsound = ""
+ suppressed = TRUE
+ ricochets_max = 0
+ ricochet_chance = 0
+ damage = 0
+ nodamage = TRUE
+ projectile_type = /obj/item/projectile/hallucination
+ log_override = TRUE
+ var/hal_icon_state
+ var/image/fake_icon
+ var/mob/living/carbon/hal_target
+ var/hal_fire_sound
+ var/hal_hitsound
+ var/hal_hitsound_wall
+ var/hal_impact_effect
+ var/hal_impact_effect_wall
+ var/hit_duration
+ var/hit_duration_wall
+
+/obj/item/projectile/hallucination/fire()
+ ..()
+ fake_icon = image('icons/obj/projectiles.dmi', src, hal_icon_state, ABOVE_MOB_LAYER)
+ if(hal_target.client)
+ hal_target.client.images += fake_icon
+
+/obj/item/projectile/hallucination/Destroy()
+ if(hal_target.client)
+ hal_target.client.images -= fake_icon
+ QDEL_NULL(fake_icon)
+ return ..()
+
+/obj/item/projectile/hallucination/Collide(atom/A)
+ if(!ismob(A))
+ if(hal_hitsound_wall)
+ hal_target.playsound_local(loc, hal_hitsound_wall, 40, 1)
+ if(hal_impact_effect_wall)
+ spawn_hit(A, TRUE)
+ else if(A == hal_target)
+ if(hal_hitsound)
+ hal_target.playsound_local(A, hal_hitsound, 100, 1)
+ target_on_hit(A)
+ qdel(src)
+ return TRUE
+
+/obj/item/projectile/hallucination/proc/target_on_hit(mob/M)
+ if(M == hal_target)
+ to_chat(hal_target, "[M] is hit by \a [src] in the chest!")
+ hal_apply_effect()
+ else if(M in view(hal_target))
+ to_chat(hal_target, "[M] is hit by \a [src] in the chest!!")
+ if(damage_type == BRUTE)
+ var/splatter_dir = dir
+ if(starting)
+ splatter_dir = get_dir(starting, get_turf(M))
+ spawn_blood(M, splatter_dir)
+ else if(hal_impact_effect)
+ spawn_hit(M, FALSE)
+
+/obj/item/projectile/hallucination/proc/spawn_blood(mob/M, set_dir)
+ set waitfor = 0
+ if(!hal_target.client)
+ return
+
+ var/splatter_icon_state
+ if(set_dir in GLOB.diagonals)
+ splatter_icon_state = "splatter[pick(1, 2, 6)]"
+ else
+ splatter_icon_state = "splatter[pick(3, 4, 5)]"
+
+ var/image/blood = image('icons/effects/blood.dmi', M, splatter_icon_state, ABOVE_MOB_LAYER)
+ var/target_pixel_x = 0
+ var/target_pixel_y = 0
+ switch(set_dir)
+ if(NORTH)
+ target_pixel_y = 16
+ if(SOUTH)
+ target_pixel_y = -16
+ layer = ABOVE_MOB_LAYER
+ if(EAST)
+ target_pixel_x = 16
+ if(WEST)
+ target_pixel_x = -16
+ if(NORTHEAST)
+ target_pixel_x = 16
+ target_pixel_y = 16
+ if(NORTHWEST)
+ target_pixel_x = -16
+ target_pixel_y = 16
+ if(SOUTHEAST)
+ target_pixel_x = 16
+ target_pixel_y = -16
+ layer = ABOVE_MOB_LAYER
+ if(SOUTHWEST)
+ target_pixel_x = -16
+ target_pixel_y = -16
+ layer = ABOVE_MOB_LAYER
+ hal_target.client.images += blood
+ animate(blood, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = 5)
+ sleep(5)
+ hal_target.client.images -= blood
+ qdel(blood)
+
+/obj/item/projectile/hallucination/proc/spawn_hit(atom/A, is_wall)
+ set waitfor = 0
+ if(!hal_target.client)
+ return
+
+ var/image/hit_effect = image('icons/effects/blood.dmi', A, is_wall ? hal_impact_effect_wall : hal_impact_effect, ABOVE_MOB_LAYER)
+ hit_effect.pixel_x = A.pixel_x + rand(-4,4)
+ hit_effect.pixel_y = A.pixel_y + rand(-4,4)
+ hal_target.client.images += hit_effect
+ sleep(is_wall ? hit_duration_wall : hit_duration)
+ hal_target.client.images -= hit_effect
+ qdel(hit_effect)
+
+
+/obj/item/projectile/hallucination/proc/hal_apply_effect()
+ return
+
+/obj/item/projectile/hallucination/bullet
+ name = "bullet"
+ hal_icon_state = "bullet"
+ hal_fire_sound = "gunshot"
+ hal_hitsound = 'sound/weapons/pierce.ogg'
+ hal_hitsound_wall = "ricochet"
+ hal_impact_effect = "impact_bullet"
+ hal_impact_effect_wall = "impact_bullet"
+ hit_duration = 5
+ hit_duration_wall = 5
+
+/obj/item/projectile/hallucination/bullet/hal_apply_effect()
+ hal_target.adjustStaminaLoss(60)
+
+/obj/item/projectile/hallucination/laser
+ name = "laser"
+ damage_type = BURN
+ hal_icon_state = "laser"
+ hal_fire_sound = 'sound/weapons/laser.ogg'
+ hal_hitsound = 'sound/weapons/sear.ogg'
+ hal_hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ hal_impact_effect = "impact_laser"
+ hal_impact_effect_wall = "impact_laser_wall"
+ hit_duration = 4
+ hit_duration_wall = 10
+ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
+
+/obj/item/projectile/hallucination/laser/hal_apply_effect()
+ hal_target.adjustStaminaLoss(20)
+ hal_target.blur_eyes(2)
+
+/obj/item/projectile/hallucination/taser
+ name = "electrode"
+ damage_type = BURN
+ hal_icon_state = "spark"
+ color = "#FFFF00"
+ hal_fire_sound = 'sound/weapons/taser.ogg'
+ hal_hitsound = 'sound/weapons/taserhit.ogg'
+ hal_hitsound_wall = null
+ hal_impact_effect = null
+ hal_impact_effect_wall = null
+
+/obj/item/projectile/hallucination/taser/hal_apply_effect()
+ hal_target.Knockdown(100)
+ hal_target.stuttering += 20
+ if(hal_target.dna && hal_target.dna.check_mutation(HULK))
+ hal_target.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
+ else if(hal_target.status_flags & CANKNOCKDOWN)
+ addtimer(CALLBACK(hal_target, /mob/living/carbon.proc/do_jitter_animation, 20), 5)
+
+/obj/item/projectile/hallucination/disabler
+ name = "disabler beam"
+ damage_type = STAMINA
+ hal_icon_state = "omnilaser"
+ hal_fire_sound = 'sound/weapons/taser2.ogg'
+ hal_hitsound = 'sound/weapons/tap.ogg'
+ hal_hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ hal_impact_effect = "impact_laser_blue"
+ hal_impact_effect_wall = null
+ hit_duration = 4
+ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
+
+/obj/item/projectile/hallucination/disabler/hal_apply_effect()
+ hal_target.adjustStaminaLoss(25)
+
+/obj/item/projectile/hallucination/ebow
+ name = "bolt"
+ damage_type = TOX
+ hal_icon_state = "cbbolt"
+ hal_fire_sound = 'sound/weapons/genhit.ogg'
+ hal_hitsound = null
+ hal_hitsound_wall = null
+ hal_impact_effect = null
+ hal_impact_effect_wall = null
+
+/obj/item/projectile/hallucination/ebow/hal_apply_effect()
+ hal_target.Knockdown(100)
+ hal_target.stuttering += 5
+ hal_target.adjustStaminaLoss(8)
+
+/obj/item/projectile/hallucination/change
+ name = "bolt of change"
+ damage_type = BURN
+ hal_icon_state = "ice_1"
+ hal_fire_sound = 'sound/magic/staff_change.ogg'
+ hal_hitsound = null
+ hal_hitsound_wall = null
+ hal_impact_effect = null
+ hal_impact_effect_wall = null
+
+/obj/item/projectile/hallucination/change/hal_apply_effect()
+ new /datum/hallucination/self_delusion(hal_target, TRUE, wabbajack = FALSE)
+
+/obj/item/projectile/hallucination/death
+ name = "bolt of death"
+ damage_type = BURN
+ hal_icon_state = "pulse1_bl"
+ hal_fire_sound = 'sound/magic/wandodeath.ogg'
+ hal_hitsound = null
+ hal_hitsound_wall = null
+ hal_impact_effect = null
+ hal_impact_effect_wall = null
+
+/obj/item/projectile/hallucination/death/hal_apply_effect()
+ new /datum/hallucination/death(hal_target, TRUE)
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index 27cad406fa..3811512a02 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -101,9 +101,11 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "chem_dispenser", name, 550, 550, master_ui, state)
+ if(user.hallucinating())
+ ui.set_autoupdate(FALSE) //to not ruin the immersion by constantly changing the fake chemicals
ui.open()
-/obj/machinery/chem_dispenser/ui_data()
+/obj/machinery/chem_dispenser/ui_data(mob/user)
var/data = list()
data["amount"] = amount
data["energy"] = cell.charge ? cell.charge * powerefficiency : "0" //To prevent NaN in the UI.
@@ -128,10 +130,16 @@
data["beakerTransferAmounts"] = null
var chemicals[0]
+ var/is_hallucinating = FALSE
+ if(user.hallucinating())
+ is_hallucinating = TRUE
for(var/re in dispensable_reagents)
var/datum/reagent/temp = GLOB.chemical_reagents_list[re]
if(temp)
- chemicals.Add(list(list("title" = temp.name, "id" = temp.id)))
+ var/chemname = temp.name
+ if(is_hallucinating && prob(5))
+ chemname = "[pick_list_replacements("hallucination.json", "chemicals")]"
+ chemicals.Add(list(list("title" = chemname, "id" = temp.id)))
data["chemicals"] = chemicals
return data
diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm
index ff44804e75..0f5f1de91d 100644
--- a/code/modules/surgery/organs/vocal_cords.dm
+++ b/code/modules/surgery/organs/vocal_cords.dm
@@ -290,9 +290,8 @@
//HALLUCINATE
else if((findtext(message, hallucinate_words)))
cooldown = COOLDOWN_MEME
- for(var/V in listeners)
- var/mob/living/L = V
- new /obj/effect/hallucination/delusion(get_turf(L),L,null,150 * power_multiplier,0)
+ for(var/mob/living/carbon/C in listeners)
+ new /datum/hallucination/delusion(C, TRUE, null,150 * power_multiplier,0)
//WAKE UP
else if((findtext(message, wakeup_words)))
diff --git a/code/world.dm b/code/world.dm
index 5ef7491f45..a5853c6e12 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -59,16 +59,16 @@
if(config.sql_enabled)
if(SSdbcore.Connect())
log_world("Database connection established.")
- var/datum/DBQuery/db_version = SSdbcore.NewQuery("SELECT major, minor FROM [format_table_name("schema_version")]")
- db_version.Execute()
- if(db_version.NextRow())
- var/db_major = db_version.item[1]
- var/db_minor = db_version.item[2]
+ var/datum/DBQuery/query_db_version = SSdbcore.NewQuery("SELECT major, minor FROM [format_table_name("schema_version")] ORDER BY date DESC LIMIT 1")
+ query_db_version.Execute()
+ if(query_db_version.NextRow())
+ var/db_major = query_db_version.item[1]
+ var/db_minor = query_db_version.item[2]
if(db_major < DB_MAJOR_VERSION || db_minor < DB_MINOR_VERSION)
- message_admins("db schema ([db_major].[db_minor]) is behind latest tg schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
- log_sql("db schema ([db_major].[db_minor]) is behind latest tg schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
+ message_admins("Database schema ([db_major].[db_minor]) is behind latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
+ log_sql("Database schema ([db_major].[db_minor]) is behind latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
else
- message_admins("Could not get schema version from db")
+ message_admins("Could not get schema version from database")
else
log_world("Your server failed to establish a connection with the database.")
@@ -105,10 +105,10 @@
/world/Topic(T, addr, master, key)
var/list/input = params2list(T)
-
+
var/pinging = ("ping" in input)
var/playing = ("players" in input)
-
+
if(!pinging && !playing && config && config.log_world_topic)
GLOB.world_game_log << "TOPIC: \"[T]\", from:[addr], master:[master], key:[key]"
diff --git a/html/changelogs/AutoChangeLog-pr-2231.yml b/html/changelogs/AutoChangeLog-pr-2231.yml
new file mode 100644
index 0000000000..c25b4b2b49
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-2231.yml
@@ -0,0 +1,4 @@
+author: "Xhuis"
+delete-after: True
+changes:
+ - tweak: "Spell action buttons now have their description in a tooltip."
diff --git a/html/changelogs/AutoChangeLog-pr-2359.yml b/html/changelogs/AutoChangeLog-pr-2359.yml
new file mode 100644
index 0000000000..8455963dc6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-2359.yml
@@ -0,0 +1,4 @@
+author: "Joan"
+delete-after: True
+changes:
+ - imageadd: "Ported CEV-Eris's APC sprites."
diff --git a/html/changelogs/AutoChangeLog-pr-2363.yml b/html/changelogs/AutoChangeLog-pr-2363.yml
new file mode 100644
index 0000000000..e8f5b574ae
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-2363.yml
@@ -0,0 +1,4 @@
+author: "CitadelStationBot"
+delete-after: True
+changes:
+ - tweak: "Canisters don't flash red lights anymore when empty."
diff --git a/html/changelogs/AutoChangeLog-pr-2371.yml b/html/changelogs/AutoChangeLog-pr-2371.yml
new file mode 100644
index 0000000000..ec1810b8a6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-2371.yml
@@ -0,0 +1,4 @@
+author: "Xhuis"
+delete-after: True
+changes:
+ - bugfix: "The Resurrect Cultist rune now works as intended."
diff --git a/icons/obj/power.dmi b/icons/obj/power.dmi
index f3b2233283..6b2ae55351 100644
Binary files a/icons/obj/power.dmi and b/icons/obj/power.dmi differ
diff --git a/icons/obj/wallframe.dmi b/icons/obj/wallframe.dmi
index 0e416cac77..70149901e3 100644
Binary files a/icons/obj/wallframe.dmi and b/icons/obj/wallframe.dmi differ
diff --git a/interface/interface.dm b/interface/interface.dm
index 64e39587ce..253738064b 100644
--- a/interface/interface.dm
+++ b/interface/interface.dm
@@ -1,14 +1,12 @@
//Please use mob or src (not usr) in these procs. This way they can be called in the same fashion as procs.
-/client/verb/wiki(query as text | null)
+/client/verb/wiki()
set name = "wiki"
- set desc = "Type what you want to know about. This will open the wiki on your web browser. Type nothing to go to the main page."
+ set desc = "Visit the wiki."
set hidden = 1
if(config.wikiurl)
- if(query)
- var/output = config.wikiurl + "/index.php?title=Special%3ASearch&profile=default&search=" + query
- src << link(output)
- else if (query != null)
- src << link(config.wikiurl)
+ if(alert("This will open the wiki in your browser. Are you sure?",,"Yes","No")=="No")
+ return
+ src << link(config.wikiurl)
else
to_chat(src, "The wiki URL is not set in the server configuration.")
return
diff --git a/power.dmi b/power.dmi
new file mode 100644
index 0000000000..d0066b96de
Binary files /dev/null and b/power.dmi differ
diff --git a/strings/brain_damage_lines.json b/strings/brain_damage_lines.json
index ce3e726dbc..af040d5ba0 100644
--- a/strings/brain_damage_lines.json
+++ b/strings/brain_damage_lines.json
@@ -1,17 +1,17 @@
{
"brain_damage": [
- "IM A PONY NEEEEEEIIIIIIIIIGH",
+ "@pick(semicolon)IM A PONY NEEEEEEIIIIIIIIIGH",
"without oxigen blob don't evoluate?",
- "CAPTAINS A COMDOM",
- "@pick(faggot_traitor) @pick(george) @pick(mellens) is grifing me HALP!!!",
+ "@pick(semicolon)CAPTAINS A COMDOM",
+ "@pick(semicolon)@pick(faggot_traitor) @pick(george) @pick(mellens) is grifing me HALP!!!",
"can u give me @pick(mutations)?",
"THe saiyans screwed",
"Bi is THE BEST OF BOTH WORLDS>",
- "I WANNA PET TEH monkeyS",
+ "@pick(semicolon)I WANNA PET TEH monkeyS",
"stop grifing me!!!!",
"SOTP IT#",
"shiggey diggey!!",
- "A PIRATE APPEAR",
+ "@pick(semicolon)A PIRATE APPEAR",
"FUS RO DAH",
"fucking 4rries!",
"stat me",
@@ -136,7 +136,8 @@
"random_gibberish": [
"g",
"squid",
- "r"
+ "r",
+ "carbon dioxide"
],
"y_replacements": [
@@ -171,6 +172,19 @@
"abdoocters"
],
+ "bug": [
+ "",
+ "IS TIS A BUG??",
+ "SI IST A BUGG/",
+ "BUG!!!"
+ ],
+
+ "semicolon": [
+ "",
+ ";",
+ ".h"
+ ],
+
"roles": [
"heds",
"ceptin",
diff --git a/strings/brain_damage_lines.json.rej b/strings/brain_damage_lines.json.rej
new file mode 100644
index 0000000000..6d8e1cdb62
--- /dev/null
+++ b/strings/brain_damage_lines.json.rej
@@ -0,0 +1,10 @@
+diff a/strings/brain_damage_lines.json b/strings/brain_damage_lines.json (rejected hunks)
+@@ -131,7 +131,7 @@
+ "",
+ "IS TIS A BUG??",
+ "SI IST A BUGG/",
+- "BUG!!!"
++ "BUG!!!"
+ ],
+
+ "semicolon": [
diff --git a/strings/hallucination.json b/strings/hallucination.json
new file mode 100644
index 0000000000..b28c70db4a
--- /dev/null
+++ b/strings/hallucination.json
@@ -0,0 +1,229 @@
+{
+ "suspicion": [
+ "I'm watching you...",
+ "I know what you're doing",
+ "What are you hiding?",
+ "I saw that"
+ ],
+
+ "greetings": [
+ "",
+ "Hey, ",
+ "Hi ",
+ "Hello ",
+ "Wait, ",
+ "It's "
+ ],
+
+ "getout": [
+ "Get out",
+ "Get out!",
+ "Go away",
+ "Fuck off",
+ "OUT!",
+ "Out!"
+ ],
+
+ "weird": [
+ "Kchck-Chkck? Kchchck!",
+ "Kchckchk...",
+ "EEEeeeeEEEE",
+ "khhhhh",
+ "#@§*&£",
+ "H**p m*",
+ "H-hhhhh..."
+ ],
+
+ "didyouhearthat": [
+ "Did you hear that?",
+ "Did you see that?",
+ "What was that?"
+ ],
+
+ "imatraitor": [
+ "Hail Ratvar",
+ "Hail Nar'Sie",
+ "Hey, i've got some TC left, want something?",
+ "Viva!",
+ "I'll spare you if you don't tell anybody about me",
+ "Hey, are you a traitor too?",
+ "You're my target, but @pick(excuses)",
+ "Are you mr. @pick(ling_names)?"
+ ],
+
+ "excuses": [
+ "i like you, so i'll spare you",
+ "i don't really feel like following objectives today",
+ "i'm not robust enough to fight you",
+ "who cares",
+ "i'll kill you later"
+ ]
+
+ "ling_names": [
+ "Alpha",
+ "Beta",
+ "Gamma",
+ "Delta",
+ "Epsilon",
+ "Eta",
+ "Theta",
+ "Lambda",
+ "Mu",
+ "Xi",
+ "Rho",
+ "Sigma",
+ "Tau",
+ "Upsilon",
+ "Phi",
+ "Psi",
+ "Omega"
+ ]
+
+ "doubt": [
+ "Why?",
+ "What?",
+ "Wait, what?",
+ "Wait",
+ "Hold on",
+ "Uh..."
+ ],
+
+ "aggressive": [
+ "Give me that!",
+ "I'm going to kill you!",
+ "Fuck you!"
+ ],
+
+ "help": [
+ "HELP",
+ "HELP ME",
+ "HELP HIM",
+ "HELP HER",
+ "HELP THEM",
+ "HELP US",
+ "HELP YOURSELF"
+ ],
+
+ "escape": [
+ "RUN!!",
+ "They're behind me!",
+ "It's here!",
+ "Follow me!",
+ "Follow me"
+ ]
+
+ "infection_advice": [
+ "stay away",
+ "don't get close",
+ "be careful",
+ "help me",
+ "kill me"
+ ]
+
+ "people": [
+ "Captain",
+ "Hos",
+ "Cmo",
+ "Rd",
+ "Ce",
+ "Hop",
+ "Janitor",
+ "AI",
+ "Viro",
+ "Qm",
+ "[target.first_name()]"
+ ]
+
+ "accusations": [
+ "rogue",
+ "cult",
+ "a cultist",
+ "clockcult",
+ "a clock cultist",
+ "a revhead",
+ "a rev",
+ "a gang leader",
+ "a gangster",
+ "a traitor",
+ "a tator",
+ "a ling",
+ "a changeling"
+ ]
+
+ "threat": [
+ "Cult",
+ "Wizard",
+ "Blob",
+ "Ling",
+ "Ops",
+ "Swarmers",
+ "Revenant",
+ "Traitor",
+ "Harm",
+ "I hear flashing",
+ "Help"
+ ]
+
+ "location": [
+ "bridge",
+ "armory",
+ "sec",
+ "security",
+ "science",
+ "engineering",
+ "cargo",
+ "medbay",
+ "atmos",
+ "maint",
+ "hops office",
+ "captains office",
+ "chapel",
+ "library",
+ "tool storage",
+ "botany",
+ "kitchen",
+ "the ai sat"
+ ]
+
+ "advice": [
+ "Hmm...not sure about that.",
+ "Yes. You're doing the right thing.",
+ "No. Stop what you're doing.",
+ "You should be wary of that person.",
+ "Trust that person.",
+ "That person wants to kill you.",
+ "Kill that person. You know who.",
+ "You should go somewhere else. Quickly.",
+ "Good luck. You'll need it.",
+ "You have my permission. Do it."
+ ]
+
+ "chemicals": [
+ "Ooze",
+ "Fire",
+ "Earth",
+ "Lightning",
+ "Air",
+ "Magic",
+ "Spiders",
+ "Button",
+ "Surprise",
+ "Happiness",
+ "Despair",
+ "Blood",
+ "Awesome",
+ "Infinity",
+ "Electronics",
+ "Time",
+ "Space",
+ "Pain",
+ "Guts",
+ "Life",
+ "Death",
+ "Phlebotinium",
+ "Mana",
+ "Energy",
+ "?????"
+ ]
+
+}