diff --git a/citadel.dme b/citadel.dme index e9ec0103367..a0526380a06 100644 --- a/citadel.dme +++ b/citadel.dme @@ -770,6 +770,7 @@ #include "code\datums\shieldcall.dm" #include "code\datums\soul_link.dm" #include "code\datums\status_emoji.dm" +#include "code\datums\status_indicator.dm" #include "code\datums\sun.dm" #include "code\datums\weakref.dm" #include "code\datums\world_topic.dm" @@ -3852,6 +3853,7 @@ #include "code\modules\mob\mob-say-legacy.dm" #include "code\modules\mob\mob-say-triggers.dm" #include "code\modules\mob\mob-say.dm" +#include "code\modules\mob\mob-status_indicator.dm" #include "code\modules\mob\mob-status_procs.dm" #include "code\modules\mob\mob-synth.dm" #include "code\modules\mob\mob.dm" diff --git a/code/__DEFINES/procs/do_after.dm b/code/__DEFINES/procs/do_after.dm index e517c8c53c2..134c029ba9a 100644 --- a/code/__DEFINES/procs/do_after.dm +++ b/code/__DEFINES/procs/do_after.dm @@ -24,6 +24,7 @@ #define DO_AFTER_ARG_MOBILITY 5 #define DO_AFTER_ARG_DIST 6 #define DO_AFTER_ARG_CALLBACK 7 +#define DO_AFTER_ARG_STATUS_INDICATOR 7 //? Interaction Checks /// checks if we're interacting with an atom diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index 88f573b3936..ec52e5e5584 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -69,11 +69,13 @@ * * mobility_flags - required mobility flags * * max_distance - if not null, the user is required to be get_dist() <= max_distance from target. * * additional_checks - a callback that allows for custom checks. this is invoked with our args directly, allowing us to modify delay. + * if this returns FALSE, we fail. * * progress_anchor - override progressbar anchor location * * progress_instance - override progressbar instance; this progressbar instance will be **deleted** when we are finished, * and should have a goal number equal to the delay. + * * status_indicator - /datum/status_indicator type */ -/proc/do_after(mob/user, delay, atom/target, flags, mobility_flags = MOBILITY_CAN_USE, max_distance, datum/callback/additional_checks, atom/progress_anchor, datum/progressbar/progress_instance) +/proc/do_after(mob/user, delay, atom/target, flags, mobility_flags = MOBILITY_CAN_USE, max_distance, datum/callback/additional_checks, atom/progress_anchor, datum/progressbar/progress_instance, status_indicator) if(isnull(user) || QDELETED(user)) progress_instance?.end_progress() return FALSE @@ -110,6 +112,14 @@ progress = new(user, delay, progress_anchor || target) var/start_time = world.time + var/static/status_indicator_notch = 0 + var/status_indicator_source + if(status_indicator) + status_indicator_source = "do_after-[++status_indicator_notch]" + if(status_indicator_notch >= SHORT_REAL_LIMIT) + status_indicator_notch = -SHORT_REAL_LIMIT + user.add_status_indicator(status_indicator, status_indicator_source) + //* loop . = TRUE @@ -183,6 +193,9 @@ if(!isnull(target)) STOP_INTERACTING_WITH(user, target, INTERACTING_FOR_DO_AFTER) + if(status_indicator_source) + user.remove_status_indicator(status_indicator, status_indicator_source) + /** * Does an action to ourselves after a delay. * diff --git a/code/datums/status_indicator.dm b/code/datums/status_indicator.dm new file mode 100644 index 00000000000..4a21a26288b --- /dev/null +++ b/code/datums/status_indicator.dm @@ -0,0 +1,35 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +GLOBAL_LIST_INIT(status_indicators, init_status_indicators()) + +/proc/init_status_indicators() + . = list() + for(var/datum/status_indicator/path as anything in subtypesof(/datum/status_indicator)) + if(path.abstract_type == path) + continue + . += new path + // quack. duck typing to the rescue (and chagrin of whoever has to fix this 2 years later) + tim_sort(., /proc/cmp_name_asc) + +/** + * A status indicator to be flicked by mobs, primarily. + * * That said, this ideally contains alignment data that can be used to align it to any atom, so... + * * Icon states are assumed to be aligned to bottom left of the icon size. Provide alignment offsets if needed. + */ +/datum/status_indicator + abstract_type = /datum/status_indicator + var/name + var/icon + var/icon_state + + var/icon_size_x = 32 + var/icon_size_y = 32 + var/indicator_size_x = 8 + var/indicator_size_y = 8 + /// pixels to shift to boost move the indicator's bounding box to 1, 1 + /// * if your indicator is at 25, 25 to 32, 32 this is -24 + var/alignment_offset_x = 0 + /// pixels to shift to boost move the indicator's bounding box to 1, 1 + /// * if your indicator is at 25, 25 to 32, 32 this is -24 + var/alignment_offset_y = 0 diff --git a/code/modules/mob/mob-status_indicator.dm b/code/modules/mob/mob-status_indicator.dm new file mode 100644 index 00000000000..8287c464546 --- /dev/null +++ b/code/modules/mob/mob-status_indicator.dm @@ -0,0 +1,81 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +/mob/proc/add_status_indicator(path, source) + if(!status_indicators) + status_indicators = list() + if(!status_indicators[path]) + status_indicators[path] = list() + add_status_indicator_internal(path) + status_indicators[path] |= source + return TRUE + +/mob/proc/remove_status_indicator(path, source) + if(!status_indicators?[path]) + return TRUE + status_indicators[path] -= source + if(!length(status_indicators[path])) + clear_status_indicator(path) + return TRUE + +/mob/proc/clear_status_indicator(path) + if(!status_indicators) + return TRUE + if(status_indicators[path]) + status_indicators -= path + if(!length(status_indicators)) + status_indicators = null + remove_status_indicator_internal(path) + return TRUE + +/mob/proc/clear_status_indicators() + for(var/path in status_indicators) + clear_status_indicator(path) + return TRUE + +/mob/proc/add_status_indicator_internal(path) + PRIVATE_PROC(TRUE) + reload_status_indicators() + +/mob/proc/remove_status_indicator_internal(path) + PRIVATE_PROC(TRUE) + reload_status_indicators() + +/mob/proc/reload_status_indicators() + cut_overlay(status_indicator_overlays, TRUE) + status_indicator_overlays = list() + var/const/row_max = 3 + var/row_count = ceil(length(status_indicators) / row_max) + // the pixel right below the current row + var/current_y = icon_y_dimension + for(var/row in 1 to row_count) + var/row_start = (row - 1) * row_count + var/row_size = min(row_max, length(status_indicators) - row_start) + var/row_total_width = 0 + var/row_max_height = 0 + var/list/image/row_images = list() + for(var/i in 1 to row_size) + var/datum/status_indicator/indicator_path = status_indicators[row] + var/datum/status_indicator/indicator = GLOB.status_indicators[indicator_path] + if(!indicator) + status_indicators -= indicator_path + CRASH("indicator [indicator_path] not found, removing and aborting reload") + row_total_width += indicator.indicator_size_x + row_max_height = max(row_max_height, indicator.indicator_size_y) + var/image/indicator_image = image(indicator_path.icon, indicator_path.icon_state) + indicator_image.appearance_flags = RESET_COLOR | KEEP_APART + indicator_image.plane = FLOAT_PLANE + indicator_image.layer = FLOAT_LAYER + indicator_image.pixel_y = indicator.alignment_offset_y + indicator_image.pixel_x = indicator.alignment_offset_x + indicator_image.pixel_w = indicator.indicator_size_x + row_images += indicator_image + var/row_current_x = (WORLD_ICON_SIZE - row_total_width) / 2 + for(var/i in 1 to row_size) + var/image/image = row_images[i] + image.pixel_y += current_y + image.pixel_x += row_current_x + row_current_x += image.pixel_z + image.pixel_z = 0 + status_indicator_overlays += image + add_overlay(status_indicator_overlays) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 621b8c8b595..b07d6c24c98 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -47,6 +47,11 @@ /// The calculated mob speed slowdown based on the modifiers list var/movespeed_hyperbolic + //* Status Indicators *// + /// datum path = list of sources + var/list/status_indicators + var/list/status_indicator_overlays + /** * Intialize a mob * diff --git a/icons/mob/status_indicators.dmi b/icons/mob/status_indicators.dmi new file mode 100644 index 00000000000..cf74d73796c Binary files /dev/null and b/icons/mob/status_indicators.dmi differ