diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index d2f61e13dca5..0c60ba0a5ddb 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -1,3 +1,16 @@ // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary. // 1 will enable set background. 0 will disable set background. #define BACKGROUND_ENABLED 0 + +// If REFTRACK_IN_CI is defined, the reftracker will run in CI. +#define REFTRACK_IN_CI +#if defined(REFTRACK_IN_CI) && defined(UNIT_TEST) && !defined(SPACEMAN_DMM) +#define REFTRACKING_ENABLED +#define GC_FAILURE_HARD_LOOKUP +#define FIND_REF_NO_CHECK_TICK +#endif + +// parity with previous behavior where TESTING enabled reftracking +#ifdef TESTING +#define REFTRACKING_ENABLED +#endif \ No newline at end of file diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm index 48a9f0e09738..fa9cf0a8debe 100644 --- a/code/__defines/qdel.dm +++ b/code/__defines/qdel.dm @@ -5,8 +5,8 @@ #define QDEL_HINT_IWILLGC 2 //functionally the same as the above. qdel should assume the object will gc on its own, and not check it. #define QDEL_HINT_HARDDEL 3 //qdel should assume this object won't gc, and queue a hard delete using a hard reference. #define QDEL_HINT_HARDDEL_NOW 4 //qdel should assume this object won't gc, and hard del it post haste. -#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if TESTING is not enabled in _compiler_options.dm. - //if TESTING is enabled, qdel will call this object's find_references() verb. +#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if REFTRACKING_ENABLED is not enabled in _compiler_options.dm. + //if REFTRACKING_ENABLED is enabled, qdel will call this object's find_references() verb. #define QDEL_HINT_IFFAIL_FINDREFERENCE 6 //Above but only if gc fails. //defines for the gc_destroyed var @@ -15,6 +15,12 @@ #define GC_QUEUE_HARDDELETE 3 #define GC_QUEUE_COUNT 3 //increase this when adding more steps. +// Defines for the ssgarbage queue items +#define GC_QUEUE_ITEM_QUEUE_TIME 1 //! Time this item entered the queue +#define GC_QUEUE_ITEM_REF 2 //! Ref to the item +#define GC_QUEUE_ITEM_GCD_DESTROYED 3 //! Item's gc_destroyed var value. Used to detect ref reuse. +#define GC_QUEUE_ITEM_INDEX_COUNT 3 //! Number of item indexes, used for allocating the nested lists. Don't forget to increase this if you add a new queue item index + #define GC_QUEUED_FOR_HARD_DEL -1 #define GC_CURRENTLY_BEING_QDELETED -2 diff --git a/code/_onclick/hud/ai_hud.dm b/code/_onclick/hud/ai_hud.dm index b8edcf540c4c..fa4e2200bb63 100644 --- a/code/_onclick/hud/ai_hud.dm +++ b/code/_onclick/hud/ai_hud.dm @@ -11,70 +11,70 @@ screen_loc = ui_ai_core name = "AI Core" icon_state = "ai_core" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, core) + proc_path = /mob/living/silicon/ai/proc/core /decl/ai_hud/ai_announcement screen_loc = ui_ai_announcement name = "AI Announcement" icon_state = "announcement" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_announcement) + proc_path = /mob/living/silicon/ai/proc/ai_announcement /decl/ai_hud/ai_cam_track screen_loc = ui_ai_cam_track name = "Track With Camera" icon_state = "track" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_camera_track) + proc_path = /mob/living/silicon/ai/proc/ai_camera_track input_procs = list(/mob/living/silicon/ai/proc/trackable_mobs = (AI_BUTTON_PROC_BELONGS_TO_CALLER|AI_BUTTON_INPUT_REQUIRES_SELECTION)) /decl/ai_hud/ai_cam_light screen_loc = ui_ai_cam_light name = "Toggle Camera Lights" icon_state = "camera_light" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, toggle_camera_light) + proc_path = /mob/living/silicon/ai/proc/toggle_camera_light /decl/ai_hud/ai_cam_change_channel screen_loc = ui_ai_cam_change_channel name = "Jump to Camera Channel" icon_state = "camera" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_channel_change) + proc_path = /mob/living/silicon/ai/proc/ai_channel_change input_procs = list(/mob/living/silicon/ai/proc/get_camera_channel_list = (AI_BUTTON_PROC_BELONGS_TO_CALLER|AI_BUTTON_INPUT_REQUIRES_SELECTION)) /decl/ai_hud/ai_sensor screen_loc = ui_ai_sensor name = "Set Sensor Mode" icon_state = "ai_sensor" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, sensor_mode) + proc_path = /mob/living/silicon/ai/proc/sensor_mode /decl/ai_hud/ai_manifest screen_loc = ui_ai_crew_manifest name = "Show Crew Manifest" icon_state = "manifest" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, run_program) + proc_path = /mob/living/silicon/ai/proc/run_program input_args = list("crewmanifest") /decl/ai_hud/ai_take_image screen_loc = ui_ai_take_image name = "Toggle Camera Mode" icon_state = "take_picture" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_take_image) + proc_path = /mob/living/silicon/ai/proc/ai_take_image /decl/ai_hud/ai_view_images screen_loc = ui_ai_view_images name = "View Images" icon_state = "view_images" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_view_images) + proc_path = /mob/living/silicon/ai/proc/ai_view_images /decl/ai_hud/ai_laws screen_loc = ui_ai_state_laws name = "State Laws" icon_state = "state_laws" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_checklaws) + proc_path = /mob/living/silicon/ai/proc/ai_checklaws /decl/ai_hud/ai_call_shuttle screen_loc = ui_ai_call_shuttle name = "Call Shuttle" icon_state = "call_shuttle" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_call_shuttle) + proc_path = /mob/living/silicon/ai/proc/ai_call_shuttle /decl/ai_hud/ai_up screen_loc = ui_ai_up @@ -92,53 +92,53 @@ screen_loc = ui_ai_color name = "Change Floor Color" icon_state = "ai_floor" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, change_floor) + proc_path = /mob/living/silicon/ai/proc/change_floor /decl/ai_hud/ai_hologram screen_loc = ui_ai_holo_change name = "Change Hologram" icon_state = "ai_holo_change" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_hologram_change) + proc_path = /mob/living/silicon/ai/proc/ai_hologram_change /decl/ai_hud/ai_crew_monitor screen_loc = ui_ai_crew_mon name = "Crew Monitor" icon_state = "crew_monitor" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, run_program) + proc_path = /mob/living/silicon/ai/proc/run_program input_args = list("sensormonitor") /decl/ai_hud/ai_power_override screen_loc = ui_ai_power_override name = "Toggle Power Override" icon_state = "ai_p_override" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_power_override) + proc_path = /mob/living/silicon/ai/proc/ai_power_override /decl/ai_hud/ai_shutdown screen_loc = ui_ai_shutdown name = "Shutdown" icon_state = "ai_shutdown" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_shutdown) + proc_path = /mob/living/silicon/ai/proc/ai_shutdown /decl/ai_hud/ai_move_hologram screen_loc = ui_ai_holo_mov name = "Toggle Hologram Movement" icon_state = "ai_holo_mov" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, toggle_hologram_movement) + proc_path = /mob/living/silicon/ai/proc/toggle_hologram_movement /decl/ai_hud/ai_core_icon screen_loc = ui_ai_core_icon name = "Pick Icon" icon_state = "ai_core_pick" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, pick_icon) + proc_path = /mob/living/silicon/ai/proc/pick_icon /decl/ai_hud/ai_status screen_loc = ui_ai_status name = "Pick Status" icon_state = "ai_status" - proc_path = TYPE_PROC_REF(/mob/living/silicon/ai, ai_statuschange) + proc_path = /mob/living/silicon/ai/proc/ai_statuschange /decl/ai_hud/ai_inbuilt_comp screen_loc = ui_ai_crew_rec name = "Inbuilt Computer" icon_state = "ai_crew_rec" - proc_path = TYPE_PROC_REF(/mob/living/silicon, access_computer) + proc_path = /mob/living/silicon/proc/access_computer diff --git a/code/_onclick/hud/screen/screen_ai_button.dm b/code/_onclick/hud/screen/screen_ai_button.dm index a7f2861002f3..e2060d6b8a1e 100644 --- a/code/_onclick/hud/screen/screen_ai_button.dm +++ b/code/_onclick/hud/screen/screen_ai_button.dm @@ -1,16 +1,18 @@ /obj/screen/ai_button icon = 'icons/mob/screen/ai.dmi' requires_ui_style = FALSE - var/mob/living/silicon/ai/ai_verb + var/ai_verb var/list/input_procs var/list/input_args var/list/template_icon = list(null, "template") var/image/template_undelay /obj/screen/ai_button/handle_click(mob/user, params) - if(!isAI(user)) - return TRUE + var/mob/living/silicon/ai/A = user + if(!istype(A)) + return TRUE + if(!(ai_verb in A.verbs)) return TRUE @@ -30,7 +32,6 @@ if(!(ai_verb in A.verbs) || A.incapacitated()) return - input_arguments += input_arg if(length(input_args)) diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index 47c1be9e2452..18f925703957 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -37,7 +37,7 @@ SUBSYSTEM_DEF(garbage) //Queue var/list/queues - #ifdef TESTING + #ifdef REFTRACKING_ENABLED var/list/reference_find_on_fail = list() #endif @@ -130,30 +130,33 @@ SUBSYSTEM_DEF(garbage) lastlevel = level +// 1 from the hard reference in the queue, and 1 from `D` in the code below +#define REFS_WE_EXPECT 2 + //We do this rather then for(var/refID in queue) because that sort of for loop copies the whole list. //Normally this isn't expensive, but the gc queue can grow to 40k items, and that gets costly/causes overrun. - for (var/refidx in 1 to length(queue)) - var/refID = queue[refidx] - if (isnull(refID)) + for (var/i in 1 to length(queue)) + var/list/L = queue[i] + if (length(L) < GC_QUEUE_ITEM_INDEX_COUNT) count++ if (MC_TICK_CHECK) return continue - var/GCd_at_time = queue[refID] - if(GCd_at_time > cut_off_time) + var/queued_at_time = L[GC_QUEUE_ITEM_QUEUE_TIME] + if(queued_at_time > cut_off_time) break // Everything else is newer, skip them count++ - var/datum/D - D = locate(refID) + var/datum/D = L[GC_QUEUE_ITEM_REF] - if (isnull(D) || D.gc_destroyed != GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake + // If that's all we've got, send er off + if (refcount(D) == REFS_WE_EXPECT) ++gcedlasttick ++totalgcs pass_counts[level]++ - #ifdef TESTING - reference_find_on_fail -= refID //It's deleted we don't care anymore. + #ifdef REFTRACKING_ENABLED + reference_find_on_fail -= ref(D) //It's deleted we don't care anymore. #endif if (MC_TICK_CHECK) return @@ -161,14 +164,19 @@ SUBSYSTEM_DEF(garbage) // Something's still referring to the qdel'd object. fail_counts[level]++ + switch (level) if (GC_QUEUE_CHECK) - #ifdef TESTING + #ifdef REFTRACKING_ENABLED + // Decides how many refs to look for (potentially) + // Based off the remaining and the ones we can account for + var/remaining_refs = refcount(D) - REFS_WE_EXPECT + var/refID = ref(D) if(reference_find_on_fail[refID]) - D.find_references() + D.find_references(remaining_refs) #ifdef GC_FAILURE_HARD_LOOKUP else - D.find_references() + D.find_references(remaining_refs) #endif reference_find_on_fail -= refID #endif @@ -201,12 +209,14 @@ SUBSYSTEM_DEF(garbage) if (level > GC_QUEUE_COUNT) HardDelete(D) return - var/gctime = world.time - var/refid = "\ref[D]" + var/queue_time = world.time - D.gc_destroyed = gctime + if(D.gc_destroyed <= 0) // hasn't been queued yet, or is queued for harddel/actively being qdeleted + D.gc_destroyed = queue_time var/list/queue = queues[level] - queue[refid] = gctime + // not += for byond reasons + // we include D.gc_destroyed to skip things under the cutoff + queue[++queue.len] = list(queue_time, D, D.gc_destroyed) //this is mainly to separate things profile wise. /datum/controller/subsystem/garbage/proc/HardDelete(datum/D) @@ -240,11 +250,6 @@ SUBSYSTEM_DEF(garbage) message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).") postpone(time) -/datum/controller/subsystem/garbage/proc/HardQueue(datum/D) - if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) - queues[GC_QUEUE_FILTER] += D - D.gc_destroyed = GC_QUEUED_FOR_HARD_DEL - /datum/controller/subsystem/garbage/Recover() if (istype(SSgarbage.queues)) for (var/i in 1 to SSgarbage.queues.len) @@ -270,7 +275,7 @@ SUBSYSTEM_DEF(garbage) /datum/qdel_item/New(mytype) name = "[mytype]" -#ifdef TESTING +#ifdef REFTRACKING_ENABLED /proc/qdel_and_find_ref_if_fail(datum/D, force = FALSE) SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE qdel(D, force) @@ -328,7 +333,7 @@ SUBSYSTEM_DEF(garbage) return // Returning LETMELIVE after being told to force destroy // indicates the objects Destroy() does not respect force - #ifdef TESTING + #ifdef REFTRACKING_ENABLED if(!I.no_respect_force) PRINT_STACK_TRACE("WARNING: [D.type] has been force deleted, but is \ returning an immortal QDEL_HINT, indicating it does \ @@ -341,21 +346,22 @@ SUBSYSTEM_DEF(garbage) SSgarbage.Queue(D) if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate() GC_CHECK_AM_NULLSPACE(D, "QDEL_HINT_HARDDEL") - SSgarbage.HardQueue(D) + SSgarbage.Queue(D, GC_QUEUE_HARDDELETE) if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. SSgarbage.HardDelete(D) - if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion. + if (QDEL_HINT_FINDREFERENCE)//qdel will, if REFTRACKING_ENABLED is enabled, display all references to this object, then queue the object for deletion. SSgarbage.Queue(D) - #ifdef TESTING - D.find_references() + #ifdef REFTRACKING_ENABLED + var/remaining_refs = refcount(D) - REFS_WE_EXPECT + D.find_references(remaining_refs) #endif if (QDEL_HINT_IFFAIL_FINDREFERENCE) SSgarbage.Queue(D) - #ifdef TESTING + #ifdef REFTRACKING_ENABLED SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE #endif else - #ifdef TESTING + #ifdef REFTRACKING_ENABLED if(!I.no_hint) PRINT_STACK_TRACE("WARNING: [D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") #endif @@ -364,7 +370,7 @@ SUBSYSTEM_DEF(garbage) else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") -#ifdef TESTING +#ifdef REFTRACKING_ENABLED /datum/verb/find_refs() set category = "Debug" @@ -378,8 +384,9 @@ SUBSYSTEM_DEF(garbage) return find_references() -/datum/proc/find_references() +/datum/proc/find_references(references_to_clear = INFINITY) running_find_references = type + src.references_to_clear = references_to_clear if(usr && usr.client) if(usr.client.running_find_references) testing("CANCELLED search for references to a [usr.client.running_find_references].") @@ -406,13 +413,19 @@ SUBSYSTEM_DEF(garbage) normal_globals[global_var] = global.vars[global_var] DoSearchVar(normal_globals, "(global) -> ") //globals testing("Finished searching globals") + if(src.references_to_clear == 0) // Found all expected references! + return for(var/atom/atom_thing) //atoms DoSearchVar(atom_thing, "World -> [atom_thing]") + if(src.references_to_clear == 0) // Found all expected references! + return testing("Finished searching atoms") for (var/datum/datum_thing) //datums DoSearchVar(datum_thing, "World -> [datum_thing]") + if(src.references_to_clear == 0) // Found all expected references! + return testing("Finished searching datums") #ifndef FIND_REF_SKIP_CLIENTS @@ -420,6 +433,8 @@ SUBSYSTEM_DEF(garbage) // IT WILL CRASH!!! for (var/client/client_thing) //clients DoSearchVar(client_thing, "World -> [client_thing]") + if(src.references_to_clear == 0) // Found all expected references! + return testing("Finished searching clients") #endif @@ -455,37 +470,47 @@ SUBSYSTEM_DEF(garbage) #define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref)-6) ) ) #define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) -/datum/proc/DoSearchVar(X, Xname, recursive_limit = 128) +/datum/proc/DoSearchVar(X, container_name, recursive_limit = 128) if(usr && usr.client && !usr.client.running_find_references) return if (!recursive_limit) return + if(references_to_clear == 0) + return #ifndef FIND_REF_NO_CHECK_TICK CHECK_TICK #endif if(istype(X, /datum)) - var/datum/D = X - if(D.last_find_references == last_find_references) + var/datum/datum_container = X + if(datum_container.last_find_references == last_find_references) return - D.last_find_references = last_find_references - var/list/L = D.vars + datum_container.last_find_references = last_find_references + var/list/vars_list = datum_container.vars - for(var/varname in L) + var/is_atom = FALSE + var/is_area = FALSE + if(isatom(datum_container)) + is_atom = TRUE + if(isarea(datum_container)) + is_area = TRUE + for(var/varname in vars_list) #ifndef FIND_REF_NO_CHECK_TICK CHECK_TICK #endif - if (varname == "vars") + //Fun fact, vis_locs don't count for references + if(varname == "vars" || (is_atom && (varname == "vis_locs" || varname == "overlays" || varname == "underlays" || varname == "filters" || varname == "verbs" || (is_area && varname == "contents")))) continue - var/variable = L[varname] + var/variable = vars_list[varname] if(variable == src) - testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]") + testing("Found [src.type] \ref[src] in [datum_container.type]'s [varname] var. [container_name]") + references_to_clear -= 1 else if(islist(variable)) - DoSearchVar(variable, "[Xname] -> [varname] (list)", recursive_limit-1) + DoSearchVar(variable, "[container_name] -> [varname] (list)", recursive_limit-1) else if(islist(X)) var/normal = IS_NORMAL_LIST(X) @@ -494,16 +519,33 @@ SUBSYSTEM_DEF(garbage) CHECK_TICK #endif if (I == src) - testing("Found [src.type] \ref[src] in list [Xname].") + testing("Found [src.type] \ref[src] in list [container_name].") + + // This is dumb as hell I'm sorry + // I don't want the garbage subsystem to count as a ref for the purposes of this number + // If we find all other refs before it I want to early exit, and if we don't I want to keep searching past it + var/ignore_ref = FALSE + var/list/queues = SSgarbage.queues + for(var/list/queue in queues) + if(X in queue) + ignore_ref = TRUE + break + if(ignore_ref) + testing("[container_name] does not count as a ref for our count") + else + references_to_clear -= 1 + if(references_to_clear == 0) + testing("All references to [type] \ref[src] found, exiting.") + return else if (I && !isnum(I) && normal) if(X[I] == src) - testing("Found [src.type] \ref[src] in list [Xname]\[[I]\]") + testing("Found [src.type] \ref[src] in list [container_name]\[[I]\]") else if(islist(X[I])) - DoSearchVar(X[I], "[Xname]\[[I]\]", recursive_limit-1) + DoSearchVar(X[I], "[container_name]\[[I]\]", recursive_limit-1) else if (islist(I)) var/list/Xlist = X - DoSearchVar(I, "[Xname]\[[Xlist.Find(I)]\] -> list", recursive_limit-1) + DoSearchVar(I, "[container_name]\[[Xlist.Find(I)]\] -> list", recursive_limit-1) #endif diff --git a/code/controllers/subsystems/overlays.dm b/code/controllers/subsystems/overlays.dm index 42e919ae980f..886508113926 100644 --- a/code/controllers/subsystems/overlays.dm +++ b/code/controllers/subsystems/overlays.dm @@ -209,7 +209,7 @@ SUBSYSTEM_DEF(overlays) if(cut_old) our_overlays = cached_other.Copy() else - our_overlays |= cached_other + LAZYDISTINCTADD(our_overlays, cached_other) if(NOT_QUEUED_ALREADY) QUEUE_FOR_COMPILE else if(cut_old) diff --git a/code/controllers/subsystems/processing/nano.dm b/code/controllers/subsystems/processing/nano.dm index dfaae9e7999e..dd358012c92a 100644 --- a/code/controllers/subsystems/processing/nano.dm +++ b/code/controllers/subsystems/processing/nano.dm @@ -37,11 +37,10 @@ PROCESSING_SUBSYSTEM_DEF(nano) * @return /nanoui Returns the found ui, or null if none exists */ /datum/controller/subsystem/processing/nano/proc/get_open_ui(mob/user, src_object, ui_key) - var/src_object_key = "\ref[src_object]" - if (!open_uis[src_object_key] || !open_uis[src_object_key][ui_key]) + if (!open_uis[src_object] || !open_uis[src_object][ui_key]) return - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + for (var/datum/nanoui/ui in open_uis[src_object][ui_key]) if (ui.user == user) return ui @@ -54,12 +53,11 @@ PROCESSING_SUBSYSTEM_DEF(nano) */ /datum/controller/subsystem/processing/nano/proc/update_uis(src_object) . = 0 - var/src_object_key = "\ref[src_object]" - if (!open_uis[src_object_key]) + if (!open_uis[src_object]) return - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + for (var/ui_key in open_uis[src_object]) + for (var/datum/nanoui/ui in open_uis[src_object][ui_key]) if(ui.src_object && ui.user && ui.src_object.nano_host()) ui.try_update(1) .++ @@ -78,12 +76,11 @@ PROCESSING_SUBSYSTEM_DEF(nano) if (!length(open_uis)) return - var/src_object_key = "\ref[src_object]" - if (!open_uis[src_object_key]) + if (!open_uis[src_object]) return - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + for (var/ui_key in open_uis[src_object]) + for (var/datum/nanoui/ui in open_uis[src_object][ui_key]) ui.close() // If it's missing src_object or user, we want to close it even more. .++ @@ -134,9 +131,9 @@ PROCESSING_SUBSYSTEM_DEF(nano) * @return nothing */ /datum/controller/subsystem/processing/nano/proc/ui_opened(datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - LAZYINITLIST(open_uis[src_object_key]) - LAZYDISTINCTADD(open_uis[src_object_key][ui.ui_key], ui) + var/src_object = ui.src_object + LAZYINITLIST(open_uis[src_object]) + LAZYDISTINCTADD(open_uis[src_object][ui.ui_key], ui) LAZYDISTINCTADD(ui.user.open_uis, ui) START_PROCESSING(SSnano, ui) @@ -149,18 +146,18 @@ PROCESSING_SUBSYSTEM_DEF(nano) * @return int 0 if no ui was removed, 1 if removed successfully */ /datum/controller/subsystem/processing/nano/proc/ui_closed(var/datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - if (!open_uis[src_object_key] || !open_uis[src_object_key][ui.ui_key]) + var/src_object = ui.src_object + if (!open_uis[src_object] || !open_uis[src_object][ui.ui_key]) return 0 // wasn't open STOP_PROCESSING(SSnano, ui) if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface) LAZYREMOVE(ui.user.open_uis, ui) - open_uis[src_object_key][ui.ui_key] -= ui - if(!length(open_uis[src_object_key][ui.ui_key])) - open_uis[src_object_key] -= ui.ui_key - if(!length(open_uis[src_object_key])) - open_uis -= src_object_key + open_uis[src_object][ui.ui_key] -= ui + if(!length(open_uis[src_object][ui.ui_key])) + open_uis[src_object] -= ui.ui_key + if(!length(open_uis[src_object])) + open_uis -= src_object return 1 /** diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm index 6bccadcb70a8..89122cad2f97 100644 --- a/code/controllers/subsystems/ticker.dm +++ b/code/controllers/subsystems/ticker.dm @@ -121,7 +121,7 @@ SUBSYSTEM_DEF(ticker) Master.SetRunLevel(RUNLEVEL_POSTGAME) end_game_state = END_GAME_READY_TO_END INVOKE_ASYNC(src, PROC_REF(declare_completion)) - if(get_config_value(/decl/config/toggle/allow_map_switching) && get_config_value(/decl/config/toggle/auto_map_vote) && global.all_maps.len > 1) + if(get_config_value(/decl/config/toggle/allow_map_switching) && get_config_value(/decl/config/toggle/auto_map_vote) && length(global.votable_maps) > 1) SSvote.initiate_vote(/datum/vote/map/end_game, automatic = 1) else if(mode_finished && (end_game_state <= END_GAME_NOT_OVER)) diff --git a/code/datums/ai/aggressive.dm b/code/datums/ai/aggressive.dm index 505017dbc0c9..6c48ef305e55 100644 --- a/code/datums/ai/aggressive.dm +++ b/code/datums/ai/aggressive.dm @@ -178,7 +178,7 @@ if(!obstacle.can_open(1)) return body.face_atom(obstacle) - body.pry_door(obstacle, (obstacle.pry_mod * body.get_door_pry_time())) + body.pry_door((obstacle.pry_mod * body.get_door_pry_time()), obstacle) return /datum/mob_controller/aggressive/retaliate(atom/source) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index a0aeed07808d..aa855c0b1e3b 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -10,9 +10,14 @@ /// Used to avoid unnecessary refstring creation in Destroy(). var/tmp/has_state_machine = FALSE -#ifdef TESTING +#ifdef REFTRACKING_ENABLED var/tmp/running_find_references + /// When was this datum last touched by a reftracker? + /// If this value doesn't match with the start of the search + /// We know this datum has never been seen before, and we should check it var/tmp/last_find_references = 0 + /// How many references we're trying to find when searching + var/tmp/references_to_clear = 0 #endif // Default implementation of clean-up code. @@ -54,11 +59,11 @@ cleanup_events(src) if(has_state_machine) - var/list/machines = global.state_machines["\ref[src]"] + var/list/machines = global.state_machines[src] if(length(machines)) for(var/base_type in machines) qdel(machines[base_type]) - global.state_machines -= "\ref[src]" + global.state_machines -= src return QDEL_HINT_QUEUE diff --git a/code/datums/extensions/state_machine.dm b/code/datums/extensions/state_machine.dm index 63e58f0dd671..edba84c7965d 100644 --- a/code/datums/extensions/state_machine.dm +++ b/code/datums/extensions/state_machine.dm @@ -3,16 +3,15 @@ var/global/list/state_machines = list() /proc/get_state_machine(var/datum/holder, var/base_type) if(istype(holder) && base_type && holder.has_state_machine) - var/list/machines = global.state_machines["\ref[holder]"] + var/list/machines = global.state_machines[holder] return islist(machines) && machines[base_type] /proc/add_state_machine(var/datum/holder, var/datum/state_machine/fsm_type) if(istype(holder) && fsm_type) - var/holder_ref = "\ref[holder]" - var/list/machines = global.state_machines[holder_ref] + var/list/machines = global.state_machines[holder] if(!islist(machines)) machines = list() - global.state_machines[holder_ref] = machines + global.state_machines[holder] = machines var/base_type = fsm_type::base_type if(!machines[base_type]) var/datum/state_machine/machine = new fsm_type(holder) @@ -22,12 +21,11 @@ var/global/list/state_machines = list() /proc/remove_state_machine(var/datum/holder, var/base_type) if(istype(holder) && base_type && holder.has_state_machine) - var/holder_ref = "\ref[holder]" - var/list/machines = global.state_machines[holder_ref] + var/list/machines = global.state_machines[holder] if(length(machines)) machines -= base_type if(!length(machines)) - global.state_machines -= holder_ref + global.state_machines -= holder holder.has_state_machine = FALSE return TRUE return FALSE diff --git a/code/datums/extensions/storage/_storage.dm b/code/datums/extensions/storage/_storage.dm index c79e91a7357b..4ff70967e1c6 100644 --- a/code/datums/extensions/storage/_storage.dm +++ b/code/datums/extensions/storage/_storage.dm @@ -252,6 +252,8 @@ var/global/list/_test_storage_items = list() storage_ui?.on_insertion() /datum/storage/proc/update_ui_after_item_removal(obj/item/removed) + if(QDELETED(holder)) + return prepare_ui() storage_ui?.on_post_remove() diff --git a/code/datums/extensions/storage/_storage_ui.dm b/code/datums/extensions/storage/_storage_ui.dm index 6f4db924f328..a3e1f23bfcb5 100644 --- a/code/datums/extensions/storage/_storage_ui.dm +++ b/code/datums/extensions/storage/_storage_ui.dm @@ -216,7 +216,8 @@ closer.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST + cols + 1]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED]" /datum/storage_ui/default/proc/space_orient_objs() - + if(QDELETED(_storage?.holder)) // don't bother if we've been deleted + return var/baseline_max_storage_space = DEFAULT_BOX_STORAGE //storage size corresponding to 224 pixels var/storage_cap_width = 2 //length of sprite for start and end of the box representing total storage space var/stored_cap_width = 4 //length of sprite for start and end of the box representing the stored item diff --git a/code/datums/graph/graph.dm b/code/datums/graph/graph.dm index 249f513e419e..1610a6b4397a 100644 --- a/code/datums/graph/graph.dm +++ b/code/datums/graph/graph.dm @@ -35,6 +35,8 @@ /datum/graph/proc/Connect(var/datum/node/node, var/list/neighbours, var/queue = TRUE) SHOULD_NOT_SLEEP(TRUE) SHOULD_NOT_OVERRIDE(TRUE) + if(QDELETED(src)) + CRASH("Attempted to connect node [node] to a qdeleted graph!") if(!istype(neighbours)) neighbours = list(neighbours) if(!length(neighbours)) @@ -90,8 +92,7 @@ if(neighbours_to_disconnect) neighbours |= neighbours_to_disconnect - if(neighbours.len) - LAZYSET(pending_disconnections, node, neighbours) + LAZYSET(pending_disconnections, node, neighbours) if(queue) SSgraphs_update.Queue(src) @@ -193,8 +194,6 @@ neighbour_edges |= N LAZYCLEARLIST(pending_connections) - if(!LAZYLEN(pending_disconnections)) - return for(var/pending_node_disconnect in pending_disconnections) var/pending_edge_disconnects = pending_disconnections[pending_node_disconnect] @@ -211,7 +210,8 @@ if(!length(other_pending_edge_disconnects)) pending_disconnections -= connected_node - edges[pending_node_disconnect] -= pending_edge_disconnects + if(edges[pending_node_disconnect]) + edges[pending_node_disconnect] -= pending_edge_disconnects if(!length(edges[pending_node_disconnect])) edges -= pending_node_disconnect @@ -225,12 +225,15 @@ var/checked_nodes = list() var/list/nodes_to_traverse = list(root_node) while(length(nodes_to_traverse)) - var/node_to_check = nodes_to_traverse[nodes_to_traverse.len] + var/datum/node/node_to_check = nodes_to_traverse[nodes_to_traverse.len] nodes_to_traverse.len-- + if(QDELETED(node_to_check)) + continue checked_nodes += node_to_check nodes_to_traverse |= ((edges[node_to_check] || list()) - checked_nodes) - all_nodes -= checked_nodes - subgraphs[++subgraphs.len] = checked_nodes + if(length(checked_nodes)) + all_nodes -= checked_nodes + subgraphs[++subgraphs.len] = checked_nodes if(length(subgraphs) == 1) if(!length(nodes)) diff --git a/code/datums/vote/map.dm b/code/datums/vote/map.dm index 9e64ac63bcb2..aabfdde28459 100644 --- a/code/datums/vote/map.dm +++ b/code/datums/vote/map.dm @@ -9,14 +9,14 @@ return ..() /datum/vote/map/setup_vote() - for(var/name in global.all_maps) + for(var/name in global.votable_maps) choices += name ..() /datum/vote/map/report_result() if(..()) return 1 - var/datum/map/M = global.all_maps[result[1]] + var/datum/map/M = global.votable_maps[result[1]] fdel("use_map") text2file(M.path, "use_map") diff --git a/code/game/alpha_masks.dm b/code/game/alpha_masks.dm index 05b23f45eaa2..9c8a341a4823 100644 --- a/code/game/alpha_masks.dm +++ b/code/game/alpha_masks.dm @@ -57,7 +57,7 @@ var/global/list/_alpha_masks = list() // Proc called by /turf/Entered() to update a mob's mask overlay. /atom/movable/proc/update_turf_alpha_mask() set waitfor = FALSE - if(!simulated || updating_turf_alpha_mask) + if(!simulated || QDELETED(src) || updating_turf_alpha_mask) return updating_turf_alpha_mask = TRUE sleep(0) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 5dd985c6064c..d13760956e3b 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -145,10 +145,12 @@ /atom/proc/try_on_reagent_change() SHOULD_NOT_OVERRIDE(TRUE) set waitfor = FALSE - if(_reagent_update_started >= world.time) + if(QDELETED(src) ||_reagent_update_started >= world.time) return FALSE _reagent_update_started = world.time sleep(0) // Defer to end of tick so we don't drop subsequent reagent updates. + if(QDELETED(src)) + return return on_reagent_change() /atom/proc/on_reagent_change() diff --git a/code/game/atoms_init.dm b/code/game/atoms_init.dm index 3a2f3b619c1d..d1cd533c7967 100644 --- a/code/game/atoms_init.dm +++ b/code/game/atoms_init.dm @@ -80,7 +80,7 @@ /atom/Destroy() // must be done before deletion // TODO: ADD PRE_DELETION OBSERVATION - if(isatom(loc) && loc.storage) + if(isatom(loc) && loc.storage && !QDELETED(loc.storage)) loc.storage.on_item_pre_deletion(src) UNQUEUE_TEMPERATURE_ATOM(src) QDEL_NULL(reagents) @@ -94,7 +94,7 @@ QDEL_NULL(atom_codex_ref) var/atom/oldloc = loc . = ..() - if(isatom(oldloc) && oldloc.storage) + if(isatom(oldloc) && oldloc.storage && !QDELETED(loc.storage)) oldloc.storage.on_item_post_deletion(src) // must be done after deletion // This might need to be moved onto a Del() override at some point. QDEL_NULL(storage) diff --git a/code/game/machinery/vending/_vending.dm b/code/game/machinery/vending/_vending.dm index 866062f2a54b..b3036e173176 100644 --- a/code/game/machinery/vending/_vending.dm +++ b/code/game/machinery/vending/_vending.dm @@ -309,10 +309,8 @@ if (href_list["vend"] && !currently_vending) var/key = text2num(href_list["vend"]) - if(!is_valid_index(key, product_records)) - return TOPIC_REFRESH - var/datum/stored_items/vending_products/R = product_records[key] - if(!istype(R)) + var/datum/stored_items/vending_products/R = LAZYACCESS(product_records, key) + if(!R) return TOPIC_REFRESH // This should not happen unless the request from NanoUI was bad diff --git a/code/game/objects/auras/aura.dm b/code/game/objects/auras/aura.dm index 8d1d35a0680c..5de18f3adf62 100644 --- a/code/game/objects/auras/aura.dm +++ b/code/game/objects/auras/aura.dm @@ -15,6 +15,7 @@ They should also be used for when you want to effect the ENTIRE mob, like having /obj/aura/Destroy() if(user) user.remove_aura(src) + removed() return ..() /obj/aura/proc/added_to(var/mob/living/target) diff --git a/code/game/objects/effects/dirty_floor.dm b/code/game/objects/effects/dirty_floor.dm index ae0c3c0cd9b5..e67ac44b0590 100644 --- a/code/game/objects/effects/dirty_floor.dm +++ b/code/game/objects/effects/dirty_floor.dm @@ -9,6 +9,12 @@ alpha = 0 var/dirt_amount = 0 +// 'the dirt falls through the x' is pretty silly, dirt is generated by people walking. +/obj/effect/decal/cleanable/dirt/begin_falling(lastloc, below) + SHOULD_CALL_PARENT(FALSE) + qdel(src) + return TRUE + /obj/effect/decal/cleanable/dirt/visible dirt_amount = 60 persistent = FALSE // This is a subtype for mapping. diff --git a/code/game/objects/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm index d6881129c230..fe253ea6f0bb 100644 --- a/code/game/objects/item_mob_overlay.dm +++ b/code/game/objects/item_mob_overlay.dm @@ -8,13 +8,14 @@ var/global/list/bodypart_to_slot_lookup_table = list( ) /obj/item/proc/reconsider_single_icon(var/update_icon) - use_single_icon = check_state_in_icon(ICON_STATE_INV, icon) || check_state_in_icon(ICON_STATE_WORLD, icon) + var/list/icon_states = get_states_in_icon_cached(icon) // pre-cache to make our up-to-three checks faster + // except now we only do two because i rewrote it + has_inventory_icon = use_single_icon = icon_states[ICON_STATE_INV] + if(!has_inventory_icon) + use_single_icon = icon_states[ICON_STATE_WORLD] if(use_single_icon) - has_inventory_icon = check_state_in_icon(ICON_STATE_INV, icon) icon_state = get_world_inventory_state() . = TRUE - else - has_inventory_icon = FALSE if(. || update_icon) update_icon() @@ -27,6 +28,23 @@ var/global/list/icon_state_cache = list() // isicon() is apparently quite expensive so short-circuit out early if we can. if(!istext(checkstate) || isnull(checkicon) || !(isfile(checkicon) || isicon(checkicon))) return FALSE + var/list/check = _fetch_icon_state_cache_entry(checkicon) // should never return null once we reach this point + return check[checkstate] + +/// A proc for getting an associative list of icon states in an icon. +/// Uses the same cache as check_state_in_icon. +/// Does not copy, MUST NOT BE MUTATED. +/proc/get_states_in_icon_cached(checkicon) /* as OD_MAP(text, OD_BOOL) */ + return _fetch_icon_state_cache_entry(checkicon) || list() + +/// get_states_in_icon_cached but it does a copy, so the return value can be mutated. +/proc/get_states_in_icon(checkicon) /* as OD_MAP(text, OD_BOOL) */ + var/list/out = get_states_in_icon_cached(checkicon) + return out.Copy() + +/proc/_fetch_icon_state_cache_entry(checkicon) + if(isnull(checkicon) || !(isfile(checkicon) || isicon(checkicon))) + return null var/checkkey = "\ref[checkicon]" var/list/check = global.icon_state_cache[checkkey] if(!check) @@ -34,7 +52,7 @@ var/global/list/icon_state_cache = list() for(var/istate in icon_states(checkicon)) check[istate] = TRUE global.icon_state_cache[checkkey] = check - . = check[checkstate] + return check /obj/item/proc/update_world_inventory_state() if(use_single_icon && has_inventory_icon) diff --git a/code/game/objects/items/_item_reagents.dm b/code/game/objects/items/_item_reagents.dm index cf7a09c7a708..abe58d48fbf6 100644 --- a/code/game/objects/items/_item_reagents.dm +++ b/code/game/objects/items/_item_reagents.dm @@ -64,12 +64,12 @@ return TRUE var/had_liquids = length(reagents.liquid_volumes) - var/trans = reagents.trans_to(target, amount) + var/transferred_amount = reagents.trans_to(target, amount) if(had_liquids) playsound(src, 'sound/effects/pour.ogg', 25, 1) else // Sounds more like pouring small pellets or dust. playsound(src, 'sound/effects/refill.ogg', 25, 1) - to_chat(user, SPAN_NOTICE("You transfer [trans] unit\s of the solution to \the [target]. \The [src] now contains [src.reagents.total_volume] units.")) + to_chat(user, SPAN_NOTICE("You transfer [transferred_amount] unit\s of the solution to \the [target]. \The [src] now contains [reagents.total_volume] unit\s.")) return TRUE diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index c9c67e53fc72..5a082bb9180c 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -59,7 +59,7 @@ return FALSE name += " - [S.name]" - desc = "Blueprints of \the [S.name]. There is a \"Classified\" stamp and several coffee stains on it." + desc = "Blueprints of \the [S]. There is a \"Classified\" stamp and several coffee stains on it." valid_z_levels += S.map_z area_prefix = S.name return TRUE diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 3565259a08e1..34115f49b63e 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -110,7 +110,7 @@ if(!user.try_unequip(L)) return TRUE AddUses(1) - to_chat(user, "You insert \the [L.name] into \the [src]. You have [uses] light\s remaining.") + to_chat(user, "You insert \the [L] into \the [src]. You have [uses] light\s remaining.") qdel(L) return TRUE else diff --git a/code/game/objects/items/devices/paint_sprayer.dm b/code/game/objects/items/devices/paint_sprayer.dm index a9dbd8a0878b..88c1e1195d73 100644 --- a/code/game/objects/items/devices/paint_sprayer.dm +++ b/code/game/objects/items/devices/paint_sprayer.dm @@ -223,7 +223,7 @@ return FALSE if(!flooring.can_paint || F.is_floor_damaged()) - to_chat(user, SPAN_WARNING("\The [src] cannot paint \the [F.name].")) + to_chat(user, SPAN_WARNING("\The [src] cannot paint \the [F].")) return FALSE var/list/decal_data = decals[decal] diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index b83f35bca481..2f2506b6aa05 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -7,11 +7,27 @@ var/obj/item/tank/tank_one var/obj/item/tank/tank_two var/obj/item/assembly/attached_device - var/mob/attacher = null + var/weakref/attacher_ref = null var/valve_open = 0 var/toggle = 1 movable_flags = MOVABLE_FLAG_PROXMOVE +/obj/item/transfer_valve/Destroy() + if(!QDELETED(tank_one)) + QDEL_NULL(tank_one) + else + tank_one = null + if(!QDELETED(tank_two)) + QDEL_NULL(tank_two) + else + tank_two = null + if(!QDELETED(attached_device)) + QDEL_NULL(attached_device) + else + attached_device = null + attacher_ref = null + return ..() + /obj/item/transfer_valve/attackby(obj/item/item, mob/user) var/turf/location = get_turf(src) // For admin logs if(istype(item, /obj/item/tank)) @@ -56,7 +72,7 @@ global.bombers += "[key_name(user)] attached a [item] to a transfer valve." message_admins("[key_name_admin(user)] attached a [item] to a transfer valve. (JMP)") log_game("[key_name_admin(user)] attached a [item] to a transfer valve.") - attacher = user + attacher_ref = weakref(user) . = TRUE if(.) update_icon() @@ -189,6 +205,7 @@ var/area/A = get_area(bombturf) var/attacher_name = "" + var/mob/attacher = attacher_ref.resolve() if(!attacher) attacher_name = "Unknown" else diff --git a/code/game/objects/items/weapons/ecigs.dm b/code/game/objects/items/weapons/ecigs.dm index dc05320c0908..0a55a70a9509 100644 --- a/code/game/objects/items/weapons/ecigs.dm +++ b/code/game/objects/items/weapons/ecigs.dm @@ -22,6 +22,10 @@ ec_cartridge = new cartridge_type(src) . = ..() +/obj/item/clothing/mask/smokable/ecig/Destroy() + QDEL_NULL(ec_cartridge) + return ..() + /obj/item/clothing/mask/smokable/ecig/setup_power_supply(loaded_cell_type, accepted_cell_type, power_supply_extension_type, charge_value) loaded_cell_type = loaded_cell_type || /obj/item/cell/device/standard accepted_cell_type = accepted_cell_type || /obj/item/cell/device diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm index c14047c71f62..7667753288a4 100644 --- a/code/game/objects/items/weapons/soap.dm +++ b/code/game/objects/items/weapons/soap.dm @@ -68,13 +68,13 @@ //So this is a workaround. This also makes more sense from an IC standpoint. ~Carn var/cleaned = FALSE if(user.client && (target in user.client.screen)) - to_chat(user, SPAN_NOTICE("You need to take that [target.name] off before cleaning it.")) + to_chat(user, SPAN_NOTICE("You need to take \the [target] off before cleaning it.")) else if(istype(target,/obj/effect/decal/cleanable/blood)) - to_chat(user, SPAN_NOTICE("You scrub \the [target.name] out.")) + to_chat(user, SPAN_NOTICE("You scrub \the [target] out.")) target.clean() //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals. cleaned = TRUE else if(istype(target,/obj/effect/decal/cleanable)) - to_chat(user, SPAN_NOTICE("You scrub \the [target.name] out.")) + to_chat(user, SPAN_NOTICE("You scrub \the [target] out.")) qdel(target) cleaned = TRUE else if(isturf(target) || istype(target, /obj/structure/catwalk)) @@ -90,7 +90,7 @@ to_chat(user, SPAN_NOTICE("You wet \the [src] in the sink.")) wet() else - to_chat(user, SPAN_NOTICE("You clean \the [target.name].")) + to_chat(user, SPAN_NOTICE("You clean \the [target].")) target.clean() //Clean bloodied atoms. Blood decals themselves need to be handled above. cleaned = TRUE diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 3a5cde951c74..641a512ce20d 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -57,12 +57,18 @@ . = ..() set_extension(src, /datum/extension/holster, storage, sound_in, sound_out, can_holster) -/obj/item/belt/holster/attackby(obj/item/W, mob/user) - var/datum/extension/holster/H = get_extension(src, /datum/extension/holster) - if(H.holster(W, user)) +/obj/item/belt/holster/get_stored_inventory() + . = ..() + if(length(.)) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) + if(holster.holstered) + . -= holster.holstered + +/obj/item/belt/holster/attackby(obj/item/used_item, mob/user) + var/datum/extension/holster/holster = get_extension(src, /datum/extension/holster) + if(holster?.holster(used_item, user)) return TRUE - else - . = ..(W, user) + return ..(used_item, user) /obj/item/belt/holster/attack_hand(mob/user) if(!user.check_dexterity(DEXTERITY_HOLD_ITEM, TRUE)) diff --git a/code/game/objects/structures/bookcase.dm b/code/game/objects/structures/bookcase.dm index 06d58e357aab..944bcd3fe0fb 100644 --- a/code/game/objects/structures/bookcase.dm +++ b/code/game/objects/structures/bookcase.dm @@ -168,7 +168,7 @@ var/global/list/station_bookcases = list() add_overlay(book_overlay) var/page_state = "[book_overlay.icon_state]-pages" - if(check_state_in_icon(book_overlay.icon, page_state)) + if(check_state_in_icon(page_state, book_overlay.icon)) var/image/page_overlay = overlay_image(book_overlay.icon, page_state, COLOR_WHITE, RESET_COLOR) page_overlay.pixel_x = book_overlay.pixel_x page_overlay.pixel_y = book_overlay.pixel_y diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm index caa3e14df932..67640a583fdf 100644 --- a/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm @@ -141,7 +141,7 @@ user.visible_message("[user] starts to lay out \the [src].") if(do_after(user, 4 SECONDS, src)) var/obj/structure/bed/chair/wheelchair/W = new structure_form_type(get_turf(user)) - user.visible_message(SPAN_NOTICE("[user] lays out \the [W.name].")) + user.visible_message(SPAN_NOTICE("[user] lays out \the [W].")) W.add_fingerprint(user) qdel(src) diff --git a/code/game/objects/structures/well.dm b/code/game/objects/structures/well.dm index 639cc2b263a1..eb6f59bc68f3 100644 --- a/code/game/objects/structures/well.dm +++ b/code/game/objects/structures/well.dm @@ -61,13 +61,6 @@ if(!is_processing && auto_refill) START_PROCESSING(SSobj, src) -/obj/structure/reagent_dispensers/well/attackby(obj/item/W, mob/user) - . = ..() - if(!. && user.check_intent(I_FLAG_HELP) && reagents?.total_volume > FLUID_PUDDLE) - user.visible_message(SPAN_NOTICE("\The [user] dips \the [W] into \the [reagents.get_primary_reagent_name()].")) - W.fluid_act(reagents) - return TRUE - /obj/structure/reagent_dispensers/well/Process() if(!reagents || !auto_refill) // if we're full, we only stop at the end of the proc; we need to check for contaminants first return PROCESS_KILL diff --git a/code/game/turfs/floors/floor_attackby.dm b/code/game/turfs/floors/floor_attackby.dm index c87c7b79ce95..d023e3d2ee58 100644 --- a/code/game/turfs/floors/floor_attackby.dm +++ b/code/game/turfs/floors/floor_attackby.dm @@ -73,8 +73,8 @@ if((istype(flooring) && flooring.constructed) || !istype(used_item) || !istype(user)) return FALSE - flooring = get_base_flooring() - if(istype(flooring) && flooring.constructed) + var/decl/flooring/base_flooring = get_base_flooring() + if(istype(base_flooring) && base_flooring.constructed) return FALSE if(!istype(used_item, /obj/item/stack/material/ore) && !istype(used_item, /obj/item/stack/material/lump)) @@ -84,11 +84,11 @@ to_chat(user, SPAN_WARNING("\The [src] is flush with ground level and cannot be backfilled.")) return TRUE - if(!used_item.material?.can_backfill_turf_type) + if(!used_item.material?.can_backfill_floor_type) to_chat(user, SPAN_WARNING("You cannot use \the [used_item] to backfill \the [src].")) return TRUE - var/can_backfill = islist(used_item.material.can_backfill_turf_type) ? is_type_in_list(src, used_item.material.can_backfill_turf_type) : istype(src, used_item.material.can_backfill_turf_type) + var/can_backfill = islist(used_item.material.can_backfill_floor_type) ? is_type_in_list(flooring, used_item.material.can_backfill_floor_type) : istype(flooring, used_item.material.can_backfill_floor_type) if(!can_backfill) to_chat(user, SPAN_WARNING("You cannot use \the [used_item] to backfill \the [src].")) return TRUE diff --git a/code/game/turfs/floors/floor_icon.dm b/code/game/turfs/floors/floor_icon.dm index 5e060f358487..c4c7cbb83153 100644 --- a/code/game/turfs/floors/floor_icon.dm +++ b/code/game/turfs/floors/floor_icon.dm @@ -45,11 +45,14 @@ layer = initial(layer) if(istype(flooring) && !flooring.render_trenches) // TODO: Update pool tiles/edges to behave properly with this new system. + default_pixel_z = initial(default_pixel_z) + pixel_z = default_pixel_z return FALSE var/my_height = get_physical_height() - if(my_height < 0) - + if(my_height >= 0) + default_pixel_z = initial(default_pixel_z) + else var/height_ratio = clamp(abs(my_height) / FLUID_DEEP, 0, 1) default_pixel_z = -(min(HEIGHT_OFFSET_RANGE, round(HEIGHT_OFFSET_RANGE * height_ratio))) pixel_z = default_pixel_z @@ -76,7 +79,7 @@ var/trench_icon = (istype(neighbor) && neighbor.get_trench_icon()) || get_trench_icon() if(trench_icon) // cache the trench image, keyed by icon and color - var/trench_color = isatom(neighbor) ? neighbor.color : color + var/trench_color = isatom(neighbor) ? neighbor.get_color() : get_color() var/trench_icon_key = "[ref(trench_icon)][trench_color]" I = _trench_image_cache[trench_icon_key] if(!I) @@ -97,6 +100,7 @@ I.appearance_flags |= RESET_COLOR | RESET_ALPHA _height_north_shadow_cache[shadow_alpha_key] = I add_overlay(I) + pixel_z = default_pixel_z /turf/floor/on_update_icon(var/update_neighbors) . = ..() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index cf4d6f67920d..4bea938640e6 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -284,19 +284,6 @@ if(IS_COIL(W) && try_build_cable(W, user)) return TRUE - if(reagents?.total_volume >= FLUID_PUDDLE) - if(ATOM_IS_OPEN_CONTAINER(W) && W.reagents) - var/taking = min(reagents.total_volume, REAGENTS_FREE_SPACE(W.reagents)) - if(taking > 0) - to_chat(user, SPAN_NOTICE("You fill \the [W] with [reagents.get_primary_reagent_name()] from \the [src].")) - reagents.trans_to(W, taking) - return TRUE - - if(user.check_intent(I_FLAG_HELP)) - user.visible_message(SPAN_NOTICE("\The [user] dips \the [W] into \the [reagents.get_primary_reagent_name()].")) - W.fluid_act(reagents) - return TRUE - return ..() /turf/Enter(atom/movable/mover, atom/forget) @@ -802,6 +789,9 @@ /turf/get_affecting_weather() return weather +/turf/can_be_poured_into(atom/source) + return !density + /turf/get_alt_interactions(mob/user) . = ..() LAZYADD(., /decl/interaction_handler/show_turf_contents) diff --git a/code/game/turfs/turf_fluids.dm b/code/game/turfs/turf_fluids.dm index 0ea31b04c6f3..7d0cec0a34f0 100644 --- a/code/game/turfs/turf_fluids.dm +++ b/code/game/turfs/turf_fluids.dm @@ -179,6 +179,8 @@ var/decl/material/primary_reagent = reagents.get_primary_reagent_decl() if(primary_reagent && (REAGENT_VOLUME(reagents, primary_reagent.type) >= primary_reagent.slippery_amount)) last_slipperiness = primary_reagent.slipperiness + else + last_slipperiness = 0 if(!fluid_overlay) fluid_overlay = new(src, TRUE) fluid_overlay.update_icon() @@ -190,6 +192,7 @@ SSfluids.pending_flows -= src if(last_slipperiness > 0) wet_floor(last_slipperiness) + last_slipperiness = 0 for(var/checkdir in global.cardinal) var/turf/neighbor = get_step_resolving_mimic(src, checkdir) diff --git a/code/game/world.dm b/code/game/world.dm index 079e43ae5439..eb1715e424fb 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -146,11 +146,14 @@ var/global/world_topic_last = world.timeofday return command.try_use(T, addr, master, key) +var/global/_reboot_announced = FALSE /world/Reboot(var/reason) if(get_config_value(/decl/config/toggle/wait_for_sigusr1_reboot) && reason != 3) text2file("foo", "reboot_called") - to_world("World reboot waiting for external scripts. Please be patient.") + if(!global._reboot_announced) + to_world("World reboot waiting for external scripts. Please be patient.") + global._reboot_announced = TRUE global.Master.restart_timeout = 5 MINUTES return diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index 8605ad977a82..06c04257babc 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -318,6 +318,7 @@ variable = param_var_name + // TODO: check for list-typed O? Proc does not exist on non-datum types. var_value = O.get_variable_value(variable) if(autodetect_class) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index a4cf742c410f..47dcc913a450 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -12,7 +12,7 @@ var/secured = 1 var/list/attached_overlays = null - var/obj/item/assembly_holder/holder = null + var/obj/item/assembly_holder/holder = null // currently can be a TTV or assemblyholder, todo make ttv use assemblyholder var/cooldown = 0//To prevent spam var/wires = WIRE_RECEIVE | WIRE_PULSE @@ -24,10 +24,7 @@ /obj/item/assembly/Destroy() if(!QDELETED(holder)) - if(holder.a_left == src) - holder.a_left = null - if(holder.a_right == src) - holder.a_right = null + // the holder has the responsibility to clear its associated vars on destroy QDEL_NULL(holder) else holder = null diff --git a/code/modules/client/ui_styles/_ui_style.dm b/code/modules/client/ui_styles/_ui_style.dm index 5e0a5ba34c41..1a5885cfac67 100644 --- a/code/modules/client/ui_styles/_ui_style.dm +++ b/code/modules/client/ui_styles/_ui_style.dm @@ -46,7 +46,7 @@ var/check_icon = icons[ui_key] var/list/missing_states = list() var/list/checking_states = states_to_check[ui_key] - var/list/remaining_states = icon_states(check_icon) + var/list/remaining_states = get_states_in_icon(check_icon) for(var/check_state in checking_states) remaining_states -= check_state if(!check_state_in_icon(check_state, check_icon)) diff --git a/code/modules/clothing/masks/voice.dm b/code/modules/clothing/masks/voice.dm index edee4acd8eb8..8ecc47d727ce 100644 --- a/code/modules/clothing/masks/voice.dm +++ b/code/modules/clothing/masks/voice.dm @@ -30,3 +30,7 @@ /obj/item/clothing/mask/chameleon/voice/Initialize() . = ..() changer = new(src) + +/obj/item/clothing/mask/chameleon/voice/Destroy() + QDEL_NULL(changer) + return ..() diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index 0386948c6020..f6a35661567b 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -48,7 +48,7 @@ /obj/item/rig_module/device/flash/installed() . = ..() - if(!holder.gloves)//gives select option for gloveless suits, why even use rig at this point + if(!holder?.gloves)//gives select option for gloveless suits, why even use rig at this point selectable = 1 activates_on_touch = 0 toggleable = 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index 7626ac00c32e..8fa00d9ada6a 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -313,14 +313,16 @@ voice_holder.active = 0 /obj/item/rig_module/voice/installed() - ..() - holder.speech = src - holder.verbs |= /obj/item/rig/proc/alter_voice + . = ..() + if(holder) + holder.speech = src + holder.verbs |= /obj/item/rig/proc/alter_voice /obj/item/rig_module/voice/removed() - ..() - holder.speech = null - holder.verbs -= /obj/item/rig/proc/alter_voice + if(holder) + holder.speech = null + holder.verbs -= /obj/item/rig/proc/alter_voice + . = ..() /obj/item/rig_module/voice/engage() diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm index 3b19bf496531..9ba365b34fa0 100644 --- a/code/modules/clothing/spacesuits/rig/modules/vision.dm +++ b/code/modules/clothing/spacesuits/rig/modules/vision.dm @@ -160,8 +160,14 @@ // There should only ever be one vision module installed in a suit. /obj/item/rig_module/vision/installed() - ..() - holder.visor = src + . = ..() + if(holder) + holder.visor = src + +/obj/item/rig_module/vision/removed() + if(holder) + holder.visor = null + . = ..() /obj/item/rig_module/vision/engage() diff --git a/code/modules/crafting/pottery/pottery_structures.dm b/code/modules/crafting/pottery/pottery_structures.dm index 2c9a369296b4..c952e9b81711 100644 --- a/code/modules/crafting/pottery/pottery_structures.dm +++ b/code/modules/crafting/pottery/pottery_structures.dm @@ -6,7 +6,7 @@ density = TRUE cap_last_fuel_burn = null - var/list/pottery = list() + var/list/pottery var/maximum_items = 3 var/firebox_open = TRUE @@ -17,12 +17,12 @@ /obj/structure/fire_source/kiln/high_temperature material = /decl/material/solid/stone/pottery -/obj/structure/fire_source/kiln/remove_atom(atom/movable/thing) +/obj/structure/fire_source/kiln/Exited(atom/movable/thing) . = ..() - pottery -= thing + LAZYREMOVE(pottery, thing) /obj/structure/fire_source/kiln/get_removable_atoms() - . = pottery?.Copy() + . = pottery?.Copy() || list() if(firebox_open) . |= ..() @@ -38,14 +38,14 @@ if(length(other)) LAZYDISTINCTADD(., other) -/obj/structure/fire_source/kiln/attackby(obj/item/W, mob/user) +/obj/structure/fire_source/kiln/attackby(obj/item/used_item, mob/user) if(firebox_open) return ..() if(length(pottery) >= maximum_items) to_chat(user, SPAN_WARNING("\The [src] is full, take something out first.")) - else if(user.try_unequip(W, src)) - user.visible_message("\The [user] slides \the [W] into \the [src].") - pottery += W + else if(user.try_unequip(used_item, src)) + user.visible_message("\The [user] slides \the [used_item] into \the [src].") + LAZYADD(pottery, used_item) return TRUE /obj/structure/fire_source/kiln/get_alt_interactions(var/mob/user) diff --git a/code/modules/crafting/slapcrafting/_crafting_stage.dm b/code/modules/crafting/slapcrafting/_crafting_stage.dm index b72c4d4cb8e2..50ede2c90bd8 100644 --- a/code/modules/crafting/slapcrafting/_crafting_stage.dm +++ b/code/modules/crafting/slapcrafting/_crafting_stage.dm @@ -76,17 +76,18 @@ . = !consume_completion_trigger || user.try_unequip(thing, target) if(. && stack_consume_amount > 0) var/obj/item/stack/stack = thing - if(!istype(stack) || stack.amount < stack_consume_amount) + if(!istype(stack) || stack.get_amount() < stack_consume_amount) on_insufficient_material(user, stack) return FALSE var/obj/item/stack/used_stack - if(stack.amount == stack_consume_amount) + if(stack.amount > stack_consume_amount) + used_stack = stack.split(stack_consume_amount) + else if(!user.try_unequip(thing, target)) return FALSE used_stack = stack - else - used_stack = stack.split(stack_consume_amount) - used_stack.forceMove(target) + if(!QDELETED(used_stack)) + used_stack.forceMove(target) target?.update_icon() /decl/crafting_stage/proc/on_insufficient_material(var/mob/user, var/obj/item/stack/thing) diff --git a/code/modules/economy/worth_currency.dm b/code/modules/economy/worth_currency.dm index ed12b938e92c..9e5de940b128 100644 --- a/code/modules/economy/worth_currency.dm +++ b/code/modules/economy/worth_currency.dm @@ -70,13 +70,13 @@ if(!name_singular) . += "No singular name set." - var/list/coinage_states = icon_states(icon) + var/list/coinage_states = get_states_in_icon_cached(icon) // cache this to avoid excessive ref() usage for(var/datum/denomination/denomination in denominations) if(!istext(denomination.name)) . += "Non-text name found for '[denomination.type]'." else if(!(denomination.state in coinage_states)) . += "State '[denomination.state]' not found in icon file for '[denomination.type]'." - else if(denomination.mark && !(denomination.mark in coinage_states)) + else if(denomination.mark && !coinage_states[denomination.mark]) . += "Mark state '[denomination.mark]' not found in icon file for '[denomination.type]'." else if(!isnum(denomination.marked_value)) . += "Non-numerical denomination marked value found for '[denomination]'." diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm index 736b18d1c976..b1c6d7c59819 100644 --- a/code/modules/events/dust.dm +++ b/code/modules/events/dust.dm @@ -14,13 +14,17 @@ The "dust" will damage the hull of the station causin minor hull breaches. command_announcement.Announce("The [location_name()] is now passing through a belt of space dust.", "[location_name()] Sensor Array", zlevels = affecting_z) /datum/event/dust/tick() - if(world.time > last_wave + min_delay && prob(10)) + if(world.time > last_wave + min_delay && prob(10) && length(affecting_z)) dust_swarm(severity, affecting_z) /datum/event/dust/end() command_announcement.Announce("The [location_name()] has now passed through the belt of space dust.", "[location_name()] Sensor Array", zlevels = affecting_z) /proc/dust_swarm(var/strength = EVENT_LEVEL_MUNDANE, var/list/zlevels) + + if(!length(zlevels)) + return // Not sure how this happened, but saw it in a runtime on Pyrelight. + var/numbers = rand(strength * 10, strength * 15) var/start_dir = pick(global.cardinal) diff --git a/code/modules/events/rogue_drones.dm b/code/modules/events/rogue_drones.dm index a6decd690ff0..c5d0df69341e 100644 --- a/code/modules/events/rogue_drones.dm +++ b/code/modules/events/rogue_drones.dm @@ -38,9 +38,7 @@ var/num_recovered = 0 for(var/mob/living/simple_animal/hostile/malf_drone/D in drones_list) spark_at(D.loc) - D.z = SSmapping.admin_levels[1] D.has_loot = 0 - qdel(D) num_recovered++ diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 2d609cf629e3..840d453ce501 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -3,14 +3,12 @@ var/amount var/datum/seed/seed_type // Keeps track of what our seed is var/list/obj/item/seeds/seeds = list() // Tracks actual objects contained in the pile - var/ID -/datum/seed_pile/New(var/obj/item/seeds/O, var/ID) +/datum/seed_pile/New(var/obj/item/seeds/O) name = O.name amount = 1 seed_type = O.seed seeds += O - src.ID = ID /datum/seed_pile/proc/matches(var/obj/item/seeds/O) if (O.seed == seed_type) @@ -195,7 +193,8 @@ if ("soil" in scanner) dat += "NutriWater" dat += "NotesAmount" - for (var/datum/seed_pile/S in piles) + for (var/key in 1 to length(piles)) + var/datum/seed_pile/S = piles[key] var/datum/seed/seed = S.seed_type if(!seed) continue @@ -281,7 +280,7 @@ dat += "LUM " dat += "" dat += "[S.amount]" - dat += "Vend Purge" + dat += "Vend Purge" dat += "" dat += "" @@ -292,29 +291,26 @@ if (..()) return var/task = href_list["task"] - var/ID = text2num(href_list["id"]) + var/id = text2num(href_list["id"]) + var/datum/seed_pile/our_pile = LAZYACCESS(piles, id) - for (var/datum/seed_pile/N in piles) - if (N.ID == ID) - if (task == "vend") - var/obj/O = pick(N.seeds) - if (O) - --N.amount - N.seeds -= O - if (N.amount <= 0 || N.seeds.len <= 0) - piles -= N - qdel(N) - flick("[initial(icon_state)]-vend", src) - O.dropInto(loc) - else - piles -= N - qdel(N) - else if (task == "purge") - for (var/obj/O in N.seeds) - qdel(O) - piles -= N - qdel(N) - break + switch(task) + if ("vend") + var/obj/O = pick(our_pile.seeds) + if (O) + --our_pile.amount + our_pile.seeds -= O + if (our_pile.amount <= 0 || our_pile.seeds.len <= 0) + piles -= our_pile + qdel(our_pile) + flick("[initial(icon_state)]-vend", src) + O.dropInto(loc) + if ("purge") + QDEL_LIST(our_pile.seeds) + our_pile.seeds.Cut() + if(!length(our_pile.seeds)) + piles -= our_pile + QDEL_NULL(our_pile) updateUsrDialog() /obj/machinery/seed_storage/attackby(var/obj/item/O, var/mob/user) @@ -334,7 +330,7 @@ if (loaded) user.visible_message(SPAN_NOTICE("\The [user] puts the seeds from \the [O] into \the [src].")) else - to_chat(user, SPAN_WARNING("There are no seeds in \the [O.name].")) + to_chat(user, SPAN_WARNING("There are no seeds in \the [O].")) return TRUE return ..() @@ -349,28 +345,13 @@ O.loc?.storage?.remove_from_storage(null, O, src) O.forceMove(src) - var/newID = 0 for (var/datum/seed_pile/N in piles) if (N.matches(O)) ++N.amount N.seeds += (O) return - else if(N.ID >= newID) - newID = N.ID + 1 - piles += new /datum/seed_pile(O, newID) + piles += new /datum/seed_pile(O) flick("[initial(icon_state)]-vend", src) return - -/obj/machinery/seed_storage/cannot_transition_to(state_path, mob/user) - if(state_path == /decl/machine_construction/default/deconstructed) - var/alert = alert(user, "Are you certain you wish to deconstruct this? It will destroy all seeds stored inside!", "Deconstruct Warning", "Yes", "No") - if(alert != "Yes" || !CanPhysicallyInteract(user)) - return MCS_BLOCK - return ..() - -/obj/machinery/seed_storage/dismantle() - for(var/obj/item/seeds/seed in src) - qdel(seed) // ..() would dump them; this would cause lots of client lag. We did warn them above... - return ..() \ No newline at end of file diff --git a/code/modules/interactions/interactions_reagents.dm b/code/modules/interactions/interactions_reagents.dm index c99ffad81ab7..79885549cc40 100644 --- a/code/modules/interactions/interactions_reagents.dm +++ b/code/modules/interactions/interactions_reagents.dm @@ -3,11 +3,17 @@ interaction_flags = INTERACTION_NEEDS_PHYSICAL_INTERACTION | INTERACTION_NEVER_AUTOMATIC /decl/interaction_handler/dip_item/is_possible(atom/target, mob/user, obj/item/prop) - return ..() && target.reagents?.total_volume >= FLUID_PUDDLE && istype(prop) && target.can_be_poured_from(user, prop) + return ..() && target.reagents?.total_volume >= FLUID_MINIMUM_TRANSFER && istype(prop) && target.can_be_poured_from(user, prop) /decl/interaction_handler/dip_item/invoked(atom/target, mob/user, obj/item/prop) user.visible_message(SPAN_NOTICE("\The [user] dips \the [prop] into \the [target.reagents.get_primary_reagent_name()].")) - prop.fluid_act(target.reagents) + if(istype(prop, /obj/item/food) && isobj(target)) + var/obj/target_obj = target + var/transferring = min(target_obj.get_food_default_transfer_amount(user), REAGENTS_FREE_SPACE(prop.reagents)) + if(transferring) + target.reagents.trans_to_holder(prop.reagents, transferring) + if(target.reagents?.total_volume >= FLUID_MINIMUM_TRANSFER) + prop.fluid_act(target.reagents) return TRUE /decl/interaction_handler/fill_from diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 687dcc2a49d9..b1e8d111ed79 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -237,6 +237,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, update_overlays(TRUE) /datum/lighting_corner/proc/update_ambient_lumcount(delta_r, delta_g, delta_b, skip_update = FALSE) + ambient_r += delta_r ambient_g += delta_g ambient_b += delta_b diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index d4008f536a20..17e233358079 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -49,9 +49,9 @@ var/ambient_b = 0 if (ambient_light) - ambient_r = ((HEX_RED(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_r - ambient_g = ((HEX_GREEN(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_g - ambient_b = ((HEX_BLUE(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_b + ambient_r = round(((HEX_RED(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_r, LIGHTING_ROUND_VALUE) + ambient_g = round(((HEX_GREEN(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_g, LIGHTING_ROUND_VALUE) + ambient_b = round(((HEX_BLUE(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_b, LIGHTING_ROUND_VALUE) else ambient_r = -ambient_light_old_r ambient_g = -ambient_light_old_g @@ -61,7 +61,7 @@ ambient_light_old_g += ambient_g ambient_light_old_b += ambient_b - if (ambient_r + ambient_g + ambient_b == 0) + if (abs(ambient_r + ambient_g + ambient_b) == 0) return // Unlit turfs will have corners if they have a lit neighbor -- don't generate corners for them, but do update them if they're there. diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 6ea17dcdf2df..b17f238fcb41 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -112,7 +112,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) var/toxicity = 0 // Organ damage from ingestion. var/toxicity_targets_organ // Bypass liver/kidneys when ingested, harm this organ directly (using BP_FOO defines). - var/can_backfill_turf_type + var/can_backfill_floor_type // Shards/tables/structures var/shard_type = SHARD_SHRAPNEL // Path of debris object. @@ -718,12 +718,12 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) /decl/material/proc/touch_obj(var/obj/O, var/amount, var/datum/reagents/holder) // Acid melting, cleaner cleaning, etc - if(solvent_power >= MAT_SOLVENT_MILD) - if(istype(O, /obj/item/paper)) + if(solvent_power >= MAT_SOLVENT_MODERATE) + if(istype(O, /obj/item/paper) && amount >= FLUID_MINIMUM_TRANSFER) var/obj/item/paper/paperaffected = O paperaffected.clearpaper() O.visible_message(SPAN_NOTICE("The solution dissolves the ink on the paper."), range = 1) - else if(istype(O, /obj/item/book) && amount >= 5) + else if(istype(O, /obj/item/book) && amount >= FLUID_PUDDLE) var/obj/item/book/affectedbook = O if(affectedbook.can_dissolve_text) affectedbook.dat = null @@ -1200,3 +1200,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) /decl/material/proc/can_hold_edge() return hardness > MAT_VALUE_FLEXIBLE + +// TODO: expand this to more than just Actual Poison. +/decl/material/proc/is_unsafe_to_drink(mob/user) + return toxicity > 0 diff --git a/code/modules/materials/definitions/solids/materials_solid_ice.dm b/code/modules/materials/definitions/solids/materials_solid_ice.dm index c90937ccd3be..34144fe83933 100644 --- a/code/modules/materials/definitions/solids/materials_solid_ice.dm +++ b/code/modules/materials/definitions/solids/materials_solid_ice.dm @@ -40,7 +40,7 @@ hardness = MAT_VALUE_MALLEABLE dug_drop_type = /obj/item/stack/material/ore/handful default_solid_form = /obj/item/stack/material/ore/handful - can_backfill_turf_type = /turf/floor/snow + can_backfill_floor_type = /decl/flooring/snow /decl/material/solid/ice/aspium name = "aspium" diff --git a/code/modules/materials/definitions/solids/materials_solid_mineral.dm b/code/modules/materials/definitions/solids/materials_solid_mineral.dm index 2d073b6fbb60..ac92e330a7d4 100644 --- a/code/modules/materials/definitions/solids/materials_solid_mineral.dm +++ b/code/modules/materials/definitions/solids/materials_solid_mineral.dm @@ -234,7 +234,7 @@ ) dug_drop_type = /obj/item/stack/material/ore/handful default_solid_form = /obj/item/stack/material/ore/handful - can_backfill_turf_type = /turf/floor/rock/sand + can_backfill_floor_type = /decl/flooring/sand /decl/material/solid/clay name = "clay" @@ -257,7 +257,7 @@ melting_point = null // Clay is already almost a liquid... // lower than the temperature expected from a kiln so that clay can be used to make bricks to make a high-temperature kiln. bakes_into_at_temperature = 950 CELSIUS - can_backfill_turf_type = /turf/floor/clay + can_backfill_floor_type = /decl/flooring/clay gemstone_chance = 0.01 gemstone_types = list(/decl/material/solid/gemstone/sapphire = 1) @@ -274,9 +274,9 @@ dirtiness = 30 dug_drop_type = /obj/item/stack/material/lump/large tillable = TRUE - can_backfill_turf_type = list( - /turf/floor/mud, - /turf/floor/dirt + can_backfill_floor_type = list( + /decl/flooring/mud, + /decl/flooring/dirt ) /decl/material/solid/hematite diff --git a/code/modules/mechs/equipment/_equipment.dm b/code/modules/mechs/equipment/_equipment.dm index 03ba4a1ac813..9df6c6915979 100644 --- a/code/modules/mechs/equipment/_equipment.dm +++ b/code/modules/mechs/equipment/_equipment.dm @@ -71,10 +71,6 @@ owner = null canremove = TRUE -/obj/item/mech_equipment/Destroy() - owner = null - . = ..() - /obj/item/mech_equipment/proc/get_effective_obj() return src diff --git a/code/modules/mechs/equipment/medical.dm b/code/modules/mechs/equipment/medical.dm index 7c39947f122b..4aadea64370d 100644 --- a/code/modules/mechs/equipment/medical.dm +++ b/code/modules/mechs/equipment/medical.dm @@ -22,7 +22,7 @@ /obj/item/mech_equipment/sleeper/uninstalled() . = ..() - sleeper.go_out() + sleeper?.go_out() /obj/item/mech_equipment/sleeper/attack_self(var/mob/user) . = ..() diff --git a/code/modules/mechs/mech.dm b/code/modules/mechs/mech.dm index 034695077605..809f205d2bdc 100644 --- a/code/modules/mechs/mech.dm +++ b/code/modules/mechs/mech.dm @@ -157,10 +157,14 @@ hud_elements.Cut() for(var/hardpoint in hardpoints) - qdel(hardpoints[hardpoint]) + var/obj/item/mech_equipment/equipment = hardpoints[hardpoint] + if(istype(equipment)) + equipment.uninstalled() + QDEL_NULL(equipment) hardpoints.Cut() QDEL_NULL(access_card) + QDEL_NULL(radio) QDEL_NULL(arms) QDEL_NULL(legs) QDEL_NULL(head) diff --git a/code/modules/mechs/mech_wreckage.dm b/code/modules/mechs/mech_wreckage.dm index b22b39231075..a78f4c7596c2 100644 --- a/code/modules/mechs/mech_wreckage.dm +++ b/code/modules/mechs/mech_wreckage.dm @@ -12,7 +12,7 @@ /obj/structure/mech_wreckage/Initialize(mapload, var/mob/living/exosuit/exosuit, var/gibbed) . = ..(mapload) if(exosuit) - name = "wreckage of \the [exosuit.name]" + name = "wreckage of \the [exosuit]" loot_pool = list() if(!gibbed) for(var/obj/item/thing in list(exosuit.arms, exosuit.legs, exosuit.head, exosuit.body)) diff --git a/code/modules/mob/living/brain/death.dm b/code/modules/mob/living/brain/death.dm index a58511aaaefb..1c185d241c60 100644 --- a/code/modules/mob/living/brain/death.dm +++ b/code/modules/mob/living/brain/death.dm @@ -19,7 +19,7 @@ var/obj/item/organ/internal/brain/sponge = loc . = ..() if(.) - if(!QDELETED(container)) + if(istype(container) && !QDELETED(container)) qdel(container) - if(!QDELETED(sponge)) + if(istype(sponge) && !QDELETED(sponge)) qdel(sponge) diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index ddcaa27e09f9..d1763426eb69 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -72,25 +72,25 @@ to_chat(user, "[use_He] [use_has] a pulse!") var/datum/reagents/touching_reagents = get_contact_reagents() - if(touching_reagents?.total_volume) + if(touching_reagents?.total_volume >= 1) var/saturation = touching_reagents.total_volume / touching_reagents.maximum_volume if(saturation > 0.9) msg += "[use_He] [use_is] completely saturated.\n" else if(saturation > 0.6) - msg += "[use_He] [use_is] looking like a drowned cat.\n" + msg += "[use_He] [use_is] looking half-drowned.\n" else if(saturation > 0.3) msg += "[use_He] [use_is] looking notably soggy.\n" else - msg += "[use_He] [use_is] looking a bit damp.\n" + msg += "[use_He] [use_is] looking a bit soggy.\n" var/fire_level = get_fire_intensity() if(fire_level > 0) msg += "[use_He] [use_is] looking highly flammable!\n" else if(fire_level < 0) - msg += "[use_He] [use_is] looking rather damp.\n" + msg += "[use_He] [use_is] looking rather incombustible.\n" if(is_on_fire()) - msg += "[use_He] [use_is] on fire!.\n" + msg += "[use_He] [use_is] on fire!\n" var/ssd_msg = species.get_ssd(src) if(ssd_msg && (!should_have_organ(BP_BRAIN) || has_brain()) && stat != DEAD) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 446bbe39a059..38f5705f5778 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -275,9 +275,10 @@ if(!loc) return var/datum/reagents/touching_reagents = get_contact_reagents() - if(!touching_reagents?.total_volume) + if(touching_reagents?.total_volume <= FLUID_MINIMUM_TRANSFER) + touching_reagents?.clear_reagents() return - var/drip_amount = max(1, round(touching_reagents.total_volume * 0.1)) + var/drip_amount = max(FLUID_MINIMUM_TRANSFER, round(touching_reagents.total_volume * 0.2)) if(drip_amount) touching_reagents.trans_to(loc, drip_amount) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7685c69fda5d..78ba364f39a6 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -857,10 +857,10 @@ default behaviour is: /mob/living/fluid_act(var/datum/reagents/fluids) ..() - if(QDELETED(src) || !fluids?.total_volume) + if(QDELETED(src) || fluids?.total_volume < FLUID_PUDDLE) return fluids.touch_mob(src) - if(QDELETED(src) || !fluids.total_volume) + if(QDELETED(src) || fluids?.total_volume < FLUID_PUDDLE) return var/on_turf = fluids.my_atom == get_turf(src) for(var/atom/movable/A as anything in get_equipped_items(TRUE)) @@ -1845,7 +1845,7 @@ default behaviour is: /mob/living/proc/get_door_pry_time() return 7 SECONDS -/mob/living/proc/pry_door(atom/target, pry_time) +/mob/living/proc/pry_door(delay, obj/machinery/door/target) return /mob/living/proc/turf_is_safe(turf/target) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index a74526afdd40..4cacbc889af7 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -189,7 +189,7 @@ visible_message(SPAN_NOTICE("\The [O] misses \the [src] narrowly!")) return FALSE - visible_message(SPAN_DANGER("\The [src] is hit [affecting ? "in \the [affecting.name] " : ""]by \the [O]!")) + visible_message(SPAN_DANGER("\The [src] is hit [affecting ? "in \the [affecting] " : ""]by \the [O]!")) if(TT?.thrower?.client) admin_attack_log(TT.thrower, src, "Threw \an [O] at the victim.", "Had \an [O] thrown at them.", "threw \an [O] at") try_embed_in_mob(TT.thrower, O, zone, throw_damage, dtype, null, affecting, direction = TT.init_dir) diff --git a/code/modules/mob/living/simple_animal/_simple_animal.dm b/code/modules/mob/living/simple_animal/_simple_animal.dm index 77c479915f82..8d2d7af52642 100644 --- a/code/modules/mob/living/simple_animal/_simple_animal.dm +++ b/code/modules/mob/living/simple_animal/_simple_animal.dm @@ -534,16 +534,16 @@ var/global/list/simplemob_icon_bitflag_cache = list() /mob/living/simple_animal/proc/get_pry_desc() return "prying" -/mob/living/simple_animal/pry_door(var/mob/user, var/delay, var/obj/machinery/door/pesky_door) +/mob/living/simple_animal/pry_door(delay, obj/machinery/door/target) if(!can_pry_door()) return - visible_message(SPAN_DANGER("\The [user] begins [get_pry_desc()] at \the [pesky_door]!")) + visible_message(SPAN_DANGER("\The [src] begins [get_pry_desc()] at \the [target]!")) if(istype(ai)) ai.pause() - if(do_after(user, delay, pesky_door)) - pesky_door.open(1) + if(do_after(src, delay, target)) + target.open(1) else - visible_message(SPAN_NOTICE("\The [user] is interrupted.")) + visible_message(SPAN_NOTICE("\The [src] is interrupted.")) if(istype(ai)) ai.resume() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index e3f5b4fe9ff0..c8de00b79d20 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -82,6 +82,10 @@ ion_trail.set_up(src) ion_trail.start() +/mob/living/simple_animal/hostile/malf_drone/Destroy() + QDEL_NULL(ion_trail) + return ..() + /mob/living/simple_animal/hostile/malf_drone/Process_Spacemove() return 1 diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index a30099212492..585f57d0d7e3 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -225,7 +225,7 @@ for(var/mob/living/M in landing.contents) if(M == src) continue - visible_message("\The [src] hits \the [M.name]!") + visible_message("\The [src] hits \the [M]!") M.take_overall_damage(fall_damage) return TRUE return FALSE diff --git a/code/modules/multiz/zmimic/mimic_movable.dm b/code/modules/multiz/zmimic/mimic_movable.dm index 32f5f80e6912..04644bcdde71 100644 --- a/code/modules/multiz/zmimic/mimic_movable.dm +++ b/code/modules/multiz/zmimic/mimic_movable.dm @@ -166,7 +166,7 @@ if (destruction_timer) deltimer(destruction_timer) destruction_timer = null - if (old_loc.z != loc.z) + if (old_loc?.z != loc?.z) // Null checking in case of qdel(), observed with dirt effect falling through multiz. reset_internal_layering() else if (!destruction_timer) destruction_timer = ZM_DESTRUCTION_TIMER(src) diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm index 375e4253ec21..24f4d999c0ac 100644 --- a/code/modules/overmap/ships/landable.dm +++ b/code/modules/overmap/ships/landable.dm @@ -231,14 +231,14 @@ if(SHIP_STATUS_LANDED) var/obj/effect/overmap/visitable/location = loc if(istype(loc, /obj/effect/overmap/visitable/sector)) - return "Landed on \the [location.name]. Use secondary thrust to get clear before activating primary engines." + return "Landed on \the [location]. Use secondary thrust to get clear before activating primary engines." if(istype(loc, /obj/effect/overmap/visitable/ship)) - return "Docked with \the [location.name]. Use secondary thrust to get clear before activating primary engines." + return "Docked with \the [location]. Use secondary thrust to get clear before activating primary engines." return "Docked with an unknown object." if(SHIP_STATUS_ENCOUNTER) var/datum/shuttle/autodock/overmap/child_shuttle = SSshuttle.shuttles[shuttle] var/obj/effect/overmap/visitable/location = global.overmap_sectors[num2text(child_shuttle.current_location.z)] - return "Maneuvering nearby \the [location.name]." + return "Maneuvering nearby \the [location]." if(SHIP_STATUS_TRANSIT) return "Maneuvering under secondary thrust." if(SHIP_STATUS_OVERMAP) diff --git a/code/modules/overmap/ships/ship_physics.dm b/code/modules/overmap/ships/ship_physics.dm index 66e85a3f0b97..28e71cf1f5b1 100644 --- a/code/modules/overmap/ships/ship_physics.dm +++ b/code/modules/overmap/ships/ship_physics.dm @@ -36,31 +36,43 @@ /obj/effect/overmap/visitable/ship/proc/recalculate_vessel_mass() var/list/zones = list() + // for(var/turf/tile in area) is an implied in-world loop + // an in-world loop per area is very bad, so instead + // we do one in-world loop and check area + var/list/areas = list() + // create an associative list of area -> TRUE so that lookup is faster for(var/area/A in get_areas()) - - // Do not include space please - if(istype(A, world.area)) + if(istype(A, world.area)) // exclude the base area continue + areas[A] = TRUE + var/start_z = min(map_z) + var/end_z = max(map_z) + if(!start_z || !end_z) + return initial(vessel_mass) // This shouldn't happen ideally so just go with the initial vessel mass + for(var/z_level in start_z to end_z) + var/datum/level_data/z_data = SSmapping.levels_by_z[z_level] + for(var/turf/tile in block(z_data.level_inner_min_x, z_data.level_inner_min_y, z_level, z_data.level_inner_max_x, z_data.level_inner_max_y)) + var/area/tile_area = tile.loc + if(!tile_area || !areas[tile_area]) + continue - for(var/turf/T in A) - - if(!T.simulated || T.is_open()) + if(!tile.simulated || tile.is_open()) continue . += DEFAULT_TURF_MASS - if(istype(T, /turf/wall)) - var/turf/wall/W = T - if(W.material) - . += W.material.weight * 5 - if(W.reinf_material) - . += W.reinf_material.weight * 5 - if(W.girder_material) - . += W.girder_material.weight * 5 + if(istype(tile, /turf/wall)) + var/turf/wall/wall_tile = tile + if(wall_tile.material) + . += wall_tile.material.weight * 5 + if(wall_tile.reinf_material) + . += wall_tile.reinf_material.weight * 5 + if(wall_tile.girder_material) + . += wall_tile.girder_material.weight * 5 - if(T.zone) - zones |= T.zone + if(tile.zone) + zones[tile.zone] = TRUE // assoc list for fast deduplication - for(var/atom/movable/C in T) + for(var/atom/movable/C as anything in tile) // as anything is safe here since only movables can be in turf contents if(!C.simulated) continue . += C.get_mass() @@ -69,8 +81,9 @@ continue . += C2.get_mass() - for(var/zone/Z in zones) - . += Z.air.get_mass() + // loop over keys of all zones in the list + for(var/zone/zone as anything in zones) + . += zone.air.get_mass() // Convert kilograms to metric tonnes. . = . / 1000 \ No newline at end of file diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm index bf75bc608465..b01ab3044261 100644 --- a/code/modules/paperwork/paper_bundle.dm +++ b/code/modules/paperwork/paper_bundle.dm @@ -40,7 +40,7 @@ // merging bundles else if(istype(W, /obj/item/paper_bundle) && merge(W, user, cur_page)) - to_chat(user, SPAN_NOTICE("You add \the [W.name] to \the [name].")) + to_chat(user, SPAN_NOTICE("You add \the [W] to \the [name].")) return TRUE // burning diff --git a/code/modules/persistence/graffiti.dm b/code/modules/persistence/graffiti.dm index e9afbbad8d87..1ed8a188417e 100644 --- a/code/modules/persistence/graffiti.dm +++ b/code/modules/persistence/graffiti.dm @@ -14,10 +14,10 @@ var/author = "unknown" /obj/effect/decal/writing/Initialize(mapload, var/_age, var/_message, var/_author) - var/list/random_icon_states = icon_states(icon) + var/list/random_icon_states = get_states_in_icon(icon) for(var/obj/effect/decal/writing/W in loc) - random_icon_states.Remove(W.icon_state) - if(random_icon_states.len) + random_icon_states -= W.icon_state + if(length(random_icon_states)) icon_state = pick(random_icon_states) SSpersistence.track_value(src, /decl/persistence_handler/graffiti) . = ..(mapload) diff --git a/code/modules/power/geothermal/_geothermal.dm b/code/modules/power/geothermal/_geothermal.dm index 5f76fa6dd0ff..3425a37cd2cd 100644 --- a/code/modules/power/geothermal/_geothermal.dm +++ b/code/modules/power/geothermal/_geothermal.dm @@ -148,6 +148,7 @@ var/global/const/MAX_GEOTHERMAL_PRESSURE = 12000 /obj/machinery/geothermal/Destroy() var/atom/last_loc = loc unset_vent() + connector = null . = ..() if(istype(last_loc)) propagate_refresh_neighbors(last_loc) diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index f163d0f64c76..9230d872fd56 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -39,6 +39,10 @@ pixel_y = rand(-randpixel, randpixel) . = ..() +/obj/item/ammo_casing/Destroy() + QDEL_NULL(BB) + return ..() + //removes the projectile from the ammo casing /obj/item/ammo_casing/proc/expend() . = BB @@ -270,10 +274,9 @@ var/global/list/magazine_icondata_states = list() /proc/magazine_icondata_cache_add(var/obj/item/ammo_magazine/M) var/list/icon_keys = list() var/list/ammo_states = list() - var/list/states = icon_states(M.icon) for(var/i = 0, i <= M.max_ammo, i++) var/ammo_state = "[M.icon_state]-[i]" - if(ammo_state in states) + if(check_state_in_icon(ammo_state, M.icon)) icon_keys += i ammo_states += ammo_state diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 1466e138166a..8534b7ddc7af 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -49,6 +49,12 @@ ammo_magazine = new magazine_type(src) update_icon() +/obj/item/gun/projectile/Destroy() + chambered = null + loaded.Cut() + ammo_magazine = null + return ..() + /obj/item/gun/projectile/consume_next_projectile() if(!is_jammed && prob(jam_chance)) src.visible_message("\The [src] jams!") diff --git a/code/modules/random_map/random_map.dm b/code/modules/random_map/random_map.dm index f6e13113077b..a9331ed90971 100644 --- a/code/modules/random_map/random_map.dm +++ b/code/modules/random_map/random_map.dm @@ -167,7 +167,7 @@ var/global/list/map_count = list() /datum/random_map/proc/apply_to_turf(var/x,var/y) var/current_cell = TRANSLATE_COORD(x,y) - if(!current_cell) + if(!current_cell || current_cell > length(map)) return 0 var/turf/T = locate((origin_x-1)+x,(origin_y-1)+y,origin_z) if(!T || (target_turf_type && !istype(T,target_turf_type))) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 28b4f593c189..67aee6ead08c 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -549,11 +549,11 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new /datum/reagents/proc/trans_to_holder(var/datum/reagents/target, var/amount = 1, var/multiplier = 1, var/copy = 0, var/safety = 0, var/defer_update = FALSE, var/list/skip_reagents, var/transferred_phases = (MAT_PHASE_LIQUID | MAT_PHASE_SOLID)) if(!target || !istype(target)) - return + return 0 amount = max(0, min(amount, total_volume, REAGENTS_FREE_SPACE(target) / multiplier)) if(!amount) - return + return 0 var/part = amount if(skip_reagents) @@ -561,7 +561,7 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new for(var/rtype in skip_reagents) using_volume -= LAZYACCESS(reagent_volumes, rtype) if(using_volume <= 0) - return + return 0 part /= using_volume else var/using_volume = total_volume @@ -872,13 +872,13 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new /datum/reagents/proc/trans_to_turf(var/turf/target, var/amount = 1, var/multiplier = 1, var/copy = 0, var/defer_update = FALSE, var/transferred_phases = (MAT_PHASE_LIQUID | MAT_PHASE_SOLID)) if(!target?.simulated) - return + return 0 // If we're only dumping solids, and there's not enough liquid present on the turf to make a slurry, we dump the solids directly. // This avoids creating an unnecessary reagent holder that won't be immediately deleted. if((!(transferred_phases & MAT_PHASE_LIQUID) || !total_liquid_volume) && (target.reagents?.total_liquid_volume < FLUID_SLURRY)) var/datum/reagents/R = new /datum/reagents(amount, global.temp_reagents_holder) - trans_to_holder(R, amount, multiplier, copy, TRUE, defer_update = defer_update, transferred_phases = MAT_PHASE_SOLID) + . = trans_to_holder(R, amount, multiplier, copy, TRUE, defer_update = defer_update, transferred_phases = MAT_PHASE_SOLID) R.touch_turf(target) target.dump_solid_reagents(R) qdel(R) @@ -887,16 +887,16 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new if(!target.reagents) target.create_reagents(FLUID_MAX_DEPTH) - trans_to_holder(target.reagents, amount, multiplier, copy, defer_update = defer_update, transferred_phases = transferred_phases) + . = trans_to_holder(target.reagents, amount, multiplier, copy, defer_update = defer_update, transferred_phases = transferred_phases) // Deferred updates are presumably being done by SSfluids. // Do an immediate fluid_act call rather than waiting for SSfluids to proc. - if(!defer_update) + if(!defer_update && target.reagents.total_volume >= FLUID_PUDDLE) target.fluid_act(target.reagents) // Objects may or may not have reagents; if they do, it's probably a beaker or something and we need to transfer properly; otherwise, just touch. /datum/reagents/proc/trans_to_obj(var/obj/target, var/amount = 1, var/multiplier = 1, var/copy = 0, var/defer_update = FALSE, var/transferred_phases = (MAT_PHASE_LIQUID | MAT_PHASE_SOLID)) if(!target || !target.simulated) - return + return 0 if(!target.reagents) var/datum/reagents/R = new /datum/reagents(amount * multiplier, global.temp_reagents_holder) diff --git a/code/modules/reagents/chems/chems_pigments.dm b/code/modules/reagents/chems/chems_pigments.dm index d110680a76e6..c5fc41928620 100644 --- a/code/modules/reagents/chems/chems_pigments.dm +++ b/code/modules/reagents/chems/chems_pigments.dm @@ -107,20 +107,20 @@ uid = "chem_pigment_paint" exoplanet_rarity_gas = MAT_RARITY_NOWHERE -/decl/material/liquid/paint/proc/apply_paint(var/atom/painting, var/datum/reagents/holder) - if(istype(painting) && istype(holder)) +/decl/material/liquid/paint/proc/apply_paint(var/atom/painting, var/datum/reagents/holder, var/threshold = 1) + if(istype(painting) && istype(holder) && REAGENT_VOLUME(holder, type) >= threshold) var/keep_alpha = painting.alpha painting.set_color(holder.get_color()) painting.set_alpha(keep_alpha) /decl/material/liquid/paint/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) if(istype(T) && !isspaceturf(T)) - apply_paint(T, holder) + apply_paint(T, holder, FLUID_MINIMUM_TRANSFER) /decl/material/liquid/paint/touch_obj(var/obj/O, var/amount, var/datum/reagents/holder) if(istype(O)) - apply_paint(O, holder) + apply_paint(O, holder, O.get_object_size()) /decl/material/liquid/paint/touch_mob(var/mob/living/M, var/amount, var/datum/reagents/holder) if(istype(M)) - apply_paint(M, holder) \ No newline at end of file + apply_paint(M, holder, M.get_object_size()) diff --git a/code/modules/reagents/reagent_containers/_glass.dm b/code/modules/reagents/reagent_containers/_glass.dm index 90981c545111..50c6024ac0b7 100644 --- a/code/modules/reagents/reagent_containers/_glass.dm +++ b/code/modules/reagents/reagent_containers/_glass.dm @@ -58,9 +58,15 @@ /obj/item/chems/glass/proc/can_lid() return TRUE +/obj/item/chems/glass/proc/should_drink_from(mob/drinker) + . = reagents?.total_volume > 0 + if(.) + var/decl/material/drinking = reagents.get_primary_reagent_decl() + return drinking ? !drinking.is_unsafe_to_drink(drinker) : FALSE + /obj/item/chems/glass/attack_self(mob/user) - . = ..() - if(!. && can_lid()) + + if(can_lid() && user.check_intent(I_FLAG_HELP)) if(ATOM_IS_OPEN_CONTAINER(src)) to_chat(user, SPAN_NOTICE("You put the lid on \the [src].")) atom_flags ^= ATOM_FLAG_OPEN_CONTAINER @@ -68,6 +74,12 @@ to_chat(user, SPAN_NOTICE("You take the lid off \the [src].")) atom_flags |= ATOM_FLAG_OPEN_CONTAINER update_icon() + return TRUE + + if(should_drink_from(user) && is_edible(user) && handle_eaten_by_mob(user, user) != EATEN_INVALID) + return TRUE + + return ..() /obj/item/chems/glass/use_on_mob(mob/living/target, mob/living/user, animate = TRUE) if(get_attack_force(user) && !(item_flags & ITEM_FLAG_NO_BLUDGEON) && user.check_intent(I_FLAG_HARM)) @@ -102,11 +114,6 @@ . = ..() // Drinking out of bowls. -/obj/item/chems/glass/attack_self(mob/user) - if(is_edible(user) && handle_eaten_by_mob(user, user) != EATEN_INVALID) - return TRUE - return ..() - /obj/item/chems/glass/get_food_default_transfer_amount(mob/eater) return eater?.get_eaten_transfer_amount(amount_per_transfer_from_this) @@ -123,33 +130,25 @@ // Should we consider moving this down to /chems for any open container? Medicine from a bottle using a spoon, etc. /obj/item/chems/glass/attackby(obj/item/used_item, mob/living/user) - if(ATOM_IS_OPEN_CONTAINER(src)) - if(istype(used_item, /obj/item/food)) - if(!reagents?.total_volume) - to_chat(user, SPAN_WARNING("\The [src] is empty.")) - return TRUE - var/transferring = min(get_food_default_transfer_amount(user), REAGENTS_FREE_SPACE(used_item.reagents)) - if(!transferring) - to_chat(user, SPAN_WARNING("You cannot dip \the [used_item] in \the [src].")) - return TRUE - reagents.trans_to_holder(used_item.reagents, transferring) - user.visible_message(SPAN_NOTICE("\The [user] dunks \the [used_item] in \the [src].")) + if(!ATOM_IS_OPEN_CONTAINER(src)) + return ..() + + var/obj/item/utensil/utensil = used_item + if(istype(utensil) && (utensil.utensil_flags & UTENSIL_FLAG_SCOOP)) + if(utensil.loaded_food) + to_chat(user, SPAN_WARNING("You already have something on \the [utensil].")) return TRUE - var/obj/item/utensil/utensil = used_item - if(istype(utensil) && (utensil.utensil_flags & UTENSIL_FLAG_SCOOP)) - if(utensil.loaded_food) - to_chat(user, SPAN_WARNING("You already have something on \the [utensil].")) - return TRUE - if(!reagents?.total_volume) - to_chat(user, SPAN_WARNING("\The [src] is empty.")) - return TRUE - seperate_food_chunk(utensil, user) - if(utensil.loaded_food?.reagents?.total_volume) - to_chat(user, SPAN_NOTICE("You scoop up some of \the [utensil.loaded_food.reagents.get_primary_reagent_name()] with \the [utensil].")) + if(!reagents?.total_volume) + to_chat(user, SPAN_WARNING("\The [src] is empty.")) return TRUE + seperate_food_chunk(utensil, user) + if(utensil.loaded_food?.reagents?.total_volume) + to_chat(user, SPAN_NOTICE("You scoop up some of \the [utensil.loaded_food.reagents.get_primary_reagent_name()] with \the [utensil].")) + return TRUE + return ..() -/obj/structure/glass/get_alt_interactions(mob/user) +/obj/item/chems/glass/get_alt_interactions(mob/user) . = ..() if(reagents?.total_volume >= FLUID_PUDDLE) LAZYADD(., /decl/interaction_handler/dip_item) diff --git a/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm b/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm index 8b5d7b48734c..d134b6782f83 100644 --- a/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm +++ b/code/modules/sprite_accessories/metadata/accessory_metadata_gradient.dm @@ -19,8 +19,7 @@ var/list/selectable_labels_to_states = list() /decl/sprite_accessory_metadata/gradient/Initialize() - var/list/selectable = icon_states(icon) - for(var/state in selectable) + for(var/state in get_states_in_icon_cached(icon)) if(!selectable_states_to_labels[state]) selectable_states_to_labels[state] = capitalize(state) for(var/state in selectable_states_to_labels) diff --git a/code/modules/surgery/necrotic.dm b/code/modules/surgery/necrotic.dm index 14b39b1f7395..6f41080ae0d7 100644 --- a/code/modules/surgery/necrotic.dm +++ b/code/modules/surgery/necrotic.dm @@ -133,15 +133,15 @@ var/list/dead_organs if(E.status & ORGAN_DEAD) var/image/radial_button = image(icon = E.icon, icon_state = E.icon_state) - radial_button.name = "Regenerate \the [E.name]" + radial_button.name = "Regenerate \the [E]" LAZYSET(dead_organs, E.organ_tag, radial_button) for(var/obj/item/organ/I in target.get_internal_organs()) if(I && (I.status & ORGAN_DEAD) && I.parent_organ == target_zone) if(!I.can_recover()) - to_chat(user, SPAN_WARNING("\The [I.name] is beyond saving.")) + to_chat(user, SPAN_WARNING("\The [I] is beyond saving.")) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Regenerate \the [I.name]" + radial_button.name = "Regenerate \the [I]" LAZYSET(dead_organs, I.organ_tag, radial_button) if(!LAZYLEN(dead_organs)) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index f6b9bffb28e3..916db17c1d2b 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -104,7 +104,7 @@ for(var/obj/item/organ/I in target.get_internal_organs()) if(I && !(I.status & ORGAN_CUT_AWAY) && I.parent_organ == target_zone) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Detach \the [I.name]" + radial_button.name = "Detach \the [I]" LAZYSET(attached_organs, I.organ_tag, radial_button) if(!LAZYLEN(attached_organs)) to_chat(user, SPAN_WARNING("You can't find any organs to separate.")) @@ -158,7 +158,7 @@ for(var/obj/item/organ/internal/I in affected.implants) if(I.status & ORGAN_CUT_AWAY) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Remove \the [I.name]" + radial_button.name = "Remove \the [I]" LAZYSET(removable_organs, I, radial_button) if(!LAZYLEN(removable_organs)) to_chat(user, SPAN_WARNING("You can't find any removable organs.")) @@ -336,7 +336,7 @@ for(var/obj/item/organ/I in (affected.implants|affected.internal_organs)) if(I.status & ORGAN_CUT_AWAY) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Attach \the [I.name]" + radial_button.name = "Attach \the [I]" LAZYSET(attachable_organs, I, radial_button) if(!LAZYLEN(attachable_organs)) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 44020605deee..8695ab984993 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -416,7 +416,7 @@ for(var/obj/item/organ/I in target.get_internal_organs()) if(I && !(I.status & ORGAN_CUT_AWAY) && !BP_IS_CRYSTAL(I) && I.parent_organ == target_zone) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Detach \the [I.name]" + radial_button.name = "Detach \the [I]" LAZYSET(attached_organs, I.organ_tag, radial_button) if(!LAZYLEN(attached_organs)) to_chat(user, SPAN_WARNING("There are no appropriate internal components to decouple.")) @@ -461,7 +461,7 @@ for(var/obj/item/organ/I in affected.implants) if ((I.status & ORGAN_CUT_AWAY) && BP_IS_PROSTHETIC(I) && !BP_IS_CRYSTAL(I) && (I.parent_organ == target_zone)) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) - radial_button.name = "Reattach \the [I.name]" + radial_button.name = "Reattach \the [I]" LAZYSET(removable_organs, I.organ_tag, radial_button) var/organ_to_replace = show_radial_menu(user, tool, removable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) if(!organ_to_replace) diff --git a/code/modules/weather/weather_fsm_states.dm b/code/modules/weather/weather_fsm_states.dm index 029f500f16ff..08c45c46c3ed 100644 --- a/code/modules/weather/weather_fsm_states.dm +++ b/code/modules/weather/weather_fsm_states.dm @@ -248,7 +248,8 @@ /decl/state/weather/rain/hail/handle_exposure_effects(var/mob/living/M, var/obj/abstract/weather_system/weather) to_chat(M, SPAN_DANGER("You are pelted by a shower of hail!")) - M.take_damage(rand(1, 3)) + if(M.getBruteLoss() < 20) // Put a cap on it to make it annoying but not lethal. + M.take_damage(rand(1, 3)) /decl/state/weather/ash name = "Ash" diff --git a/code/unit_tests/alt_appearances_test.dm b/code/unit_tests/alt_appearances_test.dm index 1bfd0ed325e3..d1c4b22a8e80 100644 --- a/code/unit_tests/alt_appearances_test.dm +++ b/code/unit_tests/alt_appearances_test.dm @@ -20,9 +20,8 @@ for(var/ca_type in decls_repository.get_decl_paths_of_subtype(/decl/cardborg_appearance)) var/decl/cardborg_appearance/ca = ca_type - var/list/existing_icon_states = icon_states(initial(ca.icon)) var/icon_state = initial(ca.icon_state) - if(!(icon_state in existing_icon_states)) + if(!check_state_in_icon(icon_state, initial(ca.icon))) log_unit_test("Icon state [icon_state] is missing.") failed = TRUE if(failed) diff --git a/code/unit_tests/closets.dm b/code/unit_tests/closets.dm index 9bc236f8ff92..10688fd72317 100644 --- a/code/unit_tests/closets.dm +++ b/code/unit_tests/closets.dm @@ -29,18 +29,18 @@ if(!closet.base_icon) LAZYADD(bad_base_icon, "[closet.type]") else - var/list/base_states = icon_states(closet.base_icon) + var/list/base_states = get_states_in_icon_cached(closet.base_icon) for(var/thing in check_base_states) - if(!(thing in base_states)) + if(!base_states[thing]) LAZYADD(bad_base_state, "[closet.type] - [thing] - [closet.base_icon]") if(LAZYLEN(closet.decals) && !closet.decal_icon) LAZYADD(bad_decal_icon, "[closet.type]") else - var/list/decal_states = icon_states(closet.decal_icon) + var/list/decal_states = get_states_in_icon_cached(closet.decal_icon) for(var/thing in closet.decals) if(isnull(closet.decals[thing])) LAZYADD(bad_decal_colour, "[check_appearance] - [thing]") - if(!(thing in decal_states)) + if(!decal_states[thing]) LAZYADD(bad_decal_state, "[check_appearance] - [thing] - [closet.decal_icon]") if( \ diff --git a/code/unit_tests/clothing.dm b/code/unit_tests/clothing.dm index 2b3889695c33..8219e6d04188 100644 --- a/code/unit_tests/clothing.dm +++ b/code/unit_tests/clothing.dm @@ -99,7 +99,7 @@ generated_tokens += "[token][clothes.markings_state_modifier]" // Keep track of which states we've looked for or otherwise evaluated for later state checking. - var/list/check_states = icon_states(clothes.icon) + var/list/check_states = get_states_in_icon(clothes.icon) // Validate against the list of generated tokens. for(var/gen_token in generated_tokens) diff --git a/code/unit_tests/del_the_world.dm b/code/unit_tests/del_the_world.dm index 3a9c575e6991..93591ed1d01e 100644 --- a/code/unit_tests/del_the_world.dm +++ b/code/unit_tests/del_the_world.dm @@ -7,15 +7,6 @@ var/turf/spawn_loc = get_safe_turf() var/list/cached_contents = spawn_loc.contents.Copy() - /// Types to except from GC checking tests. - var/list/gc_exceptions = list( - // I hate doing this, but until the graph tests are fixed by someone who actually understands them, - // this is the best I can do without breaking other stuff. - /datum/node/physical, - // Randomly fails to GC during CI, cause unclear. Remove this if the root cause is identified. - /obj/item/organ/external/chest - ) - var/list/ignore = typesof( // will error if the area already has one /obj/machinery/power/apc, @@ -64,8 +55,9 @@ if(!length(filter_queue)) filter_queue_finished = TRUE break - var/oldest_item = filter_queue[1] - var/qdel_time = filter_queue[oldest_item] + var/list/oldest_packet = filter_queue[1] + //Pull out the time we inserted at + var/qdel_time = oldest_packet[GC_QUEUE_ITEM_GCD_DESTROYED] if(qdel_time > start_time) // Everything is in the check queue now! filter_queue_finished = TRUE break @@ -89,9 +81,9 @@ garbage_queue_processed = TRUE break - var/oldest_packet = check_queue[1] - //Pull out the time we deld at - var/qdeld_at = check_queue[oldest_packet] + var/list/oldest_packet = check_queue[1] + //Pull out the time we inserted at + var/qdeld_at = oldest_packet[GC_QUEUE_ITEM_GCD_DESTROYED] //If we've found a packet that got del'd later then we finished, then all our shit has been processed if(qdeld_at > start_time) garbage_queue_processed = TRUE @@ -109,8 +101,6 @@ //Alright, time to see if anything messed up var/list/cache_for_sonic_speed = SSgarbage.items for(var/path in cache_for_sonic_speed) - if(path in gc_exceptions) - continue var/datum/qdel_item/item = cache_for_sonic_speed[path] if(item.failures) failures += "[item.name] hard deleted [item.failures] times out of a total del count of [item.qdels]" diff --git a/code/unit_tests/graph_tests.dm b/code/unit_tests/graph_tests.dm index 66a939e3a8f5..1d1cd69b6219 100644 --- a/code/unit_tests/graph_tests.dm +++ b/code/unit_tests/graph_tests.dm @@ -480,7 +480,7 @@ /atom/movable/graph_test is_spawnable_type = FALSE var/datum/node/physical/node - var/list/neighoursByDirection = list() + var/list/neighboursByDirection = list() /atom/movable/graph_test/Initialize() . = ..() @@ -497,20 +497,21 @@ /atom/movable/graph_test/proc/Connect(atom/movable/graph_test/neighbour) var/direction = get_dir(src, neighbour) - neighoursByDirection[num2text(direction)] = neighbour - neighbour.neighoursByDirection[num2text(global.flip_dir[direction])] = src + neighboursByDirection[num2text(direction)] = neighbour + neighbour.neighboursByDirection[num2text(global.flip_dir[direction])] = src node.Connect(neighbour.node) /atom/movable/graph_test/CheckNodeNeighbours() // This is a lazy setup for ease of debugging // In a practical setup you'd preferably gather a list of neighbours to be disconnected and pass them in a single Disconnect-call // You'd possibly also verify the dir of this and neighbour nodes, to ensure that they're still facing each other properly - for(var/direction in neighoursByDirection) - var/atom/movable/graph_test/neighbour = neighoursByDirection[direction] + for(var/direction in neighboursByDirection) + var/atom/movable/graph_test/neighbour = neighboursByDirection[direction] var/turf/expected_loc = get_step(src, text2num(direction)) - if(neighbour.loc != expected_loc) + // can't connect in nullspace + if(isnull(neighbour.loc) || neighbour.loc != expected_loc) node.Disconnect(neighbour.node) - neighoursByDirection -= direction + neighboursByDirection -= direction return TRUE /datum/graph/testing diff --git a/code/unit_tests/job_tests.dm b/code/unit_tests/job_tests.dm index b12951657343..858063c9bad5 100644 --- a/code/unit_tests/job_tests.dm +++ b/code/unit_tests/job_tests.dm @@ -42,20 +42,20 @@ var/failed_jobs = 0 var/failed_sanity_checks = 0 - var/job_huds = icon_states(global.using_map.id_hud_icons) + var/list/job_huds = get_states_in_icon_cached(global.using_map.id_hud_icons) - if(!("" in job_huds)) + if(!job_huds[""]) log_bad("Sanity Check - Missing default/unnamed HUD icon") failed_sanity_checks++ - if(!("hudunknown" in job_huds)) + if(!job_huds["hudunknown"]) log_bad("Sanity Check - Missing HUD icon: hudunknown") failed_sanity_checks++ for(var/job_name in SSjobs.titles_to_datums) var/datum/job/J = SSjobs.titles_to_datums[job_name] var/hud_icon_state = J.hud_icon - if(!(hud_icon_state in job_huds)) + if(!job_huds[hud_icon_state]) log_bad("[J.title] - Missing HUD icon: [hud_icon_state]") failed_jobs++ diff --git a/code/unit_tests/mob_tests.dm b/code/unit_tests/mob_tests.dm index 5867dd2ca904..0228ea803411 100644 --- a/code/unit_tests/mob_tests.dm +++ b/code/unit_tests/mob_tests.dm @@ -260,15 +260,15 @@ var/global/default_mobloc = null fail("[icon_file] is not a valid icon file.") return 1 - var/list/valid_states = icon_states(icon_file) + var/list/valid_states = get_states_in_icon_cached(icon_file) - if(!valid_states.len) + if(!length(valid_states)) return 1 for(var/i=1, i<=SSrobots.all_module_names.len, i++) var/modname = lowertext(SSrobots.all_module_names[i]) var/bad_msg = "[ascii_red]--------------- [modname]" - if(!(modname in valid_states)) + if(!valid_states[modname]) log_unit_test("[bad_msg] does not contain a valid icon state in [icon_file][ascii_reset]") failed=1 diff --git a/code/unit_tests/~unit_test_subsystems.dm b/code/unit_tests/~unit_test_subsystems.dm index 23d8aab1d0bb..eea8a75f692e 100644 --- a/code/unit_tests/~unit_test_subsystems.dm +++ b/code/unit_tests/~unit_test_subsystems.dm @@ -31,6 +31,12 @@ SUBSYSTEM_DEF(unit_tests) #endif log_unit_test("Initializing Unit Testing") + // Misc validation. + if(istype(global.using_map)) + global.using_map.validate() + else + log_error("global.using_map is null or invalid!") + // //Start the Round. // diff --git a/html/changelog.html b/html/changelog.html index 357f4dcea1ff..daa00b874bd0 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
+

08 January 2025

+

MistakeNot4892 updated:

+ +

03 January 2025

MistakeNot4892 updated: