diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm
index 586a87cd4e..a63681cc21 100644
--- a/code/__defines/_planes+layers.dm
+++ b/code/__defines/_planes+layers.dm
@@ -138,6 +138,8 @@ What is the naming convention for planes or layers?
#define PLANE_MESONS 30 //Stuff seen with mesons, like open ceilings. This is 30 for downstreams.
+#define PLANE_STATUS 31 //Status Indicators that show over mobs' heads when certain things like stuns affect them.
+
#define PLANE_ADMIN2 33 //Purely for shenanigans (above lighting)
#define PLANE_BUILDMODE 39 //Things that only show up when you have buildmode on
diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index be8f025922..498493ce6c 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -398,7 +398,9 @@
#define VIS_CLOAKED 23
-#define VIS_COUNT 23 //Must be highest number from above.
+#define VIS_STATUS 24
+
+#define VIS_COUNT 24 //Must be highest number from above.
//Some mob icon layering defines
#define BODY_LAYER -100
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 80a8651148..1c5a60aa24 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -19,6 +19,8 @@
var/icon_scale_x = 1 // Used to scale icons up or down horizonally in update_transform().
var/icon_scale_y = 1 // Used to scale icons up or down vertically in update_transform().
var/icon_rotation = 0 // Used to rotate icons in update_transform()
+ var/icon_expected_height = 32
+ var/icon_expected_width = 32
var/old_x = 0
var/old_y = 0
var/datum/riding/riding_datum = null
@@ -549,6 +551,14 @@
return null
return text2num(pickweight(candidates))
+// Returns the current scaling of the sprite.
+// Note this DOES NOT measure the height or width of the icon, but returns what number is being multiplied with to scale the icons, if any.
+/atom/movable/proc/get_icon_scale_x()
+ return icon_scale_x
+
+/atom/movable/proc/get_icon_scale_y()
+ return icon_scale_y
+
/atom/movable/proc/update_transform()
var/matrix/M = matrix()
M.Scale(icon_scale_x, icon_scale_y)
diff --git a/code/game/gamemodes/technomancer/spells/audible_deception.dm b/code/game/gamemodes/technomancer/spells/audible_deception.dm
index 1914b72f14..8faff2b4e6 100644
--- a/code/game/gamemodes/technomancer/spells/audible_deception.dm
+++ b/code/game/gamemodes/technomancer/spells/audible_deception.dm
@@ -83,7 +83,7 @@
for(var/mob/living/carbon/M in ohearers(6, T))
if(M.get_ear_protection() >= 2)
continue
- M.sleeping = 0
+ M.SetSleeping(0)
M.stuttering += 20
M.ear_deaf += 30
M.Weaken(3)
diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm
index 946470ca95..ad77b32cc0 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -235,7 +235,7 @@
occupant.set_stat(UNCONSCIOUS)
occupant.dir = SOUTH
if(occupant.bodytemperature < T0C)
- occupant.sleeping = max(5, (1/occupant.bodytemperature)*2000)
+ occupant.Sleeping(max(5, (1/occupant.bodytemperature)*2000))
occupant.Paralyse(max(5, (1/occupant.bodytemperature)*3000))
if(air_contents.gas["oxygen"] > 2)
if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1)
diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm
index b2bacc13d9..a3cf6c262c 100644
--- a/code/game/machinery/jukebox.dm
+++ b/code/game/machinery/jukebox.dm
@@ -146,7 +146,7 @@ datum/track/New(var/title_name, var/audio)
for(var/mob/living/carbon/M in ohearers(6, src))
if(M.get_ear_protection() >= 2)
continue
- M.sleeping = 0
+ M.SetSleeping(0)
M.stuttering += 20
M.ear_deaf += 30
M.Weaken(3)
diff --git a/code/game/mecha/equipment/weapons/honk.dm b/code/game/mecha/equipment/weapons/honk.dm
index 427d53295c..e7bee193dd 100644
--- a/code/game/mecha/equipment/weapons/honk.dm
+++ b/code/game/mecha/equipment/weapons/honk.dm
@@ -26,7 +26,7 @@
return
to_chat(M, "Your ears feel like they're bleeding!")
playsound(M, 'sound/effects/bang.ogg', 70, 1, 30)
- M.sleeping = 0
+ M.SetSleeping(0)
M.ear_deaf += 30
M.ear_damage += rand(5, 20)
M.Weaken(3)
diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm
index 5516ccb099..32c9d75959 100644
--- a/code/game/objects/items/devices/megaphone.dm
+++ b/code/game/objects/items/devices/megaphone.dm
@@ -138,7 +138,7 @@
for(var/mob/living/carbon/M in oviewers(4, T))
if(M.get_ear_protection() >= 2)
continue
- M.sleeping = 0
+ M.SetSleeping(0)
M.stuttering += 20
M.ear_deaf += 30
M.Weaken(3)
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 7c2b26a5fc..2b13efe24d 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -1465,12 +1465,12 @@ proc/admin_notice(var/message, var/rights)
if(check_rights(R_ADMIN|R_MOD|R_EVENT))
if (H.paralysis == 0)
- H.paralysis = 8000
+ H.SetParalysis(8000)
msg = "has paralyzed [key_name(H)]."
log_and_message_admins(msg)
else
if(alert(src, "[key_name(H)] is paralyzed, would you like to unparalyze them?",,"Yes","No") == "Yes")
- H.paralysis = 0
+ H.SetParalysis(0)
msg = "has unparalyzed [key_name(H)]."
log_and_message_admins(msg)
diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm
index 970c8dd8e6..8efb44a98b 100644
--- a/code/modules/client/preference_setup/global/setting_datums.dm
+++ b/code/modules/client/preference_setup/global/setting_datums.dm
@@ -229,6 +229,18 @@ var/list/_client_preferences_by_type
enabled_description = "Enabled"
disabled_description = "Disabled"
+/datum/client_preference/status_indicators
+ description = "Status Indicators"
+ key = "SHOW_STATUS"
+ enabled_description = "Show"
+ disabled_description = "Hide"
+
+/datum/client_preference/status_indicators/toggled(mob/preference_mob, enabled)
+ . = ..()
+ if(preference_mob && preference_mob.plane_holder)
+ var/datum/plane_holder/PH = preference_mob.plane_holder
+ PH.set_vis(VIS_STATUS, enabled)
+
/********************
* Staff Preferences *
********************/
diff --git a/code/modules/client/preferences_toggle_procs.dm b/code/modules/client/preferences_toggle_procs.dm
index f1884dea82..aabcc23c8c 100644
--- a/code/modules/client/preferences_toggle_procs.dm
+++ b/code/modules/client/preferences_toggle_procs.dm
@@ -319,6 +319,19 @@
You will have to reload VChat and/or reconnect to the server for these changes to take place. \
VChat message persistence is not guaranteed if you change this again before the start of the next round.")
+/client/verb/toggle_status_indicators()
+ set name = "Toggle Status Indicators"
+ set category = "Preferences"
+ set desc = "Enable/Disable seeing status indicators over peoples' heads."
+
+ var/pref_path = /datum/client_preference/status_indicators
+ toggle_preference(pref_path)
+ SScharacter_setup.queue_preferences_save(prefs)
+
+ to_chat(src, "You will now [(is_preference_enabled(/datum/client_preference/status_indicators)) ? "see" : "not see"] status indicators.")
+
+ feedback_add_details("admin_verb","TStatusIndicators")
+
// Not attached to a pref datum because those are strict binary toggles
/client/verb/toggle_examine_mode()
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index 64a31b5bb2..d4c41e0642 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -168,12 +168,12 @@ mob/living/carbon/proc/handle_hallucinations()
if(71 to 72)
//Fake death
// src.sleeping_willingly = 1
- src.sleeping = 20
+ SetSleeping(20)
hal_crit = 1
hal_screwyhud = 1
spawn(rand(50,100))
// src.sleeping_willingly = 0
- src.sleeping = 0
+ SetSleeping(0)
hal_crit = 0
hal_screwyhud = 0
handling_hal = 0
diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm
index 4ea28b3eea..0822dca375 100644
--- a/code/modules/mob/living/bot/bot.dm
+++ b/code/modules/mob/living/bot/bot.dm
@@ -68,9 +68,9 @@
if(health <= 0)
death()
return
- weakened = 0
- stunned = 0
- paralysis = 0
+ SetWeakened(0)
+ SetStunned(0)
+ SetParalysis(0)
if(on && !client && !busy)
spawn(0)
diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm
index 370ec77422..43bcdde499 100644
--- a/code/modules/mob/living/carbon/alien/life.dm
+++ b/code/modules/mob/living/carbon/alien/life.dm
@@ -59,7 +59,7 @@
adjustHalLoss(-3)
if (mind)
if(mind.active && client != null)
- sleeping = max(sleeping-1, 0)
+ AdjustSleeping(-1)
blinded = 1
set_stat(UNCONSCIOUS)
else if(resting)
diff --git a/code/modules/mob/living/carbon/brain/login.dm b/code/modules/mob/living/carbon/brain/login.dm
index e90297dfe5..107b8e0ab7 100644
--- a/code/modules/mob/living/carbon/brain/login.dm
+++ b/code/modules/mob/living/carbon/brain/login.dm
@@ -1,3 +1,3 @@
/mob/living/carbon/brain/Login()
..()
- sleeping = 0
\ No newline at end of file
+ SetSleeping(0)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index d3c4d36ba7..bc9b5836cd 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -136,7 +136,7 @@
to_chat(src, "Oh god, everything's spinning!")
Confuse(max(0,confuse_dur))
if(species.emp_sensitivity & EMP_WEAKEN)
- if(weaken_dur >= 1)
+ if(weaken_dur >= 1)
to_chat(src, "Your limbs go slack!")
Weaken(max(0,weaken_dur))
//physical damage block, deals (minor-4) 5-15, 10-20, 15-25, 20-30 (extreme-1) of *each* type
@@ -287,7 +287,7 @@
M.visible_message("[M] shakes [src] trying to wake [T.him] up!", \
"You shake [src], but [T.he] [T.does] not respond... Maybe [T.he] [T.has] S.S.D?")
else if(lying || src.sleeping)
- src.sleeping = max(0,src.sleeping-5)
+ AdjustSleeping(-5)
if(src.sleeping == 0)
src.resting = 0
M.visible_message("[M] shakes [src] trying to wake [T.him] up!", \
@@ -401,7 +401,7 @@
to_chat(usr, "You are already sleeping")
return
if(alert(src,"You sure you want to sleep for a while?","Sleep","Yes","No") == "Yes")
- usr.sleeping = 20 //Short nap
+ usr.AdjustSleeping(20)
/mob/living/carbon/Bump(atom/A)
if(now_pushing)
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index e4afd8e20e..44580b101a 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -237,7 +237,7 @@
message = "faints."
if(src.sleeping)
return //Can't faint while asleep
- src.sleeping += 10 //Short-short nap
+ Sleeping(10)
m_type = 1
if("cough", "coughs")
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 8001e5bd4f..de4ac31354 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -1083,7 +1083,7 @@
drowsyness = max(0, drowsyness - 1)
eye_blurry = max(2, eye_blurry)
if (prob(5))
- sleeping += 1
+ Sleeping(1)
Paralyse(5)
// If you're dirty, your gloves will become dirty, too.
@@ -1230,14 +1230,14 @@
if(blinded)
overlay_fullscreen("blind", /obj/screen/fullscreen/blind)
throw_alert("blind", /obj/screen/alert/blind)
-
+
else
clear_fullscreens()
clear_alert("blind")
if(blinded)
overlay_fullscreen("blind", /obj/screen/fullscreen/blind)
-
+
else if(!machine)
clear_fullscreens()
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 0c52b440bc..d225e8ff85 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -122,7 +122,7 @@
/mob/living/proc/handle_weakened()
if(weakened)
- weakened = max(weakened-1,0)
+ AdjustWeakened(-1)
return weakened
/mob/living/proc/handle_stuttering()
@@ -165,7 +165,7 @@
throw_alert("blind", /obj/screen/alert/blind)
else
clear_alert("blind")
-
+
if(eye_blurry) //blurry eyes heal slowly
eye_blurry = max(eye_blurry-1, 0)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index be6ac48cc6..fc6cb7252e 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -442,6 +442,15 @@ default behaviour is:
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(stunned > 0)
+ add_status_indicator("stunned")
+
+/mob/living/SetStunned(amount)
+ ..()
+ if(stunned <= 0)
+ remove_status_indicator("stunned")
+ else
+ add_status_indicator("stunned")
/mob/living/AdjustStunned(amount)
if(amount > 0)
@@ -449,12 +458,25 @@ default behaviour is:
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(stunned <= 0)
+ remove_status_indicator("stunned")
+ else
+ add_status_indicator("stunned")
/mob/living/Weaken(amount)
for(var/datum/modifier/M in modifiers)
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(weakened > 0)
+ add_status_indicator("weakened")
+
+/mob/living/SetWeakened(amount)
+ ..()
+ if(weakened <= 0)
+ remove_status_indicator("weakened")
+ else
+ add_status_indicator("weakened")
/mob/living/AdjustWeakened(amount)
if(amount > 0)
@@ -462,12 +484,25 @@ default behaviour is:
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(weakened <= 0)
+ remove_status_indicator("weakened")
+ else
+ add_status_indicator("weakened")
/mob/living/Paralyse(amount)
for(var/datum/modifier/M in modifiers)
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(paralysis > 0)
+ add_status_indicator("paralysis")
+
+/mob/living/SetParalysis(amount)
+ ..()
+ if(paralysis <= 0)
+ remove_status_indicator("paralysis")
+ else
+ add_status_indicator("paralysis")
/mob/living/AdjustParalysis(amount)
if(amount > 0)
@@ -475,12 +510,25 @@ default behaviour is:
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(paralysis <= 0)
+ remove_status_indicator("paralysis")
+ else
+ add_status_indicator("paralysis")
/mob/living/Sleeping(amount)
for(var/datum/modifier/M in modifiers)
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(sleeping > 0)
+ add_status_indicator("sleeping")
+
+/mob/living/SetSleeping(amount)
+ ..()
+ if(sleeping <= 0)
+ remove_status_indicator("sleeping")
+ else
+ add_status_indicator("sleeping")
/mob/living/AdjustSleeping(amount)
if(amount > 0)
@@ -488,12 +536,25 @@ default behaviour is:
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(sleeping <= 0)
+ remove_status_indicator("sleeping")
+ else
+ add_status_indicator("sleeping")
/mob/living/Confuse(amount)
for(var/datum/modifier/M in modifiers)
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(confused > 0)
+ add_status_indicator("confused")
+
+/mob/living/SetConfused(amount)
+ ..()
+ if(confused <= 0)
+ remove_status_indicator("confused")
+ else
+ add_status_indicator("confused")
/mob/living/AdjustConfused(amount)
if(amount > 0)
@@ -501,12 +562,25 @@ default behaviour is:
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(confused <= 0)
+ remove_status_indicator("confused")
+ else
+ add_status_indicator("confused")
/mob/living/Blind(amount)
for(var/datum/modifier/M in modifiers)
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(eye_blind > 0)
+ add_status_indicator("blinded")
+
+/mob/living/SetBlinded(amount)
+ ..()
+ if(eye_blind <= 0)
+ remove_status_indicator("blinded")
+ else
+ add_status_indicator("blinded")
/mob/living/AdjustBlinded(amount)
if(amount > 0)
@@ -514,6 +588,10 @@ default behaviour is:
if(!isnull(M.disable_duration_percent))
amount = round(amount * M.disable_duration_percent)
..(amount)
+ if(eye_blind <= 0)
+ remove_status_indicator("blinded")
+ else
+ add_status_indicator("blinded")
// ++++ROCKDTBEN++++ MOB PROCS //END
@@ -775,10 +853,10 @@ default behaviour is:
if(pulling) // we were pulling a thing and didn't lose it during our move.
var/pull_dir = get_dir(src, pulling)
-
+
if(pulling.anchored || !isturf(pulling.loc))
stop_pulling()
-
+
else if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) // puller and pullee more than one tile away or in diagonal position
// If it is too far away or across z-levels from old location, stop pulling.
if(get_dist(pulling.loc, oldloc) > 1 || pulling.loc.z != oldloc?.z)
@@ -794,7 +872,7 @@ default behaviour is:
stop_pulling()
if(!isturf(loc))
- return
+ return
else if(lastarea?.has_gravity == 0)
inertial_drift()
else if(!isspace(loc))
@@ -806,7 +884,7 @@ default behaviour is:
if(Process_Spacemove(1))
inertia_dir = 0
return
-
+
var/locthen = loc
spawn(5)
if(!anchored && !pulledby && loc == locthen)
@@ -1073,21 +1151,29 @@ default behaviour is:
/mob/living/proc/is_sentient()
return TRUE
-/mob/living/update_transform()
- // First, get the correct size.
- var/desired_scale_x = icon_scale_x
- var/desired_scale_y = icon_scale_y
+/mob/living/get_icon_scale_x()
+ . = ..()
for(var/datum/modifier/M in modifiers)
if(!isnull(M.icon_scale_x_percent))
- desired_scale_x *= M.icon_scale_x_percent
+ . *= M.icon_scale_x_percent
+
+/mob/living/get_icon_scale_y()
+ . = ..()
+ for(var/datum/modifier/M in modifiers)
if(!isnull(M.icon_scale_y_percent))
- desired_scale_y *= M.icon_scale_y_percent
+ . *= M.icon_scale_y_percent
+
+/mob/living/update_transform()
+ // First, get the correct size.
+ var/desired_scale_x = get_icon_scale_x()
+ var/desired_scale_y = get_icon_scale_y()
// Now for the regular stuff.
var/matrix/M = matrix()
M.Scale(desired_scale_x, desired_scale_y)
M.Translate(0, 16*(desired_scale_y-1))
animate(src, transform = M, time = 10)
+ handle_status_indicators()
// This handles setting the client's color variable, which makes everything look a specific color.
// This proc is here so it can be called without needing to check if the client exists, or if the client relogs.
diff --git a/code/modules/mob/living/silicon/login.dm b/code/modules/mob/living/silicon/login.dm
index 3ffefc73c8..f2e15af0e8 100644
--- a/code/modules/mob/living/silicon/login.dm
+++ b/code/modules/mob/living/silicon/login.dm
@@ -1,3 +1,3 @@
/mob/living/silicon/Login()
- sleeping = 0
+ SetSleeping(0)
..()
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index 102e2e3762..7961442b19 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -32,7 +32,7 @@
// SetStunned(min(stunned, 30))
SetParalysis(min(paralysis, 30))
// SetWeakened(min(weakened, 20))
- sleeping = 0
+ SetSleeping(0)
adjustBruteLoss(0)
adjustToxLoss(0)
adjustOxyLoss(0)
@@ -78,7 +78,7 @@
if(src.sleeping)
Paralyse(3)
- src.sleeping--
+ AdjustSleeping(-1)
if(src.resting)
Weaken(5)
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 3be377b366..b6cf748fb8 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -115,25 +115,7 @@
/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0)
return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now
-/*
- if(!effect || (blocked >= 2)) return 0
- switch(effecttype)
- if(STUN)
- stunned = max(stunned,(effect/(blocked+1)))
- if(WEAKEN)
- weakened = max(weakened,(effect/(blocked+1)))
- if(PARALYZE)
- paralysis = max(paralysis,(effect/(blocked+1)))
- if(IRRADIATE)
- radiation += min((effect - (effect*getarmor(null, "rad"))), 0)//Rads auto check armor
- if(STUTTER)
- stuttering = max(stuttering,(effect/(blocked+1)))
- if(EYE_BLUR)
- eye_blurry = max(eye_blurry,(effect/(blocked+1)))
- if(DROWSY)
- drowsyness = max(drowsyness,(effect/(blocked+1)))
- updatehealth()
- return 1*/
+
/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai)
if(!istype(bot) || !istype(ai))
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm
index 7de14dc6e7..9502940343 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm
@@ -64,6 +64,8 @@
pixel_x = -16
old_x = -16
+ icon_expected_width = 64
+ icon_expected_height = 64
meat_amount = 5
/mob/living/simple_mob/animal/space/alien/queen
@@ -95,6 +97,8 @@
pixel_x = -16
old_x = -16
+ icon_expected_width = 64
+ icon_expected_height = 64
/mob/living/simple_mob/animal/space/alien/queen/empress/mother
name = "alien mother"
@@ -111,6 +115,8 @@
pixel_x = -32
old_x = -32
+ icon_expected_width = 96
+ icon_expected_height = 96
/mob/living/simple_mob/animal/space/alien/death()
..()
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm
index dbc0ab6340..307ec83073 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm
@@ -87,6 +87,8 @@
pixel_x = -16
default_pixel_x = -16
+ icon_expected_width = 64
+ icon_expected_height = 32
meat_amount = 3
@@ -108,6 +110,8 @@
pixel_y = -16
default_pixel_y = -16
+ icon_expected_width = 64
+ icon_expected_height = 64
meat_amount = 10
diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm
index 8a56f08ddd..2898eecf45 100644
--- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm
@@ -45,7 +45,7 @@
"rad" = 100)
/mob/living/simple_mob/construct/juggernaut/Life()
- weakened = 0
+ SetWeakened(0)
..()
/mob/living/simple_mob/construct/juggernaut/bullet_act(var/obj/item/projectile/P)
diff --git a/code/modules/mob/living/status_indicators.dm b/code/modules/mob/living/status_indicators.dm
new file mode 100644
index 0000000000..a3f386b9e6
--- /dev/null
+++ b/code/modules/mob/living/status_indicators.dm
@@ -0,0 +1,88 @@
+#define STATUS_INDICATOR_Y_OFFSET 2 // Offset from the edge of the icon sprite, so 32 pixels plus whatever number is here.
+#define STATUS_INDICATOR_ICON_X_SIZE 16 // Don't need to care about the Y size due to the origin being on the bottom side.
+#define STATUS_INDICATOR_ICON_MARGIN 2 // The space between two status indicators.
+
+// 'Status indicators' are icons that display over a mob's head, that visually indicate that the mob is suffering
+// from some kind of effect, such as being stunned, blinded, confused, asleep, etc.
+// The icons are managed automatically by the mob itself, so that their positions will shift if another indicator is added,
+// and it will try to always be above the mob sprite, even for larger sprites like xenos.
+
+/mob/living
+ var/list/status_indicators = null // Will become a list as needed.
+
+// Adds an icon_state, or image overlay, to the list of indicators to be managed automatically.
+// Also initializes the list if one doesn't exist.
+/mob/living/proc/add_status_indicator(image/thing)
+ if(get_status_indicator(thing)) // No duplicates, please.
+ return
+
+ if(!istype(thing, /image))
+ thing = image(icon = 'icons/mob/status_indicators.dmi', icon_state = thing)
+
+ LAZYADD(status_indicators, thing)
+ handle_status_indicators()
+
+// Similar to above but removes it instead, and nulls the list if it becomes empty as a result.
+/mob/living/proc/remove_status_indicator(image/thing)
+ thing = get_status_indicator(thing)
+
+ cut_overlay(thing)
+ LAZYREMOVE(status_indicators, thing)
+ handle_status_indicators()
+
+/mob/living/proc/get_status_indicator(image/thing)
+ if(!istype(thing, /image))
+ for(var/image/I in status_indicators)
+ if(I.icon_state == thing)
+ return I
+ return LAZYACCESS(status_indicators, LAZYFIND(status_indicators, thing))
+
+// Refreshes the indicators over a mob's head. Should only be called when adding or removing a status indicator with the above procs,
+// or when the mob changes size visually for some reason.
+/mob/living/proc/handle_status_indicators()
+ // First, get rid of all the overlays.
+ for(var/thing in status_indicators)
+ cut_overlay(thing)
+
+ if(!LAZYLEN(status_indicators))
+ return
+
+ if(stat == DEAD)
+ return
+
+ // Now put them back on in the right spot.
+ var/our_sprite_x = icon_expected_width * get_icon_scale_x()
+ var/our_sprite_y = icon_expected_height * get_icon_scale_y()
+
+ var/x_offset = our_sprite_x // Add your own offset here later if you want.
+ var/y_offset = our_sprite_y + STATUS_INDICATOR_Y_OFFSET
+
+ // Calculates how 'long' the row of indicators and the margin between them should be.
+ // The goal is to have the center of that row be horizontally aligned with the sprite's center.
+ var/expected_status_indicator_length = (STATUS_INDICATOR_ICON_X_SIZE * status_indicators.len) + (STATUS_INDICATOR_ICON_MARGIN * max(status_indicators.len - 1, 0))
+ var/current_x_position = (x_offset / 2) - (expected_status_indicator_length / 2)
+
+ // In /mob/living's `update_transform()`, the sprite is horizontally shifted when scaled up, so that the center of the sprite doesn't move to the right.
+ // Because of that, this adjustment needs to happen with the future indicator row as well, or it will look bad.
+ current_x_position -= (icon_expected_width / 2) * (get_icon_scale_y() - 1)
+
+ // Now the indicator row can actually be built.
+ for(var/thing in status_indicators)
+ var/image/I = thing
+
+ // This is a semi-HUD element, in a similar manner as medHUDs, in that they're 'above' everything else in the world,
+ // but don't pierce obfuscation layers such as blindness or darkness, unlike actual HUD elements like inventory slots.
+ I.plane = PLANE_STATUS
+ I.layer = HUD_LAYER
+ I.appearance_flags = PIXEL_SCALE|TILE_BOUND|NO_CLIENT_COLOR|RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM|KEEP_APART
+ I.pixel_y = y_offset
+ I.pixel_x = current_x_position
+ add_overlay(I)
+ // Adding the margin space every time saves a conditional check on the last iteration,
+ // and it won't cause any issues since no more icons will be added, and the var is not used for anything else.
+ current_x_position += STATUS_INDICATOR_ICON_X_SIZE + STATUS_INDICATOR_ICON_MARGIN
+
+
+#undef STATUS_INDICATOR_Y_OFFSET
+#undef STATUS_INDICATOR_ICON_X_SIZE
+#undef STATUS_INDICATOR_ICON_MARGIN
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 3859f40045..209ba2eb7a 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -65,6 +65,10 @@
plane_holder.set_ao(VIS_OBJS, ao_enabled)
plane_holder.set_ao(VIS_MOBS, ao_enabled)
+ // Status indicators
+ var/status_enabled = client.is_preference_enabled(/datum/client_preference/status_indicators)
+ plane_holder.set_vis(VIS_STATUS, status_enabled)
+
//set macro to normal incase it was overriden (like cyborg currently does)
client.set_hotkeys_macro("macro", "hotkeymode")
diff --git a/code/modules/mob/mob_planes.dm b/code/modules/mob/mob_planes.dm
index e1c33efe72..bf2ee1929f 100644
--- a/code/modules/mob/mob_planes.dm
+++ b/code/modules/mob/mob_planes.dm
@@ -29,6 +29,8 @@
plane_masters[VIS_CH_SPECIAL] = new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff
plane_masters[VIS_CH_STATUS_OOC]= new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD
+ plane_masters[VIS_STATUS] = new /obj/screen/plane_master{plane = PLANE_STATUS} //Status indicators that show over mob heads.
+
plane_masters[VIS_ADMIN1] = new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use
plane_masters[VIS_ADMIN2] = new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use
plane_masters[VIS_ADMIN3] = new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use
diff --git a/code/modules/organs/pain.dm b/code/modules/organs/pain.dm
index df7d81a142..ffc9a6149b 100644
--- a/code/modules/organs/pain.dm
+++ b/code/modules/organs/pain.dm
@@ -42,7 +42,7 @@ mob/living/carbon/human/proc/handle_pain()
maxdam = dam
if(damaged_organ && chem_effects[CE_PAINKILLER] < maxdam)
if(maxdam > 10 && paralysis)
- paralysis = max(0, paralysis - round(maxdam/10))
+ AdjustParalysis(-round(maxdam/10))
if(maxdam > 50 && prob(maxdam / 5))
drop_item()
var/burning = damaged_organ.burn_dam > damaged_organ.brute_dam
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm
index 05a084a5c1..f8f855dba1 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm
@@ -122,8 +122,8 @@
if(effective_dose >= strength * 6) // Toxic dose
M.add_chemical_effect(CE_ALCOHOL_TOXIC, toxicity*3)
if(effective_dose >= strength * 7) // Pass out
- M.paralysis = max(M.paralysis, 60)
- M.sleeping = max(M.sleeping, 90)
+ M.Paralyse(60)
+ M.Sleeping(90)
if(druggy != 0)
M.druggy = max(M.druggy, druggy*3)
@@ -166,8 +166,8 @@
if(dose * strength_mod >= strength * 6) // Toxic dose
M.add_chemical_effect(CE_ALCOHOL_TOXIC, toxicity)
if(dose * strength_mod >= strength * 7) // Pass out
- M.paralysis = max(M.paralysis, 20)
- M.sleeping = max(M.sleeping, 30)
+ M.Paralyse(20)
+ M.Sleeping(30)
if(druggy != 0)
M.druggy = max(M.druggy, druggy)
@@ -465,7 +465,7 @@
M.Weaken(2)
M.drowsyness = max(M.drowsyness, 20)
else
- M.sleeping = max(M.sleeping, 20)
+ M.Sleeping(20)
M.drowsyness = max(M.drowsyness, 60)
/datum/reagent/sulfur
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
index f1e991ae42..d8f0d90c29 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
@@ -321,7 +321,7 @@
M.Weaken(2)
M.drowsyness = max(M.drowsyness, 20)
else
- M.sleeping = max(M.sleeping, 20)
+ M.Sleeping(20)
M.drowsyness = max(M.drowsyness, 60)
/datum/reagent/nutriment/mayo
@@ -823,7 +823,7 @@
M.adjust_nutrition(nutrition * removed)
M.dizziness = max(0, M.dizziness + adj_dizzy)
M.drowsyness = max(0, M.drowsyness + adj_drowsy)
- M.sleeping = max(0, M.sleeping + adj_sleepy)
+ M.AdjustSleeping(adj_sleepy)
if(adj_temp > 0 && M.bodytemperature < 310) // 310 is the normal bodytemp. 310.055
M.bodytemperature = min(310, M.bodytemperature + (adj_temp * TEMPERATURE_DAMAGE_COEFFICIENT))
if(adj_temp < 0 && M.bodytemperature > 310)
@@ -908,7 +908,7 @@
M.Weaken(2)
M.drowsyness = max(M.drowsyness, 20)
else
- M.sleeping = max(M.sleeping, 20)
+ M.Sleeping(20)
M.drowsyness = max(M.drowsyness, 60)
/datum/reagent/drink/juice/lemon
@@ -1491,7 +1491,7 @@
M.Weaken(2)
M.drowsyness = max(M.drowsyness, 20)
else
- M.sleeping = max(M.sleeping, 20)
+ M.Sleeping(20)
M.drowsyness = max(M.drowsyness, 60)
/datum/reagent/drink/milkshake/chocoshake
@@ -2153,7 +2153,7 @@
..()
M.dizziness = max(0, M.dizziness - 5)
M.drowsyness = max(0, M.drowsyness - 3)
- M.sleeping = max(0, M.sleeping - 2)
+ M.AdjustSleeping(-2)
if(M.bodytemperature > 310)
M.bodytemperature = max(310, M.bodytemperature - (5 * TEMPERATURE_DAMAGE_COEFFICIENT))
if(alien == IS_TAJARA)
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm
index dea0a6a987..7e34762448 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm
@@ -197,7 +197,7 @@
M.drowsyness = 0
M.stuttering = 0
M.SetConfused(0)
- M.sleeping = 0
+ M.SetSleeping(0)
M.jitteriness = 0
M.radiation = 0
M.ExtinguishMob()
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
index 7ac94215f4..b541d66b44 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
@@ -170,7 +170,7 @@
/datum/reagent/toxin/cyanide/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
..()
M.adjustOxyLoss(20 * removed)
- M.sleeping += 1
+ M.Sleeping(1)
/datum/reagent/toxin/mold
name = "Mold"
@@ -645,7 +645,7 @@
else
M.Weaken(2)
else
- M.sleeping = max(M.sleeping, 20)
+ M.Sleeping(20)
M.drowsyness = max(M.drowsyness, 60)
/datum/reagent/chloralhydrate
@@ -689,7 +689,7 @@
M.Weaken(30)
M.Confuse(40)
else
- M.sleeping = max(M.sleeping, 30)
+ M.Sleeping(30)
if(effective_dose > 1 * threshold)
M.adjustToxLoss(removed)
diff --git a/code/modules/virus2/effect.dm b/code/modules/virus2/effect.dm
index 790bf3cdbd..876d0d30b6 100644
--- a/code/modules/virus2/effect.dm
+++ b/code/modules/virus2/effect.dm
@@ -404,7 +404,7 @@
stage = 2
/datum/disease2/effect/blind/activate(var/mob/living/carbon/mob,var/multiplier)
- mob.eye_blind = max(mob.eye_blind, 4)
+ mob.SetBlinded(4)
/datum/disease2/effect/cough
name = "Severe Cough"
diff --git a/code/modules/xenoarcheaology/finds/special.dm b/code/modules/xenoarcheaology/finds/special.dm
index 93bfa10966..2a7f149dfa 100644
--- a/code/modules/xenoarcheaology/finds/special.dm
+++ b/code/modules/xenoarcheaology/finds/special.dm
@@ -194,7 +194,7 @@
'sound/hallucinations/turn_around1.ogg',\
'sound/hallucinations/turn_around2.ogg',\
), 50, 1, -3)
- M.sleeping = max(M.sleeping,rand(5,10))
+ M.Sleeping(rand(5, 10))
src.loc = null
else
STOP_PROCESSING(SSobj, src)
diff --git a/code/modules/xenoarcheaology/tools/suspension_generator.dm b/code/modules/xenoarcheaology/tools/suspension_generator.dm
index f4a60f1bfd..561ac1028f 100644
--- a/code/modules/xenoarcheaology/tools/suspension_generator.dm
+++ b/code/modules/xenoarcheaology/tools/suspension_generator.dm
@@ -21,7 +21,7 @@
var/turf/T = get_turf(suspension_field)
for(var/mob/living/M in T)
- M.weakened = max(M.weakened, 3)
+ M.Weaken(3)
cell.charge -= power_use
if(prob(5))
to_chat(M, "[pick("You feel tingly","You feel like floating","It is hard to speak","You can barely move")].")
@@ -208,7 +208,7 @@
for(var/mob/living/M in T)
to_chat(M, "You no longer feel like floating.")
- M.weakened = min(M.weakened, 3)
+ M.Weaken(3)
src.visible_message("[bicon(src)] [src] deactivates with a gentle shudder.")
qdel(suspension_field)
diff --git a/icons/mob/status_indicators.dmi b/icons/mob/status_indicators.dmi
new file mode 100644
index 0000000000..01f1f0e36f
Binary files /dev/null and b/icons/mob/status_indicators.dmi differ
diff --git a/polaris.dme b/polaris.dme
index 084611b59c..95ac77ebf0 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -2064,6 +2064,7 @@
#include "code\modules\mob\living\login.dm"
#include "code\modules\mob\living\logout.dm"
#include "code\modules\mob\living\say.dm"
+#include "code\modules\mob\living\status_indicators.dm"
#include "code\modules\mob\living\bot\bot.dm"
#include "code\modules\mob\living\bot\cleanbot.dm"
#include "code\modules\mob\living\bot\ed209bot.dm"