diff --git a/code/__DEFINES/appearance.dm b/code/__DEFINES/appearance.dm new file mode 100644 index 00000000000..4c0e05db9b3 --- /dev/null +++ b/code/__DEFINES/appearance.dm @@ -0,0 +1,7 @@ +// wrapper for update_appearance that lets me capture it in debug mode +// Does not work for callbacks unfortunately, not sure how to feel about that +// This only works if definitions of the proc get changed to _update_appearance with REGEX +// See the _compile_options comment for the regexes to use for that +#ifdef APPEARANCE_SUCCESS_TRACKING +#define update_appearance(arguments...) wrap_update_appearance(__FILE__, __LINE__, ##arguments) +#endif diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index bae3c9e7174..6cbc9310355 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -56,8 +56,13 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define IS_PLAYER_COLORABLE_1 (1<<17) /// Whether or not this atom has contextual screentips when hovered OVER #define HAS_CONTEXTUAL_SCREENTIPS_1 (1<<18) -// Whether or not this atom is storing contents for a disassociated storage object +/// Whether or not this atom is storing contents for a disassociated storage object #define HAS_DISASSOCIATED_STORAGE_1 (1<<19) +/// If this atom has experienced a decal element "init finished" sourced appearance update +/// We use this to ensure stacked decals don't double up appearance updates for no rasin +/// Flag as an optimization, don't make this a trait without profiling +/// Yes I know this is a stupid flag, no you can't take him from me +#define DECAL_INIT_UPDATE_EXPERIENCED_1 (1<<20) // Update flags for [/atom/proc/update_appearance] /// Update the atom's name diff --git a/code/__DEFINES/stat_tracking.dm b/code/__DEFINES/stat_tracking.dm index 63874d74f9e..25eb910a9d1 100644 --- a/code/__DEFINES/stat_tracking.dm +++ b/code/__DEFINES/stat_tracking.dm @@ -10,23 +10,29 @@ STAT_ENTRY[STAT_ENTRY_TIME] += STAT_TIME;\ STAT_ENTRY[STAT_ENTRY_COUNT] += 1; -// Cost tracking macros, to be used in one proc -// The static lists are under the assumption that costs and counting are global lists, and will therefor -// Break during world init +// Cost tracking macros, to be used in one proc. If you're using this raw you'll want to use global lists +// If you don't you'll need another way of reading it #define INIT_COST(costs, counting) \ var/list/_costs = costs; \ var/list/_counting = counting; \ var/usage = TICK_USAGE; -// Cost tracking macro for global lists, prevents erroring if GLOB has not yet been initialized -#define INIT_COST_GLOBAL(costs, counting) \ +// STATIC cost tracking macro. Uses static lists instead of the normal global ones +// Good for debug stuff, and for running before globals init +#define INIT_COST_STATIC(...) \ var/static/list/hidden_static_list_for_fun1 = list(); \ var/static/list/hidden_static_list_for_fun2 = list(); \ + INIT_COST(hidden_static_list_for_fun1, hidden_static_list_for_fun2) + +// Cost tracking macro for global lists, prevents erroring if GLOB has not yet been initialized +#define INIT_COST_GLOBAL(costs, counting) \ + INIT_COST_STATIC() \ if(GLOB){\ costs = hidden_static_list_for_fun1; \ counting = hidden_static_list_for_fun2 ; \ } \ - INIT_COST(hidden_static_list_for_fun1, hidden_static_list_for_fun2) + usage = TICK_USAGE; + #define SET_COST(category) \ do { \ @@ -38,7 +44,10 @@ #define SET_COST_LINE(...) SET_COST("[__LINE__]") -#define EXPORT_STATS_TO_FILE_LATER(filename, costs, counts) \ +#define EXPORT_STATS_TO_JSON_LATER(filename, costs, counts) EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, stat_tracking_export_to_json_later) +#define EXPORT_STATS_TO_CSV_LATER(filename, costs, counts) EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, stat_tracking_export_to_csv_later) + +#define EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, proc) \ do { \ var/static/last_export = 0; \ if (world.time - last_export > 1.1 SECONDS) { \ @@ -46,7 +55,7 @@ /* spawn() is used here because this is often used to track init times, where timers act oddly. */ \ /* I was making timers and even after init times were complete, the timers didn't run :shrug: */ \ spawn (1 SECONDS) { \ - stat_tracking_export_to_file_later(filename, costs, counts); \ + ##proc(filename, costs, counts); \ } \ } \ } while (FALSE); diff --git a/code/__HELPERS/_planes.dm b/code/__HELPERS/_planes.dm index a7fc3fc7db5..20f2f69ec9a 100644 --- a/code/__HELPERS/_planes.dm +++ b/code/__HELPERS/_planes.dm @@ -47,6 +47,9 @@ if(_our_turf){\ thing.plane = GET_NEW_PLANE(_cached_plane, GET_Z_PLANE_OFFSET(_our_turf.z));\ }\ + else {\ + thing.plane = new_value;\ + }\ }\ else {\ thing.plane = new_value;\ diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index cbe3fc8ca6a..5e0d1b83b27 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -127,6 +127,12 @@ DEFINE_BITFIELD(smoothing_junction, list( CRASH("smooth_icon called for [src] with smoothing_flags == [smoothing_flags]") SEND_SIGNAL(src, COMSIG_ATOM_SMOOTHED_ICON) +// As a rule, movables will most always care about smoothing changes +// Turfs on the other hand, don't, so we don't do the update for THEM unless they explicitly request it +/atom/movable/smooth_icon() + . = ..() + update_appearance(~UPDATE_SMOOTHING) + /atom/proc/corners_diagonal_smooth(adjacencies) switch(adjacencies) if(NORTH_JUNCTION|WEST_JUNCTION) diff --git a/code/__HELPERS/lighting.dm b/code/__HELPERS/lighting.dm index efde257218c..4d0105532e4 100644 --- a/code/__HELPERS/lighting.dm +++ b/code/__HELPERS/lighting.dm @@ -1,9 +1,29 @@ /// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EMISSIVE_COLOR]. /proc/emissive_appearance(icon, icon_state = "", atom/offset_spokesman, layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE, offset_const) - var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, alpha, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const) + // Note: alpha doesn't "do" anything, since it's overriden by the color set shortly after + // Consider removing it someday? (I wonder if we made emissives blend right we could make alpha actually matter. dreams man, dreams) + var/mutable_appearance/appearance = mutable_appearance(icon, icon_state, layer, offset_spokesman, EMISSIVE_PLANE, 255, appearance_flags | EMISSIVE_APPEARANCE_FLAGS, offset_const) appearance.color = GLOB.emissive_color return appearance +// This is a semi hot proc, so we micro it. saves maybe 150ms +// sorry :) +/proc/fast_emissive_blocker(atom/make_blocker) + // Note: alpha doesn't "do" anything, since it's overriden by the color set shortly after + // Consider removing it someday? + var/mutable_appearance/blocker = new() + blocker.icon = make_blocker.icon + blocker.icon_state = make_blocker.icon_state + blocker.layer = make_blocker.layer + blocker.appearance_flags |= make_blocker.appearance_flags | EMISSIVE_APPEARANCE_FLAGS + blocker.dir = make_blocker.dir + blocker.color = GLOB.em_block_color + + // Note, we are ok with null turfs, that's not an error condition we'll just default to 0, the error would be + // Not passing ANYTHING in, key difference + SET_PLANE_EXPLICIT(blocker, EMISSIVE_PLANE, make_blocker) + return blocker + /// Produces a mutable appearance glued to the [EMISSIVE_PLANE] dyed to be the [EM_BLOCK_COLOR]. /proc/emissive_blocker(icon, icon_state = "", atom/offset_spokesman, layer = FLOAT_LAYER, alpha = 255, appearance_flags = NONE, offset_const) // Note: alpha doesn't "do" anything, since it's overriden by the color set shortly after diff --git a/code/__HELPERS/stat_tracking.dm b/code/__HELPERS/stat_tracking.dm index 1fda8abdc83..3b8f82ede4e 100644 --- a/code/__HELPERS/stat_tracking.dm +++ b/code/__HELPERS/stat_tracking.dm @@ -1,3 +1,4 @@ +// For use with the stopwatch defines /proc/render_stats(list/stats, user, sort = GLOBAL_PROC_REF(cmp_generic_stat_item_time)) sortTim(stats, sort, TRUE) @@ -12,7 +13,8 @@ . = lines.Join("\n") -/proc/stat_tracking_export_to_file_later(filename, costs, counts) +// For use with the set_cost defines +/proc/stat_tracking_export_to_json_later(filename, costs, counts) if (IsAdminAdvancedProcCall()) return @@ -25,3 +27,15 @@ ) rustg_file_write(json_encode(output), "[GLOB.log_directory]/[filename]") + +/proc/stat_tracking_export_to_csv_later(filename, costs, counts) + if (IsAdminAdvancedProcCall()) + return + + var/list/output = list() + + output += "key, cost, count" + for (var/key in costs) + output += "[replacetext(key, ",", "")], [replacetext(costs[key], ",", "")], [replacetext(counts[key], ",", "")]" + + rustg_file_write(output.Join("\n"), "[GLOB.log_directory]/[filename]") diff --git a/code/_compile_options.dm b/code/_compile_options.dm index f4ce1f7f94d..7a6680dc4b2 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -11,6 +11,14 @@ #ifdef TESTING #define DATUMVAR_DEBUGGING_MODE +/// Enables update_appearance "relevence" tracking +/// This allows us to check which update_appearance procs are actually doing anything. Good thing to look in on once a year or so +/// You'll need to run a two regexes/search and replaces to make it work +/// First, one to convert type refs (PROC_REF.*)(update_appearance\)) -> $1_$2 +/// Second, one to convert definitions /update_appearance\( -> /_update_appearance( +/// We'll use another define to convert uses of the proc over. That'll be all +// #define APPEARANCE_SUCCESS_TRACKING + ///Used to find the sources of harddels, quite laggy, don't be surpised if it freezes your client for a good while //#define REFERENCE_TRACKING #ifdef REFERENCE_TRACKING diff --git a/code/datums/elements/decals/_decal.dm b/code/datums/elements/decals/_decal.dm index f5fd4a02ead..a15136eb260 100644 --- a/code/datums/elements/decals/_decal.dm +++ b/code/datums/elements/decals/_decal.dm @@ -128,11 +128,11 @@ /datum/element/decal/proc/late_update_icon(atom/source) SIGNAL_HANDLER - if(source && istype(source)) + if(istype(source) && !(source.flags_1 & DECAL_INIT_UPDATE_EXPERIENCED_1)) + source.flags_1 |= DECAL_INIT_UPDATE_EXPERIENCED_1 // I am so sorry, but it saves like 80ms I gotta source.update_appearance(UPDATE_OVERLAYS) UnregisterSignal(source, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE) - /datum/element/decal/proc/apply_overlay(atom/source, list/overlay_list) SIGNAL_HANDLER diff --git a/code/datums/materials/_material.dm b/code/datums/materials/_material.dm index 0d13fd761f9..777af6bd4f2 100644 --- a/code/datums/materials/_material.dm +++ b/code/datums/materials/_material.dm @@ -92,8 +92,6 @@ Simple datum which is instanced once per type and is used for every object of sa else if(istype(source, /turf)) //turfs on_applied_turf(source, amount, material_flags) - source.update_appearance() - source.mat_update_desc(src) ///This proc is called when a material updates an object's description diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index e1080c06a70..c066b96cea4 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -24,27 +24,25 @@ * offset_const - A constant to offset our plane by, so it renders on the right "z layer" **/ /proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, atom/offset_spokesman, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE, offset_const) + var/mutable_appearance/appearance = new() + appearance.icon = icon + appearance.icon_state = icon_state + appearance.layer = layer + appearance.alpha = alpha + appearance.appearance_flags |= appearance_flags if(plane != FLOAT_PLANE) - // Essentially, we allow users that only want one static offset to pass one in - if(!isnull(offset_const)) - plane = GET_NEW_PLANE(plane, offset_const) - // That, or you need to pass in some non null object to reference - else if(isatom(offset_spokesman)) + // You need to pass in some non null object to reference + if(isatom(offset_spokesman)) // Note, we are ok with null turfs, that's not an error condition we'll just default to 0, the error would be // Not passing ANYTHING in, key difference - var/turf/our_turf = get_turf(offset_spokesman) - plane = MUTATE_PLANE(plane, our_turf) + SET_PLANE_EXPLICIT(appearance, plane, offset_spokesman) + // That or I'll let you pass in a static offset. Don't be stupid now + else if(!isnull(offset_const)) + SET_PLANE_W_SCALAR(appearance, plane, offset_const) // otherwise if you're setting plane you better have the guts to back it up else stack_trace("No plane offset passed in as context for a non floating mutable appearance, things are gonna go to hell on multiz maps") else if(!isnull(offset_spokesman) && !isatom(offset_spokesman)) stack_trace("Why did you pass in offset_spokesman as [offset_spokesman]? We need an atom to properly offset planes") - var/mutable_appearance/MA = new() - MA.icon = icon - MA.icon_state = icon_state - MA.layer = layer - MA.plane = plane - MA.alpha = alpha - MA.appearance_flags |= appearance_flags - return MA + return appearance diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 0e38a6710e9..cb60659e925 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -740,6 +740,24 @@ . = list() SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE_MORE, user, .) +/// Wrapper for _update_appearance that is only called when APPEARANCE_SUCCESS_TRACKING is defined +#ifdef APPEARANCE_SUCCESS_TRACKING +/atom/proc/wrap_update_appearance(file, line, updates) + INIT_COST_STATIC() + EXPORT_STATS_TO_CSV_LATER("appearance_efficency.csv", _costs, _counting) + var/old_appearance = appearance + + _update_appearance(updates) + // We're checking to see if update_appearance DID anything to our appearance + // If it didn't, or it produced the same thing, we'll mark it as such so it can potentially be opitmized + if(old_appearance == appearance) + SET_COST("SAME [file] [line]") + return FALSE + else + SET_COST("DIFFERENT [file] [line]") + return TRUE +#endif + /** * Updates the appearence of the icon * diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 88b0387548b..852c60a2cf5 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -204,9 +204,7 @@ if(!blocks_emissive) return else if (blocks_emissive == EMISSIVE_BLOCK_GENERIC) - var/mutable_appearance/gen_emissive_blocker = emissive_blocker(icon, icon_state, src, alpha = src.alpha, appearance_flags = src.appearance_flags) - gen_emissive_blocker.dir = dir - return gen_emissive_blocker + return fast_emissive_blocker(src) else if(blocks_emissive == EMISSIVE_BLOCK_UNIQUE) if(!em_block && !QDELETED(src)) render_target = ref(src) diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 625a653e3a8..9fc13ed0c60 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -135,7 +135,7 @@ var/market_verb = "Customer" var/payment_department = ACCOUNT_ENG - // For storing and overriding ui id + /// For storing and overriding ui id var/tgui_id // ID of TGUI interface ///Is this machine currently in the atmos machinery queue? var/atmos_processing = FALSE @@ -148,6 +148,9 @@ var/always_area_sensitive = FALSE ///Multiplier for power consumption. var/machine_power_rectifier = 1 + /// What was our power state the last time we updated its appearance? + /// TRUE for on, FALSE for off, -1 for never checked + var/appearance_power_state = -1 /obj/machinery/Initialize(mapload) if(!armor) @@ -542,6 +545,21 @@ /obj/machinery/proc/on_set_is_operational(old_value) return +///Called when we want to change the value of the `panel_open` variable. Boolean. +/obj/machinery/proc/set_panel_open(new_value) + if(panel_open == new_value) + return + var/old_value = panel_open + panel_open = new_value + on_set_panel_open(old_value) + +///Called when the value of `panel_open` changes, so we can react to it. +/obj/machinery/proc/on_set_panel_open(old_value) + return + +/// Toggles the panel_open var. Defined for convienience +/obj/machinery/proc/toggle_panel_open() + set_panel_open(!panel_open) /obj/machinery/can_interact(mob/user) if((machine_stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE)) // Check if the machine is broken, and if we can still interact with it if so @@ -849,12 +867,11 @@ return FALSE screwdriver.play_tool_sound(src, 50) - if(!panel_open) - panel_open = TRUE + toggle_panel_open() + if(panel_open) icon_state = icon_state_open to_chat(user, span_notice("You open the maintenance hatch of [src].")) else - panel_open = FALSE icon_state = icon_state_closed to_chat(user, span_notice("You close the maintenance hatch of [src].")) return TRUE diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm index dd8f1b4d1f9..3474c5e4cb6 100644 --- a/code/game/machinery/announcement_system.dm +++ b/code/game/machinery/announcement_system.dm @@ -54,7 +54,7 @@ GLOBAL_LIST_EMPTY(announcement_systems) /obj/machinery/announcement_system/screwdriver_act(mob/living/user, obj/item/tool) tool.play_tool_sound(src) - panel_open = !panel_open + toggle_panel_open() to_chat(user, span_notice("You [panel_open ? "open" : "close"] the maintenance hatch of [src].")) update_appearance() return TRUE diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 0c2c80b9a94..3e8c293ad86 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -22,7 +22,7 @@ . = ..() if(built) setDir(ndir) - panel_open = TRUE + set_panel_open(TRUE) update_appearance() if(!built && !device && device_type) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 1e82ebf66f5..f41b3b74770 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -216,7 +216,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0) /obj/machinery/camera/screwdriver_act(mob/living/user, obj/item/I) if(..()) return TRUE - panel_open = !panel_open + toggle_panel_open() to_chat(user, span_notice("You screw the camera's panel [panel_open ? "open" : "closed"].")) I.play_tool_sound(src) update_appearance() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index c25e31f1db5..b30f8c38cae 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -735,7 +735,7 @@ if(panel_open && detonated) to_chat(user, span_warning("[src] has no maintenance panel!")) return TOOL_ACT_TOOLTYPE_SUCCESS - panel_open = !panel_open + toggle_panel_open() to_chat(user, span_notice("You [panel_open ? "open":"close"] the maintenance panel of the airlock.")) tool.play_tool_sound(src) update_appearance() @@ -1295,8 +1295,7 @@ /obj/machinery/door/airlock/proc/on_break() SIGNAL_HANDLER - if(!panel_open) - panel_open = TRUE + set_panel_open(TRUE) wires.cut_all() /obj/machinery/door/airlock/emp_act(severity) diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index 871d9722890..8c4cfafa8e4 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -503,8 +503,7 @@ return (IS_CULTIST(user) && !isAllPowerCut()) /obj/machinery/door/airlock/cult/on_break() - if(!panel_open) - panel_open = TRUE + set_panel_open(TRUE) /obj/machinery/door/airlock/cult/isElectrified() return FALSE diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 4346272b2a3..411847a0fe8 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -293,7 +293,7 @@ return add_fingerprint(user) tool.play_tool_sound(src) - panel_open = !panel_open + toggle_panel_open() to_chat(user, span_notice("You [panel_open ? "open" : "close"] the maintenance panel.")) return TRUE diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index 547b59f5cd6..43c2ecbbfb8 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -42,7 +42,7 @@ . = ..() if(building) buildstage = 0 - panel_open = TRUE + set_panel_open(TRUE) if(name == initial(name)) name = "[get_area_name(src)] [initial(name)]" update_appearance() @@ -256,7 +256,7 @@ if(tool.tool_behaviour == TOOL_SCREWDRIVER && buildstage == 2) tool.play_tool_sound(src) - panel_open = !panel_open + toggle_panel_open() to_chat(user, span_notice("The wires have been [panel_open ? "exposed" : "unexposed"].")) update_appearance() return diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 5a634edf780..51f8014026d 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -438,6 +438,7 @@ Possible to do for anyone motivated enough: if(!LAZYLEN(holo_calls)) set_can_hear_flags(CAN_HEAR_ACTIVE_HOLOCALLS, FALSE) + update_appearance(UPDATE_ICON_STATE) return TRUE /** @@ -487,7 +488,7 @@ Possible to do for anyone motivated enough: if(outgoing_call) outgoing_call.Check() - ringing = FALSE + var/are_ringing = FALSE for(var/datum/holocall/holocall as anything in holo_calls) if(holocall.connected_holopad == src) @@ -503,9 +504,11 @@ Possible to do for anyone motivated enough: holocall.Disconnect(src)//can't answer calls while calling else playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring! - ringing = TRUE + are_ringing = TRUE - update_appearance(UPDATE_ICON_STATE) + if(ringing != are_ringing) + update_appearance(UPDATE_ICON_STATE) + ringing = are_ringing /obj/machinery/holopad/proc/activate_holo(mob/living/user) var/mob/living/silicon/ai/AI = user diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index 9bde27f4c2e..c8412dd3a80 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -187,7 +187,7 @@ /obj/machinery/shieldgen/screwdriver_act(mob/living/user, obj/item/tool) tool.play_tool_sound(src, 100) - panel_open = !panel_open + toggle_panel_open() if(panel_open) to_chat(user, span_notice("You open the panel and expose the wiring.")) else diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index d4bb0038f22..572caa738b7 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -96,6 +96,10 @@ if(panel_open && display_panel) . += "[base_icon_state]-open" +/obj/machinery/space_heater/on_set_panel_open() + update_appearance() + return ..() + /obj/machinery/space_heater/process_atmos() if(!on || !is_operational) if (on) // If it's broken, turn it off too @@ -275,8 +279,7 @@ /obj/machinery/space_heater/constructed/Initialize(mapload) . = ..() - panel_open = TRUE - update_appearance() + set_panel_open(TRUE) /obj/machinery/space_heater/proc/toggle_power(user) on = !on diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index 361a4a752c6..f332a293d19 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -97,13 +97,17 @@ line1 = uppertext(line1) line2 = uppertext(line2) + var/message_changed = FALSE if(line1 != message1) message1 = line1 + message_changed = TRUE if(line2 != message2) message2 = line2 + message_changed = TRUE - update_appearance() + if(message_changed) + update_appearance() /** * Remove both message objs and null the fields. diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index 6f3f886b91e..679bbd1eafb 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -141,8 +141,12 @@ GLOBAL_LIST_EMPTY(telecomms_list) icon_state = "[initial(icon_state)][panel_open ? "_o" : null][on ? null : "_off"]" return ..() -/obj/machinery/telecomms/proc/update_power() +/obj/machinery/telecomms/on_set_panel_open(old_value) + update_appearance() + return ..() +/obj/machinery/telecomms/proc/update_power() + var/old_on = on if(toggled) if(machine_stat & (BROKEN|NOPOWER|EMPED)) // if powered, on. if not powered, off. if too damaged, off on = FALSE @@ -153,13 +157,12 @@ GLOBAL_LIST_EMPTY(telecomms_list) else on = FALSE soundloop.stop() + if(old_on != on) + update_appearance() /obj/machinery/telecomms/process(delta_time) update_power() - // Update the icon - update_appearance() - if(traffic > 0) traffic -= netspeed * delta_time diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm index 9b1891546fa..9ba4bafa62c 100644 --- a/code/game/objects/structures/barsigns.dm +++ b/code/game/objects/structures/barsigns.dm @@ -76,10 +76,10 @@ /obj/structure/sign/barsign/screwdriver_act(mob/living/user, obj/item/tool) tool.play_tool_sound(src) - if(!panel_open) + panel_open = !panel_open + if(panel_open) to_chat(user, span_notice("You open the maintenance panel.")) set_sign(new /datum/barsign/hiddensigns/signoff) - panel_open = TRUE return TOOL_ACT_TOOLTYPE_SUCCESS to_chat(user, span_notice("You close the maintenance panel.")) @@ -89,7 +89,6 @@ set_sign(new /datum/barsign/hiddensigns/signoff) else set_sign(chosen_sign) - panel_open = FALSE return TOOL_ACT_TOOLTYPE_SUCCESS /obj/structure/sign/barsign/attackby(obj/item/I, mob/user) diff --git a/code/game/turfs/closed/wall/reinf_walls.dm b/code/game/turfs/closed/wall/reinf_walls.dm index 9b507daf912..f41e21be848 100644 --- a/code/game/turfs/closed/wall/reinf_walls.dm +++ b/code/game/turfs/closed/wall/reinf_walls.dm @@ -197,6 +197,7 @@ QUEUE_SMOOTH_NEIGHBORS(src) QUEUE_SMOOTH(src) +// We don't react to smoothing changing here because this else exists only to "revert" intact changes /turf/closed/wall/r_wall/update_icon_state() if(d_state != INTACT) icon_state = "r_wall-[d_state]" diff --git a/code/game/turfs/open/floor/fancy_floor.dm b/code/game/turfs/open/floor/fancy_floor.dm index b3c7cfaf0bd..ca630953726 100644 --- a/code/game/turfs/open/floor/fancy_floor.dm +++ b/code/game/turfs/open/floor/fancy_floor.dm @@ -423,6 +423,10 @@ desc = "A glow-in-the-dark floor used to test emissive turfs." floor_tile = /obj/item/stack/tile/emissive_test +/turf/open/floor/emissive_test/smooth_icon() + . = ..() + update_appearance(~UPDATE_SMOOTHING) + /turf/open/floor/emissive_test/update_overlays() . = ..() . += emissive_appearance(icon, icon_state, src, alpha = src.alpha) diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index 9d145f05baa..afcc8063dfe 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -131,7 +131,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm) if(nbuild) buildstage = AIRALARM_BUILD_NO_CIRCUIT - panel_open = TRUE + set_panel_open(TRUE) if(name == initial(name)) name = "[get_area_name(src)] Air Alarm" @@ -723,7 +723,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm) if(buildstage != AIRALARM_BUILD_COMPLETE) return tool.play_tool_sound(src) - panel_open = !panel_open + toggle_panel_open() to_chat(user, span_notice("The wires have been [panel_open ? "exposed" : "unexposed"].")) update_appearance() return TRUE diff --git a/code/modules/atmospherics/machinery/bluespace_vendor.dm b/code/modules/atmospherics/machinery/bluespace_vendor.dm index 30b8951e3aa..5929ec70290 100644 --- a/code/modules/atmospherics/machinery/bluespace_vendor.dm +++ b/code/modules/atmospherics/machinery/bluespace_vendor.dm @@ -57,7 +57,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30) . = ..() if(nbuild) - panel_open = TRUE + set_panel_open(TRUE) update_appearance() diff --git a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm index af52c1a78a8..6151abc6520 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm @@ -147,15 +147,15 @@ if(generator) disconnectFromGenerator() mode = !mode - to_chat(user, span_notice("You set [src] to [mode?"cold":"hot"] mode.")) + to_chat(user, span_notice("You set [src] to [mode ? "cold" : "hot"] mode.")) return TRUE /obj/machinery/atmospherics/components/binary/circulator/screwdriver_act(mob/user, obj/item/I) if(..()) return TRUE - panel_open = !panel_open + toggle_panel_open() I.play_tool_sound(src) - to_chat(user, span_notice("You [panel_open?"open":"close"] the panel on [src].")) + to_chat(user, span_notice("You [panel_open ? "open" : "close"] the panel on [src].")) return TRUE /obj/machinery/atmospherics/components/binary/circulator/crowbar_act(mob/user, obj/item/I) diff --git a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm index 6cafb58e25b..ac6e129db28 100644 --- a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm +++ b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm @@ -153,7 +153,7 @@ /obj/machinery/electrolyzer/screwdriver_act(mob/living/user, obj/item/tool) tool.play_tool_sound(src, 50) - panel_open = !panel_open + toggle_panel_open() user.visible_message(span_notice("\The [user] [panel_open ? "opens" : "closes"] the hatch on \the [src]."), span_notice("You [panel_open ? "open" : "close"] the hatch on \the [src].")) update_appearance() return TRUE diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index 27bd8f2a8a3..aee0513a7f9 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -579,19 +579,25 @@ GLOBAL_LIST_INIT(gas_id_to_canister, init_gas_id_to_canister()) valve_open = !valve_open timing = FALSE + var/visual_update = FALSE // Handle gas transfer. if(valve_open) var/turf/location = get_turf(src) var/datum/gas_mixture/target_air = holding?.return_air() || location.return_air() excited = TRUE - if(air_contents.release_gas_to(target_air, release_pressure) && !holding) - air_update_turf(FALSE, FALSE) + if(air_contents.release_gas_to(target_air, release_pressure)) + visual_update = TRUE + if(!holding) + air_update_turf(FALSE, FALSE) // A bit different than other atmos devices. Wont stop if currently taking damage. if(take_atmos_damage()) + visual_update = TRUE excited = TRUE - update_appearance() + + if(visual_update) + update_appearance() return ..() /obj/machinery/portable_atmospherics/canister/ui_state(mob/user) diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index 867d305b343..b2f40ba68fc 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -161,12 +161,12 @@ if(16 to 23) airlock.welded = TRUE if(24 to 30) - airlock.panel_open = TRUE + airlock.set_panel_open(TRUE) if(airlock.cutAiWire) airlock.wires.cut(WIRE_AI) if(airlock.autoname) airlock.name = get_area_name(src, TRUE) - update_appearance() + airlock.update_appearance() qdel(src) /obj/effect/mapping_helpers/airlock/proc/payload(obj/machinery/door/airlock/payload) diff --git a/code/modules/power/apc/apc_tool_act.dm b/code/modules/power/apc/apc_tool_act.dm index 62d3b100850..543a75d7e87 100644 --- a/code/modules/power/apc/apc_tool_act.dm +++ b/code/modules/power/apc/apc_tool_act.dm @@ -62,7 +62,7 @@ if(obj_flags & EMAGGED) balloon_alert(user, "the interface is broken!") return - panel_open = !panel_open + toggle_panel_open() balloon_alert(user, "wires are [panel_open ? "exposed" : "unexposed"]") update_appearance() return diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index fe40682a1ab..768d391ccc8 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -151,6 +151,7 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri icon_state = "l[cable_layer]-noconnection" return ..() + // TODO: stop doing this shit in update_icon_state, this should be event based for the love of all that is holy var/list/dir_icon_list = list() for(var/check_dir in GLOB.cardinals) if(linked_dirs & check_dir) diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index e3e84e04511..28913ed2c4b 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -206,7 +206,7 @@ /obj/machinery/power/generator/screwdriver_act(mob/user, obj/item/I) if(..()) return TRUE - panel_open = !panel_open + toggle_panel_open() I.play_tool_sound(src) to_chat(user, span_notice("You [panel_open?"open":"close"] the panel on [src].")) return TRUE diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 6d0913ad782..56f278bdaa1 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -198,7 +198,7 @@ playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) return else if(O.tool_behaviour == TOOL_SCREWDRIVER) - panel_open = !panel_open + toggle_panel_open() O.play_tool_sound(src) if(panel_open) to_chat(user, span_notice("You open the access panel.")) diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index c6c9e477b9e..3029558d167 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -188,7 +188,14 @@ if(!(initial_stat & NOPOWER)) SEND_SIGNAL(src, COMSIG_MACHINERY_POWER_LOST) . = TRUE - update_appearance() + + if(appearance_power_state != (machine_stat & NOPOWER)) + update_appearance() + +// Saves like 300ms of init by not duping calls in the above proc +/obj/machinery/update_appearance(updates) + . = ..() + appearance_power_state = machine_stat & NOPOWER // connect the machine to a powernet if a node cable or a terminal is present on the turf /obj/machinery/power/proc/connect_to_network() @@ -426,6 +433,6 @@ return null for(var/obj/structure/cable/C in src) if(C.machinery_layer & machinery_layer) - C.update_appearance() + C.update_appearance() // I hate this. it's here because update_icon_state SCANS nearby turfs for objects to connect to. Wastes cpu time return C return null diff --git a/code/modules/power/turbine/turbine.dm b/code/modules/power/turbine/turbine.dm index 521dc342972..2ec930d36d4 100644 --- a/code/modules/power/turbine/turbine.dm +++ b/code/modules/power/turbine/turbine.dm @@ -95,13 +95,12 @@ return TOOL_ACT_TOOLTYPE_SUCCESS tool.play_tool_sound(src, 50) - panel_open = !panel_open + toggle_panel_open() if(panel_open) disable_parts(user) else enable_parts(user) - var/descriptor = panel_open ? "open" : "close" - balloon_alert(user, "you [descriptor] the maintenance hatch of [src]") + balloon_alert(user, "you [panel_open ? "open" : "close"] the maintenance hatch of [src]") update_appearance() return TOOL_ACT_TOOLTYPE_SUCCESS diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index 3aa8eb8db25..116d387e973 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -91,7 +91,7 @@ add_fingerprint(user) if(!pressure_charging && !full_pressure && !flush) if(I.tool_behaviour == TOOL_SCREWDRIVER) - panel_open = !panel_open + toggle_panel_open() I.play_tool_sound(src) to_chat(user, span_notice("You [panel_open ? "remove":"attach"] the screws around the power connection.")) return diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 2cfd88c019b..cc9d96ca8b9 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -1438,7 +1438,7 @@ GLOBAL_LIST_EMPTY(vending_products) /obj/machinery/vending/custom/greed/Initialize(mapload) . = ..() //starts in a state where you can move it - panel_open = TRUE + set_panel_open(TRUE) set_anchored(FALSE) add_overlay(panel_type) //and references the deity diff --git a/tgstation.dme b/tgstation.dme index ec287eb316f..e00f911502b 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -38,6 +38,7 @@ #include "code\__DEFINES\alerts.dm" #include "code\__DEFINES\antagonists.dm" #include "code\__DEFINES\apc_defines.dm" +#include "code\__DEFINES\appearance.dm" #include "code\__DEFINES\aquarium.dm" #include "code\__DEFINES\art.dm" #include "code\__DEFINES\assemblies.dm"