diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm
index cd38c9ad5b..265f4e14db 100644
--- a/code/__DEFINES/_flags/_flags.dm
+++ b/code/__DEFINES/_flags/_flags.dm
@@ -33,8 +33,6 @@ GLOBAL_LIST_INIT(bitflags, list(
#define CONDUCT_1 (1<<5)
///For machines and structures that should not break into parts, eg, holodeck stuff.
#define NODECONSTRUCT_1 (1<<7)
-///Atom queued to SSoverlay.
-#define OVERLAY_QUEUED_1 (1<<8)
///Item has priority to check when entering or leaving.
#define ON_BORDER_1 (1<<9)
///Whether or not this atom shows screentips when hovered over
diff --git a/code/__DEFINES/overlays.dm b/code/__DEFINES/overlays.dm
new file mode 100644
index 0000000000..3e5959a680
--- /dev/null
+++ b/code/__DEFINES/overlays.dm
@@ -0,0 +1,24 @@
+// A reasonable number of maximum overlays an object needs
+// If you think you need more, rethink it
+#define MAX_ATOM_OVERLAYS 100
+
+/// Checks if an atom has reached the overlay limit, and make a loud error if it does.
+#define VALIDATE_OVERLAY_LIMIT(changed_on) \
+ if(length(changed_on.overlays) >= MAX_ATOM_OVERLAYS) { \
+ var/text_lays = overlays2text(changed_on.overlays); \
+ stack_trace("Too many overlays on [changed_on.type] - [length(changed_on.overlays)], refusing to update and cutting.\
+ \n What follows is a printout of all existing overlays at the time of the overflow \n[text_lays]"); \
+ changed_on.overlays.Cut(); \
+ changed_on.add_overlay(mutable_appearance('icons/testing/greyscale_error.dmi')); \
+ } \
+
+/// Performs any operations that ought to run after an appearance change
+/* #define POST_OVERLAY_CHANGE(changed_on) \
+ if(alternate_appearances) { \
+ for(var/I in changed_on.alternate_appearances){\
+ var/datum/atom_hud/alternate_appearance/AA = changed_on.alternate_appearances[I];\
+ if(AA.transfer_overlays){\
+ AA.copy_overlays(changed_on, TRUE);\
+ }\
+ } \
+ } */
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 49bad31a60..a66ec3ca61 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -228,25 +228,6 @@
#define SSMOBS_DT (SSmobs.wait/10)
#define SSOBJ_DT (SSobj.wait/10)
-//! ## Overlays subsystem
-
-///Compile all the overlays for an atom from the cache lists
-// |= on overlays is not actually guaranteed to not add same appearances but we're optimistically using it anyway.
-#define COMPILE_OVERLAYS(A)\
- do {\
- var/list/ad = A.add_overlays;\
- var/list/rm = A.remove_overlays;\
- if(LAZYLEN(rm)){\
- A.overlays -= rm;\
- rm.Cut();\
- }\
- if(LAZYLEN(ad)){\
- A.overlays |= ad;\
- ad.Cut();\
- }\
- A.flags_1 &= ~OVERLAY_QUEUED_1;\
- } while(FALSE)
-
/**
Create a new timer and add it to the queue.
* Arguments:
diff --git a/code/__HELPERS/custom_holoforms.dm b/code/__HELPERS/custom_holoforms.dm
index 87f3bfbe8b..4761c1f160 100644
--- a/code/__HELPERS/custom_holoforms.dm
+++ b/code/__HELPERS/custom_holoforms.dm
@@ -25,7 +25,6 @@
var/icon/combined = new
for(var/d in GLOB.cardinals)
mannequin.setDir(d)
- COMPILE_OVERLAYS(mannequin)
CHECK_TICK
var/icon/capture = getFlatIcon(mannequin)
CHECK_TICK
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index eb0aa448d3..e5aff01eef 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -1065,7 +1065,6 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
- COMPILE_OVERLAYS(body)
for(var/D in showDirs)
var/icon/partial = getFlatIcon(body, defdir = D, no_anim = no_anim)
out_icon.Insert(partial,dir=D)
diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index aef0022c4f..1ed2075537 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -79,10 +79,6 @@
#define CBT
#endif
-// A reasonable number of maximum overlays an object needs
-// If you think you need more, rethink it
-#define MAX_ATOM_OVERLAYS 100
-
#if !defined(CBT) && !defined(SPACEMAN_DMM)
#warn Building with Dream Maker is no longer supported and will result in errors.
#warn In order to build, run BUILD.bat in the root directory.
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index d16c24be44..e4ac2fe140 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -164,7 +164,6 @@ DEFINE_BITFIELD(flags_1, list(
"NO_LAVA_GEN_1" = NO_LAVA_GEN_1,
"NO_RUINS_1" = NO_RUINS_1,
"NO_SCREENTIPS_1" = NO_SCREENTIPS_1,
- "OVERLAY_QUEUED_1" = OVERLAY_QUEUED_1,
"ON_BORDER_1" = ON_BORDER_1,
"PREVENT_CLICK_UNDER_1" = PREVENT_CLICK_UNDER_1,
"PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1,
diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm
index 74b60783c3..1284664ce5 100644
--- a/code/controllers/subsystem/overlays.dm
+++ b/code/controllers/subsystem/overlays.dm
@@ -1,187 +1,114 @@
SUBSYSTEM_DEF(overlays)
name = "Overlay"
- flags = SS_TICKER
- wait = 1
- priority = FIRE_PRIORITY_OVERLAYS
- init_order = INIT_ORDER_OVERLAY
-
- var/list/queue
+ flags = SS_NO_FIRE|SS_NO_INIT
var/list/stats
- var/list/overlay_icon_state_caches
- var/list/overlay_icon_cache
/datum/controller/subsystem/overlays/PreInit()
- overlay_icon_state_caches = list()
- overlay_icon_cache = list()
- queue = list()
stats = list()
-/datum/controller/subsystem/overlays/Initialize()
- initialized = TRUE
- fire(mc_check = FALSE)
- return ..()
-
-
-/datum/controller/subsystem/overlays/stat_entry(msg)
- msg = "Ov:[length(queue)]"
- return ..()
-
-
/datum/controller/subsystem/overlays/Shutdown()
text2file(render_stats(stats), "[GLOB.log_directory]/overlay.log")
-
/datum/controller/subsystem/overlays/Recover()
- overlay_icon_state_caches = SSoverlays.overlay_icon_state_caches
- overlay_icon_cache = SSoverlays.overlay_icon_cache
- queue = SSoverlays.queue
+ stats = SSoverlays.stats
-
-/datum/controller/subsystem/overlays/fire(resumed = FALSE, mc_check = TRUE)
- var/list/queue = src.queue
- var/static/count = 0
- if (count)
- var/c = count
- count = 0 //so if we runtime on the Cut, we don't try again.
- queue.Cut(1,c+1)
-
- for (var/thing in queue)
- count++
- if(thing)
- var/atom/A = thing
- if(A.overlays.len >= MAX_ATOM_OVERLAYS)
- //Break it real GOOD
- stack_trace("Too many overlays on [A.type] - [A.overlays.len], refusing to update and cutting")
- A.overlays.Cut()
- continue
- STAT_START_STOPWATCH
- COMPILE_OVERLAYS(A)
- UNSETEMPTY(A.add_overlays)
- UNSETEMPTY(A.remove_overlays)
- STAT_STOP_STOPWATCH
- STAT_LOG_ENTRY(stats, A.type)
- if(mc_check)
- if(MC_TICK_CHECK)
- break
- else
- CHECK_TICK
-
- if (count)
- queue.Cut(1,count+1)
- count = 0
+/// Converts an overlay list into text for debug printing
+/// Of note: overlays aren't actually mutable appearances, they're just appearances
+/// Don't have access to that type tho, so this is the best you're gonna get
+/proc/overlays2text(list/overlays)
+ var/list/unique_overlays = list()
+ // As anything because we're basically doing type coerrsion, rather then actually filtering for mutable apperances
+ for(var/mutable_appearance/overlay as anything in overlays)
+ var/key = "[overlay.icon]-[overlay.icon_state]-[overlay.dir]"
+ unique_overlays[key] += 1
+ var/list/output_text = list()
+ for(var/key in unique_overlays)
+ output_text += "([key]) = [unique_overlays[key]]"
+ return output_text.Join("\n")
/proc/iconstate2appearance(icon, iconstate)
var/static/image/stringbro = new()
- var/list/icon_states_cache = SSoverlays.overlay_icon_state_caches
- var/list/cached_icon = icon_states_cache[icon]
- if (cached_icon)
- var/cached_appearance = cached_icon["[iconstate]"]
- if (cached_appearance)
- return cached_appearance
stringbro.icon = icon
stringbro.icon_state = iconstate
- if (!cached_icon) //not using the macro to save an associated lookup
- cached_icon = list()
- icon_states_cache[icon] = cached_icon
- var/cached_appearance = stringbro.appearance
- cached_icon["[iconstate]"] = cached_appearance
- return cached_appearance
+ return stringbro.appearance
/proc/icon2appearance(icon)
var/static/image/iconbro = new()
- var/list/icon_cache = SSoverlays.overlay_icon_cache
- . = icon_cache[icon]
- if (!.)
- iconbro.icon = icon
- . = iconbro.appearance
- icon_cache[icon] = .
+ iconbro.icon = icon
+ return iconbro.appearance
-/atom/proc/build_appearance_list(old_overlays)
- var/static/image/appearance_bro = new()
- var/list/new_overlays = list()
- if (!islist(old_overlays))
- old_overlays = list(old_overlays)
- for (var/overlay in old_overlays)
+/atom/proc/build_appearance_list(list/build_overlays)
+ if (!islist(build_overlays))
+ build_overlays = list(build_overlays)
+ for (var/overlay in build_overlays)
if(!overlay)
+ build_overlays -= overlay
continue
if (istext(overlay))
- new_overlays += iconstate2appearance(icon, overlay)
+#ifdef UNIT_TESTS
+ // This is too expensive to run normally but running it during CI is a good test
+ var/list/icon_states_available = icon_states(icon)
+ if(!(overlay in icon_states_available))
+ var/icon_file = "[icon]" || "Unknown Generated Icon"
+ stack_trace("Invalid overlay: Icon object '[icon_file]' [REF(icon)] used in '[src]' [type] is missing icon state [overlay].")
+ continue
+#endif
+ var/index = build_overlays.Find(overlay)
+ build_overlays[index] = iconstate2appearance(icon, overlay)
else if(isicon(overlay))
- new_overlays += icon2appearance(overlay)
- else
- if(isloc(overlay))
- var/atom/A = overlay
- if (A.flags_1 & OVERLAY_QUEUED_1)
- COMPILE_OVERLAYS(A)
- appearance_bro.appearance = overlay //this works for images and atoms too!
- if(!ispath(overlay))
- var/image/I = overlay
- appearance_bro.dir = I.dir
- new_overlays += appearance_bro.appearance
- return new_overlays
+ var/index = build_overlays.Find(overlay)
+ build_overlays[index] = icon2appearance(overlay)
+ return build_overlays
-#define NOT_QUEUED_ALREADY (!(flags_1 & OVERLAY_QUEUED_1))
-#define QUEUE_FOR_COMPILE flags_1 |= OVERLAY_QUEUED_1; SSoverlays.queue += src;
/atom/proc/cut_overlays()
- LAZYINITLIST(remove_overlays)
- remove_overlays = overlays.Copy()
- add_overlays = null
+ STAT_START_STOPWATCH
+ overlays = null
+// POST_OVERLAY_CHANGE(src)
+ STAT_STOP_STOPWATCH
+ STAT_LOG_ENTRY(SSoverlays.stats, type)
- //If not already queued for work and there are overlays to remove
- if(NOT_QUEUED_ALREADY && remove_overlays.len)
- QUEUE_FOR_COMPILE
-
-/atom/proc/cut_overlay(list/overlays)
+/atom/proc/cut_overlay(list/remove_overlays)
if(!overlays)
return
- overlays = build_appearance_list(overlays)
- LAZYINITLIST(add_overlays)
- LAZYINITLIST(remove_overlays)
- var/a_len = add_overlays.len
- var/r_len = remove_overlays.len
- remove_overlays += overlays
- add_overlays -= overlays
+ STAT_START_STOPWATCH
+ overlays -= build_appearance_list(remove_overlays)
+// POST_OVERLAY_CHANGE(src)
+ STAT_STOP_STOPWATCH
+ STAT_LOG_ENTRY(SSoverlays.stats, type)
- var/fa_len = add_overlays.len
- var/fr_len = remove_overlays.len
-
- //If not already queued and there is work to be done
- if(NOT_QUEUED_ALREADY && (fa_len != a_len || fr_len != r_len ))
- QUEUE_FOR_COMPILE
- UNSETEMPTY(add_overlays)
-
-/atom/proc/add_overlay(list/overlays)
+/atom/proc/add_overlay(list/add_overlays)
if(!overlays)
return
+ STAT_START_STOPWATCH
+ overlays += build_appearance_list(add_overlays)
+ VALIDATE_OVERLAY_LIMIT(src)
+// POST_OVERLAY_CHANGE(src)
+ STAT_STOP_STOPWATCH
+ STAT_LOG_ENTRY(SSoverlays.stats, type)
- overlays = build_appearance_list(overlays)
-
- LAZYINITLIST(add_overlays) //always initialized after this point
- var/a_len = add_overlays.len
-
- add_overlays += overlays
- var/fa_len = add_overlays.len
- if(NOT_QUEUED_ALREADY && fa_len != a_len)
- QUEUE_FOR_COMPILE
-
-/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom
+/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom
if(!other)
if(cut_old)
cut_overlays()
return
+ STAT_START_STOPWATCH
var/list/cached_other = other.overlays.Copy()
- if(cached_other)
- if(cut_old || !LAZYLEN(overlays))
- remove_overlays = overlays
- add_overlays = cached_other
- if(NOT_QUEUED_ALREADY)
- QUEUE_FOR_COMPILE
- else if(cut_old)
- cut_overlays()
-
-#undef NOT_QUEUED_ALREADY
-#undef QUEUE_FOR_COMPILE
+ if(cut_old)
+ if(cached_other)
+ overlays = cached_other
+ else
+ overlays = null
+ VALIDATE_OVERLAY_LIMIT(src)
+// POST_OVERLAY_CHANGE(src)
+ STAT_STOP_STOPWATCH
+ STAT_LOG_ENTRY(SSoverlays.stats, type)
+ else if(cached_other)
+ overlays += cached_other
+ VALIDATE_OVERLAY_LIMIT(src)
+// POST_OVERLAY_CHANGE(src)
+ STAT_STOP_STOPWATCH
+ STAT_LOG_ENTRY(SSoverlays.stats, type)
//TODO: Better solution for these?
/image/proc/add_overlay(x)
@@ -207,3 +134,100 @@ SUBSYSTEM_DEF(overlays)
overlays |= cached_other
else if(cut_old)
cut_overlays()
+
+// Debug procs
+
+/atom
+ /// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances
+ /// Drawn from the overlays list
+ var/list/realized_overlays
+ /// List of underlay "keys" (info about the appearance) -> mutable versions of static appearances
+ /// Drawn from the underlays list
+ var/list/realized_underlays
+
+/image
+ /// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances
+ /// Drawn from the overlays list
+ var/list/realized_overlays
+ /// List of underlay "keys" (info about the appearance) -> mutable versions of static appearances
+ /// Drawn from the underlays list
+ var/list/realized_underlays
+
+/// Takes the atoms's existing overlays and underlays, and makes them mutable so they can be properly vv'd in the realized_overlays/underlays list
+/atom/proc/realize_overlays()
+ realized_overlays = realize_appearance_queue(overlays)
+ realized_underlays = realize_appearance_queue(underlays)
+
+/// Takes the image's existing overlays, and makes them mutable so they can be properly vv'd in the realized_overlays list
+/image/proc/realize_overlays()
+ realized_overlays = realize_appearance_queue(overlays)
+ realized_underlays = realize_appearance_queue(underlays)
+
+/// Takes a list of appearnces, makes them mutable so they can be properly vv'd and inspected
+/proc/realize_appearance_queue(list/appearances)
+ var/list/real_appearances = list()
+ var/list/queue = appearances.Copy()
+ var/queue_index = 0
+ while(queue_index < length(queue))
+ queue_index++
+ // If it's not a command, we assert that it's an appearance
+ var/mutable_appearance/appearance = queue[queue_index]
+ if(!appearance) // Who fucking adds nulls to their sublists god you people are the worst
+ continue
+
+ var/mutable_appearance/new_appearance = new /mutable_appearance()
+ new_appearance.appearance = appearance
+ var/key = "[appearance.icon]-[appearance.icon_state]-[appearance.plane]-[appearance.layer]-[appearance.dir]-[appearance.color]"
+ var/tmp_key = key
+ var/appearance_indx = 1
+ while(real_appearances[tmp_key])
+ tmp_key = "[key]-[appearance_indx]"
+ appearance_indx++
+
+ real_appearances[tmp_key] = new_appearance
+ var/add_index = queue_index
+ // Now check its children
+ for(var/mutable_appearance/child_appearance as anything in appearance.overlays)
+ add_index++
+ queue.Insert(add_index, child_appearance)
+ for(var/mutable_appearance/child_appearance as anything in appearance.underlays)
+ add_index++
+ queue.Insert(add_index, child_appearance)
+ return real_appearances
+
+/// Takes two appearances as args, prints out, logs, and returns a text representation of their differences
+/// Including suboverlays
+/proc/diff_appearances(mutable_appearance/first, mutable_appearance/second, iter = 0)
+ var/list/diffs = list()
+ var/list/firstdeet = first.vars
+ var/list/seconddeet = second.vars
+ var/diff_found = FALSE
+ for(var/name in first.vars)
+ var/firstv = firstdeet[name]
+ var/secondv = seconddeet[name]
+ if(firstv ~= secondv)
+ continue
+ if((islist(firstv) || islist(secondv)) && length(firstv) == 0 && length(secondv) == 0)
+ continue
+ if(name == "vars") // Go away
+ continue
+ if(name == "_listen_lookup") // This is just gonna happen with marked datums, don't care
+ continue
+ if(name == "overlays")
+ first.realize_overlays()
+ second.realize_overlays()
+ var/overlays_differ = FALSE
+ for(var/i in 1 to length(first.realized_overlays))
+ if(diff_appearances(first.realized_overlays[i], second.realized_overlays[i], iter + 1))
+ overlays_differ = TRUE
+
+ if(!overlays_differ)
+ continue
+
+ diff_found = TRUE
+ diffs += "Diffs detected at [name]: First ([firstv]), Second ([secondv])"
+
+ var/text = "Depth of: [iter]\n\t[diffs.Join("\n\t")]"
+ message_admins(text)
+ log_world(text)
+ return diff_found
diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm
index aeb810cfdd..bb033c10c9 100644
--- a/code/datums/holocall.dm
+++ b/code/datums/holocall.dm
@@ -317,7 +317,6 @@
if(outfit_type)
mannequin.equipOutfit(outfit_type,TRUE)
mannequin.setDir(SOUTH)
- COMPILE_OVERLAYS(mannequin)
. = image(mannequin)
unset_busy_human_dummy("HOLODISK_PRESET")
diff --git a/code/game/machinery/computer/_computer.dm b/code/game/machinery/computer/_computer.dm
index 6b1d28fcba..19d6688ee8 100644
--- a/code/game/machinery/computer/_computer.dm
+++ b/code/game/machinery/computer/_computer.dm
@@ -43,18 +43,23 @@
/obj/machinery/computer/update_overlays()
. = ..()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
- if(stat & NOPOWER)
- . += "[icon_keyboard]_off"
- return
- . += icon_keyboard
+ if(icon_keyboard)
+ if(stat & NOPOWER)
+ . += "[icon_keyboard]_off"
+ else
+ . += icon_keyboard
- // This whole block lets screens ignore lighting and be visible even in the darkest room
- // We can't do this for many things that emit light unfortunately because it layers over things that would be on top of it
- var/overlay_state = icon_screen
if(stat & BROKEN)
- overlay_state = "[icon_state]_broken"
- . += mutable_appearance(icon, overlay_state)
- . += emissive_appearance(icon, overlay_state)
+ . += mutable_appearance(icon, "[icon_state]_broken")
+ return // If we don't do this broken computers glow in the dark.
+
+ if(stat & NOPOWER) // Your screen can't be on if you've got no damn charge
+ return
+
+ // This lets screens ignore lighting and be visible even in the darkest room
+ if(icon_screen)
+ . += mutable_appearance(icon, icon_screen)
+ . += emissive_appearance(icon, icon_screen)
/obj/machinery/computer/power_change()
..()
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index 276c1796b3..625886611a 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -69,7 +69,7 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
name = "random arcade"
desc = "random arcade machine"
icon_state = "arcade"
- icon_keyboard = "no_keyboard"
+ icon_keyboard = null
icon_screen = "invaders"
light_color = LIGHT_COLOR_GREEN
var/list/prize_override
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index 056a0dafda..97d2f3bab4 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -209,7 +209,7 @@
name = "security camera monitor"
desc = "An old TV hooked into the station's camera network."
icon_state = "television"
- icon_keyboard = "no_keyboard"
+ icon_keyboard = null
icon_screen = "detective_tv"
pass_flags = PASSTABLE
@@ -252,6 +252,7 @@
desc = "Used for watching an empty arena."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "telescreen"
+ icon_keyboard = null
layer = SIGN_LAYER
network = list("thunder")
density = FALSE
diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm
index a7071ac72e..54b515f74c 100644
--- a/code/game/machinery/computer/pod.dm
+++ b/code/game/machinery/computer/pod.dm
@@ -138,13 +138,13 @@
name = "\improper DoorMex control console"
icon_state = "oldcomp"
icon_screen = "library"
- icon_keyboard = "no_keyboard"
+ icon_keyboard = null
// /obj/machinery/computer/pod/old/mass_driver_controller
// name = "\improper Mass Driver Controller"
// icon = 'icons/obj/airlock_machines.dmi'
// icon_state = "airlock_control_standby"
-// icon_keyboard = "no_keyboard"
+// icon_keyboard = null
// density = FALSE
// /obj/machinery/computer/pod/old/mass_driver_controller/toxinsdriver
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index f7a6b6db2a..1c78345338 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -19,6 +19,7 @@
desc = "Gambling for the antisocial."
icon = 'icons/obj/economy.dmi'
icon_state = "slots1"
+ icon_keyboard = null
density = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 50
diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm
index 6d5ab1cdce..61641e1d49 100644
--- a/code/game/objects/items/RCD.dm
+++ b/code/game/objects/items/RCD.dm
@@ -558,8 +558,9 @@ RLD
. = ..()
if(has_ammobar)
var/ratio = CEILING((matter / max_matter) * ammo_sections, 1)
- cut_overlays() //To prevent infinite stacking of overlays
- add_overlay("[icon_state]_charge[ratio]")
+ if(ratio)
+ cut_overlays() //To prevent infinite stacking of overlays
+ add_overlay("[icon_state]_charge[ratio]")
/obj/item/construction/rcd/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 4df3accb67..a154b8a638 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -19,6 +19,8 @@
var/on = FALSE //if the paddles are equipped (1) or on the defib (0)
var/safety = TRUE //if you can zap people with the defibs on harm mode
var/powered = FALSE //if there's a cell in the defib with enough power for a revive, blocks paddles from reviving otherwise
+ /// If the cell can be removed via screwdriver
+ var/cell_removable = TRUE
var/obj/item/shockpaddles/paddles
var/obj/item/stock_parts/cell/cell
var/combat = FALSE //if true, revive through hardsuits, allow for combat shocking, and tint paddles syndicate colors
@@ -29,6 +31,16 @@
var/timedeath = 10
var/disarm_shock_time = 10
var/always_emagged = FALSE
+ /// The icon state for the paddle overlay, not applied if null
+ var/paddle_state = "defibunit-paddles"
+ /// The icon state for the powered on overlay, not applied if null
+ var/powered_state = "defibunit-powered"
+ /// The icon state for the charge bar overlay, not applied if null
+ var/charge_state = "defibunit-charge"
+ /// The icon state for the missing cell overlay, not applied if null
+ var/nocell_state = "defibunit-nocell"
+ /// The icon state for the emagged overlay, not applied if null
+ var/emagged_state = "defibunit-emagged"
/obj/item/defibrillator/get_cell()
return cell
@@ -56,18 +68,19 @@
/obj/item/defibrillator/update_overlays()
. = ..()
- if(!on)
- . += "[initial(icon_state)]-paddles"
- if(powered)
- . += "[initial(icon_state)]-powered"
- if(!QDELETED(cell))
+
+ if(!on && paddle_state)
+ . += paddle_state
+ if(powered && powered_state)
+ . += powered_state
+ if(!QDELETED(cell) && charge_state)
var/ratio = cell.charge / cell.maxcharge
ratio = CEILING(ratio*4, 1) * 25
- add_overlay("[initial(icon_state)]-charge[ratio]")
- if(!cell)
- . += "[initial(icon_state)]-nocell"
- if(!safety)
- . += "[initial(icon_state)]-emagged"
+ . += "[charge_state][ratio]"
+ if(!cell && nocell_state)
+ . += "[nocell_state]"
+ if(!safety && emagged_state)
+ . += emagged_state
/obj/item/defibrillator/CheckParts(list/parts_list)
..()
@@ -103,6 +116,18 @@
var/atom/movable/screen/inventory/hand/H = over_object
M.putItemFromInventoryInHandIfPossible(src, H.held_index)
+/obj/item/defibrillator/screwdriver_act(mob/living/user, obj/item/tool)
+ if(!cell || !cell_removable)
+ return FALSE
+
+ cell.update_appearance()
+ cell.forceMove(get_turf(src))
+ balloon_alert(user, "removed [cell]")
+ cell = null
+ tool.play_tool_sound(src, 50)
+ update_power()
+ return TRUE
+
/obj/item/defibrillator/attackby(obj/item/W, mob/user, params)
if(W == paddles)
toggle_paddles()
@@ -120,13 +145,6 @@
to_chat(user, "You install a cell in [src].")
update_power()
- else if(W.tool_behaviour == TOOL_SCREWDRIVER)
- if(cell)
- cell.update_icon()
- cell.forceMove(get_turf(src))
- cell = null
- to_chat(user, "You remove the cell from [src].")
- update_power()
else
return ..()
@@ -236,6 +254,11 @@
item_state = "defibcompact"
w_class = WEIGHT_CLASS_NORMAL
slot_flags = ITEM_SLOT_BELT
+ paddle_state = "defibcompact-paddles"
+ powered_state = "defibcompact-powered"
+ charge_state = "defibcompact-charge"
+ nocell_state = "defibcompact-nocell"
+ emagged_state = "defibcompact-emagged"
/obj/item/defibrillator/compact/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == user.getBeltSlot())
@@ -254,6 +277,13 @@
always_emagged = TRUE
disarm_shock_time = 0
cell = /obj/item/stock_parts/cell/infinite
+ paddles = /obj/item/shockpaddles/syndicate
+ paddle_state = "defibcombat-paddles"
+ powered_state = null
+ emagged_state = null
+
+/obj/item/defibrillator/compact/combat/loaded
+ cell_removable = FALSE // Don't let people just have an infinite power cell
/obj/item/defibrillator/compact/combat/loaded/attackby(obj/item/W, mob/user, params)
if(W == paddles)
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index 667e8f9db9..9aa29a4d75 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -639,6 +639,7 @@
icon = 'icons/obj/cigarettes.dmi'
icon_state = "matchbox"
item_state = "zippo"
+ illustration = null
w_class = WEIGHT_CLASS_TINY
slot_flags = ITEM_SLOT_BELT
custom_price = PRICE_REALLY_CHEAP
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 0176abd505..a713ddfbf4 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -76,7 +76,8 @@
. += "[icon_door_override ? icon_door : icon_state]_open"
return
- . += "[icon_door || icon_state]_door"
+ if(icon_door)
+ . += "[icon_door || icon_state]_door"
if(welded)
. += icon_welded
diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
index 14d28b17d3..fc0b7c96c8 100644
--- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
+++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
@@ -3,6 +3,7 @@
name = "large cardboard box"
desc = "Just a box..."
icon_state = "cardboard"
+ icon_door = null
mob_storage_capacity = 1
resistance_flags = FLAMMABLE
max_integrity = 70
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index 89c0e0210e..9e570608f2 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -307,7 +307,6 @@ GLOBAL_VAR_INIT(say_disabled, FALSE)
qdel(I)
randomize_human(D)
JB.equip(D, TRUE, FALSE)
- COMPILE_OVERLAYS(D)
var/icon/I = icon(getFlatIcon(D), frame = 1)
final.Insert(I, JB.title)
qdel(D)
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index e7fc2c3f94..292c0564b0 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -314,7 +314,6 @@
equipAntagOnDummy(mannequin, ert)
- COMPILE_OVERLAYS(mannequin)
CHECK_TICK
var/icon/preview_icon = icon('icons/effects/effects.dmi', "nothing")
preview_icon.Scale(48+32, 16+32)
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 136a4c6e69..9f3556dbb6 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -391,7 +391,6 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/render_preview_outfit(datum/outfit/outfit, mob/living/carbon/human/dummy)
dummy = dummy || new /mob/living/carbon/human/dummy/consistent
dummy.equipOutfit(outfit, visualsOnly = TRUE)
- COMPILE_OVERLAYS(dummy)
var/icon = getFlatIcon(dummy)
// We don't want to qdel the dummy right away, since its items haven't initialized yet.
diff --git a/code/modules/antagonists/abductor/machinery/camera.dm b/code/modules/antagonists/abductor/machinery/camera.dm
index 9e0a2b46b1..29404eb24e 100644
--- a/code/modules/antagonists/abductor/machinery/camera.dm
+++ b/code/modules/antagonists/abductor/machinery/camera.dm
@@ -13,6 +13,7 @@
icon = 'icons/obj/abductor.dmi'
icon_state = "camera"
+ icon_keyboard = null
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
/obj/machinery/computer/camera_advanced/abductor/CreateEye()
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index 479a80b0f6..d7d53ba691 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -1342,7 +1342,6 @@ GLOBAL_LIST_INIT(hallucination_list, list(
var/datum/preferences/prefs = C.client.prefs
var/mob/living/carbon/human/dummy/M = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_HALLUCINATION)
prefs.copy_to(M)
- COMPILE_OVERLAYS(M)
CHECK_TICK
image = image(M,C)
unset_busy_human_dummy(DUMMY_HUMAN_SLOT_HALLUCINATION)
diff --git a/code/modules/mining/aux_base.dm b/code/modules/mining/aux_base.dm
index 14545f9570..78314ee6b6 100644
--- a/code/modules/mining/aux_base.dm
+++ b/code/modules/mining/aux_base.dm
@@ -15,6 +15,7 @@
name = "auxillary base management console"
icon = 'icons/obj/terminals.dmi'
icon_state = "dorm_available"
+ icon_keyboard = null
var/shuttleId = "colony_drop"
desc = "Allows a deployable expedition base to be dropped from the station to a designated mining location. It can also \
interface with the mining shuttle at the landing site if a mobile beacon is also deployed."
diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm
index a978e541bf..aaa5e04c0e 100644
--- a/code/modules/mob/dead/new_player/preferences_setup.dm
+++ b/code/modules/mob/dead/new_player/preferences_setup.dm
@@ -70,7 +70,6 @@
mannequin.regenerate_icons()
- COMPILE_OVERLAYS(mannequin)
parent.show_character_previews(new /mutable_appearance(mannequin))
unset_busy_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES)
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index 51710affc0..bf8899e5d5 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -20,7 +20,8 @@
var/chargerate = 100 //how much power is given every tick in a recharger
var/self_recharge = 0 //does it self recharge, over time, or not?
var/ratingdesc = TRUE
- var/grown_battery = FALSE // If it's a grown that acts as a battery, add a wire overlay to it.
+ ///If it's a grown that acts as a battery, add a wire overlay to it.
+ var/grown_battery = FALSE
rad_flags = RAD_NO_CONTAMINATE // Prevent the same cheese as with the stock parts
/obj/item/stock_parts/cell/get_cell()
@@ -61,7 +62,8 @@
/obj/item/stock_parts/cell/update_overlays()
. = ..()
if(grown_battery)
- . += image('icons/obj/power.dmi',"grown_wires")
+ . += image('icons/obj/power.dmi', "grown_wires")
+ return
if(charge < 0.01)
return
else if(charge/maxcharge >=0.995)
diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm
index 0bb1c3cada..f0285a5ad9 100644
--- a/code/modules/projectiles/guns/ballistic/automatic.dm
+++ b/code/modules/projectiles/guns/ballistic/automatic.dm
@@ -220,6 +220,7 @@
can_suppress = FALSE
burst_size = 3
burst_shot_delay = 1
+ automatic_burst_overlay = FALSE
// Bulldog shotgun //
diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm
index a34e0f6f12..4fd0220ee3 100644
--- a/code/modules/projectiles/guns/energy/energy_gun.dm
+++ b/code/modules/projectiles/guns/energy/energy_gun.dm
@@ -45,6 +45,8 @@
desc = "A modified version of the basic phaser gun, this one fires less concentrated energy bolts designed for target practice."
ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser/practice)
icon_state = "decloner"
+ //You have no icons for energy types, you're a decloner
+ modifystate = FALSE
/obj/item/gun/energy/e_gun/hos
name = "\improper X-01 MultiPhase Energy Gun"
@@ -78,6 +80,7 @@
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
ammo_type = list(/obj/item/ammo_casing/energy/net, /obj/item/ammo_casing/energy/trap)
+ modifystate = FALSE
can_flashlight = 0
ammo_x_offset = 1
diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm
index 6991aeef43..7ae4aae5fc 100644
--- a/code/modules/reagents/chemistry/machinery/pandemic.dm
+++ b/code/modules/reagents/chemistry/machinery/pandemic.dm
@@ -7,6 +7,7 @@
density = TRUE
icon = 'icons/obj/chemical.dmi'
icon_state = "mixer0"
+ icon_keyboard = null
use_power = TRUE
idle_power_usage = 20
resistance_flags = ACID_PROOF
diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm
index 794f2bcd44..d6d934530c 100644
--- a/code/modules/shuttle/syndicate.dm
+++ b/code/modules/shuttle/syndicate.dm
@@ -45,6 +45,7 @@
desc = "Controls the drop pod's launch system."
icon = 'icons/obj/terminals.dmi'
icon_state = "dorm_available"
+ icon_keyboard = null
light_color = LIGHT_COLOR_BLUE
req_access = list(ACCESS_SYNDICATE)
shuttleId = "steel_rain"
diff --git a/icons/Testing/greyscale_error.dmi b/icons/Testing/greyscale_error.dmi
new file mode 100644
index 0000000000..6c781a70ad
Binary files /dev/null and b/icons/Testing/greyscale_error.dmi differ
diff --git a/icons/obj/dice.dmi b/icons/obj/dice.dmi
index 1d6601aa31..aecdbde71c 100644
Binary files a/icons/obj/dice.dmi and b/icons/obj/dice.dmi differ
diff --git a/icons/obj/monitors.dmi b/icons/obj/monitors.dmi
index 580d78900d..fdffb346ea 100644
Binary files a/icons/obj/monitors.dmi and b/icons/obj/monitors.dmi differ
diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi
index 3204b36cc5..d4f1eb71ee 100644
Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 9d25bf3a57..4d5b5f23de 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -90,6 +90,7 @@
#include "code\__DEFINES\movespeed_modification.dm"
#include "code\__DEFINES\nanites.dm"
#include "code\__DEFINES\networks.dm"
+#include "code\__DEFINES\overlays.dm"
#include "code\__DEFINES\pinpointers.dm"
#include "code\__DEFINES\pipe_construction.dm"
#include "code\__DEFINES\plumbing.dm"