useless update_appearance reduction, emissive_blocker micro optimization (saves a second of init) (#71658)

## About The Pull Request

[Saves 0.2 seconds of init time. 50% of emissive
blockers](https://github.com/tgstation/tgstation/commit/8318b648f6d32844aacbfb4c309152cd45801f5c)

Emissive blockers are a decent expense during init, even these, which
are the ones that update outside of initialize.
I've inlined them, removed some redundant vars and checks, reduced the
arg count, and shifted some things around. This ends up saving 200ms, or
50% of its total cost.

I also shifted mutable_appearance about a bit. it's not a massive
saving, but it is technically faster

[Prevents a few redundant appearance_updates, saves 0.8 seconds of
init](https://github.com/tgstation/tgstation/commit/5475cd778b66b22b1e2c8d86b2c6d59fb84f219a)

Prequisit info: update_appearance is decently expensive
It's good then to only do it if we have a reason to, right?

Me and moth were shooting the shit about just general init time, and we
came up with the idea of tracking which update_appearances actually
"worked" and which didn't.

That bit comes later, let's enjoy the fruits of that work first

First, holograms were calling update_appearance on process, for almost
no reason.
I patched the one event they don't already "react" to, and then locked
it behind a change decection if.
good for live, doesn't impact init.

Next, decals. If you add a decal to something before it inits, it'll
react to the after successful init signal.
The trouble is the same atom could have its appearance updated from this
MORE then once, since decals can be stacked on tiles, and signal
unregisters don't work once the signal is sent.
So we add a flag to track if we've had this happen to us or not, so it
only happens once.
saves 80 ms

Power! lots of things call power_change on init, often more then once.
We'll update appearance for each of those calls, even if only one is an
actual change.
That's silly, better to track what sort of power we're using for our
appearance and go off that changing

This was taking about 300ms. Really stupid

Icon smoothing. After emissive blockers were added, any change to
something's icon smoothing would lead to an update_appearance call.
Nasty shit, specially cause of walls, which don't even use emissive
blockers.
Ok then, so we'll always update appearance for movables, and will allow
turfs that are interested to hook it manually.
Not many of those anyhow
This is slightly a dev ux thing, but it saves 600ms so I think it's
worth it. Rare case anyway

Telecomms:
telecomm machines were updating appearance on process. This is to cover
for them turning on/off on process.
Better then to just check if on actually changed.
This cost adds up midgame, doesn't impact init tho

Materials:
There's this update_appearance call in material on_apply. it doesn't do
anything.
The logs will lie to you and say it does, but it's just like reapplying
emissives. It doesn't need to exist
Saves like 50ms

Canisters:
Live thing, lots of time wasted updating appearance for no reason, lets
see if we change anything first yes?

[Uses defines to wrap update_appearance for
tracking](https://github.com/tgstation/tgstation/commit/4fa82e1c9d93577aadb3c743f17196331f62e67c)

[Undoes _update_appearance changes, instead reccomends 2 regexes to
use](https://github.com/tgstation/tgstation/commit/a8c8fec57a4e43d1fa636b5ac68459903faa9fc5)

I need file and line number for my tracking, so I need to override
update_appearance calls, and also preferably not require every override
of update_appearance to handle dummy file + line args.

So instead, I created a wrapper proc that checks to see if appearanaces
match (they're unique remember, the two of the same visual appearance
will be equivalent)
The trouble is I can't intercept JUST proc calls, or JUST function
definitions with defines. it needs to be both.

So I renamed the /update_appearance proc to /_update_appearance

this way I can capture old uses, and don't need to worry about merge/dev
brain skew

~~It does mean that all update_appearance proc definitions now look
weird tho.
My profiling is leaking into dev ux. I wish I had better templating.~~

**The above is no longer being pr'd**, it's instead just recommended via
a few regexes adjacent to the define.
Smelled wrong anyhow

[Adds a setter for panel_open, so I can update_appearance on
it](https://github.com/tgstation/tgstation/pull/71658/commits/cf1df8a69fc1a816391d085ee7419b14f9fe9167)

## Why It's Good For The Game

Speed
This commit is contained in:
LemonInTheDark
2022-12-20 00:51:52 -08:00
committed by GitHub
parent 05ec94e463
commit cf02f62298
44 changed files with 214 additions and 84 deletions
+7
View File
@@ -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
+6 -1
View File
@@ -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
+17 -8
View File
@@ -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);
+3
View File
@@ -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;\
+6
View File
@@ -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)
+21 -1
View File
@@ -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
+15 -1
View File
@@ -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]")
+8
View File
@@ -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
+2 -2
View File
@@ -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
-2
View File
@@ -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
+13 -15
View File
@@ -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
+18
View File
@@ -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
*
+1 -3
View File
@@ -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)
+21 -4
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -22,7 +22,7 @@
. = ..()
if(built)
setDir(ndir)
panel_open = TRUE
set_panel_open(TRUE)
update_appearance()
if(!built && !device && device_type)
+1 -1
View File
@@ -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()
+2 -3
View File
@@ -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)
+1 -2
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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
+6 -3
View File
@@ -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
+1 -1
View File
@@ -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
+5 -2
View File
@@ -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
+5 -1
View File
@@ -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.
@@ -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
+2 -3
View File
@@ -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)
@@ -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]"
@@ -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)
@@ -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
@@ -57,7 +57,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30)
. = ..()
if(nbuild)
panel_open = TRUE
set_panel_open(TRUE)
update_appearance()
@@ -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)
@@ -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
@@ -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)
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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
+1
View File
@@ -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)
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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."))
+9 -2
View File
@@ -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
+2 -3
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1
View File
@@ -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"