From 5fec421dae641f7ce825f61dcf9a29f4fab8d432 Mon Sep 17 00:00:00 2001 From: Wildkins Date: Sat, 4 Oct 2025 07:05:07 -0400 Subject: [PATCH] Destroy several sources of lag (#21422) Destroys a ton of sources of lag: - /obj/machinery/biogenerator/interact and /obj/machinery/computer/rdconsole/attack_hand Both of these call REF(src) constantly on their UI update, which also happens constantly. Death by a thousand cuts - /obj/machinery/button/ignition/attack_hand and /obj/machinery/button/switch/holosign/attack_hand Sleeps rather than timers, iterating the entire machinery list rather than just storing the IDs we need at roundstart - /obj/machinery/telecomms/update_icon and /obj/machinery/meter/process Calling overlay updates WAY TOO MUCH. UpdateOverlays is a significant overhead for the server at this point and the two of these combined are responsible for a little over 20% of updates. They now should only update overlays when necessary rather than every tick. - NEW! /obj/machinery/disposal/proc/update and /obj/machinery/portable_atmospherics/hydroponics/update_icon These two were also offenders, worse than telecomms but better than meters. They have also been brought into line. - /mob/living/carbon/slime Hard-del'd if grinded into slime extract less than two minutes after being "born". Fixed by adding TIMER_DELETE_ME flag. --------- Co-authored-by: Matt Atlas --- .github/workflows/byond.yml | 2 +- aurorastation.dme | 1 + code/__DEFINES/spatial_gridmap.dm | 7 +- .../controllers/subsystems/spatial_gridmap.dm | 92 ++++++++- code/game/atoms_movable.dm | 34 ++-- code/game/machinery/atmoalter/meter.dm | 59 +++--- code/game/machinery/biogenerator.dm | 14 +- code/game/machinery/buttons.dm | 18 +- code/game/machinery/camera/motion.dm | 3 + code/game/machinery/holosign.dm | 20 +- code/game/machinery/igniter.dm | 45 +++-- .../machinery/telecomms/telecommunications.dm | 5 +- code/game/objects/effects/spiders.dm | 41 ++-- code/modules/client/client_procs.dm | 3 + .../heavy_vehicle/equipment/utility.dm | 13 ++ code/modules/hydroponics/trays/_defines.dm | 10 + code/modules/hydroponics/trays/tray.dm | 7 + .../hydroponics/trays/tray_update_icons.dm | 151 +++++++++------ .../modules/mob/living/carbon/slime/powers.dm | 2 +- code/modules/mob/living/living.dm | 1 - code/modules/mob/login.dm | 4 +- code/modules/mob/logout.dm | 3 +- code/modules/mob/mob.dm | 1 - code/modules/organs/organ.dm | 5 +- .../projectiles/ammunition/ammo_pile.dm | 14 ++ code/modules/projectiles/pins.dm | 1 + code/modules/recycling/disposal.dm | 28 ++- code/modules/recycling/sortingmachinery.dm | 14 +- .../designs/circuit/hardsuit_circuits.dm | 4 +- .../designs/circuit/machine_circuits.dm | 12 +- .../research/designs/mechfab/hardsuit/rigs.dm | 2 +- code/modules/research/rdconsole.dm | 177 +++++++++--------- html/changelogs/johnwildkins-lagdeath.yml | 17 ++ 33 files changed, 526 insertions(+), 284 deletions(-) create mode 100644 code/modules/hydroponics/trays/_defines.dm create mode 100644 html/changelogs/johnwildkins-lagdeath.yml diff --git a/.github/workflows/byond.yml b/.github/workflows/byond.yml index 07fa61efb1f..c4659c0bd70 100644 --- a/.github/workflows/byond.yml +++ b/.github/workflows/byond.yml @@ -15,7 +15,7 @@ on: env: MACRO_COUNT: 0 GENDER_COUNT: 6 - TO_WORLD_COUNT: 179 + TO_WORLD_COUNT: 178 #These variables are filled from dependencies.sh inside the steps, DO NOT SET THEM HERE BYOND_MAJOR: "" diff --git a/aurorastation.dme b/aurorastation.dme index 1b1bcbf5fe8..b5aa22ab2ef 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -2438,6 +2438,7 @@ #include "code\modules\hydroponics\spreading\spreading.dm" #include "code\modules\hydroponics\spreading\spreading_growth.dm" #include "code\modules\hydroponics\spreading\spreading_response.dm" +#include "code\modules\hydroponics\trays\_defines.dm" #include "code\modules\hydroponics\trays\tray.dm" #include "code\modules\hydroponics\trays\tray_process.dm" #include "code\modules\hydroponics\trays\tray_reagents.dm" diff --git a/code/__DEFINES/spatial_gridmap.dm b/code/__DEFINES/spatial_gridmap.dm index 97a6f991539..4a59339875b 100644 --- a/code/__DEFINES/spatial_gridmap.dm +++ b/code/__DEFINES/spatial_gridmap.dm @@ -16,8 +16,10 @@ #define SPATIAL_GRID_CONTENTS_TYPE_CLIENTS RECURSIVE_CONTENTS_CLIENT_MOBS ///all atmos machines are stored in this channel (I'm sorry kyler) #define SPATIAL_GRID_CONTENTS_TYPE_ATMOS "spatial_grid_contents_type_atmos" +/// everything that can be targeted +#define SPATIAL_GRID_CONTENTS_TYPE_TARGETS RECURSIVE_CONTENTS_AI_TARGETS -#define ALL_CONTENTS_OF_CELL(cell) (cell.hearing_contents | cell.client_contents | cell.atmos_contents) +#define ALL_CONTENTS_OF_CELL(cell) (cell.hearing_contents | cell.client_contents | cell.atmos_contents | cell.target_contents) ///whether movable is itself or containing something which should be in one of the spatial grid channels. #define HAS_SPATIAL_GRID_CONTENTS(movable) (movable.spatial_grid_key) @@ -52,4 +54,5 @@ #define GRID_CELL_REMOVE_ALL(cell, movable) \ GRID_CELL_REMOVE(cell.hearing_contents, movable) \ GRID_CELL_REMOVE(cell.client_contents, movable) \ - GRID_CELL_REMOVE(cell.atmos_contents, movable) + GRID_CELL_REMOVE(cell.atmos_contents, movable) \ + GRID_CELL_REMOVE(cell.target_contents, movable) diff --git a/code/controllers/subsystems/spatial_gridmap.dm b/code/controllers/subsystems/spatial_gridmap.dm index 8c25e5f91d6..b6216dd042e 100644 --- a/code/controllers/subsystems/spatial_gridmap.dm +++ b/code/controllers/subsystems/spatial_gridmap.dm @@ -28,6 +28,8 @@ var/list/client_contents ///every atmos machine inside this cell var/list/atmos_contents + ///every valid ai turret target inside this cell + var/list/target_contents /datum/spatial_grid_cell/New(cell_x, cell_y, cell_z) . = ..() @@ -43,6 +45,7 @@ hearing_contents = dummy_list client_contents = dummy_list atmos_contents = dummy_list + target_contents = dummy_list /datum/spatial_grid_cell/Destroy(force) if(force)//the response to someone trying to qdel this is a right proper fuck you @@ -85,7 +88,7 @@ SUBSYSTEM_DEF(spatial_grid) ///list of the spatial_grid_cell datums per z level, arranged in the order of y index then x index var/list/grids_by_z_level = list() ///everything that spawns before us is added to this list until we initialize - var/list/waiting_to_add_by_type = list(SPATIAL_GRID_CONTENTS_TYPE_HEARING = list(), SPATIAL_GRID_CONTENTS_TYPE_CLIENTS = list(), SPATIAL_GRID_CONTENTS_TYPE_ATMOS = list()) + var/list/waiting_to_add_by_type = list(SPATIAL_GRID_CONTENTS_TYPE_HEARING = list(), SPATIAL_GRID_CONTENTS_TYPE_CLIENTS = list(), SPATIAL_GRID_CONTENTS_TYPE_ATMOS = list(), SPATIAL_GRID_CONTENTS_TYPE_TARGETS = list()) ///associative list of the form: movable.spatial_grid_key (string) -> inner list of spatial grid types for that key. ///inner lists contain contents channel types such as SPATIAL_GRID_CONTENTS_TYPE_HEARING etc. ///we use this to make adding to a cell static cost, and to save on memory @@ -257,6 +260,12 @@ SUBSYSTEM_DEF(spatial_grid) for(var/x_index in BOUNDING_BOX_MIN(center_x) to BOUNDING_BOX_MAX(center_x, cells_on_x_axis)) . += grid_level[row][x_index].atmos_contents + if(SPATIAL_GRID_CONTENTS_TYPE_TARGETS) + for(var/row in BOUNDING_BOX_MIN(center_y) to BOUNDING_BOX_MAX(center_y, cells_on_y_axis)) + for(var/x_index in BOUNDING_BOX_MIN(center_x) to BOUNDING_BOX_MAX(center_x, cells_on_x_axis)) + + . += grid_level[row][x_index].target_contents + return . ///get the grid cell encomapassing targets coordinates @@ -378,6 +387,11 @@ SUBSYSTEM_DEF(spatial_grid) GRID_CELL_SET(intersecting_cell.hearing_contents, new_target.important_recursive_contents[SPATIAL_GRID_CONTENTS_TYPE_HEARING]) SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_HEARING), new_target_contents[SPATIAL_GRID_CONTENTS_TYPE_HEARING]) + if(SPATIAL_GRID_CONTENTS_TYPE_TARGETS) + var/list/new_target_contents = new_target.important_recursive_contents + GRID_CELL_SET(intersecting_cell.target_contents, new_target.important_recursive_contents[SPATIAL_GRID_CONTENTS_TYPE_TARGETS]) + SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_TARGETS), new_target_contents[SPATIAL_GRID_CONTENTS_TYPE_TARGETS]) + if(SPATIAL_GRID_CONTENTS_TYPE_ATMOS) GRID_CELL_SET(intersecting_cell.atmos_contents, new_target) SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_ATMOS), new_target) @@ -408,6 +422,11 @@ SUBSYSTEM_DEF(spatial_grid) GRID_CELL_SET(intersecting_cell.hearing_contents, new_target.important_recursive_contents[SPATIAL_GRID_CONTENTS_TYPE_HEARING]) SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_HEARING), new_target_contents[SPATIAL_GRID_CONTENTS_TYPE_HEARING]) + if(SPATIAL_GRID_CONTENTS_TYPE_TARGETS) + var/list/new_target_contents = new_target.important_recursive_contents + GRID_CELL_SET(intersecting_cell.target_contents, new_target.important_recursive_contents[SPATIAL_GRID_CONTENTS_TYPE_TARGETS]) + SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_TARGETS), new_target_contents[SPATIAL_GRID_CONTENTS_TYPE_TARGETS]) + if(SPATIAL_GRID_CONTENTS_TYPE_ATMOS) GRID_CELL_SET(intersecting_cell.atmos_contents, new_target) SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_ATMOS), new_target) @@ -447,6 +466,11 @@ SUBSYSTEM_DEF(spatial_grid) GRID_CELL_REMOVE(intersecting_cell.hearing_contents, old_target_contents) SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_EXITED(type), old_target_contents) + if(SPATIAL_GRID_CONTENTS_TYPE_TARGETS) + var/list/old_target_contents = old_target.important_recursive_contents?[type] || old_target + GRID_CELL_REMOVE(intersecting_cell.target_contents, old_target_contents) + SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_EXITED(type), old_target_contents) + if(SPATIAL_GRID_CONTENTS_TYPE_ATMOS) GRID_CELL_REMOVE(intersecting_cell.atmos_contents, old_target) SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_EXITED(type), old_target) @@ -479,6 +503,11 @@ SUBSYSTEM_DEF(spatial_grid) GRID_CELL_REMOVE(intersecting_cell.hearing_contents, old_target_contents) SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_EXITED(exclusive_type), old_target_contents) + if(SPATIAL_GRID_CONTENTS_TYPE_TARGETS) + var/list/old_target_contents = old_target.important_recursive_contents?[exclusive_type] || old_target + GRID_CELL_REMOVE(intersecting_cell.target_contents, old_target_contents) + SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_EXITED(exclusive_type), old_target_contents) + if(SPATIAL_GRID_CONTENTS_TYPE_ATMOS) GRID_CELL_REMOVE(intersecting_cell.atmos_contents, old_target) SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_EXITED(exclusive_type), old_target) @@ -521,6 +550,12 @@ SUBSYSTEM_DEF(spatial_grid) if(movable_to_check in cell.hearing_contents) contents = "hearing" + if(movable_to_check in cell.target_contents) + if(length(contents) > 0) + contents = "[contents], target" + else + contents = "target" + if(movable_to_check in cell.client_contents) if(length(contents) > 0) contents = "[contents], client" @@ -566,7 +601,7 @@ SUBSYSTEM_DEF(spatial_grid) remove_from_pre_init_queue(to_remove)//the spatial grid doesnt exist yet, so just take it out of the queue return -#ifdef UNIT_TESTS +#ifdef UNIT_TEST if(untracked_movable_error(to_remove)) find_hanging_cell_refs_for_movable(to_remove, remove_from_cells=FALSE) //dont remove from cells because we should be able to see 2 errors return @@ -604,7 +639,7 @@ SUBSYSTEM_DEF(spatial_grid) for(var/list/z_level_grid as anything in grids_by_z_level) for(var/list/cell_row as anything in z_level_grid) for(var/datum/spatial_grid_cell/cell as anything in cell_row) - if(to_remove in (cell.hearing_contents | cell.client_contents | cell.atmos_contents)) + if(to_remove in (cell.hearing_contents | cell.client_contents | cell.atmos_contents | cell.target_contents)) containing_cells += cell if(remove_from_cells) force_remove_from_cell(to_remove, cell) @@ -677,14 +712,17 @@ SUBSYSTEM_DEF(spatial_grid) var/raw_clients = 0 var/raw_hearables = 0 var/raw_atmos = 0 + var/raw_targets = 0 var/cells_with_clients = 0 var/cells_with_hearables = 0 var/cells_with_atmos = 0 + var/cells_with_targets = 0 var/list/client_list = list() var/list/hearable_list = list() var/list/atmos_list = list() + var/list/target_list = list() var/x_cell_count = world.maxx / SPATIAL_GRID_CELLSIZE var/y_cell_count = world.maxy / SPATIAL_GRID_CELLSIZE @@ -694,6 +732,7 @@ SUBSYSTEM_DEF(spatial_grid) var/average_clients_per_cell = 0 var/average_hearables_per_cell = 0 var/average_atmos_mech_per_call = 0 + var/average_targets_per_cell var/hearable_min_x = x_cell_count var/hearable_max_x = 1 @@ -707,6 +746,12 @@ SUBSYSTEM_DEF(spatial_grid) var/client_min_y = y_cell_count var/client_max_y = 1 + var/target_min_x = x_cell_count + var/target_max_x = 1 + + var/target_min_y = y_cell_count + var/target_max_y = 1 + var/atmos_min_x = x_cell_count var/atmos_max_x = 1 @@ -727,7 +772,7 @@ SUBSYSTEM_DEF(spatial_grid) for(var/client_to_insert in 0 to insert_clients) var/turf/random_turf = pick(turfs) var/mob/fake_client = new() - fake_client.important_recursive_contents = list(SPATIAL_GRID_CONTENTS_TYPE_HEARING = list(fake_client), SPATIAL_GRID_CONTENTS_TYPE_CLIENTS = list(fake_client)) + fake_client.important_recursive_contents = list(SPATIAL_GRID_CONTENTS_TYPE_HEARING = list(fake_client), SPATIAL_GRID_CONTENTS_TYPE_CLIENTS = list(fake_client), SPATIAL_GRID_CONTENTS_TYPE_TARGETS = list(fake_client)) fake_client.forceMove(random_turf) inserted_clients += fake_client @@ -737,10 +782,12 @@ SUBSYSTEM_DEF(spatial_grid) var/client_length = length(cell.client_contents) var/hearable_length = length(cell.hearing_contents) var/atmos_length = length(cell.atmos_contents) + var/target_length = length(cell.target_contents) raw_clients += client_length raw_hearables += hearable_length raw_atmos += atmos_length + raw_targets += target_length if(client_length) cells_with_clients++ @@ -776,6 +823,23 @@ SUBSYSTEM_DEF(spatial_grid) if(cell.cell_y > hearable_max_y) hearable_max_y = cell.cell_y + if(target_length) + cells_with_targets++ + + target_list += cell.target_contents + + if(cell.cell_x < hearable_min_x) + target_min_x = cell.cell_x + + if(cell.cell_x > hearable_max_x) + target_max_x = cell.cell_x + + if(cell.cell_y < hearable_min_y) + target_min_y = cell.cell_y + + if(cell.cell_y > hearable_max_y) + target_max_y = cell.cell_y + if(raw_atmos) cells_with_atmos++ @@ -796,10 +860,12 @@ SUBSYSTEM_DEF(spatial_grid) var/total_client_distance = 0 var/total_hearable_distance = 0 var/total_atmos_distance = 0 + var/total_target_distance = 0 var/average_client_distance = 0 var/average_hearable_distance = 0 var/average_atmos_distance = 0 + var/average_target_distance = 0 for(var/hearable in hearable_list)//n^2 btw for(var/other_hearable in hearable_list) @@ -813,6 +879,12 @@ SUBSYSTEM_DEF(spatial_grid) continue total_client_distance += get_dist(client, other_client) + for(var/target in target_list)//n^2 btw + for(var/other_target in target_list) + if(target == other_target) + continue + total_target_distance += get_dist(target, other_target) + for(var/atmos in atmos_list)//n^2 btw for(var/other_atmos in atmos_list) if(atmos == other_atmos) @@ -825,24 +897,28 @@ SUBSYSTEM_DEF(spatial_grid) average_client_distance = total_client_distance / length(client_list) if(length(atmos_list)) average_atmos_distance = total_atmos_distance / length(atmos_list) + if(length(target_list)) + average_target_distance = total_target_distance / length(target_list) average_clients_per_cell = raw_clients / total_cells average_hearables_per_cell = raw_hearables / total_cells average_atmos_mech_per_call = raw_atmos / total_cells + average_targets_per_cell = raw_targets / total_cells for(var/mob/inserted_client as anything in inserted_clients) qdel(inserted_client) message_admins("on z level [z] there are [raw_clients] clients ([insert_clients] of whom are fakes inserted to random station turfs)\ - , [raw_hearables] hearables, and [raw_atmos] atmos machines. all of whom are inside the bounding box given by \ + , [raw_hearables] hearables, [raw_targets] targets, and [raw_atmos] atmos machines. all of whom are inside the bounding box given by \ clients: ([client_min_x], [client_min_y]) x ([client_max_x], [client_max_y]), \ hearables: ([hearable_min_x], [hearable_min_y]) x ([hearable_max_x], [hearable_max_y]) \ + targets: ([target_min_x], [target_min_y] x [target_max_x], [target_max_y]) \ and atmos machines: ([atmos_min_x], [atmos_min_y]) x ([atmos_max_x], [atmos_max_y]), \ on average there are [average_clients_per_cell] clients per cell, [average_hearables_per_cell] hearables per cell, \ - and [average_atmos_mech_per_call] per cell, \ - [cells_with_clients] cells have clients, [cells_with_hearables] have hearables, and [cells_with_atmos] have atmos machines \ + [average_targets_per_cell] targets per cell, and [average_atmos_mech_per_call] atmos machines per cell, \ + [cells_with_clients] cells have clients, [cells_with_hearables] have hearables, [cells_with_targets] have targets, and [cells_with_atmos] have atmos machines \ the average client distance is: [average_client_distance], the average hearable_distance is [average_hearable_distance], \ - and the average atmos distance is [average_atmos_distance] ") + the average target distance is [average_target_distance], and the average atmos distance is [average_atmos_distance] ") #undef BOUNDING_BOX_MAX #undef BOUNDING_BOX_MIN diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 5dcd6d9a5d9..b0aa198a85b 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -580,7 +580,7 @@ LAZYINITLIST(recursive_contents[channel]) recursive_contents[channel] -= gone.important_recursive_contents[channel] switch(channel) - if(RECURSIVE_CONTENTS_CLIENT_MOBS, RECURSIVE_CONTENTS_HEARING_SENSITIVE) + if(RECURSIVE_CONTENTS_CLIENT_MOBS, RECURSIVE_CONTENTS_HEARING_SENSITIVE,RECURSIVE_CONTENTS_AI_TARGETS) if(!length(recursive_contents[channel])) // This relies on a nice property of the linked recursive and gridmap types // They're defined in relation to each other, so they have the same value @@ -601,7 +601,7 @@ var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity LAZYINITLIST(recursive_contents[channel]) switch(channel) - if(RECURSIVE_CONTENTS_CLIENT_MOBS, RECURSIVE_CONTENTS_HEARING_SENSITIVE) + if(RECURSIVE_CONTENTS_CLIENT_MOBS, RECURSIVE_CONTENTS_HEARING_SENSITIVE,RECURSIVE_CONTENTS_AI_TARGETS) if(!length(recursive_contents[channel])) SSspatial_grid.add_grid_awareness(location, channel) recursive_contents[channel] |= arrived.important_recursive_contents[channel] @@ -701,25 +701,29 @@ // This proc adds atom/movables to the AI targetable list, i.e. things that the AI (turrets, hostile animals) will attempt to target /atom/movable/proc/add_to_target_grid() for (var/atom/movable/location as anything in get_nested_locs(src) + src) - LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_AI_TARGETS, src) + LAZYINITLIST(location.important_recursive_contents) + var/list/recursive_contents = location.important_recursive_contents + if(!length(recursive_contents[RECURSIVE_CONTENTS_AI_TARGETS])) + SSspatial_grid.add_grid_awareness(location, SPATIAL_GRID_CONTENTS_TYPE_TARGETS) + LAZYINITLIST(recursive_contents[RECURSIVE_CONTENTS_AI_TARGETS]) + recursive_contents[RECURSIVE_CONTENTS_AI_TARGETS] |= src var/turf/our_turf = get_turf(src) - if(our_turf && SSspatial_grid.initialized) - SSspatial_grid.add_grid_awareness(src, RECURSIVE_CONTENTS_AI_TARGETS) - SSspatial_grid.add_grid_membership(src, our_turf, RECURSIVE_CONTENTS_AI_TARGETS) - - else if(our_turf && !SSspatial_grid.initialized)//SSspatial_grid isnt init'd yet, add ourselves to the queue - SSspatial_grid.enter_pre_init_queue(src, RECURSIVE_CONTENTS_AI_TARGETS) + SSspatial_grid.add_grid_membership(src, our_turf, SPATIAL_GRID_CONTENTS_TYPE_TARGETS) /atom/movable/proc/clear_from_target_grid() var/turf/our_turf = get_turf(src) - if(our_turf && SSspatial_grid.initialized) - SSspatial_grid.exit_cell(src, our_turf, RECURSIVE_CONTENTS_AI_TARGETS) - else if(our_turf && !SSspatial_grid.initialized) - SSspatial_grid.remove_from_pre_init_queue(src, RECURSIVE_CONTENTS_AI_TARGETS) + SSspatial_grid.remove_grid_membership(src, our_turf, SPATIAL_GRID_CONTENTS_TYPE_TARGETS) - for(var/atom/movable/location as anything in get_nested_locs(src) + src) - LAZYREMOVEASSOC(location.important_recursive_contents, RECURSIVE_CONTENTS_AI_TARGETS, src) + for(var/atom/movable/movable_loc as anything in get_nested_locs(src) + src) + LAZYINITLIST(movable_loc.important_recursive_contents) + var/list/recursive_contents = movable_loc.important_recursive_contents // blue hedgehog velocity + LAZYINITLIST(recursive_contents[RECURSIVE_CONTENTS_AI_TARGETS]) + recursive_contents[RECURSIVE_CONTENTS_AI_TARGETS] -= src + if(!length(recursive_contents[RECURSIVE_CONTENTS_AI_TARGETS])) + SSspatial_grid.remove_grid_awareness(movable_loc, SPATIAL_GRID_CONTENTS_TYPE_TARGETS) + ASSOC_UNSETEMPTY(recursive_contents, RECURSIVE_CONTENTS_AI_TARGETS) + UNSETEMPTY(movable_loc.important_recursive_contents) /atom/movable/proc/do_simple_ranged_interaction(var/mob/user) return FALSE diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index 59dfa61b5eb..cbfc2c77d6d 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -38,22 +38,20 @@ if (!target) src.target = locate(/obj/machinery/atmospherics/pipe) in loc -/obj/machinery/meter/process() - ClearOverlays() - if(!target) - AddOverlays("pressure_off") - AddOverlays("buttons-x") - return FALSE - - if(stat & (BROKEN|NOPOWER)) - AddOverlays("pressure_off") - return FALSE +/obj/machinery/meter/update_icon() + var/list/new_overlays = get_rebuild_overlays() + if(LAZYLEN(new_overlays)) + ClearOverlays() + AddOverlays(new_overlays) +/obj/machinery/meter/proc/get_rebuild_overlays() + if (!target) + return list("pressure_off", "buttons_x") + if (stat & (BROKEN|NOPOWER)) + return list("pressure_off") var/datum/gas_mixture/environment = target.return_air() if(!environment) - AddOverlays("buttons_x") - AddOverlays("pressure0") - return FALSE + return list("pressure0", "buttons_x") var/button_overlay_name var/atmos_overlay_name @@ -76,19 +74,16 @@ else atmos_overlay_name = "pressure4" - button_overlay = overlay_image(icon, button_overlay_name) - atmos_overlay = overlay_image(icon, atmos_overlay_name) - button_emissive = emissive_appearance(icon, button_overlay_name) - atmos_emissive = emissive_appearance(icon, atmos_overlay_name) + if (!button_overlay || button_overlay.icon_state != button_overlay_name) + button_overlay = overlay_image(icon, button_overlay_name) + button_emissive = emissive_appearance(icon, button_overlay_name) + . = TRUE var/env_temperature = environment.temperature - var/temp_color + var/temp_color = COLOR_GRAY - if(env_pressure == 0 || env_temperature == 0) - temp_color = COLOR_GRAY - - else + if(env_pressure != 0 && env_temperature != 0) switch(env_temperature) if((BODYTEMP_HEAT_DAMAGE_LIMIT + 360) to INFINITY) temp_color = COLOR_RED @@ -105,13 +100,23 @@ else temp_color = COLOR_VIOLET - if(atmos_overlay.color != temp_color) + if (!atmos_overlay || atmos_overlay.icon_state != atmos_overlay_name || atmos_overlay.color != temp_color) + atmos_overlay = overlay_image(icon, atmos_overlay_name) + atmos_emissive = emissive_appearance(icon, atmos_overlay_name) atmos_overlay.color = temp_color + . = TRUE - AddOverlays(button_overlay) - AddOverlays(atmos_overlay) - AddOverlays(button_emissive) - AddOverlays(atmos_emissive) + if (.) + return list(button_overlay, button_emissive, atmos_overlay, atmos_emissive) + +/obj/machinery/meter/process() + update_icon() + if (!target || (stat & (BROKEN|NOPOWER))) + return FALSE + var/datum/gas_mixture/environment = target.return_air() + if(!environment) + return FALSE + var/env_pressure = environment.return_pressure() if(frequency) var/datum/radio_frequency/radio_connection = SSradio.return_frequency(frequency) diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm index b697f63693b..df534df99da 100644 --- a/code/game/machinery/biogenerator.dm +++ b/code/game/machinery/biogenerator.dm @@ -13,6 +13,7 @@ var/build_eff = 1 var/eat_eff = 1 var/capacity = 100 + var/ref_for_ui component_types = list( /obj/item/circuitboard/biogenerator, @@ -536,6 +537,7 @@ EMAG/ILLEGAL R.my_atom = src beaker = new /obj/item/reagent_containers/glass/bottle(src) update_icon() + ref_for_ui = "[REF(src)]" /obj/machinery/biogenerator/on_reagent_change() //When the reagents change, change the icon as well. update_icon() @@ -618,8 +620,8 @@ EMAG/ILLEGAL if("menu") if (beaker) dat += "" - dat += "" - dat += "" + dat += "" + dat += "" dat += "" var/lastclass = "Commands" @@ -639,7 +641,7 @@ EMAG/ILLEGAL if(num*round(current_recipe.cost/build_eff) > points) dat += "
([fakenum][num])
" else - dat += "([fakenum][num])" + dat += "([fakenum][num])" dat += "" dat += "" @@ -649,13 +651,13 @@ EMAG/ILLEGAL dat += "
No beaker inside. Please insert a beaker.
" if("nopoints") dat += "You do not have biomass to create products.
Please put growns into the reactor and activate it.
" - dat += "Return to menu" + dat += "Return to menu" if("complete") dat += "Operation complete.
" - dat += "Return to menu" + dat += "Return to menu" if("void") dat += "Error: No growns inside.
Please put growns into the reactor.
" - dat += "Return to menu" + dat += "Return to menu" dat += "" var/datum/browser/biogen_win = new(user, "biogenerator", "Biogenerator", 450, 500) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 3ff785b8ced..d7635427858 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -7,6 +7,7 @@ var/id = null var/active = 0 var/operating = 0 + var/active_time = 1 SECOND anchored = 1.0 idle_power_usage = 2 active_power_usage = 4 @@ -38,19 +39,22 @@ activate(user) intent_message(BUTTON_FLICK, 5) +/obj/machinery/button/proc/deactivate(mob/living/user) + active = FALSE + update_icon() + operating = FALSE + /obj/machinery/button/proc/activate(mob/living/user) if(operating || !istype(wifi_sender)) - return + return FALSE - operating = 1 - active = 1 + operating = TRUE + active = TRUE use_power_oneoff(5) update_icon() wifi_sender.activate(user) - sleep(10) - active = 0 - update_icon() - operating = 0 + addtimer(CALLBACK(src, PROC_REF(deactivate)), active_time, TIMER_DELETE_ME) + return TRUE /obj/machinery/button/update_icon() if(active) diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index 167a7afe046..e13887c5655 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -17,12 +17,15 @@ detectTime = world.time // start the clock if (!(target in motionTargets) && !QDELING(target)) motionTargets += target + RegisterSignal(target, COMSIG_QDELETING, PROC_REF(lostTarget)) return TRUE /obj/machinery/camera/proc/lostTarget(var/mob/target) + SIGNAL_HANDLER if (target in motionTargets) motionTargets -= target + UnregisterSignal(target, COMSIG_QDELETING) if (motionTargets.len == 0) cancelAlarm() diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm index 99cdaf632ae..0beb253af31 100644 --- a/code/game/machinery/holosign.dm +++ b/code/game/machinery/holosign.dm @@ -66,18 +66,28 @@ name = "holosign switch" desc = "A remote control switch for a holosign." icon_state = "light0" + var/list/datum/weakref/linked_signs + +/obj/machinery/button/switch/holosign/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/button/switch/holosign/LateInitialize() + . = ..() + for(var/obj/machinery/holosign/H in SSmachinery.machinery) + if(H.id == id) + LAZYADD(linked_signs, WEAKREF(H)) /obj/machinery/button/switch/holosign/attack_hand(mob/user as mob) if(..()) return add_fingerprint(user) - use_power_oneoff(5) - active = !active update_icon() - for(var/obj/machinery/holosign/M in SSmachinery.machinery) - if (M.id == src.id) - INVOKE_ASYNC(M, TYPE_PROC_REF(/obj/machinery/holosign, toggle)) + for(var/datum/weakref/W in linked_signs) + var/obj/machinery/holosign/H = W.resolve() + if(!isnull(H)) + INVOKE_ASYNC(H, TYPE_PROC_REF(/obj/machinery/holosign, toggle)) return diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 5a7269483cf..a0911618a48 100755 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -146,28 +146,35 @@ /obj/machinery/button/ignition name = "ignition switch" desc = "A remote control switch for a mounted igniter." + active_time = 5 SECONDS + var/list/datum/weakref/linked_sparkers + var/list/datum/weakref/linked_igniters -/obj/machinery/button/ignition/attack_hand(mob/user as mob) +/obj/machinery/button/ignition/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD +/obj/machinery/button/ignition/LateInitialize() + . = ..() + for (var/obj/machinery/M in SSmachinery.machinery) + var/obj/machinery/sparker/S = M + if (istype(S) && S.id == id) + LAZYADD(linked_sparkers, WEAKREF(S)) + else + var/obj/machinery/igniter/I = M + if (istype(I) && I.id == id) + LAZYADD(linked_igniters, WEAKREF(I)) + +/obj/machinery/button/ignition/activate(mob/living/user) if(..()) return - use_power_oneoff(5) + for (var/datum/weakref/W in linked_sparkers) + var/obj/machinery/sparker/S = W.resolve() + if(!isnull(S)) + INVOKE_ASYNC(S, TYPE_PROC_REF(/obj/machinery/sparker, ignite)) - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/sparker/M in SSmachinery.machinery) - if (M.id == id) - INVOKE_ASYNC(M, TYPE_PROC_REF(/obj/machinery/sparker, ignite)) - - for(var/obj/machinery/igniter/M in SSmachinery.machinery) - if(M.id == id) - M.ignite() - - sleep(50) - - icon_state = "launcherbtt" - active = 0 - - return + for (var/datum/weakref/W in linked_igniters) + var/obj/machinery/igniter/I = W.resolve() + if(!isnull(I)) + I.ignite() diff --git a/code/game/machinery/telecomms/telecommunications.dm b/code/game/machinery/telecomms/telecommunications.dm index c54db8ecbd3..37c9357fb6c 100644 --- a/code/game/machinery/telecomms/telecommunications.dm +++ b/code/game/machinery/telecomms/telecommunications.dm @@ -152,8 +152,9 @@ AddOverlays("[icon_state]_panel") /obj/machinery/telecomms/process() - update_icon() - if(!use_power) return PROCESS_KILL + if(!use_power) + update_icon() + return PROCESS_KILL if(!operable(EMPED)) toggle_power(additional_flags = EMPED) return PROCESS_KILL diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 46349b798b4..10971f6820a 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -207,28 +207,29 @@ var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = pick(vents) spawn(rand(20,60)) - loc = exit_vent - var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) - spawn(travel_time) + if(!QDELETED(src)) + loc = exit_vent + var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) + spawn(travel_time) + if(!QDELETED(src)) + if(!exit_vent || exit_vent.welded) + loc = entry_vent + entry_vent = null + return - if(!exit_vent || exit_vent.welded) - loc = entry_vent - entry_vent = null - return + if(prob(50)) + visible_message(SPAN_NOTICE("You hear something squeezing through the ventilation ducts."), range = 2) + sleep(travel_time) - if(prob(50)) - visible_message(SPAN_NOTICE("You hear something squeezing through the ventilation ducts."), range = 2) - sleep(travel_time) - - if(!exit_vent || exit_vent.welded) - loc = entry_vent - entry_vent = null - return - loc = exit_vent.loc - entry_vent = null - var/area/new_area = get_area(loc) - if(new_area) - new_area.Entered(src) + if(!exit_vent || exit_vent.welded) + loc = entry_vent + entry_vent = null + return + loc = exit_vent.loc + entry_vent = null + var/area/new_area = get_area(loc) + if(new_area) + new_area.Entered(src) else entry_vent = null else if(prob(25) && isturf(loc)) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index eb62fc97a16..19e66600f5b 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -531,6 +531,9 @@ GLOBAL_LIST_INIT(localhost_addresses, list( holder.owner = null GLOB.staff -= src + if(mob) + mob.clear_important_client_contents() + SSping.currentrun -= src QDEL_NULL(tooltips) diff --git a/code/modules/heavy_vehicle/equipment/utility.dm b/code/modules/heavy_vehicle/equipment/utility.dm index 78db97983cf..48399168b9c 100644 --- a/code/modules/heavy_vehicle/equipment/utility.dm +++ b/code/modules/heavy_vehicle/equipment/utility.dm @@ -400,6 +400,7 @@ name = "drill head" desc = "A replaceable drill head usually used in exosuit drills." icon_state = "drill_head" + var/obj/item/mecha_equipment/drill/mounted_drill /obj/item/material/drill_head/condition_hints(mob/user, distance, is_adjacent) . += ..() @@ -419,6 +420,12 @@ /obj/item/material/drill_head/Initialize(newloc, material_key) . = ..() durability = 2 * material.integrity + if(istype(newloc, /obj/item/mecha_equipment/drill)) + mounted_drill = newloc + +/obj/item/material/drill_head/Destroy() + mounted_drill = null + . = ..() /obj/item/material/drill_head/proc/get_durability_percentage() return (durability * 100) / (2 * material.integrity) @@ -445,6 +452,10 @@ . = ..() drill_head = new /obj/item/material/drill_head(src, DEFAULT_WALL_MATERIAL)//You start with a basic steel head +/obj/item/mecha_equipment/drill/Destroy() + QDEL_NULL(drill_head) + . = ..() + /obj/item/mecha_equipment/drill/attack_self(var/mob/user) . = ..() if(.) @@ -469,8 +480,10 @@ if(drill_head) owner.visible_message(SPAN_NOTICE("\The [owner] detaches the [drill_head] mounted on the [src].")) drill_head.forceMove(owner.loc) + drill_head.mounted_drill = null DH.forceMove(src) drill_head = DH + drill_head.mounted_drill = src owner.visible_message(SPAN_NOTICE("\The [owner] mounts the [drill_head] on the [src].")) return diff --git a/code/modules/hydroponics/trays/_defines.dm b/code/modules/hydroponics/trays/_defines.dm new file mode 100644 index 00000000000..4c93ec0d85f --- /dev/null +++ b/code/modules/hydroponics/trays/_defines.dm @@ -0,0 +1,10 @@ +#define TRAY_LOW_HEALTH BITFLAG(0) +#define TRAY_LOW_WATER BITFLAG(1) +#define TRAY_LOW_NUT BITFLAG(2) +#define TRAY_ALERT BITFLAG(3) +#define TRAY_STASIS BITFLAG(4) +#define TRAY_COVERED BITFLAG(5) +#define TRAY_PLANT_DEAD BITFLAG(6) +#define TRAY_PLANT_LIVE BITFLAG(7) +#define TRAY_PLANT_HARVEST BITFLAG(8) +#define TRAY_HARVEST BITFLAG(9) diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index c922521af1b..3b1a2598e74 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -80,6 +80,11 @@ /// Seed details/line data. var/datum/seed/seed = null // The currently planted seed + /// BITFLAG of TRAY_ defines, set in update_icon. Used to determine if we need to update. + var/icon_status = 0 + /// Currently displayed growth stage, set in update_icon. + var/displayed_stage + /** Reagent information for process(), consider moving this to a controller along @@ -308,6 +313,7 @@ if(GET_SEED_TRAIT(seed, TRAIT_SPOROUS) && !closed_system) seed.create_spores(get_turf(src)) visible_message(SPAN_DANGER("\The [src] releases its spores!")) + update_icon() /// Process reagents being input into the tray. /obj/machinery/portable_atmospherics/hydroponics/proc/process_reagents() @@ -482,6 +488,7 @@ else health = 0 dead = FALSE + update_icon() mutation_level = max(0,min(mutation_level,100)) nutrilevel = max(0,min(nutrilevel,10)) diff --git a/code/modules/hydroponics/trays/tray_update_icons.dm b/code/modules/hydroponics/trays/tray_update_icons.dm index 95ddf8f8e55..d873e83841b 100644 --- a/code/modules/hydroponics/trays/tray_update_icons.dm +++ b/code/modules/hydroponics/trays/tray_update_icons.dm @@ -1,3 +1,87 @@ +/obj/machinery/portable_atmospherics/hydroponics/proc/need_update_icon() + . = 0 + var/overlay_stage + if (seed) + if (mechanical && health <= (GET_SEED_TRAIT(seed, TRAIT_ENDURANCE) / 2)) + . |= TRAY_LOW_HEALTH + + if (dead) + . |= TRAY_PLANT_DEAD + else + . |= TRAY_PLANT_LIVE + if (harvest) + . |= TRAY_PLANT_HARVEST + + if (. & TRAY_PLANT_LIVE) + if(!seed.growth_stages) + seed.update_growth_stages() + overlay_stage = 1 + if(age >= GET_SEED_TRAIT(seed, TRAIT_MATURATION)) + overlay_stage = seed.growth_stages + else + var/maturation = GET_SEED_TRAIT(seed, TRAIT_MATURATION)/seed.growth_stages + if(maturation < 1) + maturation = 1 + overlay_stage = maturation ? max(1,round(age/maturation)) : 1 + + if (mechanical) + if (waterlevel <= 10) + . |= TRAY_LOW_WATER + if (nutrilevel <= 2) + . |= TRAY_LOW_NUT + if (pestlevel >= 5 || toxins >= 40) + . |= TRAY_ALERT + if (harvest) + . |= TRAY_HARVEST + if (stasis) + . |= TRAY_STASIS + + if (closed_system) + . |= TRAY_COVERED + + if (. != icon_status) + do_update_icon(., overlay_stage) + +/obj/machinery/portable_atmospherics/hydroponics/proc/do_update_icon(var/needed_state, var/plant_stage) + . = list() + if (needed_state & TRAY_PLANT_DEAD) + var/ikey = "[GET_SEED_TRAIT(seed, TRAIT_PLANT_ICON)]" + var/image/dead_overlay = SSplants.plant_icon_cache["[ikey]"] + if(!dead_overlay) + dead_overlay = image('icons/obj/hydroponics_growing.dmi', "[ikey]") + dead_overlay.color = DEAD_PLANT_COLOUR + . += dead_overlay + else if (needed_state & TRAY_PLANT_LIVE) + var/image/plant_overlay = seed.get_icon(plant_stage ? plant_stage : displayed_stage) + . += plant_overlay + if (plant_stage) displayed_stage = plant_stage + if (needed_state & TRAY_PLANT_HARVEST) + var/ikey = "[GET_SEED_TRAIT(seed, TRAIT_PRODUCT_ICON)]" + var/image/harvest_overlay = SSplants.plant_icon_cache["product-[ikey]-[GET_SEED_TRAIT(seed, TRAIT_PLANT_COLOUR)]"] + if(!harvest_overlay) + harvest_overlay = image('icons/obj/hydroponics_products.dmi', "[ikey]") + harvest_overlay.color = GET_SEED_TRAIT(seed, TRAIT_PRODUCT_COLOUR) + SSplants.plant_icon_cache["product-[ikey]-[GET_SEED_TRAIT(seed, TRAIT_PRODUCT_COLOUR)]"] = harvest_overlay + . += harvest_overlay + if (mechanical) + if (needed_state & TRAY_LOW_HEALTH) + . += "over_lowhealth3" + if (needed_state & TRAY_LOW_WATER) + . += "over_lowwater3" + if (needed_state & TRAY_LOW_NUT) + . += "over_lownutri3" + if (needed_state & TRAY_ALERT) + . += "over_alert3" + if (needed_state & TRAY_HARVEST) + . += "over_harvest3" + if (needed_state & TRAY_STASIS) + . += "stasis" + if (needed_state & TRAY_COVERED) + . += "hydrocover" + + SetOverlays(.) + icon_status = . + /// Refreshes the icon and sets the luminosity. /obj/machinery/portable_atmospherics/hydroponics/update_icon() // Update name. @@ -12,62 +96,7 @@ if(labelled) name += " ([labelled])" - ClearOverlays() - // Updates the plant overlay. - if(!isnull(seed)) - - if(mechanical && health <= (GET_SEED_TRAIT(seed, TRAIT_ENDURANCE) / 2)) - AddOverlays("over_lowhealth3") - - if(dead) - var/ikey = "[GET_SEED_TRAIT(seed, TRAIT_PLANT_ICON)]-dead" - var/image/dead_overlay = SSplants.plant_icon_cache["[ikey]"] - if(!dead_overlay) - dead_overlay = image('icons/obj/hydroponics_growing.dmi', "[ikey]") - dead_overlay.color = DEAD_PLANT_COLOUR - AddOverlays(dead_overlay) - else - if(!seed.growth_stages) - seed.update_growth_stages() - if(!seed.growth_stages) - to_world(SPAN_DANGER("Seed type [GET_SEED_TRAIT(seed, TRAIT_PLANT_ICON)] cannot find a growth stage value.")) - return - var/overlay_stage = 1 - if(age >= GET_SEED_TRAIT(seed, TRAIT_MATURATION)) - overlay_stage = seed.growth_stages - else - var/maturation = GET_SEED_TRAIT(seed, TRAIT_MATURATION)/seed.growth_stages - if(maturation < 1) - maturation = 1 - overlay_stage = maturation ? max(1,round(age/maturation)) : 1 - var/image/plant_overlay = seed.get_icon(overlay_stage) - AddOverlays(plant_overlay) - - if(harvest && overlay_stage == seed.growth_stages) - var/ikey = "[GET_SEED_TRAIT(seed, TRAIT_PRODUCT_ICON)]" - var/image/harvest_overlay = SSplants.plant_icon_cache["product-[ikey]-[GET_SEED_TRAIT(seed, TRAIT_PLANT_COLOUR)]"] - if(!harvest_overlay) - harvest_overlay = image('icons/obj/hydroponics_products.dmi', "[ikey]") - harvest_overlay.color = GET_SEED_TRAIT(seed, TRAIT_PRODUCT_COLOUR) - SSplants.plant_icon_cache["product-[ikey]-[GET_SEED_TRAIT(seed, TRAIT_PRODUCT_COLOUR)]"] = harvest_overlay - AddOverlays(harvest_overlay) - - //Draw the cover. - if(closed_system) - AddOverlays("hydrocover") - - //Updated the various alert icons. - if(mechanical) - if(waterlevel <= 10) - AddOverlays("over_lowwater3") - if(nutrilevel <= 2) - AddOverlays("over_lownutri3") - if(pestlevel >= 5 || toxins >= 40) // Hydroponics trays no longer face issues with weeds. GET OUTTA HERE. - AddOverlays("over_alert3") - if(harvest) - AddOverlays("over_harvest3") - if(stasis) - AddOverlays("stasis") + need_update_icon() // Apply density and opacity if a large plant is growing in the plot. Exempt mechanical trays from density alterations, since they're always dense. if(seed && GET_SEED_TRAIT(seed, TRAIT_LARGE)) @@ -96,3 +125,13 @@ set_light(0) last_biolum = null return + +#undef TRAY_LOW_HEALTH +#undef TRAY_LOW_WATER +#undef TRAY_LOW_NUT +#undef TRAY_ALERT +#undef TRAY_STASIS +#undef TRAY_COVERED +#undef TRAY_PLANT_DEAD +#undef TRAY_PLANT_LIVE +#undef TRAY_HARVEST diff --git a/code/modules/mob/living/carbon/slime/powers.dm b/code/modules/mob/living/carbon/slime/powers.dm index f95384c6dcd..819498df54c 100644 --- a/code/modules/mob/living/carbon/slime/powers.dm +++ b/code/modules/mob/living/carbon/slime/powers.dm @@ -167,7 +167,7 @@ M.mutation_chance = clamp(mutation_chance + rand(-3, 3), 0, 100) babies += M M.set_content(TRUE) - addtimer(CALLBACK(M, TYPE_PROC_REF(/mob/living/carbon/slime, set_content), FALSE), 1200) // You get two minutes of safety + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob/living/carbon/slime, set_content), FALSE), 1200, TIMER_DELETE_ME) // You get two minutes of safety feedback_add_details("slime_babies_born", "slimebirth_[replacetext(M.colour," ","_")]") var/mob/living/carbon/slime/new_slime = pick(babies) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 33ec6dec0ae..9e46202703d 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -951,7 +951,6 @@ default behaviour is: qdel(M) QDEL_NULL(reagents) - clear_from_target_grid() if(auras) for(var/a in auras) diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 67a9ad7e497..08deb11c8a1 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -97,8 +97,8 @@ winset(src, null, "mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true") MOB_STOP_THINKING(src) - clear_important_client_contents(client) - enable_client_mobs_in_contents(client) + clear_important_client_contents() + enable_client_mobs_in_contents() AddDefaultRenderers() update_client_color() diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 832ba6a640b..6de5c80239e 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -11,8 +11,7 @@ log_access("Logout: [key_name(src)]") SSstatistics.update_status() ClearRenderers() - if(client) - clear_important_client_contents(client) + clear_important_client_contents() my_client = null diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 9f6a47f603e..cd7de8d61d6 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -10,7 +10,6 @@ GLOB.living_mob_list -= src unset_machine() QDEL_NULL(hud_used) - lose_hearing_sensitivity() QDEL_LIST(spell_masters) remove_screen_obj_references() diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 4b8399ffc48..51a319d028a 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -436,7 +436,10 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) var/obj/item/organ/external/affected = owner.get_organ(parent_organ) if(affected) affected.internal_organs -= src - loc = get_turf(owner) + var/turf/T = get_turf(owner) + // to avoid brains and things like that getting put into nullspace and thus entering spatial grids + if(!isnull(T)) + loc = T START_PROCESSING(SSprocessing, src) rejecting = null if (!reagents) diff --git a/code/modules/projectiles/ammunition/ammo_pile.dm b/code/modules/projectiles/ammunition/ammo_pile.dm index 71025dd1b90..0a17eec9a39 100644 --- a/code/modules/projectiles/ammunition/ammo_pile.dm +++ b/code/modules/projectiles/ammunition/ammo_pile.dm @@ -8,6 +8,11 @@ var/ammo_type // the type of ammo this ammo pile accepts var/max_ammo = 5 +/obj/item/ammo_pile/Destroy() + ammo.Cut() + ammo_overlays.Cut() + . = ..() + /obj/item/ammo_pile/feedback_hints(mob/user, distance, is_adjacent) . += ..() if(is_adjacent) @@ -136,6 +141,7 @@ /obj/item/ammo_pile/proc/add_ammo(var/obj/item/ammo_casing/bullet) if(!bullet.BB) return + RegisterSignal(bullet, COMSIG_QDELETING, PROC_REF(clear_ammo)) if(ismob(bullet.loc)) var/mob/gunman = bullet.loc gunman.drop_from_inventory(bullet, src) @@ -150,12 +156,20 @@ ammo_overlays[bullet] = ammo_picture AddOverlays(ammo_overlays[bullet]) +/// Called when one of our contained bullets is qdel'd -- why does this happen? it is a mystery +/obj/item/ammo_pile/proc/clear_ammo(datum/source) + SIGNAL_HANDLER + CutOverlays(ammo_overlays[source]) + ammo -= source + UnregisterSignal(source, COMSIG_QDELETING) + /obj/item/ammo_pile/proc/remove_ammo(var/atom/target) var/obj/bullet = ammo[1] if(target) bullet.forceMove(target) CutOverlays(ammo_overlays[bullet]) ammo -= bullet + UnregisterSignal(bullet, COMSIG_QDELETING) check_ammo() /obj/item/ammo_pile/proc/scatter() diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm index 6273b5f00b4..8cf46ceefc5 100644 --- a/code/modules/projectiles/pins.dm +++ b/code/modules/projectiles/pins.dm @@ -235,6 +235,7 @@ Pins Below. /obj/item/device/firing_pin/Destroy() if(gun) gun.pin = null + gun = null return ..() //this firing pin checks for access diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 980dcb37fc5..1dd55a69846 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -33,6 +33,10 @@ /// Internal reservoir var/datum/gas_mixture/air_contents var/mode = MODE_PRESSURIZING + /// MODE_X define that was set when we last updated overlays. + var/mode_icon + /// Whether or not we are currently indicating as occupied. + var/showing_full = FALSE /// Controlled by flush wire status var/can_flush = TRUE /// TRUE if flush handle is pulled @@ -436,30 +440,43 @@ * Update the icon & overlays to reflect mode & status */ /obj/machinery/disposal/proc/update() - ClearOverlays() - if(stat & BROKEN) + if(!isnull(mode_icon) && (stat & BROKEN)) + ClearOverlays() icon_state = "[icon_state]-broken" + mode_icon = null mode = MODE_OFF flush = 0 return + var/has_contents = !!length(contents) + + if (mode_icon == mode && showing_full == has_contents) + return + + // if we're here, we must rebuild... + ClearOverlays() + // flush handle if(flush) AddOverlays("[icon_state]-handle") // only handle is shown if no power if(stat & NOPOWER || mode == MODE_OFF) + mode_icon = MODE_OFF return // check for items in disposal - occupied light - if(length(contents)) + if(has_contents) AddOverlays("[icon_state]-full") + showing_full = has_contents // charging and ready light if(mode == MODE_PRESSURIZING) AddOverlays("[icon_state]-charge") + mode_icon = MODE_PRESSURIZING else if(mode == MODE_READY) AddOverlays("[icon_state]-ready") + mode_icon = MODE_READY /** * Timed process. Charge the gas reservoir and perform flush if ready. @@ -1731,8 +1748,3 @@ dirs = GLOB.alldirs.Copy() src.streak(dirs) - -#undef MODE_OFF -#undef MODE_PRESSURIZING -#undef MODE_READY -#undef MODE_FLUSHING diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index 8d485736d5f..159e28be83f 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -260,16 +260,17 @@ /obj/item/device/destTagger/proc/openwindow(mob/user) var/dat = "

TagMaster 2.3

" + var/ui_ref = REF(src) dat += "

Commands

Activate Biogenerator
Detach Container
Activate Biogenerator
Detach Container
NameCostProduction Amount
" for(var/i = 1, i <= SSdisposals.tagger_locations.len, i++) - dat += "" + dat += "" if (i % 4==0) dat += "" dat += "
[SSdisposals.tagger_locations[i]][SSdisposals.tagger_locations[i]]

Current Selection: [currTag ? currTag : "None"]
" - dat += "
Enter custom location." + dat += "
Enter custom location." user << browse(HTML_SKELETON(dat), "window=destTagScreen;size=450x375") onclose(user, "destTagScreen") @@ -353,8 +354,8 @@ flushing = FALSE // now reset disposal state flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging + if(mode == MODE_READY) // if was ready, + mode = MODE_PRESSURIZING // switch to charging update() return @@ -399,3 +400,8 @@ if(trunk) trunk.linked = null return ..() + +#undef MODE_OFF +#undef MODE_PRESSURIZING +#undef MODE_READY +#undef MODE_FLUSHING diff --git a/code/modules/research/designs/circuit/hardsuit_circuits.dm b/code/modules/research/designs/circuit/hardsuit_circuits.dm index bf0784c958a..bac874b9e71 100644 --- a/code/modules/research/designs/circuit/hardsuit_circuits.dm +++ b/code/modules/research/designs/circuit/hardsuit_circuits.dm @@ -10,12 +10,12 @@ build_path = /obj/item/circuitboard/rig_assembly/civilian/industrial /datum/design/circuit/hardsuit/eva - name = "EVA suit central circuit Board" + name = "EVA Suit Central Circuit Board" req_tech = list(TECH_DATA = 3) build_path = /obj/item/circuitboard/rig_assembly/civilian/eva /datum/design/circuit/hardsuit/eva/pilot - name = "pilot suit central circuit Board" + name = "Pilot Suit Central Circuit Board" req_tech = list(TECH_DATA = 3) build_path = /obj/item/circuitboard/rig_assembly/civilian/eva/pilot diff --git a/code/modules/research/designs/circuit/machine_circuits.dm b/code/modules/research/designs/circuit/machine_circuits.dm index 0345aa4a216..d6b3c9e5b06 100644 --- a/code/modules/research/designs/circuit/machine_circuits.dm +++ b/code/modules/research/designs/circuit/machine_circuits.dm @@ -147,22 +147,22 @@ build_path = /obj/item/circuitboard/candymachine /datum/design/circuit/machine/portgen - name = "portable generator" + name = "Portable Generator" req_tech = list(TECH_DATA = 3, TECH_PHORON = 3, TECH_POWER = 3, TECH_ENGINEERING = 3) build_path = /obj/item/circuitboard/portgen /datum/design/circuit/machine/advancedportgen - name = "advanced portable generator" + name = "Advanced Portable Generator" req_tech = list(TECH_DATA = 3, TECH_POWER = 4, TECH_ENGINEERING = 4) build_path = /obj/item/circuitboard/portgen/advanced /datum/design/circuit/machine/superportgen - name = "super portable generator" + name = "Super Portable Generator" req_tech = list(TECH_DATA = 3, TECH_POWER = 5, TECH_ENGINEERING = 5) build_path = /obj/item/circuitboard/portgen/super /datum/design/circuit/machine/fusionportgen - name = "miniature fusion reactor" + name = "Miniature Fusion Reactor" req_tech = list(TECH_DATA = 3, TECH_POWER = 6, TECH_ENGINEERING = 6) build_path = /obj/item/circuitboard/portgen/fusion @@ -238,12 +238,12 @@ build_path = /obj/item/circuitboard/slime_extractor /datum/design/circuit/machine/iv_drip - name = "IV drip" + name = "IV Drip" req_tech = list(TECH_DATA = 1, TECH_BIO = 2) build_path = /obj/item/circuitboard/iv_drip /datum/design/circuit/oxyregenerator - name = "oxygen regenerator" + name = "Oxygen Regenerator" req_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2) build_path = /obj/item/circuitboard/oxyregenerator diff --git a/code/modules/research/designs/mechfab/hardsuit/rigs.dm b/code/modules/research/designs/mechfab/hardsuit/rigs.dm index c6bb4987280..5566affd5f5 100644 --- a/code/modules/research/designs/mechfab/hardsuit/rigs.dm +++ b/code/modules/research/designs/mechfab/hardsuit/rigs.dm @@ -27,7 +27,7 @@ materials = list(MATERIAL_ALUMINIUM = 12500, MATERIAL_GLASS = 12500, DEFAULT_WALL_MATERIAL = 7000, MATERIAL_LEAD = 5500) /datum/design/rig/eva/pilot - name = "pilot Suit Control Module Assembly" + name = "Pilot Suit Control Module Assembly" desc = "An assembly for a light hardsuit that is designed for pilots. It features a plasteel lining that offers excellent protection from shrapnel." build_path = /obj/item/rig_assembly/eva/pilot materials = list(DEFAULT_WALL_MATERIAL = 25000, MATERIAL_GLASS = 12500, MATERIAL_PLASTEEL = 5500) diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index 2659efcc19d..a7204b441f3 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -55,6 +55,8 @@ won't update every console in existence) but it's more of a hassle to do. Also, var/protolathe_category = "All" var/imprinter_category = "All" + var/ref_for_ui + req_access = list(ACCESS_TOX) //Data and setting manipulation requires scientist access. /obj/machinery/computer/rdconsole/proc/CallMaterialName(var/ID) @@ -134,6 +136,7 @@ won't update every console in existence) but it's more of a hassle to do. Also, S.setup() break SyncRDevices() + ref_for_ui = "[REF(src)]" return INITIALIZE_HINT_LATELOAD /obj/machinery/computer/rdconsole/LateInitialize() @@ -505,7 +508,7 @@ won't update every console in existence) but it's more of a hassle to do. Also, if(0.2) dat += "SYSTEM LOCKED

" - dat += "Unlock" + dat += "Unlock" if(0.3) dat += "Constructing Prototype. Please Wait..." @@ -521,70 +524,70 @@ won't update every console in existence) but it's more of a hassle to do. Also, dat += "Loaded disk: " dat += (t_disk || d_disk) ? (t_disk ? "technology storage disk" : "design storage disk") : "None" dat += "
" dat += "" if(1.1) //Research viewer - dat += "Main Menu || " - dat += "Print This Page
" + dat += "Main Menu || " + dat += "Print This Page
" dat += "Current Research Levels:
" dat += GetResearchLevelsInfo() dat += "" if(1.2) //Technology Disk Menu - dat += "Main Menu
" + dat += "Main Menu
" dat += "Disk Contents: (Technology Data Disk)

" dat += "" if(1.3) //Technology Disk submenu - dat += "
Main Menu || " - dat += "Return to Disk Operations
" + dat += "
Main Menu || " + dat += "Return to Disk Operations
" dat += "" if(1.4) //Design Disk menu. - dat += "Main Menu
" + dat += "Main Menu
" dat += "" if(1.5) //Technology disk submenu - dat += "Main Menu || " - dat += "Return to Disk Operations
" + dat += "Main Menu || " + dat += "Return to Disk Operations
" dat += "" if(1.6) //R&D console settings - dat += "Main Menu
" + dat += "Main Menu
" dat += "R&D Console Setting:
" dat += "" if(1.7) //R&D device linkage - dat += "Main Menu || " - dat += "Settings Menu
" + dat += "Main Menu || " + dat += "Settings Menu
" dat += "R&D Console Device Linkage Menu:
" dat += "