From a6e8f69c55ee907580d9f1259f01efd456c2ef78 Mon Sep 17 00:00:00 2001
From: Mira <42539014+MiraHell@users.noreply.github.com>
Date: Thu, 6 Mar 2025 19:04:51 +0300
Subject: [PATCH] [PORT] Adds TG "busy" cogbar animation for players (#27932)
* looks like it works
* move to PREFTOGGLE_3 and add sneaky do_after
* linter
* param doc and some cleanup
* autodoc and comment signal
* fix wrong type in link_processing
* fix wrong var in preferences
* cover do_mob and do_after_once
* fix toggles total
* replace indent with space
* offset and helpers procs
* fix conditional statement
* Apply suggestions from code review
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Signed-off-by: Mira <42539014+MiraHell@users.noreply.github.com>
* Update code/__HELPERS/mob_helpers.dm
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Signed-off-by: Mira <42539014+MiraHell@users.noreply.github.com>
* use SECONDS
* Update code/game/objects/items/weapons/weaponry.dm
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
Signed-off-by: Mira <42539014+MiraHell@users.noreply.github.com>
---------
Signed-off-by: Mira <42539014+MiraHell@users.noreply.github.com>
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
---
code/__DEFINES/layers.dm | 1 +
code/__DEFINES/preferences_defines.dm | 8 +-
code/__DEFINES/subsystems.dm | 20 +--
code/__HELPERS/iconprocs.dm | 19 +++
code/__HELPERS/mob_helpers.dm | 19 ++-
code/_globalvars/lists/icons.dm | 2 +
code/_onclick/hud/plane_master.dm | 10 ++
code/controllers/subsystem/SSvis_overlays.dm | 130 ++++++++++++++++++
code/datums/cogbar.dm | 119 ++++++++++++++++
code/datums/progressbar.dm | 2 +-
code/game/atoms.dm | 3 +
.../gamemodes/malfunction/Malf_Modules.dm | 2 +-
.../abduction/machinery/experiment.dm | 2 +-
code/game/objects/effects/overlays.dm | 9 ++
.../objects/items/devices/traitordevices.dm | 2 +-
code/game/objects/items/weapons/handcuffs.dm | 2 +-
code/game/objects/items/weapons/legcuffs.dm | 2 +-
code/game/objects/items/weapons/weaponry.dm | 2 +-
code/game/objects/structures/kitchen_spike.dm | 2 +-
code/game/objects/structures/safe.dm | 2 +-
.../stool_bed_chair_nest/alien_nests.dm | 2 +-
.../changeling/powers/summon_spiders.dm | 2 +-
.../antagonists/mind_flayer/flayer_datum.dm | 2 +-
.../powers/flayer_stealth_powers.dm | 2 +-
.../contractor/datums/syndicate_contract.dm | 2 +-
.../modules/antagonists/vampire/vamp_datum.dm | 2 +-
.../vampire_powers/dantalion_powers.dm | 2 +-
.../vampire/vampire_powers/umbrae_powers.dm | 2 +-
.../client/preference/link_processing.dm | 6 +
code/modules/client/preference/preferences.dm | 1 +
code/modules/clothing/clothing.dm | 2 +-
code/modules/martial_arts/cqc.dm | 2 +-
.../modules/mob/living/carbon/carbon_procs.dm | 4 +-
.../modules/mob/living/living_status_procs.dm | 2 +-
.../living/simple_animal/hostile/headslug.dm | 2 +-
code/modules/mob/mob_misc_procs.dm | 2 +-
icons/blanks/32x32.dmi | Bin 0 -> 216 bytes
icons/effects/progessbar.dmi | Bin 559 -> 0 bytes
icons/effects/progressbar.dmi | Bin 0 -> 833 bytes
paradise.dme | 3 +
40 files changed, 357 insertions(+), 41 deletions(-)
create mode 100644 code/_globalvars/lists/icons.dm
create mode 100644 code/controllers/subsystem/SSvis_overlays.dm
create mode 100644 code/datums/cogbar.dm
create mode 100644 icons/blanks/32x32.dmi
delete mode 100644 icons/effects/progessbar.dmi
create mode 100644 icons/effects/progressbar.dmi
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index cfe41cb599e..ac64faafbfb 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -112,6 +112,7 @@
#define EMISSIVE_RENDER_TARGET "*EMISSIVE_PLANE"
#define POINT_PLANE 14
+#define COGBAR_PLANE 14
#define LIGHTING_PLANE 15
#define LIGHTING_LAYER 15
diff --git a/code/__DEFINES/preferences_defines.dm b/code/__DEFINES/preferences_defines.dm
index 8aaf307cdc4..846fe3893b4 100644
--- a/code/__DEFINES/preferences_defines.dm
+++ b/code/__DEFINES/preferences_defines.dm
@@ -82,11 +82,11 @@
// toggles_3 variables. These MUST be prefixed with PREFTOGGLE_3
-#define TOGGLES_3_TOTAL 1023 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined.
+#define PREFTOGGLE_3_COGBAR_ANIMATIONS (1<<0) // 1
-// When you add a toggle here, inform AA on merge so the column can be zeroed out. This needs to exist to avoid the compiler freaking out
-// Also update the above value to the actual total
-#define TOGGLES_3_DEFAULT (1)
+#define TOGGLES_3_TOTAL 1 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined.
+
+#define TOGGLES_3_DEFAULT (PREFTOGGLE_3_COGBAR_ANIMATIONS)
// Sanity checks
// I should really convert these to a JSON list at some point hnnnnnng
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 740b2276fca..3ae5c42211c 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -50,15 +50,16 @@
#define INIT_ORDER_PROFILER 101
#define INIT_ORDER_QUEUE 100 // Load this quickly so people cant queue skip
#define INIT_ORDER_TITLE 99 // Load this quickly so people dont see a blank lobby screen
-#define INIT_ORDER_GARBAGE 24
-#define INIT_ORDER_DBCORE 23
-#define INIT_ORDER_REDIS 22 // Make sure we dont miss any events
-#define INIT_ORDER_BLACKBOX 21
-#define INIT_ORDER_CLEANUP 20
-#define INIT_ORDER_INPUT 19
-#define INIT_ORDER_SOUNDS 18
-#define INIT_ORDER_INSTRUMENTS 17
-#define INIT_ORDER_RESEARCH 16 // SoonTM
+#define INIT_ORDER_GARBAGE 25
+#define INIT_ORDER_DBCORE 24
+#define INIT_ORDER_REDIS 23 // Make sure we dont miss any events
+#define INIT_ORDER_BLACKBOX 22
+#define INIT_ORDER_CLEANUP 21
+#define INIT_ORDER_INPUT 20
+#define INIT_ORDER_SOUNDS 19
+#define INIT_ORDER_INSTRUMENTS 18
+#define INIT_ORDER_RESEARCH 17 // SoonTM
+#define INIT_ORDER_VIS 16
#define INIT_ORDER_STATION 15 //This is high priority because it manipulates a lot of the subsystems that will initialize after it.
#define INIT_ORDER_EVENTS 14
#define INIT_ORDER_JOBS 13
@@ -100,6 +101,7 @@
#define FIRE_PRIORITY_TICKETS 10
#define FIRE_PRIORITY_RESEARCH 10 // SoonTM
#define FIRE_PRIORITY_AMBIENCE 10
+#define FIRE_PRIORITY_VIS 10
#define FIRE_PRIORITY_GARBAGE 15
#define FIRE_PRIORITY_AIR 20
#define FIRE_PRIORITY_NPC 20
diff --git a/code/__HELPERS/iconprocs.dm b/code/__HELPERS/iconprocs.dm
index e12305b2d3f..41975d1a354 100644
--- a/code/__HELPERS/iconprocs.dm
+++ b/code/__HELPERS/iconprocs.dm
@@ -529,6 +529,25 @@ GLOBAL_LIST_EMPTY(bicon_cache)
var/icon/I = getFlatIcon(thing)
return icon2asset(I, target)
+/// Returns a list containing the width and height of an icon file
+/proc/get_icon_dimensions(icon_path)
+ if(isnull(GLOB.icon_dimensions[icon_path]))
+ var/icon/my_icon = icon(icon_path)
+ GLOB.icon_dimensions[icon_path] = list("width" = my_icon.Width(), "height" = my_icon.Height())
+ return GLOB.icon_dimensions[icon_path]
+
+/// Returns an x and y value require to reverse the transformations made to center an oversized icon
+/atom/proc/get_oversized_icon_offsets()
+ if(pixel_x == 0 && pixel_y == 0)
+ return list("x" = 0, "y" = 0)
+ var/list/icon_dimensions = get_icon_dimensions(icon)
+ var/icon_width = icon_dimensions["width"]
+ var/icon_height = icon_dimensions["height"]
+ return list(
+ "x" = icon_width > world.icon_size && pixel_x != 0 ? (icon_width - world.icon_size) * 0.5 : 0,
+ "y" = icon_height > world.icon_size && pixel_y != 0 ? (icon_height - world.icon_size) * 0.5 : 0,
+ )
+
/// Slimifies an icon by painting a color onto the icon, then changing the alpha.
/icon/proc/slimify(color = "#FFFFFF", alpha = "FF")
GrayScale()
diff --git a/code/__HELPERS/mob_helpers.dm b/code/__HELPERS/mob_helpers.dm
index f02e86e6856..e68a1916aa4 100644
--- a/code/__HELPERS/mob_helpers.dm
+++ b/code/__HELPERS/mob_helpers.dm
@@ -330,7 +330,7 @@
msg_admin_attack("[key_name_admin(user)] vs [target_info]: [what_done]", loglevel)
-/proc/do_mob(mob/user, mob/target, time = 30, progress = 1, list/extra_checks = list(), only_use_extra_checks = FALSE, requires_upright = TRUE)
+/proc/do_mob(mob/user, mob/target, time = 30, progress = 1, list/extra_checks = list(), only_use_extra_checks = FALSE, requires_upright = TRUE, hidden = FALSE)
if(!user || !target)
return 0
var/user_loc = user.loc
@@ -343,9 +343,13 @@
var/holding = user.get_active_hand()
var/datum/progressbar/progbar
+ var/datum/cogbar/cog
if(progress)
progbar = new(user, time, target)
+ if(!hidden && time >= 1 SECONDS)
+ cog = new(user)
+
var/endtime = world.time+time
var/starttime = world.time
. = 1
@@ -376,6 +380,7 @@
break
if(progress)
qdel(progbar)
+ cog?.remove()
/* Use this proc when you want to have code under it execute after a delay, and ensure certain conditions are met during that delay...
* Such as the user not being interrupted via getting stunned or by moving off the tile they're currently on.
@@ -387,8 +392,9 @@
*
* This will create progress bar that lasts for 5 seconds. If the user doesn't move or otherwise do something that would cause the checks to fail in those 5 seconds, do_stuff() would execute.
* The Proc returns TRUE upon success (the progress bar reached the end), or FALSE upon failure (the user moved or some other check failed)
+ * param {boolean} hidden - By default, any action 1 second or longer shows a cog over the user while it is in progress. If hidden is set to TRUE, the cog will not be shown.
*/
-/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, allow_moving = 0, must_be_held = 0, list/extra_checks = list(), use_default_checks = TRUE, allow_moving_target = FALSE)
+/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, allow_moving = 0, must_be_held = 0, list/extra_checks = list(), use_default_checks = TRUE, allow_moving_target = FALSE, hidden = FALSE)
if(!user)
return FALSE
var/atom/Tloc = null
@@ -408,9 +414,13 @@
holdingnull = FALSE //Users hand started holding something, check to see if it's still holding that
var/datum/progressbar/progbar
+ var/datum/cogbar/cog
if(progress)
progbar = new(user, delay, target)
+ if(!hidden && delay >= 1 SECONDS)
+ cog = new(user)
+
var/endtime = world.time + delay
var/starttime = world.time
. = TRUE
@@ -457,6 +467,7 @@
break
if(progress)
qdel(progbar)
+ cog?.remove()
// Upon any of the callbacks in the list returning TRUE, the proc will return TRUE.
/proc/check_for_true_callbacks(list/extra_checks)
@@ -467,7 +478,7 @@
#define DOAFTERONCE_MAGIC "Magic~~"
GLOBAL_LIST_EMPTY(do_after_once_tracker)
-/proc/do_after_once(mob/user, delay, needhand = 1, atom/target = null, progress = 1, allow_moving, must_be_held, attempt_cancel_message = "Attempt cancelled.", special_identifier)
+/proc/do_after_once(mob/user, delay, needhand = 1, atom/target = null, progress = 1, allow_moving, must_be_held, attempt_cancel_message = "Attempt cancelled.", special_identifier, hidden = FALSE)
if(!user || !target)
return
@@ -477,7 +488,7 @@ GLOBAL_LIST_EMPTY(do_after_once_tracker)
to_chat(user, "[attempt_cancel_message]")
return FALSE
GLOB.do_after_once_tracker[cache_key] = TRUE
- . = do_after(user, delay, needhand, target, progress, allow_moving, must_be_held, extra_checks = list(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(do_after_once_checks), cache_key)))
+ . = do_after(user, delay, needhand, target, progress, allow_moving, must_be_held, extra_checks = list(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(do_after_once_checks), cache_key, hidden)))
GLOB.do_after_once_tracker[cache_key] = FALSE
/proc/do_after_once_checks(cache_key)
diff --git a/code/_globalvars/lists/icons.dm b/code/_globalvars/lists/icons.dm
new file mode 100644
index 00000000000..ff60e6bc8d9
--- /dev/null
+++ b/code/_globalvars/lists/icons.dm
@@ -0,0 +1,2 @@
+/// Cache of the width and height of icon files, to avoid repeating the same expensive operation
+GLOBAL_LIST_EMPTY(icon_dimensions)
diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm
index faabf24b70c..e0bb820fcd6 100644
--- a/code/_onclick/hud/plane_master.dm
+++ b/code/_onclick/hud/plane_master.dm
@@ -61,6 +61,16 @@
if(istype(mymob) && mymob.client && mymob.client.prefs)
alpha = (mymob.client.prefs.toggles2 & PREFTOGGLE_2_THOUGHT_BUBBLE) ? 255 : 0
+/atom/movable/screen/plane_master/cogbar
+ name = "cogbar plane master"
+ plane = COGBAR_PLANE
+ appearance_flags = PLANE_MASTER //should use client color
+ blend_mode = BLEND_OVERLAY
+
+/atom/movable/screen/plane_master/cogbar/backdrop(mob/mymob)
+ if(istype(mymob) && mymob.client?.prefs)
+ alpha = (mymob.client.prefs.toggles3 & PREFTOGGLE_3_COGBAR_ANIMATIONS) ? 255 : 0
+
/**
* Things placed on this mask the lighting plane. Doesn't render directly.
*
diff --git a/code/controllers/subsystem/SSvis_overlays.dm b/code/controllers/subsystem/SSvis_overlays.dm
new file mode 100644
index 00000000000..6dc7f0c4f38
--- /dev/null
+++ b/code/controllers/subsystem/SSvis_overlays.dm
@@ -0,0 +1,130 @@
+/**
+ * # SSvis_overlays
+ *
+ * This subsystem handles visual overlays for objects in the game world.
+ *
+ * It manages the creation, caching, and removal of visual overlays, allowing for dynamic visual effects
+ * associated with various atoms. Overlays can be reused if they are not unique, and they are automatically
+ * cleaned up if they go unused for a specified duration.
+ */
+SUBSYSTEM_DEF(vis_overlays)
+ name = "Vis contents overlays"
+ wait = 1 MINUTES
+ priority = FIRE_PRIORITY_VIS
+ init_order = INIT_ORDER_VIS
+
+ /// Cache for visual overlays
+ var/list/vis_overlay_cache
+ /// List of currently processed overlays during a run
+ var/list/currentrun
+
+/datum/controller/subsystem/vis_overlays/Initialize()
+ vis_overlay_cache = list()
+
+/datum/controller/subsystem/vis_overlays/fire(resumed = FALSE)
+ if(!resumed)
+ currentrun = vis_overlay_cache.Copy()
+ var/list/current_run = currentrun
+
+ while(length(current_run))
+ var/key = current_run[current_run.len]
+ var/obj/effect/overlay/vis/overlay = current_run[key]
+ current_run.len--
+ if(!overlay.unused && !length(overlay.vis_locs))
+ overlay.unused = world.time
+ else if(overlay.unused && overlay.unused + overlay.cache_expiration < world.time)
+ vis_overlay_cache -= key
+ qdel(overlay)
+ if(MC_TICK_CHECK)
+ return
+
+/**
+ * Adds a visual overlay to a given atom.
+ *
+ * This procedure creates and adds a visual overlay to the specified object. It can either create a unique
+ * overlay or reuse an existing one based on the provided parameters.
+ *
+ * Arguments:
+ * * thing - The atom to which the overlay will be added. The "thing" var can be anything with vis_contents which includes images
+ * * icon - The icon to display for the overlay.
+ * * iconstate - The state of the icon.
+ * * layer - The layer on which the overlay will be rendered.
+ * * plane - The plane for the overlay.
+ * * dir - The direction the overlay faces.
+ * * alpha - The transparency level of the overlay (default is 255).
+ * * add_appearance_flags - Additional flags for overlay appearance (default is NONE).
+ * * unique - A boolean indicating if the overlay should be unique (default is FALSE).
+ *
+ * Returns the overlay that was added.
+ */
+/datum/controller/subsystem/vis_overlays/proc/add_vis_overlay(atom/movable/thing, icon, iconstate, layer, plane, dir, alpha = 255, add_appearance_flags = NONE, unique = FALSE)
+ var/obj/effect/overlay/vis/overlay
+ if(!unique)
+ . = "[icon]|[iconstate]|[layer]|[plane]|[dir]|[alpha]|[add_appearance_flags]"
+ overlay = vis_overlay_cache[.]
+ if(!overlay)
+ overlay = _create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
+ vis_overlay_cache[.] = overlay
+ else
+ overlay.unused = 0
+ else
+ overlay = _create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
+ overlay.cache_expiration = -1
+ var/cache_id = "[text_ref(overlay)]@{[world.time]}"
+ vis_overlay_cache[cache_id] = overlay
+ . = overlay
+ thing.vis_contents += overlay
+
+ if(!isatom(thing)) // Automatic rotation is not supported on non atoms
+ return overlay
+
+ if(!thing.managed_vis_overlays)
+ thing.managed_vis_overlays = list(overlay)
+ else
+ thing.managed_vis_overlays += overlay
+ return overlay
+
+/**
+ * Creates a new visual overlay with the specified parameters.
+ *
+ * This procedure initializes a new visual overlay object with the given attributes.
+ *
+ * Arguments:
+ * * icon - The icon to display for the overlay.
+ * * iconstate - The state of the icon.
+ * * layer - The layer on which the overlay will be rendered.
+ * * plane - The plane for the overlay.
+ * * dir - The direction the overlay faces.
+ * * alpha - The transparency level of the overlay.
+ * * add_appearance_flags - Additional flags for overlay appearance.
+ *
+ * Returns the newly created overlay.
+ */
+/datum/controller/subsystem/vis_overlays/proc/_create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
+ var/obj/effect/overlay/vis/overlay = new
+ overlay.icon = icon
+ overlay.icon_state = iconstate
+ overlay.layer = layer
+ overlay.plane = plane
+ overlay.dir = dir
+ overlay.alpha = alpha
+ overlay.appearance_flags |= add_appearance_flags
+ return overlay
+
+/**
+ * Removes specified visual overlays from an atom.
+ *
+ * This procedure removes the given overlays from the atom's visual contents and also updates the
+ * managed overlays list if applicable.
+ *
+ * Arguments:
+ * * thing - The atom from which the overlays will be removed.
+ * * overlays - A list of overlays to remove.
+ */
+/datum/controller/subsystem/vis_overlays/proc/remove_vis_overlay(atom/movable/thing, list/overlays)
+ thing.vis_contents -= overlays
+ if(!isatom(thing))
+ return
+ thing.managed_vis_overlays -= overlays
+ if(!length(thing.managed_vis_overlays))
+ thing.managed_vis_overlays = null
diff --git a/code/datums/cogbar.dm b/code/datums/cogbar.dm
new file mode 100644
index 00000000000..851a6f935cb
--- /dev/null
+++ b/code/datums/cogbar.dm
@@ -0,0 +1,119 @@
+#define COGBAR_ANIMATION_TIME (0.5 SECONDS)
+
+/**
+ * # Cogbar
+ * Represents that the user is busy doing something.
+ *
+ * The cogbar is a visual representation in the game that indicates to other players
+ * that a user is currently busy with an action. This class handles the creation,
+ * display, and removal of the cog animation.
+ */
+/datum/cogbar
+ /// Who's doing the thing
+ var/mob/user
+ /// The user client
+ var/client/user_client
+ /// The visible element to other players
+ var/obj/effect/overlay/vis/cog
+ /// The blank image that overlaps the cog - hides it from the source user
+ var/image/blank
+ /// The offset of the icon
+ var/offset_y
+
+// Class constructor
+/**
+ * Initializes a new instance of the cogbar for the specified user.
+ *
+ * This constructor sets up the user and user client properties, adds the cog overlay
+ * to the user, and registers a signal to handle user deletion.
+ *
+ * @param user - The user for whom the cogbar is created.
+ */
+/datum/cogbar/New(mob/user)
+ src.user = user
+ src.user_client = user.client
+
+ var/list/icon_offsets = user.get_oversized_icon_offsets()
+ offset_y = icon_offsets["y"]
+
+ add_cog_to_user()
+
+ RegisterSignal(user, COMSIG_PARENT_QDELETING, PROC_REF(on_user_delete))
+
+// Destructor
+/**
+ * Cleans up the cogbar instance, removing overlays and images associated with the user.
+ *
+ * This method ensures that all visual elements are properly removed when the cogbar instance is destroyed
+ */
+/datum/cogbar/Destroy()
+ if(user)
+ SSvis_overlays.remove_vis_overlay(user, user.managed_vis_overlays)
+ user_client?.images -= blank
+
+ user = null
+ user_client = null
+ cog = null
+ QDEL_NULL(blank)
+
+ return ..()
+
+/// Adds the cog to the user, visible by other players
+/**
+ * Adds the cog overlay to the user, making it visible to other players.
+ *
+ * This function creates the cog overlay with appropriate properties and
+ * initiates the animation to fade it in. Additionally, a blank image is
+ * added to the user's client to hide the cog from the user's own perspective.
+ */
+/datum/cogbar/proc/add_cog_to_user()
+ cog = SSvis_overlays.add_vis_overlay(user,
+ icon = 'icons/effects/progressbar.dmi',
+ iconstate = "cog",
+ plane = COGBAR_PLANE,
+ add_appearance_flags = APPEARANCE_UI_IGNORE_ALPHA,
+ unique = TRUE,
+ alpha = 0,
+ )
+
+ cog.pixel_y = world.icon_size + offset_y
+ animate(cog, alpha = 255, time = COGBAR_ANIMATION_TIME)
+
+ if(isnull(user_client))
+ return
+
+ blank = image('icons/blanks/32x32.dmi', cog, "nothing")
+ blank.plane = HUD_PLANE
+ blank.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
+ blank.override = TRUE
+
+ user_client.images += blank
+
+/// Removes the cog from the user
+/**
+ * Initiates the removal process for the cog overlay.
+ *
+ * This function fades out the cog overlay and schedules the cogbar instance
+ * for deletion after the animation completes.
+ */
+/datum/cogbar/proc/remove()
+ if(isnull(cog))
+ qdel(src)
+ return
+
+ animate(cog, alpha = 0, time = COGBAR_ANIMATION_TIME)
+
+ QDEL_IN(src, COGBAR_ANIMATION_TIME)
+
+/// When the user is deleted, remove the cog
+/**
+ * Handles the cleanup when the associated user is deleted.
+ *
+ * This function is triggered by a signal and ensures that the cogbar instance is properly destroyed
+ */
+/datum/cogbar/proc/on_user_delete(datum/source)
+ SIGNAL_HANDLER // COMSIG_PARENT_QDELETING
+
+ qdel(src)
+
+#undef COGBAR_ANIMATION_TIME
diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm
index aecbe44d2d2..ef4c823cc94 100644
--- a/code/datums/progressbar.dm
+++ b/code/datums/progressbar.dm
@@ -14,7 +14,7 @@
EXCEPTION("Invalid target given")
if(goal_number)
goal = goal_number
- bar = image('icons/effects/progessbar.dmi', target, "prog_bar_0", ABOVE_HUD_LAYER)
+ bar = image('icons/effects/progressbar.dmi', target, "prog_bar_0", ABOVE_HUD_LAYER)
bar.alpha = 0
bar.plane = ABOVE_HUD_PLANE
bar.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 105066721ff..38e4954ff40 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -42,6 +42,9 @@
/// Value used to increment ex_act() if reactionary_explosions is on
var/explosion_block = 0
+ ///vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays.
+ var/list/managed_vis_overlays
+
// Detective Work, used for the duplicate data points kept in the scanners
var/list/original_atom
/// List of fibers that this atom has
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index 5aff5df2a95..209d537f3a8 100644
--- a/code/game/gamemodes/malfunction/Malf_Modules.dm
+++ b/code/game/gamemodes/malfunction/Malf_Modules.dm
@@ -865,7 +865,7 @@
user.playsound_local(user, "sparks", 50, FALSE, use_reverb = FALSE)
adjust_uses(-1, user)
robot_target.audible_message("You hear a loud electrical buzzing sound coming from [robot_target]!")
- if(!do_mob(user, robot_target, 10 SECONDS))
+ if(!do_mob(user, robot_target, 10 SECONDS, hidden = TRUE))
is_active = FALSE
return
is_active = FALSE
diff --git a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
index 1d195d25f35..a94195071f8 100644
--- a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
+++ b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
@@ -229,7 +229,7 @@
if(!occupant)
return
to_chat(occupant, "You start trying to break free!")
- if(!do_after_once(occupant, 20 SECONDS, FALSE, src))
+ if(!do_after_once(occupant, 20 SECONDS, FALSE, src, hidden = TRUE))
return
var/list/possible_results = list(
CALLBACK(src, PROC_REF(electrocute_abductee)) = 1,
diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm
index c57228f9fb6..8e07c188bad 100644
--- a/code/game/objects/effects/overlays.dm
+++ b/code/game/objects/effects/overlays.dm
@@ -68,3 +68,12 @@
layer = FLOAT_LAYER
vis_flags = VIS_INHERIT_ID
appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PIXEL_SCALE
+
+/obj/effect/overlay/vis
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ anchored = TRUE
+ vis_flags = VIS_INHERIT_DIR
+ ///When detected to be unused it gets set to world.time, after a while it gets removed
+ var/unused = 0
+ ///overlays which go unused for this amount of time get cleaned up
+ var/cache_expiration = 2 MINUTES
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index 6164c5ea57e..7ae1770f955 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -392,7 +392,7 @@
to_chat(user, "The mind batterer has been burnt out!")
times_used--
return
- if(!do_after_once(user, 2 SECONDS, target = src, allow_moving = TRUE, attempt_cancel_message = "You think it's best to save this for later."))
+ if(!do_after_once(user, 2 SECONDS, target = src, allow_moving = TRUE, attempt_cancel_message = "You think it's best to save this for later.", hidden = TRUE))
times_used--
return
to_chat(user, "You trigger [src]. It has [max_uses-times_used] charges left.")
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 392e6af517a..c725c10781e 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -12,7 +12,7 @@
user.visible_message("[user] attempts to [break_cuffs ? "break" : "remove"] [src]!", "You attempt to [break_cuffs ? "break" : "remove"] [src]...")
to_chat(user, "(This will take around [DisplayTimeText(effective_breakout_time)] and you need to stand still.)")
- if(!do_after(user, effective_breakout_time, FALSE, user))
+ if(!do_after(user, effective_breakout_time, FALSE, user, hidden = TRUE))
user.remove_status_effect(STATUS_EFFECT_REMOVE_CUFFS)
to_chat(user, "You fail to [break_cuffs ? "break" : "remove"] [src]!")
return
diff --git a/code/game/objects/items/weapons/legcuffs.dm b/code/game/objects/items/weapons/legcuffs.dm
index 2269e2afece..d5ec39a013b 100644
--- a/code/game/objects/items/weapons/legcuffs.dm
+++ b/code/game/objects/items/weapons/legcuffs.dm
@@ -224,7 +224,7 @@
L.update_inv_l_hand()
spinning = TRUE
for(var/i in 1 to max_spins)
- if(!do_mob(L, L, 1 SECONDS, only_use_extra_checks = TRUE, extra_checks = list(CALLBACK(src, PROC_REF(can_spin_check), L))))
+ if(!do_mob(L, L, 1 SECONDS, only_use_extra_checks = TRUE, extra_checks = list(CALLBACK(src, PROC_REF(can_spin_check), L)), hidden = TRUE))
reset_values(L)
break
throw_range += range_increment
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index ed176b3bd2a..aa88a661a4f 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -258,7 +258,7 @@
return ..()
to_chat(user, "You begin gathering strength...")
playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, 1)
- if(do_after(user, 90, target = user))
+ if(do_after(user, 9 SECONDS, target = user, hidden = TRUE))
to_chat(user, "You gather power! Time for a home run!")
homerun_ready = 1
..()
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index 66703986234..7710ec94b4e 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -154,7 +154,7 @@
"You struggle to break free from [src], exacerbating your wounds! (Stay still for two minutes.)",\
"You hear a wet squishing noise..")
M.adjustBruteLoss(30)
- if(!do_after(M, 2 MINUTES, target = src))
+ if(!do_after(M, 2 MINUTES, target = src, hidden = TRUE))
if(M && M.buckled)
to_chat(M, "You fail to free yourself!")
return
diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm
index 70ea095f962..9ec0494929d 100644
--- a/code/game/objects/structures/safe.dm
+++ b/code/game/objects/structures/safe.dm
@@ -101,7 +101,7 @@ GLOBAL_LIST_EMPTY(safes)
if(!drill_timer)
return
cut_overlay(progress_bar)
- progress_bar = image('icons/effects/progessbar.dmi', src, "prog_bar_[round((((world.time - drill_start_time) / time_to_drill) * 100), 5)]", HUD_LAYER)
+ progress_bar = image('icons/effects/progressbar.dmi', src, "prog_bar_[round((((world.time - drill_start_time) / time_to_drill) * 100), 5)]", HUD_LAYER)
add_overlay(progress_bar)
if(prob(DRILL_SPARK_CHANCE))
drill.spark_system.start()
diff --git a/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm b/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm
index 355dbfb687a..e5025095100 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm
@@ -33,7 +33,7 @@
M.visible_message("[M.name] struggles to break free from the gelatinous resin!",\
"You struggle to break free from the gelatinous resin... (Stay still for two minutes.)",\
"You hear squelching...")
- if(!do_after(M, 120 SECONDS, target = src))
+ if(!do_after(M, 120 SECONDS, target = src, hidden = TRUE))
if(M && M.buckled)
to_chat(M, "You fail to escape \the [src]!")
return
diff --git a/code/modules/antagonists/changeling/powers/summon_spiders.dm b/code/modules/antagonists/changeling/powers/summon_spiders.dm
index d693bfd3721..37ab081d031 100644
--- a/code/modules/antagonists/changeling/powers/summon_spiders.dm
+++ b/code/modules/antagonists/changeling/powers/summon_spiders.dm
@@ -27,7 +27,7 @@
is_operating = FALSE
return FALSE
user.visible_message("[user] begins vomiting an arachnid!")
- if(do_after(user, 4 SECONDS, FALSE, target = user)) // Takes 4 seconds to spawn a spider
+ if(do_after(user, 4 SECONDS, FALSE, target = user, hidden = TRUE)) // Takes 4 seconds to spawn a spider
spider_counter++
user.visible_message("[user] vomits up an arachnid!")
var/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/S = new(user.loc)
diff --git a/code/modules/antagonists/mind_flayer/flayer_datum.dm b/code/modules/antagonists/mind_flayer/flayer_datum.dm
index d8ec8a39149..4f01fecb32f 100644
--- a/code/modules/antagonists/mind_flayer/flayer_datum.dm
+++ b/code/modules/antagonists/mind_flayer/flayer_datum.dm
@@ -134,7 +134,7 @@
"We begin our harvest on [H].",
"You hear the hum of electricity."
)
- if(!do_mob(owner.current, H, time = 2 SECONDS))
+ if(!do_mob(owner.current, H, time = 2 SECONDS, hidden = TRUE))
send_swarm_message("Our connection was incomplete.")
harvesting = null
return
diff --git a/code/modules/antagonists/mind_flayer/powers/flayer_stealth_powers.dm b/code/modules/antagonists/mind_flayer/powers/flayer_stealth_powers.dm
index 22152269a6a..f3d76cf1e2f 100644
--- a/code/modules/antagonists/mind_flayer/powers/flayer_stealth_powers.dm
+++ b/code/modules/antagonists/mind_flayer/powers/flayer_stealth_powers.dm
@@ -201,7 +201,7 @@
to_chat(user, "Your override attempt fails before it can even begin.")
qdel(src)
return
- if(!do_mob(user, borg, conversion_time))
+ if(!do_mob(user, borg, conversion_time, hidden = TRUE))
to_chat(user, "Your concentration breaks.")
qdel(src)
return
diff --git a/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm
index 139bd798c34..3fa2e4eebc5 100644
--- a/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm
+++ b/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm
@@ -253,7 +253,7 @@
M.visible_message("[M] starts entering a cryptic series of characters on [U].",\
"You start entering an extraction signal to your handlers on [U]...")
- if(do_after(M, EXTRACTION_PHASE_PREPARE, target = M))
+ if(do_after(M, EXTRACTION_PHASE_PREPARE, target = M, hidden = TRUE))
if(!U.Adjacent(M) || extraction_deadline > world.time)
return
var/obj/effect/contractor_flare/F = new(get_turf(M))
diff --git a/code/modules/antagonists/vampire/vamp_datum.dm b/code/modules/antagonists/vampire/vamp_datum.dm
index 3a83ef87e08..e734ca36437 100644
--- a/code/modules/antagonists/vampire/vamp_datum.dm
+++ b/code/modules/antagonists/vampire/vamp_datum.dm
@@ -113,7 +113,7 @@ RESTRICT_TYPE(/datum/antagonist/vampire)
H.LAssailant = null
else
H.LAssailant = owner
- while(do_mob(owner.current, H, suck_rate))
+ while(do_mob(owner.current, H, suck_rate, hidden = TRUE))
owner.current.do_attack_animation(H, ATTACK_EFFECT_BITE)
if(unique_suck_id in drained_humans)
if(drained_humans[unique_suck_id] >= BLOOD_DRAIN_LIMIT)
diff --git a/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm b/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm
index c38b8523e3d..5ac1051ade9 100644
--- a/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm
+++ b/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm
@@ -28,7 +28,7 @@
var/mob/living/target = targets[1]
user.visible_message("[user] bites [target]'s neck!", "You bite [target]'s neck and begin the flow of power.")
to_chat(target, "You feel the tendrils of evil invade your mind.")
- if(do_mob(user, target, 15 SECONDS))
+ if(do_mob(user, target, 15 SECONDS, hidden = TRUE))
if(can_enthrall(user, target))
handle_enthrall(user, target)
var/datum/spell_handler/vampire/V = custom_handler
diff --git a/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm b/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm
index d79f0ba61a6..618ed7a0e88 100644
--- a/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm
+++ b/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm
@@ -140,7 +140,7 @@
if(!making_anchor && !anchor) // first cast, setup the anchor
var/turf/anchor_turf = get_turf(user)
making_anchor = TRUE
- if(do_mob(user, user, 5 SECONDS, only_use_extra_checks = TRUE)) // no checks, cant fail
+ if(do_mob(user, user, 5 SECONDS, only_use_extra_checks = TRUE, hidden = TRUE)) // no checks, cant fail
make_anchor(user, anchor_turf)
making_anchor = FALSE
return
diff --git a/code/modules/client/preference/link_processing.dm b/code/modules/client/preference/link_processing.dm
index f9573255575..c9fa5752ac0 100644
--- a/code/modules/client/preference/link_processing.dm
+++ b/code/modules/client/preference/link_processing.dm
@@ -1011,6 +1011,12 @@
var/atom/movable/screen/plane_master/point/PM = locate(/atom/movable/screen/plane_master/point) in parent.screen
PM.backdrop(parent.mob)
+ if("cogbar")
+ toggles3 ^= PREFTOGGLE_3_COGBAR_ANIMATIONS
+ if(length(parent?.screen))
+ var/atom/movable/screen/plane_master/cogbar/PM = locate(/atom/movable/screen/plane_master/cogbar) in parent.screen
+ PM.backdrop(parent.mob)
+
if("be_special")
var/r = href_list["role"]
if(r in GLOB.special_roles)
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 6437cb1de66..815339890bd 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -481,6 +481,7 @@ GLOBAL_LIST_INIT(special_role_times, list(
dat += "Set screentip mode: [(screentip_mode == 0) ? "Disabled" : "[screentip_mode]px"]
"
dat += "Screentip color: Change
"
dat += "Thought Bubble when pointing: [(toggles2 & PREFTOGGLE_2_THOUGHT_BUBBLE) ? "Yes" : "No"]
"
+ dat += "Cogbar indicators: [(toggles3 & PREFTOGGLE_3_COGBAR_ANIMATIONS) ? "Yes" : "No"]
"
dat += "Custom UI settings:
"
dat += " - Alpha (transparency): [UI_style_alpha]
"
dat += " - Color: [UI_style_color]
"
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 4a3eb36401d..eab00980d10 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -882,7 +882,7 @@
user.visible_message("[user] attempts to [break_restraints ? "break" : "remove"] [src]!", "You attempt to [break_restraints ? "break" : "remove"] [src]...")
to_chat(user, "(This will take around [DisplayTimeText(effective_breakout_time)] and you need to stand still.)")
- if(!do_after(user, effective_breakout_time, FALSE, user))
+ if(!do_after(user, effective_breakout_time, FALSE, user, hidden = TRUE))
user.remove_status_effect(STATUS_EFFECT_REMOVE_CUFFS)
to_chat(user, "You fail to [break_restraints ? "break" : "remove"] [src]!")
return
diff --git a/code/modules/martial_arts/cqc.dm b/code/modules/martial_arts/cqc.dm
index 1f9e8653966..50d4bf07a90 100644
--- a/code/modules/martial_arts/cqc.dm
+++ b/code/modules/martial_arts/cqc.dm
@@ -68,7 +68,7 @@
add_attack_logs(A, D, "Put into a chokehold with martial-art [src]", ATKLOG_ALL)
chokehold_active = TRUE
var/damage_multiplier = 1 + D.getStaminaLoss() / 100 //The chokehold is more effective the more tired the target is.
- while(do_mob(A, D, 2 SECONDS) && chokehold_active)
+ while(do_mob(A, D, 2 SECONDS, hidden = TRUE) && chokehold_active)
D.apply_damage(10 * damage_multiplier, OXY)
D.LoseBreath(3 SECONDS)
if(D.getOxyLoss() >= 50 || D.health <= 20)
diff --git a/code/modules/mob/living/carbon/carbon_procs.dm b/code/modules/mob/living/carbon/carbon_procs.dm
index 5e1aa482519..1a07f40d022 100644
--- a/code/modules/mob/living/carbon/carbon_procs.dm
+++ b/code/modules/mob/living/carbon/carbon_procs.dm
@@ -865,7 +865,7 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
visible_message("[src] attempts to unbuckle [p_themselves()]!",
"You attempt to unbuckle yourself... (This will take around [breakout_time / 10] seconds and you need to stay still.)")
- if(!do_after(src, breakout_time, FALSE, src, allow_moving = TRUE, extra_checks = list(CALLBACK(src, PROC_REF(buckle_check))), allow_moving_target = TRUE))
+ if(!do_after(src, breakout_time, FALSE, src, allow_moving = TRUE, extra_checks = list(CALLBACK(src, PROC_REF(buckle_check))), allow_moving_target = TRUE, hidden = TRUE))
if(src && buckled)
to_chat(src, "You fail to unbuckle yourself!")
else
@@ -931,7 +931,7 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
apply_status_effect(STATUS_EFFECT_REMOVE_MUZZLE)
visible_message("[src] gnaws on [I], trying to remove it!")
to_chat(src, "You attempt to remove [I]... (This will take around [time/10] seconds and you need to stand still.)")
- if(do_after(src, time, FALSE, src, extra_checks = list(CALLBACK(src, PROC_REF(muzzle_check)))))
+ if(do_after(src, time, FALSE, src, extra_checks = list(CALLBACK(src, PROC_REF(muzzle_check))), hidden = TRUE))
visible_message("[src] removes [I]!")
to_chat(src, "You get rid of [I]!")
if(I.security_lock)
diff --git a/code/modules/mob/living/living_status_procs.dm b/code/modules/mob/living/living_status_procs.dm
index 5b3fd2466a4..e83bab19b03 100644
--- a/code/modules/mob/living/living_status_procs.dm
+++ b/code/modules/mob/living/living_status_procs.dm
@@ -91,7 +91,7 @@ STATUS EFFECTS
if(stat == DEAD && !work_when_dead)
return
- if(!instant && !do_mob(src, src, 1 SECONDS, extra_checks = list(CALLBACK(src, TYPE_PROC_REF(/mob/living, cannot_stand))), only_use_extra_checks = TRUE))
+ if(!instant && !do_mob(src, src, 1 SECONDS, extra_checks = list(CALLBACK(src, TYPE_PROC_REF(/mob/living, cannot_stand))), only_use_extra_checks = TRUE, hidden = TRUE))
return
if(resting || body_position == STANDING_UP || HAS_TRAIT(src, TRAIT_FLOORED))
return
diff --git a/code/modules/mob/living/simple_animal/hostile/headslug.dm b/code/modules/mob/living/simple_animal/hostile/headslug.dm
index 2a9066a6784..562f97d1c0e 100644
--- a/code/modules/mob/living/simple_animal/hostile/headslug.dm
+++ b/code/modules/mob/living/simple_animal/hostile/headslug.dm
@@ -48,7 +48,7 @@
/mob/living/simple_animal/hostile/headslug/AltClickOn(mob/living/carbon/carbon_target)
if(egg_layed || !istype(carbon_target) || !Adjacent(carbon_target) || ismachineperson(carbon_target))
return ..()
- if(carbon_target.stat != DEAD && !do_mob(src, carbon_target, 5 SECONDS))
+ if(carbon_target.stat != DEAD && !do_mob(src, carbon_target, 5 SECONDS, hidden = TRUE))
return
if(HAS_TRAIT(carbon_target, TRAIT_XENO_HOST))
to_chat(src, "A foreign presence repels us from this body. Perhaps we should try to infest another?")
diff --git a/code/modules/mob/mob_misc_procs.dm b/code/modules/mob/mob_misc_procs.dm
index 1a79d3fd4ba..2ed23918833 100644
--- a/code/modules/mob/mob_misc_procs.dm
+++ b/code/modules/mob/mob_misc_procs.dm
@@ -524,7 +524,7 @@
else
to_chat(src, "You are now trying to get up.")
- if(!do_mob(src, src, 1 SECONDS, extra_checks = list(CALLBACK(src, TYPE_PROC_REF(/mob/living, cannot_stand))), only_use_extra_checks = TRUE))
+ if(!do_mob(src, src, 1 SECONDS, extra_checks = list(CALLBACK(src, TYPE_PROC_REF(/mob/living, cannot_stand))), only_use_extra_checks = TRUE, hidden = TRUE))
return
if(resting)
diff --git a/icons/blanks/32x32.dmi b/icons/blanks/32x32.dmi
new file mode 100644
index 0000000000000000000000000000000000000000..6c4f2b33e0fee9d3f99ff3febc0760cfd078aed2
GIT binary patch
literal 216
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;m;-!5Tn`*LkmkKF1;}MA3GxeO
zaCmkj4amu^3W+FjNi9w;$}A|!%+F(BsF)KRR!~&>{Y!Ac$FEPcymhtCojD)8A=Kca
z@q&JF>8>?005u_0{{R3dEt5<00026dQ@0+L}hbh
za%pgMX>V=-0C=30)4dA9FcbygIeCiE?$Z49Z*Y+o>0n=>L>ik6wUPvVeS;lE@NDN^
zF5hxFIXm^|VPxH9pikq2+wSbh)S;l{<>qM9AO$7XxW&eO2vJ9qV)pRcGF`Qwij0!k
z`H+^JlAG77+4J{l`Tr>gSPr-Xa0TE>z?Fcj09OHSiM$PPE5NM)w+7rAa2vpF0JjC)
z7V5=MIA{IXeY*SxH+31ds1ix<0000dP)t-s00030nKJ3~v
zfH{q;tvrRV=>S#uCQN|n0__N_KyCvJy6I5l2;8-w0r4j^kmOqV1)dz2ouN9iGVG~JT$>|Xk;-AkWSn_p=D4b5Mfe+B>m_TkU>;V_J=
zHRZk6K1h4*L+-fm`qkcZ&S~$Zo%TMp)rIUs006)~{Bj@qXtAd1sFAL<8|galLl-Uf
zUKv%=rFJD<#x}i>eFy*m*oU7xuczj{rn(pHlXRzjikyUv6>jTTmt_$mwa%cVH)w`r5`$l_8MzVB(
zwr4<1b+WbA3&qoW+YfJVQAxR7b$!d4TjpQZ^<2F?HFrjY`xKj!_ip=D=1dBFnOJf<
zX>Q4>{)1t^z*%3Z^s?B(Yccg3ha?DGHBh6j!{oMxQPoX50-^8wcZ(T3Sd
z*V%SRK1ger&78-#gY|(}Lo`z!=icu79}1=RmACj`jkLS}G0V;*r8N4i`~hzBVD<9Z
zG1f&ukA3oVaSW-L^LEDVzDEWEtTW%JhTr-Be>*oHx8Iv@j=TB4c!WBA4)Wby_{O7H
zPaNob5a2i$KG~x#STlUxR+Dq}QXFKfwRGQU2UVKhXsKFHGe^~)$Ki1YPe(>UT
z)8zGBZSS`qd)cKAwZb(%iXS=@5L#$
zV7H&1K)CYd{)2pO_RWlWnfF0)2?P!)c5`Ji`MW&`1EL;0S4sLthIcYUA#-=>n!FYRw}=bin0JoaCpg47T0FSqv_Kk!>#e_s9J
z|8MVe3+G?SFA%kV@NzDjr)j{Q=-I048{hu>w%qo>-FR!e_XW%MAGy+y)VSu%AK6d!
z532v)e4qW>KO_Ejy^EUM!vCdnds8Nr{$BoE_Jz&CyVmXp_IO|MnSSk|MZziOh<_HJ
zpK`soE!w`V+IabaZ|lQ%%{-rbwV}*f;k)_D>-Rq~8VT=<+y9|bSnK;WzGu2SEH2yb
xHNNZo{m{u@dBp$#
literal 0
HcmV?d00001
diff --git a/paradise.dme b/paradise.dme
index 9c09e347c66..064026ceecb 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -227,6 +227,7 @@
#include "code\_globalvars\lists\dye_registry.dm"
#include "code\_globalvars\lists\flavor_misc.dm"
#include "code\_globalvars\lists\fortunes.dm"
+#include "code\_globalvars\lists\icons.dm"
#include "code\_globalvars\lists\keybindings_lists.dm"
#include "code\_globalvars\lists\maint_loot_tables.dm"
#include "code\_globalvars\lists\misc_lists.dm"
@@ -352,6 +353,7 @@
#include "code\controllers\subsystem\SStime_track.dm"
#include "code\controllers\subsystem\SStimer.dm"
#include "code\controllers\subsystem\SSverb_manager.dm"
+#include "code\controllers\subsystem\SSvis_overlays.dm"
#include "code\controllers\subsystem\SSvote.dm"
#include "code\controllers\subsystem\SSweather.dm"
#include "code\controllers\subsystem\movement\movement_types.dm"
@@ -398,6 +400,7 @@
#include "code\datums\chat_payload.dm"
#include "code\datums\chatmessage.dm"
#include "code\datums\click_intercept.dm"
+#include "code\datums\cogbar.dm"
#include "code\datums\custom_user_item.dm"
#include "code\datums\datacore.dm"
#include "code\datums\datum.dm"