From bd10fddb52469789c1a2c2bb3fec65632a3f1642 Mon Sep 17 00:00:00 2001 From: KrissKr0ss <123323882+KrissKr0ss@users.noreply.github.com> Date: Sun, 22 Jan 2023 17:02:45 -0500 Subject: [PATCH 01/12] harddel docs --- .github/CONTRIBUTING.md | 4 + .github/guides/HARDDELETES.md | 289 ++++++++++++++++++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 .github/guides/HARDDELETES.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d25839b92fcb..2d912d710405 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -641,6 +641,10 @@ Isn't that confusing? There is also an undocumented keyword called `static` that has the same behaviour as global but more correctly describes BYOND's behaviour. Therefore, we always use static instead of global where we need it, as it reduces suprise when reading BYOND code. +### Don't create code that hangs references + +This is part of the larger issue of hard deletes, read this file for more info: [Guide to Harddels](.\guides\HARDDELETES.md) + ## Pull Request Process There is no strict process when it comes to merging pull requests. Pull requests will sometimes take a while before they are looked at by a maintainer; the bigger the change, the more time it will take before they are accepted into the code. Every team member is a volunteer who is giving up their own time to help maintain and contribute, so please be courteous and respectful. Here are some helpful ways to make it easier for you and for the maintainers when making a pull request. diff --git a/.github/guides/HARDDELETES.md b/.github/guides/HARDDELETES.md new file mode 100644 index 000000000000..defc09360af6 --- /dev/null +++ b/.github/guides/HARDDELETES.md @@ -0,0 +1,289 @@ +# Hard Deletes + +> Garbage collection is pretty gothic when you think about it. +> +>An object in code is like a ghost, clinging to its former life, and especially to the people it knew. It can only pass on and truly die when it has dealt with its unfinished business. And only when its been forgotten by everyone who ever knew it. If even one other object remembers it, it has a connection to the living world that lets it keep hanging on +> +>There is a kind of sombre tone to fixing GC errors too, its almost shamanistic, making sure all these little objects clear up their final affairs in life before they die, to ensure they don't become ghosts +> +> -- Nanako + +### Table of contents + +1. [What is hard deletion](#What-is-hard-deletion) +2. [Causes of hard deletes](#causes-of-hard-deletes) +3. [Detecting hard deletes](#detecting-hard-deletes) +4. [Techniques for fixing hard deletes](#techniques-for-fixing-hard-deletes) +5. [Help my code is erroring how fix](#help-my-code-is-erroring-how-fix) + + +## What is Hard Deletion + +Hard deletion is a very expensive operation that basically clears all references to some "thing" from memory. Objects that undergo this process are referred to as hard deletes, or simply harddels + +What follows is a discussion of the theory behind this, why we would ever do it, and the what we do to avoid doing it as often as possible + +I'm gonna be using words like references and garbage collection, but don't worry, it's not complex, just a bit hard to pierce + +### Why do we need to Hard Delete? + +Ok so let's say you're some guy called Jerry, and you're writing a programming language + +You want your coders to be able to pass around objects without doing a full copy. So you'll store the pack of data somewhere in memory + +```dm +/someobject + var/id = 42 + var/name = "some shit" +``` + +Then you want them to be able to pass that object into say a proc, without doing a full copy. So you let them pass in the object's location in memory instead +This is called passing something by reference + +```dm +someshit(someobject) //This isn't making a copy of someobject, it's passing in a reference to it +``` + +This of course means they can store that location in memory in another object's vars, or in a list, or whatever + +```dm +/datum + var/reference + +/proc/someshit(mem_location) + var/datum/some_obj = new() + some_obj.reference = mem_location +``` + +But what happens when you get rid of the object we're passing around references to? If we just cleared it out from memory, everything that holds a reference to it would suddenly be pointing to nowhere, or worse, something totally different! + +So then, you've gotta do something to clean up these references when you want to delete an object + +We could hold a list of references to everything that references us, but god, that'd get really expensive wouldn't it + +Why not keep count of how many times we're referenced then? If an object's ref count is ever 0, nothing whatsoever cares about it, so we can freely get rid of it + +But if something's holding onto a reference to us, we're not gonna have any idea where or what it is + +So I guess you should scan all of memory for that reference? + +```dm +del(someobject) //We now need to scan memory until we find the thing holding a ref to us, and clear it +``` + +This pattern is about how BYOND handles this problem of hanging references, or Garbage Collection + +It's not a broken system, but as you can imagine scanning all of memory gets expensive fast + +What can we do to help that? + +### How we can avoid hard deletes + +If hard deletion is so slow, we're gonna need to clean up all our references ourselves + +In our codebase we do this with `/datum/proc/Destroy()`, a proc called by `qdel()`, whose purpose I will explain later + +This procs only job is cleaning up references to the object it's called on. Nothing more, nothing else. Don't let me catch you giving it side effects + +There's a long long list of things this does, since we use it a TON. So I can't really give you a short description. It will always move the object to nullspace though + +## Causes Of Hard Deletes + +Now that you know the theory, let's go over what can actually cause hard deletes. Some of this is obvious, some of it's much less so. + +The BYOND reference has a list [Here](https://secure.byond.com/docs/ref/#/DM/garbage), but it's not a complete one + +* Stored in a var +* An item in a list, or associated with a list item +* Has a tag +* Is on the map (always true for turfs) +* Inside another atom's contents +* Inside an atom's vis_contents +* A temporary value in a still-running proc +* Is a mob with a key +* Is an image object attached to an atom + +Let's briefly go over the more painful ones yeah? + +### Sleeping procs + +Any proc that calls `sleep()`, `spawn()`, or anything that creates a separate "thread" (not technically a thread, but it's the same in these terms. Not gonna cause any race conditions tho) will hang references to any var inside it. This includes the usr it started from, the src it was called on, and any vars created as a part of processing + +### Static vars + +`/static` and `/global` vars count for this too, they'll hang references just as well as anything. Be wary of this, these suckers can be a pain to solve + +### Range() and View() like procs + +Some internal BYOND procs will hold references to objects passed into them for a time after the proc is finished doing work, because they cache the returned info to make some code faster. You should never run into this issue, since we wait for what should be long enough to avoid this issue as a part of garbage collection + +This is what `qdel()` does by the by, it literally just means queue deletion. A reference to the object gets put into a queue, and if it still exists after 5 minutes or so, we hard delete it + +### Walk() procs + +Calling `walk()` on something will put it in an internal queue, which it'll remain in until `walk(thing, 0)` is called on it, which removes it from the queue + +This sort is very cheap to harddel, since BYOND prioritizes checking this queue first when it's clearing refs, but it should be avoided since it causes false positives + +You can read more about how BYOND prioritizes these things [Here](https://www.patreon.com/posts/diving-for-35855766) + +## Detecting Hard Deletes + +For very simple hard deletes, simple inspection should be enough to find them. Look at what the object does during `Initialize()`, and see if it's doing anything it doesn't undo later. +If that fails, search the object's typepath, and look and see if anything is holding a reference to it without regard for the object deleting + +BYOND currently doesn't have the capability to give us information about where a hard delete is. Fortunately we can search for most all of then ourselves. +The procs to perform this search are hidden behind compile time defines, since they'd be way too risky to expose to admin button pressing + +If you're having issues solving a harddel and want to perform this check yourself, go to `_compile_options.dm` and uncomment `TESTING`, `REFERENCE_TRACKING`, and `GC_FAILURE_HARD_LOOKUP` + +You can read more about what each of these do in that file, but the long and short of it is if something would hard delete our code will search for the reference (This will look like your game crashing, just hold out) and print information about anything it finds to the runtime log, which you can find inside the round folder inside `/data/logs/year/month/day` + +It'll tell you what object is holding the ref if it's in an object, or what pattern of list transversal was required to find the ref if it's hiding in a list of some sort + +## Techniques For Fixing Hard Deletes + +Once you've found the issue, it becomes a matter of making sure the ref is cleared as a part of Destroy(). I'm gonna walk you through a few patterns and discuss how you might go about fixing them + +### Our Tools + +First and simplest we have `Destroy()`. Use this to clean up after yourself for simple cases + +```dm +/someobject/Initialize(mapload) + . = ..() + GLOB.somethings += src //We add ourselves to some global list + +/someobject/Destroy() + GLOB.somethings -= src //So when we Destroy() clean yourself from the list + return ..() +``` + +Next, and slightly more complex, pairs of objects that reference each other + +This is helpful when for cases where both objects "own" each other + +```dm +/someobject + var/someotherobject/buddy + +/someotherobject + var/someobject/friend + +/someobject/Initialize(mapload) + if(!buddy) + buddy = new() + buddy.friend = src + +/someotherobject/Initialize(mapload) + if(!friend) + friend = new() + friend.buddy = src + +/someobject/Destroy() + if(buddy) + buddy.friend = null //Make sure to clear their ref to you + buddy = null //We clear our ref to them to make sure nothing goes wrong + +/someotherobject/Destroy() + if(friend) + friend.buddy = null //Make sure to clear their ref to you + friend = null //We clear our ref to them to make sure nothing goes wrong +``` + +Something similar can be accomplished with `QDELETED()`, a define that checks to see if something has started being `Destroy()`'d yet, and `QDEL_NULL()`, a define that `qdel()`'s a var and then sets it to null + +Now let's discuss something a bit more complex, weakrefs + +You'll need a bit of context, so let's do that now + +BYOND has an internal bit of behavior that looks like this + +`var/string = "\ref[someobject]"` + +This essentially gets that object's position in memory directly. Unlike normal references, this doesn't count for hard deletes. You can retrieve the object in question by using `locate()` + +`var/someobject/someobj = locate(string)` + +This has some flaws however, since the bit of memory we're pointing to might change, which would cause issues. Fortunately we've developed a datum to handle worrying about this for you, `/datum/weakref` + +You can create one using the `WEAKREF()` proc, and use weakref.resolve() to retrieve the actual object + +This should be used for things that your object doesn't "own", but still cares about + +For instance, a paper bin would own the paper inside it, but the paper inside it would just hold a weakref to the bin + +There's no need to clean these up, just make sure you account for it being null, since it'll return that if the object doesn't exist or has been queued for deletion + +```dm +/someobject + var/datum/weakref/our_coin + +/someobject/proc/set_coin(/obj/item/coin/new_coin) + our_coin = WEAKREF(new_coin) + +/someobject/proc/get_value() + if(!our_coin) + return 0 + + var/obj/item/coin/potential_coin = our_coin.resolve() + if(!potential_coin) + our_coin = null //Remember to clear the weakref if we get nothing + return 0 + return potential_coin.value +``` + +Now, for the worst case scenario + +Let's say you've got a var that's used too often to be weakref'd without making the code too expensive + +You can't hold a paired reference to it because it's not like it would ever care about you outside of just clearing the ref + +So then, we want to temporarily remember to clear a reference when it's deleted + +This is where I might lose you, but we're gonna use signals + +`qdel()`, the proc that sets off this whole deletion business, sends a signal called `COMSIG_PARENT_QDELETING` + +We can listen for that signal, and if we hear it clear whatever reference we may have + +Here's an example + +```dm +/somemob + var/mob/target + +/somemob/proc/set_target(new_target) + if(target) + UnregisterSignal(target, COMSIG_PARENT_QDELETING) //We need to make sure any old signals are cleared + target = new_target + if(target) + RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(clear_target)) //Call clear_target if target is ever qdel()'d + +/somemob/proc/clear_target(datum/source) + SIGNAL_HANDLER + set_target(null) +``` + +This really should be your last resort, since signals have some limitations. If some subtype of somemob also registered for parent_qdeleting on the same target you'd get a runtime, since signals don't support it + +But if you can't do anything else for reasons of conversion ease, or hot code, this will work + +## Help My Code Is Erroring How Fix + +First, do a quick check. + +Are you doing anything to the object in `Initialize()` that you don't undo in `Destroy()`? I don't mean like, setting its name, but are you adding it to any lists, stuff like that + +If this fails, you're just gonna have to read over this doc. You can skip the theory if you'd like, but it's all pretty important for having an understanding of this problem + +## Misc facts + +> i like rust and all, buuut it removes garbage collecctor, and i pretend garbage collector is a cute girl checking my code +> +> -- Armhulenn + +- The reference tracker, while powerful, is incredibly easy to break
+If it weren't for those unit tests we'd still be missing list["a"] = list(ref) +- Everyone but me sucks, because everyone but me keeps adding new hard deletes +- Garbage collection is a spook, best practice is to use a random reference in place of null, it scares the compiler demons From 147df0e8fc9282b30fa8df11af88132ec0f855b5 Mon Sep 17 00:00:00 2001 From: KrissKr0ss <123323882+KrissKr0ss@users.noreply.github.com> Date: Sun, 22 Jan 2023 17:11:18 -0500 Subject: [PATCH 02/12] unit test this is the easy part, believe it or not --- code/datums/datum.dm | 6 + code/modules/error_handler/error_handler.dm | 2 +- code/modules/unit_tests/_unit_tests.dm | 40 +++- code/modules/unit_tests/anchored_mobs.dm | 4 +- code/modules/unit_tests/bespoke_id.dm | 2 +- code/modules/unit_tests/card_mismatch.dm | 7 +- .../unit_tests/chain_pull_through_space.dm | 25 +-- code/modules/unit_tests/character_saving.dm | 10 +- code/modules/unit_tests/component_tests.dm | 4 +- code/modules/unit_tests/crafting_recipes.dm | 6 +- code/modules/unit_tests/create_and_destroy.dm | 197 ++++++++++++++++++ .../unit_tests/dynamic_ruleset_sanity.dm | 8 +- .../unit_tests/find_reference_sanity.dm | 65 +++++- code/modules/unit_tests/heretic_knowledge.dm | 2 +- code/modules/unit_tests/keybinding_init.dm | 2 +- code/modules/unit_tests/merge_type.dm | 2 +- code/modules/unit_tests/outfit_sanity.dm | 4 +- code/modules/unit_tests/plantgrowth_tests.dm | 6 +- code/modules/unit_tests/projectiles.dm | 2 +- code/modules/unit_tests/reactions.dm | 2 +- code/modules/unit_tests/reagent_id_typos.dm | 2 +- .../unit_tests/reagent_recipe_collisions.dm | 2 +- code/modules/unit_tests/species_whitelists.dm | 2 +- code/modules/unit_tests/subsystem_init.dm | 2 +- code/modules/unit_tests/timer_sanity.dm | 4 +- code/modules/unit_tests/unit_test.dm | 104 ++++++++- code/modules/unit_tests/vore_tests.dm | 24 +-- 27 files changed, 452 insertions(+), 84 deletions(-) create mode 100644 code/modules/unit_tests/create_and_destroy.dm diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 2cc72f7ca91e..6c6030625a47 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -110,6 +110,12 @@ continue qdel(timer) + #ifdef REFERENCE_TRACKING + #ifdef REFERENCE_TRACKING_DEBUG + found_refs = null + #endif + #endif + //BEGIN: ECS SHIT signal_enabled = FALSE diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm index 6a3d2c22333b..4c6c105eb792 100644 --- a/code/modules/error_handler/error_handler.dm +++ b/code/modules/error_handler/error_handler.dm @@ -128,7 +128,7 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0) #ifdef UNIT_TESTS if(GLOB.current_test) //good day, sir - GLOB.current_test.Fail("[main_line]\n[desclines.Join("\n")]") + GLOB.current_test.Fail("[main_line]\n[desclines.Join("\n")]", file = E.file, line = E.line) #endif diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index c7f69fcaf1e9..c1551cc77b11 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -3,9 +3,18 @@ #if defined(UNIT_TESTS) || defined(SPACEMAN_DMM) +/// For advanced cases, fail unconditionally but don't return (so a test can return multiple results) +#define TEST_FAIL(reason) (Fail(reason || "No reason", __FILE__, __LINE__)) + /// Asserts that a condition is true /// If the condition is not true, fails the test -#define TEST_ASSERT(assertion, reason) if (!(assertion)) { return Fail("Assertion failed: [reason || "No reason"]") } +#define TEST_ASSERT(assertion, reason) if (!(assertion)) { return Fail("Assertion failed: [reason || "No reason"]", __FILE__, __LINE__) } + +/// Asserts that a parameter is not null +#define TEST_ASSERT_NOTNULL(a, reason) if (isnull(a)) { return Fail("Expected non-null value: [reason || "No reason"]", __FILE__, __LINE__) } + +/// Asserts that a parameter is null +#define TEST_ASSERT_NULL(a, reason) if (!isnull(a)) { return Fail("Expected null value but received [a]: [reason || "No reason"]", __FILE__, __LINE__) } /// Asserts that the two parameters passed are equal, fails otherwise /// Optionally allows an additional message in the case of a failure @@ -13,7 +22,7 @@ var/lhs = ##a; \ var/rhs = ##b; \ if (lhs != rhs) { \ - return Fail("Expected [isnull(lhs) ? "null" : lhs] to be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]"); \ + return Fail("Expected [isnull(lhs) ? "null" : lhs] to be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]", __FILE__, __LINE__); \ } \ } while (FALSE) @@ -23,7 +32,7 @@ var/lhs = ##a; \ var/rhs = ##b; \ if (lhs == rhs) { \ - return Fail("Expected [isnull(lhs) ? "null" : lhs] to not be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]"); \ + return Fail("Expected [isnull(lhs) ? "null" : lhs] to not be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]", __FILE__, __LINE__); \ } \ } while (FALSE) @@ -37,8 +46,25 @@ #define UNIT_TEST_FAILED 1 #define UNIT_TEST_SKIPPED 2 +#define TEST_PRE 0 #define TEST_DEFAULT 1 -#define TEST_DEL_WORLD INFINITY +/// After most test steps, used for tests that run long so shorter issues can be noticed faster +#define TEST_LONGER 10 +/// This must be the last test to run due to the inherent nature of the test iterating every single tangible atom in the game and qdeleting all of them (while taking long sleeps to make sure the garbage collector fires properly) taking a large amount of time. +#define TEST_CREATE_AND_DESTROY INFINITY + +/// Change color to red on ANSI terminal output, if enabled with -DANSICOLORS. +#ifdef ANSICOLORS +#define TEST_OUTPUT_RED(text) "\x1B\x5B1;31m[text]\x1B\x5B0m" +#else +#define TEST_OUTPUT_RED(text) (text) +#endif +/// Change color to green on ANSI terminal output, if enabled with -DANSICOLORS. +#ifdef ANSICOLORS +#define TEST_OUTPUT_GREEN(text) "\x1B\x5B1;32m[text]\x1B\x5B0m" +#else +#define TEST_OUTPUT_GREEN(text) (text) +#endif /// A trait source when adding traits through unit tests #define TRAIT_SOURCE_UNIT_TESTS "unit_tests" @@ -55,7 +81,7 @@ // #include "connect_loc.dm" // #include "confusion.dm" // #include "crayons.dm" -// #include "create_and_destroy.dm" +#include "create_and_destroy.dm" // #include "designs.dm" #include "dynamic_ruleset_sanity.dm" // #include "egg_glands.dm" @@ -102,12 +128,12 @@ /// CIT TESTS #include "character_saving.dm" -#ifdef REFERENCE_TRACKING //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter +#ifdef REFERENCE_TRACKING_DEBUG //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter #include "find_reference_sanity.dm" #endif #undef TEST_ASSERT #undef TEST_ASSERT_EQUAL #undef TEST_ASSERT_NOTEQUAL -#undef TEST_FOCUS +//#undef TEST_FOCUS - This define is used by vscode unit test extension to pick specific unit tests to run and appended later so needs to be used out of scope here #endif diff --git a/code/modules/unit_tests/anchored_mobs.dm b/code/modules/unit_tests/anchored_mobs.dm index 103b97e7a993..88487ea2b8d7 100644 --- a/code/modules/unit_tests/anchored_mobs.dm +++ b/code/modules/unit_tests/anchored_mobs.dm @@ -4,6 +4,4 @@ var/mob/M = i if(initial(M.anchored)) L += "[i]" - if(!L.len) - return //passed! - Fail("The following mobs are defined as anchored. This is incompatible with the new move force/resist system and needs to be revised.: [L.Join(" ")]") + TEST_ASSERT(!L.len, "The following mobs are defined as anchored. This is incompatible with the new move force/resist system and needs to be revised.: [L.Join(" ")]") diff --git a/code/modules/unit_tests/bespoke_id.dm b/code/modules/unit_tests/bespoke_id.dm index 06676c626c7e..e1356650ded2 100644 --- a/code/modules/unit_tests/bespoke_id.dm +++ b/code/modules/unit_tests/bespoke_id.dm @@ -5,4 +5,4 @@ for(var/i in subtypesof(/datum/element)) var/datum/element/faketype = i if((initial(faketype.element_flags) & ELEMENT_BESPOKE) && initial(faketype.id_arg_index) == base_index) - Fail("A bespoke element was not configured with a proper id_arg_index: [faketype]") + TEST_FAIL("A bespoke element was not configured with a proper id_arg_index: [faketype]") diff --git a/code/modules/unit_tests/card_mismatch.dm b/code/modules/unit_tests/card_mismatch.dm index 506e88f19c3f..90d8250ff0db 100644 --- a/code/modules/unit_tests/card_mismatch.dm +++ b/code/modules/unit_tests/card_mismatch.dm @@ -1,7 +1,6 @@ /datum/unit_test/card_mismatch /datum/unit_test/card_mismatch/Run() - var/message = checkCardpacks(SStrading_card_game.card_packs) - message += checkCardDatums() - if(message) - Fail(message) + var/message = SStrading_card_game.check_cardpacks(SStrading_card_game.card_packs) + message += SStrading_card_game.check_card_datums() + TEST_ASSERT(!message, message) diff --git a/code/modules/unit_tests/chain_pull_through_space.dm b/code/modules/unit_tests/chain_pull_through_space.dm index 10363d5aadf6..b59de2a46703 100644 --- a/code/modules/unit_tests/chain_pull_through_space.dm +++ b/code/modules/unit_tests/chain_pull_through_space.dm @@ -41,22 +41,15 @@ // Walk normally to the left, make sure we're still a chain alice.Move(locate(run_loc_floor_bottom_left.x + 1, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z)) - if (bob.x != run_loc_floor_bottom_left.x + 2) - return Fail("During normal move, Bob was not at the correct x ([bob.x])") - if (charlie.x != run_loc_floor_bottom_left.x + 3) - return Fail("During normal move, Charlie was not at the correct x ([charlie.x])") + TEST_ASSERT_EQUAL(bob.x, run_loc_floor_bottom_left.x + 2, "During normal move, Bob was not at the correct x ([bob.x])") + TEST_ASSERT_EQUAL(charlie.x, run_loc_floor_bottom_left.x + 3, "During normal move, Charlie was not at the correct x ([charlie.x])") // We're going through the space turf now that should teleport us alice.Move(run_loc_floor_bottom_left) - if (alice.z != space_tile.destination_z) - return Fail("Alice did not teleport to the destination z-level. Current location: ([alice.x], [alice.y], [alice.z])") - - if (bob.z != space_tile.destination_z) - return Fail("Bob did not teleport to the destination z-level. Current location: ([bob.x], [bob.y], [bob.z])") - if (!bob.Adjacent(alice)) - return Fail("Bob is not adjacent to Alice. Bob is at [bob.x], Alice is at [alice.x]") - - if (charlie.z != space_tile.destination_z) - return Fail("Charlie did not teleport to the destination z-level. Current location: ([charlie.x], [charlie.y], [charlie.z])") - if (!charlie.Adjacent(bob)) - return Fail("Charlie is not adjacent to Bob. Charlie is at [charlie.x], Bob is at [bob.x]") + TEST_ASSERT_EQUAL(alice.z, space_tile.destination_z, "Alice did not teleport to the destination z-level. Current location: ([alice.x], [alice.y], [alice.z])") + + TEST_ASSERT_EQUAL(bob.z, space_tile.destination_z, "Bob did not teleport to the destination z-level. Current location: ([bob.x], [bob.y], [bob.z])") + TEST_ASSERT(bob.Adjacent(alice), "Bob is not adjacent to Alice. Bob is at [bob.x], Alice is at [alice.x]") + + TEST_ASSERT_EQUAL(charlie.z, space_tile.destination_z, "Charlie did not teleport to the destination z-level. Current location: ([charlie.x], [charlie.y], [charlie.z])") + TEST_ASSERT(charlie.Adjacent(bob), "Charlie is not adjacent to Bob. Charlie is at [charlie.x], Bob is at [bob.x]") diff --git a/code/modules/unit_tests/character_saving.dm b/code/modules/unit_tests/character_saving.dm index cca17b81e4fe..f7ca8b738b05 100644 --- a/code/modules/unit_tests/character_saving.dm +++ b/code/modules/unit_tests/character_saving.dm @@ -12,17 +12,17 @@ P.save_character() P.load_character() if(P.features["flavor_text"] != UNIT_TEST_SAVING_FLAVOR_TEXT) - Fail("Flavor text is failing to save.") + TEST_FAIL("Flavor text is failing to save.") if(P.features["silicon_flavor_text"] != UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT) - Fail("Silicon flavor text is failing to save.") + TEST_FAIL("Silicon flavor text is failing to save.") if(P.features["ooc_notes"] != UNIT_TEST_SAVING_OOC_NOTES) - Fail("OOC text is failing to save.") + TEST_FAIL("OOC text is failing to save.") P.save_character() P.load_character() if((P.features["flavor_text"] != UNIT_TEST_SAVING_FLAVOR_TEXT) || (P.features["silicon_flavor_text"] != UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT) || (P.features["ooc_notes"] != UNIT_TEST_SAVING_OOC_NOTES)) - Fail("Repeated saving and loading possibly causing save deletion.") + TEST_FAIL("Repeated saving and loading possibly causing save deletion.") catch(var/exception/e) - Fail("Failed to save and load character due to exception [e.file]:[e.line], [e.name]") + TEST_FAIL("Failed to save and load character due to exception [e.file]:[e.line], [e.name]") #undef UNIT_TEST_SAVING_FLAVOR_TEXT #undef UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT diff --git a/code/modules/unit_tests/component_tests.dm b/code/modules/unit_tests/component_tests.dm index 0099d7508c5d..f609e73c4b72 100644 --- a/code/modules/unit_tests/component_tests.dm +++ b/code/modules/unit_tests/component_tests.dm @@ -8,5 +8,5 @@ var/dupe_type = initial(comp.dupe_type) if(dupe_type && !ispath(dupe_type)) bad_dts += t - if(length(bad_dms) || length(bad_dts)) - Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])") + TEST_ASSERT(!length(bad_dms) && !length(bad_dts), + "Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])") diff --git a/code/modules/unit_tests/crafting_recipes.dm b/code/modules/unit_tests/crafting_recipes.dm index 33803bd1113e..9b437c801c04 100644 --- a/code/modules/unit_tests/crafting_recipes.dm +++ b/code/modules/unit_tests/crafting_recipes.dm @@ -2,6 +2,6 @@ for(var/i in GLOB.crafting_recipes) var/datum/crafting_recipe/R = i if(!R.subcategory) - Fail("Invalid subcategory on [R] ([R.type]).") - if(!R.category && (R.cateogry != CAT_NONE)) - Fail("Invalid category on [R] ([R.type])") + TEST_FAIL("Invalid subcategory on [R] ([R.type]).") + if(!R.category && (R.category != CAT_NONE)) + TEST_FAIL("Invalid category on [R] ([R.type])") diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm new file mode 100644 index 000000000000..37f1476f5fb1 --- /dev/null +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -0,0 +1,197 @@ +///Delete one of every type, sleep a while, then check to see if anything has gone fucky +/datum/unit_test/create_and_destroy + //You absolutely must run last + priority = TEST_CREATE_AND_DESTROY + +GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) +/datum/unit_test/create_and_destroy/Run() + //We'll spawn everything here + var/turf/spawn_at = run_loc_floor_bottom_left + var/list/ignore = list( + //Never meant to be created, errors out the ass for mobcode reasons + /mob/living/carbon, + //Nother template type, doesn't like being created with no seed + // /obj/item/food/grown, + //And another + /obj/item/slimecross/recurring, + //This should be obvious + /obj/machinery/doomsday_device, + //Yet more templates + // /obj/machinery/restaurant_portal, + //Template type + /obj/effect/mob_spawn, + //Template type + // /obj/structure/holosign/robot_seat, + //Singleton + /mob/dview, + //Template type + /obj/item/bodypart, + //This is meant to fail extremely loud every single time it occurs in any environment in any context, and it falsely alarms when this unit test iterates it. Let's not spawn it in. + // /obj/merge_conflict_marker, + //briefcase launchpads erroring + /obj/machinery/launchpad/briefcase, + ) + //Say it with me now, type template + ignore += typesof(/obj/effect/mapping_helpers) + //This turf existing is an error in and of itself + ignore += typesof(/turf/baseturf_skipover) + ignore += typesof(/turf/baseturf_bottom) + //This demands a borg, so we'll let if off easy + // ignore += typesof(/obj/item/modular_computer/pda/silicon) + //This one demands a computer, ditto + ignore += typesof(/obj/item/modular_computer/processor) + //Very finiky, blacklisting to make things easier + ignore += typesof(/obj/item/poster/wanted) + //This expects a seed, we can't pass it + // ignore += typesof(/obj/item/food/grown) + //Needs clients / mobs to observe it to exist. Also includes hallucinations. + // ignore += typesof(/obj/effect/client_image_holder) + //Same to above. Needs a client / mob / hallucination to observe it to exist. + // ignore += typesof(/obj/projectile/hallucination) + // ignore += typesof(/obj/item/hallucinated) + //Can't pass in a thing to glow + ignore += typesof(/obj/effect/abstract/eye_lighting) + //We don't have a pod + ignore += typesof(/obj/effect/pod_landingzone_effect) + ignore += typesof(/obj/effect/pod_landingzone) + //We have a baseturf limit of 10, adding more than 10 baseturf helpers will kill CI, so here's a future edge case to fix. + ignore += typesof(/obj/effect/baseturf_helper) + //No tauma to pass in + ignore += typesof(/mob/camera/imaginary_friend) + //No pod to gondola + ignore += typesof(/mob/living/simple_animal/pet/gondola/gondolapod) + //No heart to give + // ignore += typesof(/obj/structure/ethereal_crystal) + //No linked console + // ignore += typesof(/mob/camera/ai_eye/remote/base_construction) + //See above + // ignore += typesof(/mob/camera/ai_eye/remote/shuttle_docker) + //Hangs a ref post invoke async, which we don't support. Could put a qdeleted check but it feels hacky + ignore += typesof(/obj/effect/anomaly/grav/high) + //See above + ignore += typesof(/obj/effect/timestop) + //Invoke async in init, skippppp + // ignore += typesof(/mob/living/silicon/robot/model) + //This lad also sleeps + ignore += typesof(/obj/item/hilbertshotel) + //this boi spawns turf changing stuff, and it stacks and causes pain. Let's just not + ignore += typesof(/obj/effect/sliding_puzzle) + //Stacks baseturfs, can't be tested here + ignore += typesof(/obj/effect/temp_visual/lava_warning) + //Stacks baseturfs, can't be tested here + // ignore += typesof(/obj/effect/landmark/ctf) + //Our system doesn't support it without warning spam from unregister calls on things that never registered + ignore += typesof(/obj/docking_port) + //Asks for a shuttle that may not exist, let's leave it alone + ignore += typesof(/obj/item/pinpointer/shuttle) + //This spawns beams as a part of init, which can sleep past an async proc. This hangs a ref, and fucks us. It's only a problem here because the beam sleeps with CHECK_TICK + // ignore += typesof(/obj/structure/alien/resin/flower_bud) + //Needs a linked mecha + ignore += typesof(/obj/effect/skyfall_landingzone) + //Expects a mob to holderize, we have nothing to give + ignore += typesof(/obj/item/clothing/head/mob_holder) + //Needs cards passed into the initilazation args + ignore += typesof(/obj/item/toy/cards/cardhand) + //Needs cards passed into the initilazation args + ignore += typesof(/obj/item/toy/cards/cardhand) + //Needs a holodeck area linked to it which is not guarenteed to exist and technically is supposed to have a 1:1 relationship with computer anyway. + ignore += typesof(/obj/machinery/computer/holodeck) + //runtimes if not paired with a landmark + // ignore += typesof(/obj/structure/industrial_lift) + // Runtimes if the associated machinery does not exist, but not the base type + // ignore += subtypesof(/obj/machinery/airlock_controller) + + var/list/cached_contents = spawn_at.contents.Copy() + var/original_turf_type = spawn_at.type + var/original_baseturfs = islist(spawn_at.baseturfs) ? spawn_at.baseturfs.Copy() : spawn_at.baseturfs + var/original_baseturf_count = length(original_baseturfs) + + GLOB.running_create_and_destroy = TRUE + for(var/type_path in typesof(/atom/movable, /turf) - ignore) //No areas please + if(ispath(type_path, /turf)) + spawn_at.ChangeTurf(type_path) + //We change it back to prevent baseturfs stacking and hitting the limit + spawn_at.ChangeTurf(original_turf_type, original_baseturfs) + if(original_baseturf_count != length(spawn_at.baseturfs)) + TEST_FAIL("[type_path] changed the amount of baseturfs from [original_baseturf_count] to [length(spawn_at.baseturfs)]; [english_list(original_baseturfs)] to [islist(spawn_at.baseturfs) ? english_list(spawn_at.baseturfs) : spawn_at.baseturfs]") + //Warn if it changes again + original_baseturfs = islist(spawn_at.baseturfs) ? spawn_at.baseturfs.Copy() : spawn_at.baseturfs + original_baseturf_count = length(original_baseturfs) + else + var/atom/creation = new type_path(spawn_at) + if(QDELETED(creation)) + continue + //Go all in + qdel(creation, force = TRUE) + //This will hold a ref to the last thing we process unless we set it to null + //Yes byond is fucking sinful + creation = null + + //There's a lot of stuff that either spawns stuff in on create, or removes stuff on destroy. Let's cut it all out so things are easier to deal with + var/list/to_del = spawn_at.contents - cached_contents + if(length(to_del)) + for(var/atom/to_kill in to_del) + qdel(to_kill) + + GLOB.running_create_and_destroy = FALSE + //Hell code, we're bound to have ended the round somehow so let's stop if from ending while we work + SSticker.delay_end = TRUE + //Prevent the garbage subsystem from harddeling anything, if only to save time + SSgarbage.collection_timeout[GC_QUEUE_HARDDELETE] = 10000 HOURS + //Clear it, just in case + cached_contents.Cut() + + //Now that we've qdel'd everything, let's sleep until the gc has processed all the shit we care about + var/time_needed = SSgarbage.collection_timeout[GC_QUEUE_CHECK] + var/start_time = world.time + var/garbage_queue_processed = FALSE + + sleep(time_needed) + while(!garbage_queue_processed) + var/list/queue_to_check = SSgarbage.queues[GC_QUEUE_CHECK] + //How the hell did you manage to empty this? Good job! + if(!length(queue_to_check)) + garbage_queue_processed = TRUE + break + + var/list/oldest_packet = queue_to_check[1] + //Pull out the time we deld at + var/qdeld_at = oldest_packet[1] + //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 + break + + if(world.time > start_time + time_needed + 30 MINUTES) //If this gets us gitbanned I'm going to laugh so hard + TEST_FAIL("Something has gone horribly wrong, the garbage queue has been processing for well over 30 minutes. What the hell did you do") + break + + //Immediately fire the gc right after + SSgarbage.next_fire = 1 + //Unless you've seriously fucked up, queue processing shouldn't take "that" long. Let her run for a bit, see if anything's changed + sleep(20 SECONDS) + + //Alright, time to see if anything messed up + var/list/cache_for_sonic_speed = SSgarbage.items + for(var/path in cache_for_sonic_speed) + var/datum/qdel_item/item = cache_for_sonic_speed[path] + if(item.failures) + TEST_FAIL("[item.name] hard deleted [item.failures] times out of a total del count of [item.qdels]") + if(item.no_respect_force) + TEST_FAIL("[item.name] failed to respect force deletion [item.no_respect_force] times out of a total del count of [item.qdels]") + if(item.no_hint) + TEST_FAIL("[item.name] failed to return a qdel hint [item.no_hint] times out of a total del count of [item.qdels]") + + cache_for_sonic_speed = SSatoms.BadInitializeCalls + for(var/path in cache_for_sonic_speed) + var/fails = cache_for_sonic_speed[path] + if(fails & BAD_INIT_NO_HINT) + TEST_FAIL("[path] didn't return an Initialize hint") + if(fails & BAD_INIT_QDEL_BEFORE) + TEST_FAIL("[path] qdel'd in New()") + if(fails & BAD_INIT_SLEPT) + TEST_FAIL("[path] slept during Initialize()") + + SSticker.delay_end = FALSE + //This shouldn't be needed, but let's be polite + SSgarbage.collection_timeout[GC_QUEUE_HARDDELETE] = 10 SECONDS diff --git a/code/modules/unit_tests/dynamic_ruleset_sanity.dm b/code/modules/unit_tests/dynamic_ruleset_sanity.dm index 837e0b235cca..8ec500e1a82a 100644 --- a/code/modules/unit_tests/dynamic_ruleset_sanity.dm +++ b/code/modules/unit_tests/dynamic_ruleset_sanity.dm @@ -9,9 +9,9 @@ var/is_lone = initial(ruleset.flags) & (LONE_RULESET | HIGH_IMPACT_RULESET) if (has_scaling_cost && is_lone) - Fail("[ruleset] has a scaling_cost, but is also a lone/highlander ruleset.") + TEST_FAIL("[ruleset] has a scaling_cost, but is also a lone/highlander ruleset.") else if (!has_scaling_cost && !is_lone) - Fail("[ruleset] has no scaling cost, but is also not a lone/highlander ruleset.") + TEST_FAIL("[ruleset] has no scaling cost, but is also not a lone/highlander ruleset.") /// Verifies that dynamic rulesets have unique antag_flag. /datum/unit_test/dynamic_unique_antag_flags @@ -26,11 +26,11 @@ var/antag_flag = initial(ruleset.antag_flag) if (isnull(antag_flag)) - Fail("[ruleset] has a null antag_flag!") + TEST_FAIL("[ruleset] has a null antag_flag!") continue if (antag_flag in known_antag_flags) - Fail("[ruleset] has a non-unique antag_flag [antag_flag] (used by [known_antag_flags[antag_flag]])!") + TEST_FAIL("[ruleset] has a non-unique antag_flag [antag_flag] (used by [known_antag_flags[antag_flag]])!") continue known_antag_flags[antag_flag] = ruleset diff --git a/code/modules/unit_tests/find_reference_sanity.dm b/code/modules/unit_tests/find_reference_sanity.dm index f41714f0659d..1dcabc7bacb2 100644 --- a/code/modules/unit_tests/find_reference_sanity.dm +++ b/code/modules/unit_tests/find_reference_sanity.dm @@ -2,12 +2,14 @@ /datum/unit_test/find_reference_sanity /atom/movable/ref_holder + var/static/atom/movable/ref_test/static_test var/atom/movable/ref_test/test var/list/test_list = list() var/list/test_assoc_list = list() /atom/movable/ref_holder/Destroy() test = null + static_test = null test_list.Cut() test_assoc_list.Cut() return ..() @@ -25,6 +27,12 @@ SSgarbage.should_save_refs = TRUE //Sanity check + /* + #if DM_VERSION >= 515 + var/refcount = refcount(victim) + TEST_ASSERT_EQUAL(refcount, 3, "Should be: test references: 0 + baseline references: 3 (victim var,loc,allocated list)") + #endif + */ victim.DoSearchVar(testbed, "Sanity Check", search_time = 1) //We increment search time to get around an optimization TEST_ASSERT(!victim.found_refs.len, "The ref-tracking tool found a ref where none existed") SSgarbage.should_save_refs = FALSE @@ -39,6 +47,12 @@ testbed.test_list += victim testbed.test_assoc_list["baseline"] = victim + /* + #if DM_VERSION >= 515 + var/refcount = refcount(victim) + TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)") + #endif + */ victim.DoSearchVar(testbed, "First Run", search_time = 2) TEST_ASSERT(victim.found_refs["test"], "The ref-tracking tool failed to find a regular value") @@ -56,6 +70,12 @@ testbed.vis_contents += victim testbed.test_assoc_list[victim] = TRUE + /* + #if DM_VERSION >= 515 + var/refcount = refcount(victim) + TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)") + #endif + */ victim.DoSearchVar(testbed, "Second Run", search_time = 3) //This is another sanity check @@ -76,6 +96,12 @@ var/list/to_find_assoc = list(victim) testbed.test_assoc_list["Nesting"] = to_find_assoc + /* + #if DM_VERSION >= 515 + var/refcount = refcount(victim) + TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)") + #endif + */ victim.DoSearchVar(victim, "Third Run Self", search_time = 4) victim.DoSearchVar(testbed, "Third Run Testbed", search_time = 4) TEST_ASSERT(victim.found_refs["self_ref"], "The ref-tracking tool failed to find a self reference") @@ -90,7 +116,12 @@ //Calm before the storm testbed.test_assoc_list = list(null = victim) - + /* + #if DM_VERSION >= 515 + var/refcount = refcount(victim) + TEST_ASSERT_EQUAL(refcount, 4, "Should be: test references: 1 + baseline references: 3 (victim var,loc,allocated list)") + #endif + */ victim.DoSearchVar(testbed, "Fourth Run", search_time = 5) TEST_ASSERT(testbed.test_assoc_list, "The ref-tracking tool failed to find a null key'd assoc list entry") @@ -105,7 +136,39 @@ var/list/to_find_null_assoc_nested = list(victim) testbed.test_assoc_list[null] = to_find_null_assoc_nested + /* + #if DM_VERSION >= 515 + var/refcount = refcount(victim) + TEST_ASSERT_EQUAL(refcount, 5, "Should be: test references: 2 + baseline references: 3 (victim var,loc,allocated list)") + #endif + */ victim.DoSearchVar(testbed, "Fifth Run", search_time = 6) TEST_ASSERT(victim.found_refs[to_find_in_key], "The ref-tracking tool failed to find a nested assoc list key") TEST_ASSERT(victim.found_refs[to_find_null_assoc_nested], "The ref-tracking tool failed to find a null key'd nested assoc list entry") SSgarbage.should_save_refs = FALSE + +/datum/unit_test/find_reference_static_inPvestigation/Run() + var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test) + var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder) + pass(testbed) + SSgarbage.should_save_refs = TRUE + + //Lets check static vars now, since those can be a real headache + testbed.static_test = victim + + //Yes we do actually need to do this. The searcher refuses to read weird lists + //And global.vars is a really weird list + var/global_vars = list() + for(var/key in global.vars) + global_vars[key] = global.vars[key] + + /* + #if DM_VERSION >= 515 + var/refcount = refcount(victim) + TEST_ASSERT_EQUAL(refcount, 5, "Should be: test references: 2 + baseline references: 3 (victim var,loc,allocated list)") + #endif + */ + victim.DoSearchVar(global_vars, "Sixth Run", search_time = 7) + + TEST_ASSERT(victim.found_refs[global_vars], "The ref-tracking tool failed to find a natively global variable") + SSgarbage.should_save_refs = FALSE diff --git a/code/modules/unit_tests/heretic_knowledge.dm b/code/modules/unit_tests/heretic_knowledge.dm index a433bce1ec99..484cc90245c0 100644 --- a/code/modules/unit_tests/heretic_knowledge.dm +++ b/code/modules/unit_tests/heretic_knowledge.dm @@ -18,4 +18,4 @@ var/list/unreachables = all_possible_knowledge - list_to_check for(var/X in unreachables) var/datum/eldritch_knowledge/eldritch_knowledge = X - Fail("[initial(eldritch_knowledge.name)] is unreachable by players! Add it to the blacklist in /code/modules/unit_tests/heretic_knowledge.dm if it is purposeful!") + TEST_FAIL("[initial(eldritch_knowledge.name)] is unreachable by players! Add it to the blacklist in /code/modules/unit_tests/heretic_knowledge.dm if it is purposeful!") diff --git a/code/modules/unit_tests/keybinding_init.dm b/code/modules/unit_tests/keybinding_init.dm index 16141bc55380..c9d17f688afd 100644 --- a/code/modules/unit_tests/keybinding_init.dm +++ b/code/modules/unit_tests/keybinding_init.dm @@ -3,4 +3,4 @@ var/datum/keybinding/KB = i if(initial(KB.keybind_signal) || !initial(KB.name)) continue - Fail("[initial(KB.name)] does not have a keybind signal defined.") + TEST_FAIL("[KB.name] does not have a keybind signal defined.") diff --git a/code/modules/unit_tests/merge_type.dm b/code/modules/unit_tests/merge_type.dm index 1aed82e6a3e2..a89df7b492f5 100644 --- a/code/modules/unit_tests/merge_type.dm +++ b/code/modules/unit_tests/merge_type.dm @@ -12,4 +12,4 @@ for(var/stackpath in paths) var/obj/item/stack/stack = new stackpath if(!stack.merge_type) - Fail("([stack]) lacks set merge_type variable!") + TEST_FAIL("([stack]) lacks set merge_type variable!") diff --git a/code/modules/unit_tests/outfit_sanity.dm b/code/modules/unit_tests/outfit_sanity.dm index 57ce22434e11..f6a5d1d79ae4 100644 --- a/code/modules/unit_tests/outfit_sanity.dm +++ b/code/modules/unit_tests/outfit_sanity.dm @@ -2,7 +2,7 @@ H.equip_to_slot_or_del(new outfit.##outfit_key(H), ##slot_name, TRUE); \ /* We don't check the result of equip_to_slot_or_del because it returns false for random jumpsuits, as they delete themselves on init */ \ if (!H.get_item_by_slot(##slot_name)) { \ - Fail("[outfit.name]'s [#outfit_key] is invalid!"); \ + TEST_FAIL("[outfit.name]'s [#outfit_key] is invalid!"); \ } \ } @@ -45,6 +45,6 @@ var/number = backpack_contents[path] || 1 for (var/_ in 1 to number) if (!H.equip_to_slot_or_del(new path(H), ITEM_SLOT_BACKPACK, TRUE)) - Fail("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.") + TEST_FAIL("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.") #undef CHECK_OUTFIT_SLOT diff --git a/code/modules/unit_tests/plantgrowth_tests.dm b/code/modules/unit_tests/plantgrowth_tests.dm index 6b40236860ef..b1b213f03489 100644 --- a/code/modules/unit_tests/plantgrowth_tests.dm +++ b/code/modules/unit_tests/plantgrowth_tests.dm @@ -17,11 +17,11 @@ for(var/i in 1 to seed.growthstages) if("[seed.icon_grow][i]" in states) continue - Fail("[seed.name] ([seed.type]) lacks the [seed.icon_grow][i] icon!") + TEST_FAIL("[seed.name] ([seed.type]) lacks the [seed.icon_grow][i] icon!") if(!(seed.icon_dead in states)) - Fail("[seed.name] ([seed.type]) lacks the [seed.icon_dead] icon!") + TEST_FAIL("[seed.name] ([seed.type]) lacks the [seed.icon_dead] icon!") if(seed.icon_harvest) // mushrooms have no grown sprites, same for items with no product if(!(seed.icon_harvest in states)) - Fail("[seed.name] ([seed.type]) lacks the [seed.icon_harvest] icon!") + TEST_FAIL("[seed.name] ([seed.type]) lacks the [seed.icon_harvest] icon!") diff --git a/code/modules/unit_tests/projectiles.dm b/code/modules/unit_tests/projectiles.dm index f1a2391c0701..ddc7979d3df7 100644 --- a/code/modules/unit_tests/projectiles.dm +++ b/code/modules/unit_tests/projectiles.dm @@ -2,4 +2,4 @@ for(var/path in typesof(/obj/item/projectile)) var/obj/item/projectile/projectile = path if(initial(projectile.movement_type) & PHASING) - Fail("[path] has default movement type PHASING. Piercing projectiles should be done using the projectile piercing system, not movement_types!") + TEST_FAIL("[path] has default movement type PHASING. Piercing projectiles should be done using the projectile piercing system, not movement_types!") diff --git a/code/modules/unit_tests/reactions.dm b/code/modules/unit_tests/reactions.dm index c2b62f6fdcd1..596e9eca8d75 100644 --- a/code/modules/unit_tests/reactions.dm +++ b/code/modules/unit_tests/reactions.dm @@ -4,4 +4,4 @@ var/test_info = G.test() if(!test_info["success"]) var/message = test_info["message"] - Fail("Gas reaction [G.name] is failing its unit test with the following message: [message]") + TEST_FAIL("Gas reaction [G.name] is failing its unit test with the following message: [message]") diff --git a/code/modules/unit_tests/reagent_id_typos.dm b/code/modules/unit_tests/reagent_id_typos.dm index d6548852fa52..f85834999962 100644 --- a/code/modules/unit_tests/reagent_id_typos.dm +++ b/code/modules/unit_tests/reagent_id_typos.dm @@ -11,4 +11,4 @@ var/datum/chemical_reaction/R = V for(var/id in (R.required_reagents + R.required_catalysts)) if(!GLOB.chemical_reagents_list[id]) - Fail("Unknown chemical id \"[id]\" in recipe [R.type]") + TEST_FAIL("Unknown chemical id \"[id]\" in recipe [R.type]") diff --git a/code/modules/unit_tests/reagent_recipe_collisions.dm b/code/modules/unit_tests/reagent_recipe_collisions.dm index 20e875422f29..b75a17a7e73c 100644 --- a/code/modules/unit_tests/reagent_recipe_collisions.dm +++ b/code/modules/unit_tests/reagent_recipe_collisions.dm @@ -12,4 +12,4 @@ var/datum/chemical_reaction/r1 = reactions[i] var/datum/chemical_reaction/r2 = reactions[i2] if(chem_recipes_do_conflict(r1, r2)) - Fail("Chemical recipe conflict between [r1.type] and [r2.type]") + TEST_FAIL("Chemical recipe conflict between [r1.type] and [r2.type]") diff --git a/code/modules/unit_tests/species_whitelists.dm b/code/modules/unit_tests/species_whitelists.dm index 145f3a259fc2..ec05d0cf9f8f 100644 --- a/code/modules/unit_tests/species_whitelists.dm +++ b/code/modules/unit_tests/species_whitelists.dm @@ -2,4 +2,4 @@ for(var/typepath in subtypesof(/datum/species)) var/datum/species/S = typepath if(initial(S.changesource_flags) == NONE) - Fail("A species type was detected with no changesource flags: [S]") + TEST_FAIL("A species type was detected with no changesource flags: [S]") diff --git a/code/modules/unit_tests/subsystem_init.dm b/code/modules/unit_tests/subsystem_init.dm index 7d5473bc1bb7..c377302ba6a1 100644 --- a/code/modules/unit_tests/subsystem_init.dm +++ b/code/modules/unit_tests/subsystem_init.dm @@ -4,4 +4,4 @@ if(ss.flags & SS_NO_INIT) continue if(!ss.initialized) - Fail("[ss]([ss.type]) is a subsystem meant to initialize but doesn't get set as initialized.") + TEST_FAIL("[ss]([ss.type]) is a subsystem meant to initialize but doesn't get set as initialized.") diff --git a/code/modules/unit_tests/timer_sanity.dm b/code/modules/unit_tests/timer_sanity.dm index d92323a5253f..dbdf3f6d8e8d 100644 --- a/code/modules/unit_tests/timer_sanity.dm +++ b/code/modules/unit_tests/timer_sanity.dm @@ -1,3 +1,3 @@ /datum/unit_test/timer_sanity/Run() - if(SStimer.bucket_count < 0) - Fail("SStimer is going into negative bucket count from something") + TEST_ASSERT(SStimer.bucket_count >= 0, + "SStimer is going into negative bucket count from something") diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index aee62b7a52a4..4fc289a22063 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -3,7 +3,7 @@ Usage: Override /Run() to run your test code -Call Fail() to fail the test (You should specify a reason) +Call TEST_FAIL() to fail the test (You should specify a reason) You may use /New() and /Destroy() for setup/teardown respectively @@ -15,6 +15,18 @@ GLOBAL_DATUM(current_test, /datum/unit_test) GLOBAL_VAR_INIT(failed_any_test, FALSE) GLOBAL_VAR(test_log) +/// A list of every test that is currently focused. +/// Use the PERFORM_ALL_TESTS macro instead. +GLOBAL_VAR_INIT(focused_tests, focused_tests()) + +/proc/focused_tests() + var/list/focused_tests = list() + for (var/datum/unit_test/unit_test as anything in subtypesof(/datum/unit_test)) + if (initial(unit_test.focus)) + focused_tests += unit_test + + return focused_tests.len > 0 ? focused_tests : null + /datum/unit_test //Bit of metadata for the future maybe var/list/procs_tested @@ -62,15 +74,15 @@ GLOBAL_VAR(test_log) return ..() /datum/unit_test/proc/Run() - Fail("Run() called parent or not implemented") + TEST_FAIL("Run() called parent or not implemented") -/datum/unit_test/proc/Fail(reason = "No reason") +/datum/unit_test/proc/Fail(reason = "No reason", file = "OUTDATED_TEST", line = 1) succeeded = FALSE if(!istext(reason)) reason = "FORMATTED: [reason != null ? reason : "NULL"]" - LAZYADD(fail_reasons, reason) + LAZYADD(fail_reasons, list(list(reason, file, line))) /// Allocates an instance of the provided type, and places it somewhere in an available loc /// Instances allocated through this proc will be destroyed when the test is over @@ -80,16 +92,71 @@ GLOBAL_VAR(test_log) arguments = list(run_loc_floor_bottom_left) else if (arguments[1] == null) arguments[1] = run_loc_floor_bottom_left - var/instance = new type(arglist(arguments)) + var/instance + // Byond will throw an index out of bounds if arguments is empty in that arglist call. Sigh + if(length(arguments)) + instance = new type(arglist(arguments)) + else + instance = new type() allocated += instance return instance +/* +/datum/unit_test/proc/test_screenshot(name, icon/icon) + if (!istype(icon)) + TEST_FAIL("[icon] is not an icon.") + return + + var/path_prefix = replacetext(replacetext("[type]", "/datum/unit_test/", ""), "/", "_") + name = replacetext(name, "/", "_") + + var/filename = "code/modules/unit_tests/screenshots/[path_prefix]_[name].png" + + if (fexists(filename)) + var/data_filename = "data/screenshots/[path_prefix]_[name].png" + fcopy(icon, data_filename) + log_test("\t[path_prefix]_[name] was found, putting in data/screenshots") + else if (fexists("code")) + // We are probably running in a local build + fcopy(icon, filename) + TEST_FAIL("Screenshot for [name] did not exist. One has been created.") + else + // We are probably running in real CI, so just pretend it worked and move on + fcopy(icon, "data/screenshots_new/[path_prefix]_[name].png") + + log_test("\t[path_prefix]_[name] was put in data/screenshots_new") + +/// Helper for screenshot tests to take an image of an atom from all directions and insert it into one icon +/datum/unit_test/proc/get_flat_icon_for_all_directions(atom/thing, no_anim = TRUE) + var/icon/output = icon('icons/effects/effects.dmi', "nothing") + + for (var/direction in GLOB.cardinals) + var/icon/partial = getFlatIcon(thing, defdir = direction, no_anim = no_anim) + output.Insert(partial, dir = direction) + + return output +*/ +/// Logs a test message. Will use GitHub action syntax found at https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions +/datum/unit_test/proc/log_for_test(text, priority, file, line) + var/map_name = SSmapping.config.map_name + + // Need to escape the text to properly support newlines. + var/annotation_text = replacetext(text, "%", "%25") + annotation_text = replacetext(annotation_text, "\n", "%0A") + + log_world("::[priority] file=[file],line=[line],title=[map_name]: [type]::[annotation_text]") + /proc/RunUnitTest(test_path, list/test_results) +/* + if (ispath(test_path, /datum/unit_test/focus_only)) + return +*/ var/datum/unit_test/test = new test_path GLOB.current_test = test var/duration = REALTIMEOFDAY + log_world("::group::[test_path]") test.Run() duration = REALTIMEOFDAY - duration @@ -99,11 +166,28 @@ GLOBAL_VAR(test_log) var/list/log_entry = list("[test.succeeded ? "PASS" : "FAIL"]: [test_path] [duration / 10]s") var/list/fail_reasons = test.fail_reasons - for(var/J in 1 to LAZYLEN(fail_reasons)) - log_entry += "\tREASON #[J]: [fail_reasons[J]]" + for(var/reasonID in 1 to LAZYLEN(fail_reasons)) + var/text = fail_reasons[reasonID][1] + var/file = fail_reasons[reasonID][2] + var/line = fail_reasons[reasonID][3] + + test.log_for_test(text, "error", file, line) + + // Normal log message + log_entry += "\tREASON #[reasonID]: [text] at [file]:[line]" + var/message = log_entry.Join("\n") log_test(message) + var/test_output_desc = "[test_path] [duration / 10]s" + if (test.succeeded) + log_world("[TEST_OUTPUT_GREEN("PASS")] [test_output_desc]") + + log_world("::endgroup::") + + if (!test.succeeded) + log_world("::error::[TEST_OUTPUT_RED("FAIL")] [test_output_desc]") + test_results[test_path] = list("status" = test.succeeded ? UNIT_TEST_PASSED : UNIT_TEST_FAILED, "message" = message, "name" = test_path) qdel(test) @@ -112,11 +196,13 @@ GLOBAL_VAR(test_log) CHECK_TICK var/list/tests_to_run = subtypesof(/datum/unit_test) + var/list/focused_tests = list() for (var/_test_to_run in tests_to_run) var/datum/unit_test/test_to_run = _test_to_run if (initial(test_to_run.focus)) - tests_to_run = list(test_to_run) - break + focused_tests += test_to_run + if(length(focused_tests)) + tests_to_run = focused_tests tests_to_run = sortTim(tests_to_run, /proc/cmp_unit_test_priority) diff --git a/code/modules/unit_tests/vore_tests.dm b/code/modules/unit_tests/vore_tests.dm index 6549aa9ce7fe..08a525c5d59e 100644 --- a/code/modules/unit_tests/vore_tests.dm +++ b/code/modules/unit_tests/vore_tests.dm @@ -11,7 +11,7 @@ break mobloc = default_mobloc if(!mobloc) - Fail("Unable to find a location to create test mob") + TEST_FAIL("Unable to find a location to create test mob") return FALSE var/mob/living/carbon/human/H = new mobtype(mobloc) @@ -44,7 +44,7 @@ endOxyloss = H.getOxyLoss() if(!startOxyloss < endOxyloss) - Fail("Human mob is not taking oxygen damage in space. (Before: [startOxyloss]; after: [endOxyloss])") + TEST_FAIL("Human mob is not taking oxygen damage in space. (Before: [startOxyloss]; after: [endOxyloss])") qdel(H) return 1 @@ -74,7 +74,7 @@ // Now that pred belly exists, we can eat the prey. if(!pred.vore_selected) - Fail("[pred] has no vore_selected.") + TEST_FAIL("[pred] has no vore_selected.") return TRUE // Attempt to eat the prey @@ -82,7 +82,7 @@ pred.vore_selected.nom_mob(prey) if(prey.loc != pred.vore_selected) - Fail("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]") + TEST_FAIL("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]") return TRUE // Okay, we succeeded in eating them, now lets wait a bit @@ -96,7 +96,7 @@ // Alright lets check it! endOxyloss = prey.getOxyLoss() if(startOxyloss < endOxyloss) - Fail("Prey takes oxygen damage in a pred's belly! (Before: [startOxyloss]; after: [endOxyloss])") + TEST_FAIL("Prey takes oxygen damage in a pred's belly! (Before: [startOxyloss]; after: [endOxyloss])") qdel(prey) qdel(pred) return TRUE @@ -128,7 +128,7 @@ // Now that pred belly exists, we can eat the prey. if(!pred.vore_selected) - Fail("[pred] has no vore_selected.") + TEST_FAIL("[pred] has no vore_selected.") return TRUE // Attempt to eat the prey @@ -136,12 +136,12 @@ pred.vore_selected.nom_mob(prey) if(prey.loc != pred.vore_selected) - Fail("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]") + TEST_FAIL("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]") return TRUE else var/turf/T = locate(/turf/open/space) if(!T) - Fail("could not find a space turf for testing") + TEST_FAIL("could not find a space turf for testing") return TRUE else pred.forceMove(T) @@ -159,7 +159,7 @@ endOxyloss = prey.getOxyLoss() endBruteloss = prey.getBruteLoss() if(startBruteloss < endBruteloss) - Fail("Prey takes brute damage in space! (Before: [startBruteloss]; after: [endBruteloss])") + TEST_FAIL("Prey takes brute damage in space! (Before: [startBruteloss]; after: [endBruteloss])") qdel(prey) qdel(pred) return TRUE @@ -189,7 +189,7 @@ // Now that pred belly exists, we can eat the prey. if(!pred.vore_selected) - Fail("[pred] has no vore_selected.") + TEST_FAIL("[pred] has no vore_selected.") return TRUE // Attempt to eat the prey @@ -197,7 +197,7 @@ pred.vore_selected.nom_mob(prey) if(prey.loc != pred.vore_selected) - Fail("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]") + TEST_FAIL("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]") return TRUE // Okay, we succeeded in eating them, now lets wait a bit @@ -212,7 +212,7 @@ // Alright lets check it! endBruteBurn = prey.getBruteLoss() + prey.getFireLoss() if(startBruteBurn >= endBruteBurn) - Fail("Prey doesn't take damage in digesting belly! (Before: [startBruteBurn]; after: [endBruteBurn])") + TEST_FAIL("Prey doesn't take damage in digesting belly! (Before: [startBruteBurn]; after: [endBruteBurn])") qdel(prey) qdel(pred) return TRUE From f205d223a4f597f7ecdd7d6468fe56286103214c Mon Sep 17 00:00:00 2001 From: KrissKr0ss <123323882+KrissKr0ss@users.noreply.github.com> Date: Sun, 22 Jan 2023 18:17:42 -0500 Subject: [PATCH 03/12] oop --- code/modules/unit_tests/create_and_destroy.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index 37f1476f5fb1..1b6d5935b043 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -87,7 +87,7 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) //This spawns beams as a part of init, which can sleep past an async proc. This hangs a ref, and fucks us. It's only a problem here because the beam sleeps with CHECK_TICK // ignore += typesof(/obj/structure/alien/resin/flower_bud) //Needs a linked mecha - ignore += typesof(/obj/effect/skyfall_landingzone) + // ignore += typesof(/obj/effect/skyfall_landingzone) //Expects a mob to holderize, we have nothing to give ignore += typesof(/obj/item/clothing/head/mob_holder) //Needs cards passed into the initilazation args From 686bcd9ebb2018e5dbb03b62b4879f65207604c6 Mon Sep 17 00:00:00 2001 From: Tsurupeta <41485301+Tsurupeta@users.noreply.github.com> Date: Sat, 5 Aug 2023 20:41:26 +0300 Subject: [PATCH 04/12] modular cit wtf?? --- modular_citadel/code/modules/clothing/neck.dm | 2 +- .../code/modules/eventmaps/Spookystation/JTGSZwork.dm | 2 +- modular_citadel/code/modules/vectorcrafts/vectorcraft.dm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modular_citadel/code/modules/clothing/neck.dm b/modular_citadel/code/modules/clothing/neck.dm index 9507e65e0e53..9e1870307d3f 100644 --- a/modular_citadel/code/modules/clothing/neck.dm +++ b/modular_citadel/code/modules/clothing/neck.dm @@ -19,5 +19,5 @@ /obj/item/clothing/neck/undertale/Initialize(mapload) - ..() + . = ..() AddComponent(/datum/component/souldeath/neck) diff --git a/modular_citadel/code/modules/eventmaps/Spookystation/JTGSZwork.dm b/modular_citadel/code/modules/eventmaps/Spookystation/JTGSZwork.dm index a2ccc319d054..c816bf5af136 100644 --- a/modular_citadel/code/modules/eventmaps/Spookystation/JTGSZwork.dm +++ b/modular_citadel/code/modules/eventmaps/Spookystation/JTGSZwork.dm @@ -1031,7 +1031,7 @@ GLOBAL_LIST_EMPTY(rain_sounds) var/open = FALSE /obj/item/umbrella/Initialize(mapload) - ..() + . = ..() color = RANDOM_COLOUR update_icon() diff --git a/modular_citadel/code/modules/vectorcrafts/vectorcraft.dm b/modular_citadel/code/modules/vectorcrafts/vectorcraft.dm index 399a1c5f061a..3c983ea40e50 100644 --- a/modular_citadel/code/modules/vectorcrafts/vectorcraft.dm +++ b/modular_citadel/code/modules/vectorcrafts/vectorcraft.dm @@ -36,7 +36,7 @@ var/mob/living/carbon/human/driver /obj/vehicle/sealed/vectorcraft/Initialize(mapload) - ..() + . = ..() i_m_acell = max_acceleration i_m_decell = max_deceleration i_boost = boost_power From 38e1cd47ac1fdabc50dcfe0a19fa3920d237c74c Mon Sep 17 00:00:00 2001 From: Tsurupeta <41485301+Tsurupeta@users.noreply.github.com> Date: Sat, 5 Aug 2023 20:43:23 +0300 Subject: [PATCH 05/12] reftracking --- code/__HELPERS/_logging.dm | 11 +++++++++-- code/_compile_options.dm | 8 ++++++++ code/_globalvars/logging.dm | 5 +++++ code/game/world.dm | 8 ++++++++ .../admin/view_variables/reference_tracking.dm | 13 ++++++++++++- 5 files changed, 42 insertions(+), 3 deletions(-) diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 58b7516be355..92cc6da23735 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -42,9 +42,16 @@ SEND_TEXT(world.log, text) #endif -#ifdef REFERENCE_TRACKING_LOG +#if defined(REFERENCE_DOING_IT_LIVE) +#define log_reftracker(msg) log_harddel("## REF SEARCH [msg]") + +/proc/log_harddel(text) + WRITE_LOG(GLOB.harddel_log, text) + +#elif defined(REFERENCE_TRACKING) // Doing it locally #define log_reftracker(msg) log_world("## REF SEARCH [msg]") -#else + +#else //Not tracking at all #define log_reftracker(msg) #endif diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 1aca8959c25e..e8083c1064f4 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -43,6 +43,14 @@ // #define TRACK_MAX_SHARE //Allows max share tracking, for use in the atmos debugging ui #endif //ifdef TESTING +//#define REFERENCE_DOING_IT_LIVE +#ifdef REFERENCE_DOING_IT_LIVE +// compile the backend +#define REFERENCE_TRACKING +// actually look for refs +#define GC_FAILURE_HARD_LOOKUP +#endif // REFERENCE_DOING_IT_LIVE + //#define UNIT_TESTS //If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between #ifndef PRELOAD_RSC //set to: diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index e9c38546a73f..aa70b7094aa6 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -87,6 +87,11 @@ GLOBAL_PROTECT(picture_logging_id) GLOBAL_VAR(picture_logging_prefix) GLOBAL_PROTECT(picture_logging_prefix) ///// +#ifdef REFERENCE_DOING_IT_LIVE +GLOBAL_LIST_EMPTY(harddel_log) +GLOBAL_PROTECT(harddel_log) +#endif + //// cit logging GLOBAL_VAR(subsystem_log) diff --git a/code/game/world.dm b/code/game/world.dm index a9eda444f08e..11fd25956263 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -26,6 +26,10 @@ GLOBAL_LIST(topic_status_cache) make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once) + #ifdef REFERENCE_DOING_IT_LIVE + GLOB.harddel_log = GLOB.world_game_log + #endif + GLOB.revdata = new InitTgs() @@ -140,6 +144,10 @@ GLOBAL_LIST(topic_status_cache) #ifdef UNIT_TESTS GLOB.test_log = "[GLOB.log_directory]/tests.log" start_log(GLOB.test_log) +#endif +#ifdef REFERENCE_DOING_IT_LIVE + GLOB.harddel_log = "[GLOB.log_directory]/harddels.log" + start_log(GLOB.harddel_log) #endif start_log(GLOB.world_game_log) start_log(GLOB.world_attack_log) diff --git a/code/modules/admin/view_variables/reference_tracking.dm b/code/modules/admin/view_variables/reference_tracking.dm index 714b54cd45f0..5953c8cf279f 100644 --- a/code/modules/admin/view_variables/reference_tracking.dm +++ b/code/modules/admin/view_variables/reference_tracking.dm @@ -134,6 +134,15 @@ GLOBAL_LIST_EMPTY(deletion_failures) DoSearchVar(GLOB, "GLOB") //globals log_reftracker("Finished searching globals") + //Yes we do actually need to do this. The searcher refuses to read weird lists + //And global.vars is a really weird list + var/global_vars = list() + for(var/key in global.vars) + global_vars[key] = global.vars[key] + + DoSearchVar(global_vars, "Native Global", search_time = starting_time) + log_reftracker("Finished searching native globals") + for(var/datum/thing in world) //atoms (don't beleive its lies) DoSearchVar(thing, "World -> [thing.type]", search_time = starting_time) log_reftracker("Finished searching atoms") @@ -143,9 +152,11 @@ GLOBAL_LIST_EMPTY(deletion_failures) log_reftracker("Finished searching datums") //Warning, attempting to search clients like this will cause crashes if done on live. Watch yourself +#ifndef REFERENCE_DOING_IT_LIVE for(var/client/thing) //clients DoSearchVar(thing, "Clients -> [thing.type]", search_time = starting_time) log_reftracker("Finished searching clients") +#endif log_reftracker("Completed search for references to a [type].") @@ -159,7 +170,7 @@ GLOBAL_LIST_EMPTY(deletion_failures) /datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64, search_time = world.time) #ifdef REFERENCE_TRACKING_DEBUG - if(!found_refs && SSgarbage.should_save_refs) + if(SSgarbage.should_save_refs && !found_refs) found_refs = list() #endif From 6adae2787da5cb8662f34bd11cd16944fa4d2fdf Mon Sep 17 00:00:00 2001 From: Tsurupeta <41485301+Tsurupeta@users.noreply.github.com> Date: Tue, 8 Aug 2023 13:57:28 +0300 Subject: [PATCH 06/12] QDEL_IN improvements --- code/__HELPERS/qdel.dm | 6 +++++- code/datums/weakrefs.dm | 3 ++- code/game/gamemodes/meteor/meteors.dm | 2 +- .../effects/temporary_visuals/temporary_visual.dm | 2 +- .../clockcult/clock_effects/spatial_gateway.dm | 10 +++++----- .../clockcult/clock_structures/taunting_trail.dm | 2 +- .../simple_animal/hostile/mining_mobs/curse_blob.dm | 2 +- .../simple_animal/hostile/mining_mobs/goliath.dm | 2 +- 8 files changed, 17 insertions(+), 12 deletions(-) diff --git a/code/__HELPERS/qdel.dm b/code/__HELPERS/qdel.dm index 0d2bf8915293..af7e7b99f0a9 100644 --- a/code/__HELPERS/qdel.dm +++ b/code/__HELPERS/qdel.dm @@ -1,4 +1,8 @@ -#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE) +// This is a bit hacky, we do it to avoid people relying on a return value for the macro +// If you need that you should use QDEL_IN_STOPPABLE instead +#define QDEL_IN(item, time) ; \ + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), (time) > GC_FILTER_QUEUE ? WEAKREF(item) : item), time); +#define QDEL_IN_STOPPABLE(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), (time) > GC_FILTER_QUEUE ? WEAKREF(item) : item), time, TIMER_STOPPABLE) #define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) #define QDEL_NULL(item) qdel(item); item = null #define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } diff --git a/code/datums/weakrefs.dm b/code/datums/weakrefs.dm index 31e0c3501b78..c243f35f3432 100644 --- a/code/datums/weakrefs.dm +++ b/code/datums/weakrefs.dm @@ -17,9 +17,10 @@ reference = REF(thing) /datum/weakref/Destroy(force) + var/datum/target = resolve() + qdel(target) if(!force) return QDEL_HINT_LETMELIVE //Let BYOND autoGC thiswhen nothing is using it anymore. - var/datum/target = resolve() target?.weak_reference = null return ..() diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 446573e2afc4..8f97e232ae68 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -131,7 +131,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event GLOB.meteor_list += src SSaugury.register_doom(src, threat) SpinAnimation() - timerid = QDEL_IN(src, lifetime) + timerid = QDEL_IN_STOPPABLE(src, lifetime) chase_target(target) /obj/effect/meteor/Bump(atom/A) diff --git a/code/game/objects/effects/temporary_visuals/temporary_visual.dm b/code/game/objects/effects/temporary_visuals/temporary_visual.dm index bf4e82f7b7f0..29696f5ad73b 100644 --- a/code/game/objects/effects/temporary_visuals/temporary_visual.dm +++ b/code/game/objects/effects/temporary_visuals/temporary_visual.dm @@ -13,7 +13,7 @@ if(randomdir) setDir(pick(GLOB.cardinals)) - timerid = QDEL_IN(src, duration) + timerid = QDEL_IN_STOPPABLE(src, duration) /obj/effect/temp_visual/Destroy() . = ..() diff --git a/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm b/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm index 79ad69b76f68..e5d5de2f0893 100644 --- a/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm +++ b/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm @@ -35,7 +35,7 @@ clockwork_desc = "A gateway in reality. It can only [sender ? "send" : "receive"] objects." if(is_stable) return - timerid = QDEL_IN(src, lifetime) //We only need this if the gateway is not stable + timerid = QDEL_IN_STOPPABLE(src, lifetime) //We only need this if the gateway is not stable //set up a gateway with another gateway /obj/effect/clockwork/spatial_gateway/proc/setup_gateway(obj/effect/clockwork/spatial_gateway/gatewayB, set_duration, set_uses, two_way) @@ -108,12 +108,12 @@ visible_message("[src] is disrupted!") animate(src, alpha = 0, transform = matrix()*2, time = 10, flags = ANIMATION_END_NOW) deltimer(timerid) - timerid = QDEL_IN(src, 10) + timerid = QDEL_IN_STOPPABLE(src, 10) linked_gateway.uses = 0 linked_gateway.visible_message("[linked_gateway] is disrupted!") animate(linked_gateway, alpha = 0, transform = matrix()*2, time = 10, flags = ANIMATION_END_NOW) deltimer(linked_gateway.timerid) - linked_gateway.timerid = QDEL_IN(linked_gateway, 10) + linked_gateway.timerid = QDEL_IN_STOPPABLE(linked_gateway, 10) return TRUE return FALSE @@ -279,8 +279,8 @@ /obj/effect/clockwork/spatial_gateway/stable/proc/start_shutdown() deltimer(timerid) deltimer(linked_gateway.timerid) - timerid = QDEL_IN(src, 20) - linked_gateway.timerid = QDEL_IN(linked_gateway, 20) + timerid = QDEL_IN_STOPPABLE(src, 20) + linked_gateway.timerid = QDEL_IN_STOPPABLE(linked_gateway, 20) animate(src, alpha = 0, transform = matrix()*2, time = 20, flags = ANIMATION_END_NOW) animate(linked_gateway, alpha = 0, transform = matrix()*2, time = 20, flags = ANIMATION_END_NOW) src.visible_message("[src] begins to destabilise!") diff --git a/code/modules/antagonists/clockcult/clock_structures/taunting_trail.dm b/code/modules/antagonists/clockcult/clock_structures/taunting_trail.dm index 5305758b255b..853acdbe1999 100644 --- a/code/modules/antagonists/clockcult/clock_structures/taunting_trail.dm +++ b/code/modules/antagonists/clockcult/clock_structures/taunting_trail.dm @@ -16,7 +16,7 @@ /obj/structure/destructible/clockwork/taunting_trail/Initialize(mapload) . = ..() - timerid = QDEL_IN(src, 15) + timerid = QDEL_IN_STOPPABLE(src, 15) var/obj/structure/destructible/clockwork/taunting_trail/Tt = locate(/obj/structure/destructible/clockwork/taunting_trail) in loc if(Tt && Tt != src) if(!step(src, pick(GLOB.alldirs))) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm index 04b003b315be..06b10a34c29e 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm @@ -30,7 +30,7 @@ /mob/living/simple_animal/hostile/asteroid/curseblob/Initialize(mapload) . = ..() - timerid = QDEL_IN(src, 600) + timerid = QDEL_IN_STOPPABLE(src, 600) playsound(src, 'sound/effects/curse1.ogg', 100, 1, -1) /mob/living/simple_animal/hostile/asteroid/curseblob/Destroy() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm index 115681a7d4c5..f33bd6a3b782 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm @@ -211,4 +211,4 @@ /obj/effect/temp_visual/goliath_tentacle/proc/retract() icon_state = "Goliath_tentacle_retract" deltimer(timerid) - timerid = QDEL_IN(src, 7) + timerid = QDEL_IN_STOPPABLE(src, 7) From 2f7ac14b2ff655aac066840efb19c15ba738365a Mon Sep 17 00:00:00 2001 From: Tsurupeta <41485301+Tsurupeta@users.noreply.github.com> Date: Sat, 5 Aug 2023 20:46:13 +0300 Subject: [PATCH 07/12] all else --- code/__DEFINES/_flags/_flags.dm | 42 ++++++++--------- code/__DEFINES/cooldowns.dm | 2 +- code/__DEFINES/qdel.dm | 2 +- code/__DEFINES/subsystems.dm | 3 ++ code/__HELPERS/do_after.dm | 9 ++-- code/_compile_options.dm | 1 + code/_onclick/hud/credits.dm | 11 +++-- code/_onclick/hud/radial.dm | 15 +++++- code/_onclick/hud/robot.dm | 12 +++++ code/controllers/subsystem/atoms.dm | 3 ++ code/controllers/subsystem/jukeboxes.dm | 2 + .../subsystem/persistence/_persistence.dm | 4 +- code/datums/action.dm | 1 + code/datums/brain_damage/imaginary_friend.dm | 4 +- code/datums/components/mood.dm | 1 + code/datums/components/pellet_cloud.dm | 2 +- code/datums/components/squeak.dm | 2 +- code/datums/components/tackle.dm | 2 +- code/datums/dash_weapon.dm | 4 ++ code/datums/elements/dwarfism.dm | 5 +- code/datums/elements/spellcasting.dm | 1 + code/datums/explosion.dm | 14 +++--- code/datums/mind.dm | 28 +++++++++-- code/datums/mood_events/mood_event.dm | 1 + code/datums/mutations/antenna.dm | 2 +- code/game/atoms.dm | 2 +- code/game/machinery/_machinery.dm | 3 ++ code/game/machinery/cryopod.dm | 3 +- code/game/machinery/launch_pad.dm | 12 +++-- code/game/machinery/navbeacon.dm | 33 +++++++++---- code/game/machinery/suit_storage_unit.dm | 1 + .../machinery/telecomms/telecomunications.dm | 2 +- code/game/objects/effects/anomalies.dm | 2 +- code/game/objects/effects/decals/crayon.dm | 2 +- code/game/objects/effects/landmarks.dm | 4 +- code/game/objects/items.dm | 1 + code/game/objects/items/cards_ids.dm | 1 + code/game/objects/items/chrono_eraser.dm | 9 +++- code/game/objects/items/crab17.dm | 20 ++++---- .../objects/items/devices/chameleonproj.dm | 4 +- .../items/devices/forcefieldprojector.dm | 8 ++-- .../objects/items/devices/radio/headset.dm | 4 -- .../objects/items/devices/transfer_valve.dm | 17 +++++++ .../objects/items/grenades/clusterbuster.dm | 3 +- code/game/objects/items/grenades/plastic.dm | 2 +- code/game/objects/items/melee/energy.dm | 4 ++ code/game/objects/items/plushes.dm | 2 +- code/game/objects/items/religion.dm | 10 +--- code/game/objects/items/storage/_storage.dm | 2 +- code/game/objects/items/storage/boxes.dm | 6 ++- code/game/objects/items/storage/fancy.dm | 2 + code/game/objects/items/summon.dm | 8 +++- code/game/objects/items/tanks/watertank.dm | 12 +++++ code/game/objects/items/toys.dm | 5 +- code/game/objects/items/weaponry.dm | 4 +- code/game/objects/structures/bedsheet_bin.dm | 4 +- code/game/objects/structures/displaycase.dm | 5 +- .../objects/structures/ghost_role_spawners.dm | 4 +- code/game/objects/structures/guncase.dm | 2 +- code/game/objects/structures/manned_turret.dm | 2 +- code/game/objects/structures/morgue.dm | 3 +- code/game/objects/structures/traps.dm | 23 +++++++--- code/game/shuttle_engines.dm | 2 +- code/game/turfs/simulated/lava.dm | 2 +- code/game/turfs/simulated/openspace.dm | 2 + code/game/turfs/space/transit.dm | 8 ++-- .../blob/blob/blobstrains/synchronous_mesh.dm | 1 + .../modules/antagonists/blob/blob/overmind.dm | 30 ++++++------ .../ark_of_the_clockwork_justicar.dm | 38 +++++++-------- code/modules/antagonists/cult/blood_magic.dm | 9 ++-- code/modules/antagonists/cult/cult_items.dm | 7 ++- code/modules/antagonists/devil/imp/imp.dm | 2 +- .../devil/true_devil/_true_devil.dm | 2 +- .../eldritch_cult/eldritch_items.dm | 4 ++ code/modules/antagonists/revenant/revenant.dm | 2 +- .../antagonists/slaughter/slaughter.dm | 2 +- code/modules/antagonists/swarmer/swarmer.dm | 4 +- code/modules/assembly/flash.dm | 4 ++ code/modules/awaymissions/capture_the_flag.dm | 9 +++- .../awaymissions/mission_code/Cabin.dm | 2 +- code/modules/buildmode/effects/line.dm | 3 ++ code/modules/cargo/gondolapod.dm | 5 +- code/modules/cargo/supplypod.dm | 6 +++ code/modules/clothing/gloves/mittens.dm | 2 +- code/modules/clothing/masks/gasmask.dm | 2 +- code/modules/clothing/spacesuits/hardsuit.dm | 14 +++++- code/modules/clothing/suits/toggles.dm | 4 +- code/modules/clothing/under/color.dm | 6 +-- code/modules/detectivework/detective_work.dm | 2 +- code/modules/events/travelling_trader.dm | 8 ++-- code/modules/events/wizard/greentext.dm | 37 ++++++++------- code/modules/fields/infinite_void.dm | 3 +- code/modules/flufftext/Hallucination.dm | 3 ++ .../food_and_drinks/food/snacks/meat.dm | 4 +- code/modules/holiday/halloween/halloween.dm | 2 +- code/modules/instruments/songs/_song.dm | 2 + .../integrated_electronics/subtypes/input.dm | 13 +++--- .../integrated_electronics/subtypes/smart.dm | 4 +- code/modules/library/random_books.dm | 2 +- .../mining/equipment/kinetic_crusher.dm | 5 ++ code/modules/mining/fulton.dm | 2 +- code/modules/mining/lavaland/ash_tree.dm | 2 +- .../mining/lavaland/necropolis_chests.dm | 12 +++++ code/modules/mob/dead/new_player/login.dm | 2 +- .../modules/mob/dead/new_player/new_player.dm | 2 +- code/modules/mob/living/blood.dm | 2 + code/modules/mob/living/brain/brain.dm | 3 +- code/modules/mob/living/brain/brain_item.dm | 2 + .../living/carbon/alien/humanoid/humanoid.dm | 10 +++- .../mob/living/carbon/alien/humanoid/queen.dm | 10 +++- .../modules/mob/living/carbon/alien/organs.dm | 3 ++ .../carbon/human/species_types/jellypeople.dm | 24 ++++++++++ .../mob/living/carbon/monkey/monkey.dm | 9 ++-- code/modules/mob/living/living.dm | 1 + code/modules/mob/living/silicon/ai/ai.dm | 3 +- code/modules/mob/living/silicon/pai/pai.dm | 10 ++-- .../modules/mob/living/silicon/robot/robot.dm | 11 +++-- .../mob/living/simple_animal/bot/ed209bot.dm | 28 +++++------ .../mob/living/simple_animal/bot/firebot.dm | 5 +- .../mob/living/simple_animal/bot/mulebot.dm | 4 +- .../simple_animal/friendly/farm_animals.dm | 2 +- .../simple_animal/guardian/types/support.dm | 2 +- .../simple_animal/hostile/giant_spider.dm | 4 -- .../hostile/megafauna/blood_drunk_miner.dm | 4 ++ .../hostile/megafauna/colossus.dm | 9 ++-- .../hostile/mining_mobs/curse_blob.dm | 3 +- .../hostile/mining_mobs/elites/herald.dm | 2 +- .../hostile/mining_mobs/gutlunch.dm | 5 -- .../living/simple_animal/hostile/wizard.dm | 6 +++ code/modules/mob/mob.dm | 3 ++ .../computers/item/computer.dm | 1 - .../file_system/programs/signaler.dm | 5 ++ .../modular_computers/hardware/ai_slot.dm | 6 ++- .../hardware/battery_module.dm | 4 +- .../modular_computers/hardware/card_slot.dm | 2 +- code/modules/ninja/suit/suit.dm | 10 ++-- code/modules/paperwork/contract.dm | 2 + code/modules/pool/pool_controller.dm | 1 + code/modules/pool/pool_drain.dm | 10 ++-- code/modules/power/apc.dm | 17 +++---- code/modules/power/reactor/rbmk.dm | 10 ++++ .../power/singularity/containment_field.dm | 22 +++++---- code/modules/power/singularity/emitter.dm | 1 + code/modules/power/tesla/coil.dm | 6 +++ .../projectiles/ammunition/energy/portal.dm | 4 +- code/modules/projectiles/gun.dm | 4 +- .../projectiles/guns/ballistic/pistol.dm | 4 +- code/modules/projectiles/guns/energy.dm | 2 + .../projectiles/guns/energy/laser_gatling.dm | 9 ++++ .../projectiles/guns/energy/special.dm | 4 +- code/modules/projectiles/guns/magic.dm | 3 +- .../projectiles/guns/magic/motivation.dm | 4 ++ .../projectiles/guns/misc/beam_rifle.dm | 4 +- .../projectiles/projectile/special/curse.dm | 13 ++++-- .../projectiles/projectile/special/gravity.dm | 2 +- .../projectile/special/hallucination.dm | 2 +- .../projectile/special/wormhole.dm | 8 ++-- code/modules/reagents/chemistry/holder.dm | 2 +- .../reagents/reagent_containers/borghypo.dm | 1 + .../reagents/reagent_containers/pill.dm | 2 +- code/modules/research/techweb/_techweb.dm | 1 - .../modules/ruins/lavalandruin_code/puzzle.dm | 3 +- code/modules/smithing/anvil.dm | 2 +- code/modules/smithing/finished_items.dm | 9 ++-- code/modules/smithing/furnace.dm | 4 +- code/modules/spells/spell.dm | 12 ++--- code/modules/spells/spell_types/lichdom.dm | 3 ++ code/modules/spells/spell_types/shapeshift.dm | 3 +- .../spells/spell_types/touch_attacks.dm | 8 ++++ code/modules/surgery/bodyparts/_bodyparts.dm | 4 ++ code/modules/surgery/organs/augments_arms.dm | 5 ++ code/modules/surgery/organs/organ_internal.dm | 2 +- code/modules/tcg/cards.dm | 9 ++-- code/modules/unit_tests/create_and_destroy.dm | 46 ++++++++++++++++--- code/modules/vehicles/atv.dm | 42 +++++++++-------- code/modules/vehicles/mecha/_mecha.dm | 2 +- .../modules/vehicles/mecha/mech_fabricator.dm | 5 ++ code/modules/vending/cola.dm | 2 +- code/modules/vending/snack.dm | 2 +- 179 files changed, 805 insertions(+), 370 deletions(-) diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm index cd38c9ad5be3..cbba0d9eeba3 100644 --- a/code/__DEFINES/_flags/_flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -26,46 +26,46 @@ GLOBAL_LIST_INIT(bitflags, list( //FLAGS BITMASK ///This flag is what recursive_hear_check() uses to determine wether to add an item to the hearer list or not. -#define HEAR_1 (1<<3) +#define HEAR_1 (1<<0) ///Projectiles will use default chance-based ricochet handling on things with this. -#define DEFAULT_RICOCHET_1 (1<<4) +#define DEFAULT_RICOCHET_1 (1<<1) ///Conducts electricity (metal etc.). -#define CONDUCT_1 (1<<5) +#define CONDUCT_1 (1<<2) ///For machines and structures that should not break into parts, eg, holodeck stuff. -#define NODECONSTRUCT_1 (1<<7) +#define NODECONSTRUCT_1 (1<<3) ///Atom queued to SSoverlay. -#define OVERLAY_QUEUED_1 (1<<8) +#define OVERLAY_QUEUED_1 (1<<4) ///Item has priority to check when entering or leaving. -#define ON_BORDER_1 (1<<9) +#define ON_BORDER_1 (1<<5) ///Whether or not this atom shows screentips when hovered over -#define NO_SCREENTIPS_1 (1<<10) +#define NO_SCREENTIPS_1 (1<<6) ///Prevent clicking things below it on the same turf eg. doors/ fulltile windows. -#define PREVENT_CLICK_UNDER_1 (1<<11) -#define HOLOGRAM_1 (1<<12) +#define PREVENT_CLICK_UNDER_1 (1<<7) +#define HOLOGRAM_1 (1<<8) ///Prevents mobs from getting chainshocked by teslas and the supermatter. -#define SHOCKED_1 (1<<13) +#define SHOCKED_1 (1<<9) ///Whether /atom/Initialize() has already run for the object. -#define INITIALIZED_1 (1<<14) +#define INITIALIZED_1 (1<<10) ///was this spawned by an admin? used for stat tracking stuff. -#define ADMIN_SPAWNED_1 (1<<15) +#define ADMIN_SPAWNED_1 (1<<11) /// should not get harmed if this gets caught by an explosion? -#define PREVENT_CONTENTS_EXPLOSION_1 (1<<16) +#define PREVENT_CONTENTS_EXPLOSION_1 (1<<12) /// Early returns mob.face_atom() -#define BLOCK_FACE_ATOM_1 (1<<17) +#define BLOCK_FACE_ATOM_1 (1<<13) //turf-only flags -#define NOJAUNT_1 (1<<0) -#define UNUSED_RESERVATION_TURF_1 (1<<1) +#define NOJAUNT_1 (1<<14) +#define UNUSED_RESERVATION_TURF_1 (1<<15) /// If a turf can be made dirty at roundstart. This is also used in areas. -#define CAN_BE_DIRTY_1 (1<<2) +#define CAN_BE_DIRTY_1 (1<<16) /// Blocks lava rivers being generated on the turf -#define NO_LAVA_GEN_1 (1<<6) +#define NO_LAVA_GEN_1 (1<<17) /// Blocks ruins spawning on the turf -#define NO_RUINS_1 (1<<10) +#define NO_RUINS_1 (1<<18) /// Should this tile be cleaned up and reinserted into an excited group? -#define EXCITED_CLEANUP_1 (1 << 13) +#define EXCITED_CLEANUP_1 (1 << 19) /// Whether or not this atom has contextual screentips when hovered OVER -#define HAS_CONTEXTUAL_SCREENTIPS_1 (1 << 14) +#define HAS_CONTEXTUAL_SCREENTIPS_1 (1 << 20) ////////////////Area flags\\\\\\\\\\\\\\ /// If it's a valid territory for cult summoning or the CRAB-17 phone to spawn diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm index 39240ed7e52e..c5ad0d745d12 100644 --- a/code/__DEFINES/cooldowns.dm +++ b/code/__DEFINES/cooldowns.dm @@ -78,7 +78,7 @@ #define COOLDOWN_DECLARE(cd_index) var/##cd_index = 0 -#define COOLDOWN_START(cd_source, cd_index, cd_time) (cd_source.cd_index = world.time + cd_time) +#define COOLDOWN_START(cd_source, cd_index, cd_time) (cd_source.cd_index = world.time + (cd_time)) //Returns true if the cooldown has run its course, false otherwise #define COOLDOWN_FINISHED(cd_source, cd_index) (cd_source.cd_index < world.time) diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm index 32e0025ab2a2..7a94df025e5c 100644 --- a/code/__DEFINES/qdel.dm +++ b/code/__DEFINES/qdel.dm @@ -40,6 +40,6 @@ #define GC_DEL_QUEUE 10 SECONDS #define QDELING(X) (X.gc_destroyed) -#define QDELETED(X) (!X || QDELING(X)) +#define QDELETED(X) (isnull(X) || QDELING(X)) #define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 49bad31a60bc..72475368853e 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -87,6 +87,9 @@ ///Call qdel on the atom after intialization #define INITIALIZE_HINT_QDEL 2 +//Call qdel with a force of TRUE after initialization +#define INITIALIZE_HINT_QDEL_FORCE 3 + ///type and all subtypes should always immediately call Initialize in New() #define INITIALIZE_IMMEDIATE(X) ##X/New(loc, ...){\ ..();\ diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm index 5b17ed687c0f..eb6372716609 100644 --- a/code/__HELPERS/do_after.dm +++ b/code/__HELPERS/do_after.dm @@ -185,7 +185,8 @@ while (world.time + resume_time < endtime) stoplag(1) if (progress) - progbar.update(world.time - starttime + resume_time) + if(!QDELETED(progbar)) + progbar.update(world.time - starttime + resume_time) if(QDELETED(user) || QDELETED(target)) . = 0 break @@ -264,7 +265,8 @@ while (world.time + resume_time < endtime) stoplag(1) if (progress) - progbar.update(world.time - starttime + resume_time) + if(!QDELETED(progbar)) + progbar.update(world.time - starttime + resume_time) if(drifting && !user.inertia_dir) drifting = 0 @@ -339,7 +341,8 @@ while(world.time < endtime) stoplag(1) if(progress) - progbar.update(world.time - starttime) + if(!QDELETED(progbar)) + progbar.update(world.time - starttime) if(QDELETED(user) || !targets) . = 0 break diff --git a/code/_compile_options.dm b/code/_compile_options.dm index e8083c1064f4..f25f39ab2b74 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -89,6 +89,7 @@ #define REFERENCE_TRACKING #define REFERENCE_TRACKING_DEBUG #define FIND_REF_NO_CHECK_TICK +// #define GC_FAILURE_HARD_LOOKUP // Uncomment this to have harddel reftracking in unit tests (takes 3-5min to run per single harddel) #endif #ifdef TGS diff --git a/code/_onclick/hud/credits.dm b/code/_onclick/hud/credits.dm index aaf423ecbcde..632355e09d34 100644 --- a/code/_onclick/hud/credits.dm +++ b/code/_onclick/hud/credits.dm @@ -55,14 +55,15 @@ animate(src, alpha = 255, time = CREDIT_EASE_DURATION, flags = ANIMATION_PARALLEL) addtimer(CALLBACK(src, .proc/FadeOut), CREDIT_ROLL_SPEED - CREDIT_EASE_DURATION) QDEL_IN(src, CREDIT_ROLL_SPEED) - P.screen += src + if(parent) + parent.screen += src /atom/movable/screen/credit/Destroy() - var/client/P = parent - P.screen -= src icon = null - LAZYREMOVE(P.credits, src) - parent = null + if(parent) + parent.screen -= src + LAZYREMOVE(parent.credits, src) + parent = null return ..() /atom/movable/screen/credit/proc/FadeOut() diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index 60a78e5ff3df..1460570555c5 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -9,6 +9,17 @@ GLOBAL_LIST_EMPTY(radial_menus) plane = ABOVE_HUD_PLANE var/datum/radial_menu/parent +/atom/movable/screen/radial/proc/set_parent(new_value) + if(parent) + UnregisterSignal(parent, COMSIG_PARENT_QDELETING) + parent = new_value + if(parent) + RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/handle_parent_del) + +/atom/movable/screen/radial/proc/handle_parent_del() + SIGNAL_HANDLER + set_parent(null) + /atom/movable/screen/radial/slice icon_state = "radial_slice" var/choice @@ -124,7 +135,7 @@ GLOBAL_LIST_EMPTY(radial_menus) for(var/i in 1 to elements_to_add) //Create all elements var/atom/movable/screen/radial/slice/new_element = new /atom/movable/screen/radial/slice new_element.tooltips = use_tooltips - new_element.parent = src + new_element.set_parent(src) elements += new_element var/page = 1 @@ -210,7 +221,7 @@ GLOBAL_LIST_EMPTY(radial_menus) /datum/radial_menu/New() close_button = new - close_button.parent = src + close_button.set_parent(src) /datum/radial_menu/proc/Reset() choices.Cut() diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index e20bae87d781..bfdaae8069af 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -301,6 +301,12 @@ else icon_state = "lamp_off" +/atom/movable/screen/robot/lamp/Destroy() + if(robot) + robot.lampButton = null + robot = null + return ..() + /atom/movable/screen/robot/alerts name = "Alert Panel" icon = 'icons/mob/screen_ai.dmi' @@ -337,6 +343,12 @@ icon_state = "template" var/mob/living/silicon/robot/robot +/atom/movable/screen/robot/modPC/Destroy() + if(robot) + robot.interfaceButton = null + robot = null + return ..() + /atom/movable/screen/robot/modPC/Click() . = ..() if(.) diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index ba2da8365bf9..b0c7d67d96e6 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -97,6 +97,9 @@ SUBSYSTEM_DEF(atoms) if(INITIALIZE_HINT_QDEL) qdel(A) qdeleted = TRUE + if(INITIALIZE_HINT_QDEL_FORCE) + qdel(A, force = TRUE) + qdeleted = TRUE else BadInitializeCalls[the_type] |= BAD_INIT_NO_HINT diff --git a/code/controllers/subsystem/jukeboxes.dm b/code/controllers/subsystem/jukeboxes.dm index 1e457703c2ab..b19058e97f59 100644 --- a/code/controllers/subsystem/jukeboxes.dm +++ b/code/controllers/subsystem/jukeboxes.dm @@ -74,6 +74,8 @@ SUBSYSTEM_DEF(jukeboxes) activejukeboxes[IDtoupdate][JUKE_FALLOFF] = jukefalloff /datum/controller/subsystem/jukeboxes/proc/removejukebox(IDtoremove) + if(!IDtoremove) + return if(islist(activejukeboxes[IDtoremove])) var/jukechannel = activejukeboxes[IDtoremove][JUKE_CHANNEL] for(var/mob/M in GLOB.player_list) diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm index d494561d0fae..c9898f297688 100644 --- a/code/controllers/subsystem/persistence/_persistence.dm +++ b/code/controllers/subsystem/persistence/_persistence.dm @@ -335,7 +335,7 @@ SUBSYSTEM_DEF(persistence) if(!istype(ending_human) || !ending_human.mind || !ending_human.client || !ending_human.client.prefs || !ending_human.client.prefs.persistent_scars) continue - var/mob/living/carbon/human/original_human = ending_human.mind.original_character + var/mob/living/carbon/human/original_human = ending_human.mind.original_character.resolve() if(!original_human || original_human.stat == DEAD || !original_human.all_scars || !(original_human == ending_human)) if(ending_human.client) // i was told if i don't check this every step of the way byond might decide a client ceases to exist mid proc so here we go ending_human.client.prefs.scars_list["[ending_human.client.prefs.scars_index]"] = "" @@ -356,7 +356,7 @@ SUBSYSTEM_DEF(persistence) if(!istype(ending_human) || !ending_human.mind || !ending_human.client || !ending_human.client.prefs || !ending_human.client.prefs.tcg_cards) continue - var/mob/living/carbon/human/original_human = ending_human.mind.original_character + var/mob/living/carbon/human/original_human = ending_human.mind.original_character.resolve() if(!original_human || original_human.stat == DEAD || !(original_human == ending_human)) continue diff --git a/code/datums/action.dm b/code/datums/action.dm index b34cafc03f85..e743f0fc2d68 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -648,6 +648,7 @@ /datum/action/spell_action/Destroy() var/obj/effect/proc_holder/S = target S.action = null + target = null return ..() /datum/action/spell_action/Trigger() diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index f35389f1712f..3d8eedbcb597 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -87,6 +87,8 @@ to_chat(src, "You cannot directly influence the world around you, but you can see what [owner] cannot.") /mob/camera/imaginary_friend/Initialize(mapload, _trauma) + if(!_trauma) + return INITIALIZE_HINT_QDEL . = ..() trauma = _trauma @@ -129,7 +131,7 @@ client.images |= current_image /mob/camera/imaginary_friend/Destroy() - if(owner.client) + if(owner?.client) owner.client.images.Remove(human_image) if(client) client.images.Remove(human_image) diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm index 6a1d8a3d7537..da8ad1c39305 100644 --- a/code/datums/components/mood.dm +++ b/code/datums/components/mood.dm @@ -43,6 +43,7 @@ hud.show_hud(hud.hud_version) /datum/component/mood/Destroy() + QDEL_LIST_ASSOC_VAL(mood_events) STOP_PROCESSING(SSobj, src) unmodify_hud() return ..() diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm index a06242962f13..b214b8b282dd 100644 --- a/code/datums/components/pellet_cloud.dm +++ b/code/datums/components/pellet_cloud.dm @@ -266,7 +266,7 @@ var/w_bonus = wound_info_by_part[hit_part][CLOUD_POSITION_W_BONUS] var/bw_bonus = wound_info_by_part[hit_part][CLOUD_POSITION_BW_BONUS] var/wound_type = (initial(P.damage_type) == BRUTE) ? WOUND_BLUNT : WOUND_BURN // sharpness is handled in the wound rolling - wound_info_by_part[hit_part] = null + wound_info_by_part -= hit_part hit_part.painless_wound_roll(wound_type, damage_dealt, w_bonus, bw_bonus, initial(P.sharpness)) if(num_hits > 1) diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm index faca18caff47..5462b35d53d5 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -111,7 +111,7 @@ if(AM.movement_type & (FLYING|FLOATING) || !AM.has_gravity()) return var/atom/current_parent = parent - if(isturf(current_parent.loc)) + if(isturf(current_parent?.loc)) if(do_play_squeak()) SEND_SIGNAL(AM, COMSIG_CROSS_SQUEAKED) diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm index c336213388a8..bf2c45a79c93 100644 --- a/code/datums/components/tackle.dm +++ b/code/datums/components/tackle.dm @@ -49,7 +49,7 @@ var/mob/living/carbon/P = parent to_chat(P, "You can no longer tackle.") P.tackling = FALSE - ..() + return ..() /datum/component/tackler/RegisterWithParent() RegisterSignal(parent, COMSIG_MOB_CLICKON, .proc/checkTackle) diff --git a/code/datums/dash_weapon.dm b/code/datums/dash_weapon.dm index db5fa677f23f..627216aace16 100644 --- a/code/datums/dash_weapon.dm +++ b/code/datums/dash_weapon.dm @@ -19,6 +19,10 @@ dashing_item = dasher holder = user +/datum/action/innate/dash/Destroy() + dashing_item = null + return ..() + /datum/action/innate/dash/IsAvailable(silent = FALSE) if(current_charges > 0) return TRUE diff --git a/code/datums/elements/dwarfism.dm b/code/datums/elements/dwarfism.dm index fefbe4fbe796..4ba612126424 100644 --- a/code/datums/elements/dwarfism.dm +++ b/code/datums/elements/dwarfism.dm @@ -29,14 +29,15 @@ /datum/element/dwarfism/Detach(mob/living/L) . = ..() + attached_targets -= L + UnregisterSignal(L, comsig) if(QDELETED(L)) return if(L.lying != 0) L.transform = L.transform.Scale(TALL, 1) else L.transform = L.transform.Scale(1, TALL) - UnregisterSignal(L, comsig) - attached_targets -= L + L.transform = L.transform.Translate(0, 16*(TALL-1)) //Makes sure you stand on the tile no matter the size - sand #undef SHORT #undef TALL diff --git a/code/datums/elements/spellcasting.dm b/code/datums/elements/spellcasting.dm index 676168ea49ed..c789972d6039 100644 --- a/code/datums/elements/spellcasting.dm +++ b/code/datums/elements/spellcasting.dm @@ -24,6 +24,7 @@ UnregisterSignal(target, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_MOB_SPELL_CAN_CAST)) if(users_by_item[target]) var/mob/user = users_by_item[target] + users_by_item -= target stacked_spellcasting_by_user[user]-- if(!stacked_spellcasting_by_user[user]) stacked_spellcasting_by_user -= user diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm index ec0d35b5a31a..216fe19f2b0e 100644 --- a/code/datums/explosion.dm +++ b/code/datums/explosion.dm @@ -205,8 +205,8 @@ GLOBAL_LIST_EMPTY(explosions) //lists are guaranteed to contain at least 1 turf at this point var/iteration = 0 - var/affTurfLen = affected_turfs.len - var/expBlockLen = cached_exp_block.len + var/affTurfLen = length(affected_turfs) + var/expBlockLen = length(cached_exp_block) for(var/TI in affected_turfs) var/turf/T = TI ++iteration @@ -282,8 +282,8 @@ GLOBAL_LIST_EMPTY(explosions) break //update the trackers - affTurfLen = affected_turfs.len - expBlockLen = cached_exp_block.len + affTurfLen = length(affected_turfs) + expBlockLen = length(cached_exp_block) if(break_condition) if(reactionary) @@ -299,8 +299,8 @@ GLOBAL_LIST_EMPTY(explosions) break //update the trackers - affTurfLen = affected_turfs.len - expBlockLen = cached_exp_block.len + affTurfLen = length(affected_turfs) + expBlockLen = length(cached_exp_block) var/circumference = (PI * (init_dist + 4) * 2) //+4 to radius to prevent shit gaps if(exploded_this_tick.len > circumference) //only do this every revolution @@ -357,7 +357,7 @@ GLOBAL_LIST_EMPTY(explosions) var/processed = 0 while(running) var/I - for(I in (processed + 1) to affected_turfs.len) // we cache the explosion block rating of every turf in the explosion area + for(I in (processed + 1) to length(affected_turfs)) // we cache the explosion block rating of every turf in the explosion area var/turf/T = affected_turfs[I] var/current_exp_block = T.density ? T.explosion_block : 0 diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 9f6ccb742df2..26b7b5334c4d 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -79,8 +79,8 @@ var/list/ambitions //ambition end - ///What character we spawned in as- either at roundstart or latejoin, so we know for persistent scars if we ended as the same person or not - var/mob/original_character + ///Weakref to the character we spawned in as- either at roundstart or latejoin, so we know for persistent scars if we ended as the same person or not + var/datum/weakref/original_character /// A lazy list of statuses to add next to this mind in the traitor panel var/list/special_statuses @@ -100,8 +100,26 @@ qdel(i) antag_datums = null QDEL_NULL(skill_holder) + set_current(null) + soulOwner = null return ..() +/datum/mind/proc/set_current(mob/new_current) + if(new_current && QDELETED(new_current)) + CRASH("Tried to set a mind's current var to a qdeleted mob, what the fuck") + if(current) + UnregisterSignal(src, COMSIG_PARENT_QDELETING) + current = new_current + if(current) + RegisterSignal(src, COMSIG_PARENT_QDELETING, PROC_REF(clear_current)) + +/datum/mind/proc/clear_current(datum/source) + SIGNAL_HANDLER + set_current(null) + +/datum/mind/proc/set_original_character(new_original_character) + original_character = WEAKREF(new_original_character) + /datum/mind/proc/get_language_holder() if(!language_holder) language_holder = new (src) @@ -124,13 +142,13 @@ key = new_character.key if(new_character.mind) //disassociate any mind currently in our new body's mind variable - new_character.mind.current = null + new_character.mind.set_current(null) var/datum/atom_hud/antag/hud_to_transfer = antag_hud//we need this because leave_hud() will clear this list var/mob/living/old_current = current if(current) current.transfer_observers_to(new_character) //transfer anyone observing the old character to the new one - current = new_character //associate ourself with our new body + set_current(new_character) //associate ourself with our new body new_character.mind = src //and associate our new body with ourself for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body var/datum/antagonist/A = a @@ -1698,7 +1716,7 @@ GLOBAL_LIST(objective_choices) SEND_SIGNAL(src, COMSIG_MOB_ON_NEW_MIND) if(!mind.name) mind.name = real_name - mind.current = src + mind.set_current(src) mind.hide_ckey = client?.prefs?.hide_ckey /mob/living/carbon/mind_initialize() diff --git a/code/datums/mood_events/mood_event.dm b/code/datums/mood_events/mood_event.dm index c125ba054a00..7afc4d1e32ed 100644 --- a/code/datums/mood_events/mood_event.dm +++ b/code/datums/mood_events/mood_event.dm @@ -11,6 +11,7 @@ /datum/mood_event/Destroy() remove_effects() + owner = null return ..() /datum/mood_event/proc/add_effects(param) diff --git a/code/datums/mutations/antenna.dm b/code/datums/mutations/antenna.dm index ad08b8ebdc2e..54139f74a010 100644 --- a/code/datums/mutations/antenna.dm +++ b/code/datums/mutations/antenna.dm @@ -15,7 +15,7 @@ icon_state = "walkietalkie" /obj/item/implant/radio/antenna/Initialize(mapload) - ..() + . = ..() if (radio) radio.name = "internal antenna" diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 70e52e5c8c38..61a8dc439c5d 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -240,7 +240,7 @@ AA.remove_from_hud(src) if(reagents) - qdel(reagents) + QDEL_NULL(reagents) orbiters = null // The component is attached to us normaly and will be deleted elsewhere diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index e1d7737f5c5e..c19c59448f2e 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -175,6 +175,8 @@ Class Procs: for(var/atom/A in component_parts) qdel(A) component_parts.Cut() + if(circuit) + QDEL_NULL(circuit) return ..() /obj/machinery/proc/locate_machinery() @@ -454,6 +456,7 @@ Class Procs: for(var/obj/item/I in component_parts) I.forceMove(loc) LAZYCLEARLIST(component_parts) + circuit = null qdel(src) /obj/machinery/proc/spawn_frame(disassembled) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index e3778811ea15..f09627d07dd9 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -346,7 +346,8 @@ GLOBAL_LIST_EMPTY(cryopod_computers) else if(ishuman(mob_occupant)) var/mob/living/carbon/human/H = mob_occupant - if(H.mind && H.client && H.client.prefs && H == H.mind.original_character) + var/mob/living/carbon/human/H_original_caharcter = H.mind.original_character.resolve() + if(H.mind && H.client && H.client.prefs && H == H_original_caharcter) H.SaveTCGCards() var/list/gear = list() diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm index 14a90ff3bf3d..08fcae10137a 100644 --- a/code/game/machinery/launch_pad.dm +++ b/code/game/machinery/launch_pad.dm @@ -39,9 +39,10 @@ MA.plane = 0 holder.appearance = MA update_indicator() - + /obj/machinery/launchpad/Destroy() - qdel(hud_list[DIAG_LAUNCHPAD_HUD]) + for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) + diag_hud.remove_from_hud(src) return ..() /obj/machinery/launchpad/examine(mob/user) @@ -229,7 +230,9 @@ src.briefcase = briefcase /obj/machinery/launchpad/briefcase/Destroy() - QDEL_NULL(briefcase) + if(!QDELETED(briefcase)) + qdel(briefcase) + briefcase = null return ..() /obj/machinery/launchpad/briefcase/isAvailable(silent = FALSE) @@ -271,7 +274,8 @@ /obj/item/storage/briefcase/launchpad/Destroy() if(!QDELETED(pad)) - QDEL_NULL(pad) + qdel(pad) + pad = null return ..() /obj/item/storage/briefcase/launchpad/PopulateContents() diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 2bcb9b0762e7..2d442c745d76 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -26,20 +26,13 @@ set_codes() + glob_lists_register(init=TRUE) + var/turf/T = loc hide(T.intact) - if(codes["patrol"]) - if(!GLOB.navbeacons["[z]"]) - GLOB.navbeacons["[z]"] = list() - GLOB.navbeacons["[z]"] += src //Register with the patrol list! - if(codes["delivery"]) - GLOB.deliverybeacons += src - GLOB.deliverybeacontags += location /obj/machinery/navbeacon/Destroy() - if (GLOB.navbeacons["[z]"]) - GLOB.navbeacons["[z]"] -= src //Remove from beacon list, if in one. - GLOB.deliverybeacons -= src + glob_lists_deregister() return ..() /obj/machinery/navbeacon/onTransitZ(old_z, new_z) @@ -67,6 +60,26 @@ else codes[e] = "1" +/obj/machinery/navbeacon/proc/glob_lists_deregister() + if (GLOB.navbeacons["[z]"]) + GLOB.navbeacons["[z]"] -= src //Remove from beacon list, if in one. + GLOB.deliverybeacons -= src + GLOB.deliverybeacontags -= location + +///Registers the navbeacon to the global beacon lists +/obj/machinery/navbeacon/proc/glob_lists_register(init=FALSE) + if(!init) + glob_lists_deregister() + if(!codes) + return + if(codes["patrol"]) + if(!GLOB.navbeacons["[z]"]) + GLOB.navbeacons["[z]"] = list() + GLOB.navbeacons["[z]"] += src //Register with the patrol list! + if(codes["delivery"]) + GLOB.deliverybeacons += src + GLOB.deliverybeacontags += location + // called when turf state changes // hide the object if turf is intact diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index ca57e8457a2c..f948478a1da4 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -205,6 +205,7 @@ QDEL_NULL(mask) QDEL_NULL(mod) QDEL_NULL(storage) + QDEL_NULL(wires) return ..() /obj/machinery/suit_storage_unit/update_overlays() diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index a49fb325384e..9593066248c9 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -97,7 +97,7 @@ GLOBAL_LIST_EMPTY(telecomms_list) /obj/machinery/telecomms/proc/add_link(obj/machinery/telecomms/T) var/turf/position = get_turf(src) var/turf/T_position = get_turf(T) - if((position.z == T_position.z) || (long_range_link && T.long_range_link)) + if((position?.z == T_position?.z) || (long_range_link && T.long_range_link)) if(src != T) for(var/x in autolinkers) if(x in T.autolinkers) diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index 39f68bc97c9c..69a7bc906a6f 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -57,7 +57,7 @@ /obj/effect/anomaly/Destroy() GLOB.poi_list.Remove(src) STOP_PROCESSING(SSobj, src) - qdel(countdown) + QDEL_NULL(countdown) if(aSignal) QDEL_NULL(aSignal) return ..() diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index d84b3f15edbf..2293c79c1d94 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -81,4 +81,4 @@ GLOBAL_LIST(gang_tags) /obj/effect/decal/cleanable/crayon/gang/Destroy() LAZYREMOVE(GLOB.gang_tags, src) - ..() + return ..() diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 22a9e86bb03a..dd0bdb72933f 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -42,13 +42,13 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark) if(delete_after_roundstart) qdel(src) -/obj/effect/landmark/start/New() +/obj/effect/landmark/start/Initialize(mapload) + . = ..() GLOB.start_landmarks_list += src if(jobspawn_override) if(!GLOB.jobspawn_overrides[name]) GLOB.jobspawn_overrides[name] = list() GLOB.jobspawn_overrides[name] += src - ..() if(name != "start") tag = "start*[name]" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 7a953089a5f7..0b82acbdfdd8 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -209,6 +209,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb LAZYADD(used_skills[path], S.skill_traits) /obj/item/Destroy() + master = null item_flags &= ~DROPDEL //prevent reqdels if(ismob(loc)) var/mob/m = loc diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index fac44ddf5447..cf11e2852695 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -782,6 +782,7 @@ /obj/item/card/id/departmental_budget/Destroy() SSeconomy.dep_cards -= src + registered_account.bank_cards -= src return ..() /obj/item/card/id/departmental_budget/update_label() diff --git a/code/game/objects/items/chrono_eraser.dm b/code/game/objects/items/chrono_eraser.dm index 8c510bb4895a..938f24fc2a71 100644 --- a/code/game/objects/items/chrono_eraser.dm +++ b/code/game/objects/items/chrono_eraser.dm @@ -130,6 +130,11 @@ if(istype(C)) gun = C.gun +/obj/item/projectile/energy/chrono_beam/Destroy() + gun = null + return ..() + + /obj/item/projectile/energy/chrono_beam/on_hit(atom/target) if(target && gun && isliving(target)) var/obj/effect/chrono_field/F = new(target.loc, target, gun) @@ -148,7 +153,9 @@ gun = loc . = ..() - +/obj/item/ammo_casing/energy/chrono_beam/Destroy() + gun = null + return ..() diff --git a/code/game/objects/items/crab17.dm b/code/game/objects/items/crab17.dm index b71b520517d4..c42db9624fb4 100644 --- a/code/game/objects/items/crab17.dm +++ b/code/game/objects/items/crab17.dm @@ -81,6 +81,14 @@ addtimer(CALLBACK(src, .proc/startUp), 50) QDEL_IN(src, 8 MINUTES) //Self destruct after 8 min +/obj/structure/checkoutmachine/Destroy() + bogdanoff = null + stop_dumping() + STOP_PROCESSING(SSfastprocess, src) + priority_announce("The credit deposit machine at [get_area(src)] has been destroyed. Station funds have stopped draining!", sender_override = "CRAB-17 Protocol") + explosion(src, 0,0,1, flame_range = 2) + return ..() + /obj/structure/checkoutmachine/proc/startUp() //very VERY snowflake code that adds a neat animation when the pod lands. start_dumping() //The machine doesnt move during this time, giving people close by a small window to grab their funds before it starts running around @@ -145,13 +153,6 @@ canwalk = TRUE START_PROCESSING(SSfastprocess, src) -/obj/structure/checkoutmachine/Destroy() - stop_dumping() - STOP_PROCESSING(SSfastprocess, src) - priority_announce("The credit deposit machine at [get_area(src)] has been destroyed. Station funds have stopped draining!", sender_override = "CRAB-17 Protocol") - explosion(src, 0,0,1, flame_range = 2) - return ..() - /obj/structure/checkoutmachine/proc/start_dumping() accounts_to_rob = SSeconomy.bank_accounts.Copy() accounts_to_rob -= bogdanoff.get_bank_account() @@ -220,7 +221,10 @@ playsound(src, 'sound/weapons/mortar_whistle.ogg', 70, TRUE, 6) addtimer(CALLBACK(src, .proc/endLaunch), 5, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation - +/obj/effect/dumpeetTarget/Destroy() + dump = null + bogdanoff = null + return ..() /obj/effect/dumpeetTarget/proc/endLaunch() QDEL_NULL(DF) //Delete the falling machine effect, because at this point its animation is over. We dont use temp_visual because we want to manually delete it as soon as the pod appears diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 8656cc21f168..da252a322666 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -175,5 +175,7 @@ return /obj/effect/dummy/chameleon/Destroy() - master.disrupt(0) + if(master) + master.disrupt(0) + master = null return ..() diff --git a/code/game/objects/items/devices/forcefieldprojector.dm b/code/game/objects/items/devices/forcefieldprojector.dm index 47c3bc8d134c..58fafbc298a1 100644 --- a/code/game/objects/items/devices/forcefieldprojector.dm +++ b/code/game/objects/items/devices/forcefieldprojector.dm @@ -98,8 +98,9 @@ /obj/structure/projected_forcefield/Destroy() visible_message("[src] flickers and disappears!") playsound(src,'sound/weapons/resonator_blast.ogg',25,1) - generator.current_fields -= src - generator = null + if(generator) + generator.current_fields -= src + generator = null return ..() /obj/structure/projected_forcefield/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) @@ -108,4 +109,5 @@ /obj/structure/projected_forcefield/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) if(sound_effect) play_attack_sound(damage_amount, damage_type, damage_flag) - generator.shield_integrity = max(generator.shield_integrity - damage_amount, 0) + if(generator) + generator.shield_integrity = max(generator.shield_integrity - damage_amount, 0) diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 8403ea30e73b..99cc0c58935b 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -265,10 +265,6 @@ GLOBAL_LIST_INIT(channel_tokens, list( name = "\proper mini Integrated Subspace Transceiver " subspace_transmission = FALSE -/obj/item/radio/headset/silicon/pai/ComponentInitialize() - . = ..() - AddElement(/datum/element/empprotection, EMP_PROTECT_WIRES) - /obj/item/radio/headset/silicon/pai/emp_act(severity) . = ..() return EMP_PROTECT_SELF diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 536209b5753c..6d8781ba960d 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -19,6 +19,23 @@ /obj/item/transfer_valve/IsAssemblyHolder() return TRUE +/obj/item/transfer_valve/Destroy() + attached_device = null + QDEL_NULL(tank_one) + QDEL_NULL(tank_two) + return ..() + +/obj/item/transfer_valve/handle_atom_del(atom/deleted_atom) + . = ..() + if(deleted_atom == tank_one) + tank_one = null + update_appearance() + return + if(deleted_atom == tank_two) + tank_two = null + update_appearance() + return + /obj/item/transfer_valve/attackby(obj/item/item, mob/user, params) if(istype(item, /obj/item/tank)) if(tank_one && tank_two) diff --git a/code/game/objects/items/grenades/clusterbuster.dm b/code/game/objects/items/grenades/clusterbuster.dm index 9980ff34cecc..94438652eed5 100644 --- a/code/game/objects/items/grenades/clusterbuster.dm +++ b/code/game/objects/items/grenades/clusterbuster.dm @@ -70,7 +70,8 @@ ///////////////////////////////// /obj/effect/payload_spawner/Initialize(mapload, type, numspawned) ..() - spawn_payload(type, numspawned) + if(type && isnum(numspawned)) + spawn_payload(type, numspawned) return INITIALIZE_HINT_QDEL /obj/effect/payload_spawner/proc/spawn_payload(type, numspawned) diff --git a/code/game/objects/items/grenades/plastic.dm b/code/game/objects/items/grenades/plastic.dm index 9c5f1475fd7d..1ae396b28bcc 100644 --- a/code/game/objects/items/grenades/plastic.dm +++ b/code/game/objects/items/grenades/plastic.dm @@ -33,7 +33,7 @@ qdel(nadeassembly) nadeassembly = null target = null - ..() + return ..() /obj/item/grenade/plastic/attackby(obj/item/I, mob/user, params) if(!nadeassembly && istype(I, /obj/item/assembly_holder)) diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index b389efe5425c..f80538b01596 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -332,6 +332,10 @@ spark_system.set_up(5, 0, src) spark_system.attach(src) +/obj/item/melee/transforming/energy/blade/Destroy() + QDEL_NULL(spark_system) + . = ..() + /obj/item/melee/transforming/energy/blade/transform_weapon(mob/living/user, supress_message_text) return diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index e60871dec75f..c033aec648f9 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -467,7 +467,7 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths()) can_random_spawn = FALSE /obj/item/toy/plush/random/Initialize(mapload) - ..() + . = ..() var/newtype var/list/snowflake_list = CONFIG_GET(keyed_list/snowflake_plushies) diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm index 20a2aae52a77..59ca56ff8a53 100644 --- a/code/game/objects/items/religion.dm +++ b/code/game/objects/items/religion.dm @@ -294,19 +294,13 @@ name = "Crusader's Armour Set" //i can't into ck2 references desc = "This armour is said to be based on the armor of kings on another world thousands of years ago, who tended to assassinate, conspire, and plot against everyone who tried to do the same to them. Some things never change." -/obj/item/storage/box/itemset/crusader/blue/New() - ..() - contents = list() - sleep(1) +/obj/item/storage/box/itemset/crusader/blue/PopulateContents() new /obj/item/clothing/suit/armor/plate/crusader/blue(src) new /obj/item/clothing/head/helmet/plate/crusader/blue(src) new /obj/item/clothing/gloves/plate/blue(src) new /obj/item/clothing/shoes/plate/blue(src) -/obj/item/storage/box/itemset/crusader/red/New() - ..() - contents = list() - sleep(1) +/obj/item/storage/box/itemset/crusader/red/PopulateContents() new /obj/item/clothing/suit/armor/plate/crusader/red(src) new /obj/item/clothing/head/helmet/plate/crusader/red(src) new /obj/item/clothing/gloves/plate/red(src) diff --git a/code/game/objects/items/storage/_storage.dm b/code/game/objects/items/storage/_storage.dm index cbaa1775eb0d..af61ee1ea99e 100644 --- a/code/game/objects/items/storage/_storage.dm +++ b/code/game/objects/items/storage/_storage.dm @@ -16,7 +16,7 @@ AddComponent(component_type) /obj/item/storage/AllowDrop() - return TRUE + return !QDELETED(src) /obj/item/storage/contents_explosion(severity, target, origin) var/in_storage = istype(loc, /obj/item/storage)? (max(0, severity - 1)) : (severity) diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index a2ab0c2ff4ff..5d38fb31058b 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -631,7 +631,8 @@ STR.max_items = 8 /obj/item/storage/box/snappops/PopulateContents() - SEND_SIGNAL(src, COMSIG_TRY_STORAGE_FILL_TYPE, /obj/item/toy/snappop) + for(var/i in 1 to 8) + new /obj/item/toy/snappop(src) /obj/item/storage/box/matches name = "matchbox" @@ -650,7 +651,8 @@ STR.can_hold = typecacheof(list(/obj/item/match)) /obj/item/storage/box/matches/PopulateContents() - SEND_SIGNAL(src, COMSIG_TRY_STORAGE_FILL_TYPE, /obj/item/match) + for(var/i in 1 to 10) + new /obj/item/match(src) /obj/item/storage/box/matches/attackby(obj/item/match/W as obj, mob/user as mob, params) if(istype(W, /obj/item/match)) diff --git a/code/game/objects/items/storage/fancy.dm b/code/game/objects/items/storage/fancy.dm index 111e3c96a052..050dcd0df073 100644 --- a/code/game/objects/items/storage/fancy.dm +++ b/code/game/objects/items/storage/fancy.dm @@ -23,6 +23,8 @@ var/fancy_open = FALSE /obj/item/storage/fancy/PopulateContents() + if(!spawn_type) + return var/datum/component/storage/STR = GetComponent(/datum/component/storage) for(var/i = 1 to STR.max_items) new spawn_type(src) diff --git a/code/game/objects/items/summon.dm b/code/game/objects/items/summon.dm index ca678e2cbb6f..7cdc540f1771 100644 --- a/code/game/objects/items/summon.dm +++ b/code/game/objects/items/summon.dm @@ -34,6 +34,10 @@ if(host_type) host = new host_type(src, summon_count, range) +/obj/item/summon/Destroy() + QDEL_NULL(host) + return ..() + /obj/item/summon/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() if(!host) @@ -329,7 +333,9 @@ if(del_no_host) qdel(src) return - HardReset(null) + if(animation_timerid) + deltimer(animation_timerid) + atom.transform = null atom.moveToNullspace() return if(immediate) diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm index 188a8763cd7c..469a715a6be6 100644 --- a/code/game/objects/items/tanks/watertank.dm +++ b/code/game/objects/items/tanks/watertank.dm @@ -119,11 +119,17 @@ /obj/item/reagent_containers/spray/mister/Initialize(mapload) . = ..() + QDEL_NULL(reagents) tank = loc if(!istype(tank)) return INITIALIZE_HINT_QDEL reagents = tank.reagents //This mister is really just a proxy for the tank's reagents +/obj/item/reagent_containers/spray/mister/Destroy() + tank = null + reagents = null + return ..() + /obj/item/reagent_containers/spray/mister/attack_self() return @@ -221,12 +227,18 @@ /obj/item/extinguisher/mini/nozzle/Initialize(mapload) . = ..() + QDEL_NULL(reagents) tank = loc if (!istype(tank)) return INITIALIZE_HINT_QDEL reagents = tank.reagents max_water = tank.volume +/obj/item/extinguisher/mini/nozzle/Destroy() + reagents = null //This is a borrowed reference from the tank. + tank = null + return ..() + /obj/item/extinguisher/mini/nozzle/doMove(atom/destination) if(destination && (destination != tank.loc || !ismob(destination))) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index e6600d323e9b..aac8e39fafce 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -546,9 +546,10 @@ /obj/effect/decal/cleanable/ash/snappop_phoenix var/respawn_time = 300 -/obj/effect/decal/cleanable/ash/snappop_phoenix/New() +/obj/effect/decal/cleanable/ash/snappop_phoenix/Initialize(mapload) . = ..() - addtimer(CALLBACK(src, .proc/respawn), respawn_time) + if(!QDELETED(src)) + addtimer(CALLBACK(src, .proc/respawn), respawn_time) /obj/effect/decal/cleanable/ash/snappop_phoenix/proc/respawn() new /obj/item/toy/snappop/phoenix(get_turf(src)) diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index fa8a0bd239be..32e0b1eb4973 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -226,9 +226,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/claymore/highlander/robot/Initialize(mapload) var/obj/item/robot_module/kiltkit = loc robot = kiltkit.loc + . = ..() if(!istype(robot)) - qdel(src) - return ..() + return INITIALIZE_HINT_QDEL /obj/item/claymore/highlander/robot/process() loc.layer = LARGE_MOB_LAYER diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index 2c3b0545f81b..f1b580dc765e 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -293,7 +293,7 @@ GLOBAL_LIST_INIT(double_bedsheets, list(/obj/item/bedsheet/double, desc = "If you're reading this description ingame, something has gone wrong! Honk!" /obj/item/bedsheet/random/Initialize(mapload) - ..() + . = ..() if(bedsheet_type == BEDSHEET_SINGLE) var/type = pick(typesof(/obj/item/bedsheet) - (list(/obj/item/bedsheet/random, /obj/item/bedsheet/chameleon) + typesof(/obj/item/bedsheet/unlockable) + GLOB.double_bedsheets)) new type(loc) @@ -454,7 +454,7 @@ GLOBAL_LIST_INIT(double_bedsheets, list(/obj/item/bedsheet/double, bedsheet_type = BEDSHEET_DOUBLE /obj/item/bedsheet/random/double/Initialize(mapload) - ..() + . = ..() if(bedsheet_type == BEDSHEET_DOUBLE) var/type = pick(GLOB.double_bedsheets) new type(loc) diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 4df8d709391f..e887577ecb4e 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -376,8 +376,9 @@ I = icon('icons/obj/stationobjs.dmi',"laserbox_broken") if(showpiece) var/icon/S = getFlatIcon(showpiece) - S.Scale(17,17) - I.Blend(S,ICON_UNDERLAY,8,12) + if(S) + S.Scale(17,17) + I.Blend(S,ICON_UNDERLAY,8,12) src.icon = I return diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index 90224af8cc64..da94985ddace 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -429,7 +429,7 @@ /obj/effect/mob_spawn/human/hotel_staff/Destroy() new/obj/structure/fluff/empty_sleeper/syndicate(get_turf(src)) - ..() + return ..() /obj/effect/mob_spawn/human/hotel_staff/special(mob/living/carbon/human/new_spawn) ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT) @@ -453,6 +453,8 @@ /obj/effect/mob_spawn/human/demonic_friend/Initialize(mapload, datum/mind/owner_mind, obj/effect/proc_holder/spell/targeted/summon_friend/summoning_spell) . = ..() + if(!owner_mind) + return owner = owner_mind flavour_text = "You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil." important_info = "Be aware that if you do not live up to [owner.name]'s expectations, they can send you back to hell with a single thought. [owner.name]'s death will also return you to hell." diff --git a/code/game/objects/structures/guncase.dm b/code/game/objects/structures/guncase.dm index 78f0da2db22f..33803d8e8595 100644 --- a/code/game/objects/structures/guncase.dm +++ b/code/game/objects/structures/guncase.dm @@ -13,7 +13,7 @@ var/capacity = 4 /obj/structure/guncase/Initialize(mapload) - ..() + . = ..() if(mapload) for(var/obj/item/I in loc.contents) if(istype(I, gun_category)) diff --git a/code/game/objects/structures/manned_turret.dm b/code/game/objects/structures/manned_turret.dm index f70510e17355..25d87aef7d43 100644 --- a/code/game/objects/structures/manned_turret.dm +++ b/code/game/objects/structures/manned_turret.dm @@ -193,7 +193,7 @@ /obj/item/gun_control/Destroy() turret = null - ..() + return ..() /obj/item/gun_control/CanItemAutoclick() return TRUE diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 69206f0d08ff..1f93932c8354 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -121,7 +121,8 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) playsound(src, 'sound/effects/roll.ogg', 5, 1) var/turf/T = get_step(src, dir) - connected.setDir(dir) + if(connected) + connected.setDir(dir) for(var/atom/movable/AM in src) AM.forceMove(T) update_icon() diff --git a/code/game/objects/structures/traps.dm b/code/game/objects/structures/traps.dm index e6a76f36caf9..2c3f01935da0 100644 --- a/code/game/objects/structures/traps.dm +++ b/code/game/objects/structures/traps.dm @@ -169,6 +169,12 @@ . = ..() time_between_triggers = 10 +/obj/structure/trap/stun/hunter/Destroy() + if(!QDELETED(stored_item)) + qdel(stored_item) + stored_item = null + return ..() + /obj/structure/trap/stun/hunter/Crossed(atom/movable/AM) if(isliving(AM)) var/mob/living/L = AM @@ -179,6 +185,11 @@ /obj/structure/trap/stun/hunter/flare() ..() + var/turf/our_turf = get_turf(src) + if(!our_turf) + return + if(!stored_item) + return stored_item.forceMove(get_turf(src)) forceMove(stored_item) if(caught) @@ -208,6 +219,12 @@ stored_trap.name = name stored_trap.stored_item = src +/obj/item/bountytrap/Destroy() + QDEL_NULL(stored_trap) + QDEL_NULL(radio) + QDEL_NULL(spark_system) + . = ..() + /obj/item/bountytrap/proc/announce_fugitive() spark_system.start() playsound(src, 'sound/machines/ding.ogg', 50, TRUE) @@ -220,9 +237,3 @@ to_chat(user, "You set up [src]. Examine while close to disarm it.") stored_trap.forceMove(T)//moves trap to ground forceMove(stored_trap)//moves item into trap - -/obj/item/bountytrap/Destroy() - qdel(stored_trap) - QDEL_NULL(radio) - QDEL_NULL(spark_system) - . = ..() diff --git a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm index b0f06a1495c3..5327ce9419e9 100644 --- a/code/game/shuttle_engines.dm +++ b/code/game/shuttle_engines.dm @@ -72,7 +72,7 @@ /obj/structure/shuttle/engine/Destroy() if(state == ENGINE_WELDED) alter_engine_power(-engine_power) - . = ..() + return ..() //Propagates the change to the shuttle. /obj/structure/shuttle/engine/proc/alter_engine_power(mod) diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm index 943f60e7529a..a30be94cc29d 100644 --- a/code/game/turfs/simulated/lava.dm +++ b/code/game/turfs/simulated/lava.dm @@ -48,6 +48,7 @@ initial_gas_mix = AIRLESS_ATMOS /turf/open/lava/Entered(atom/movable/AM) + . = ..() if(burn_stuff(AM)) START_PROCESSING(SSobj, src) @@ -126,7 +127,6 @@ ///Proc that sets on fire something or everything on the turf that's not immune to lava. Returns TRUE to make the turf start processing. /turf/open/lava/proc/burn_stuff(atom/movable/to_burn, delta_time = 1) - if(is_safe()) return FALSE diff --git a/code/game/turfs/simulated/openspace.dm b/code/game/turfs/simulated/openspace.dm index 935bfdc1a7ab..aed5afe315bc 100644 --- a/code/game/turfs/simulated/openspace.dm +++ b/code/game/turfs/simulated/openspace.dm @@ -168,6 +168,8 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr /turf/open/openspace/icemoon/Initialize(mapload) . = ..() var/turf/T = below() + if(!T) + return if(T.flags_1 & NO_RUINS_1 && protect_ruin) ChangeTurf(replacement_turf, null, CHANGETURF_IGNORE_AIR) return diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index ed3b20fce473..fae12cc51efc 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -92,9 +92,11 @@ _y = min var/turf/T = locate(_x, _y, _z) - AM.forceMove(T) - var/turf/throwturf = get_ranged_target_turf(T, dir, 1) - AM.safe_throw_at(throwturf, 1, 4, null, FALSE) + + if(!QDELETED(AM)) + AM.forceMove(T) + var/turf/throwturf = get_ranged_target_turf(T, dir, 1) + AM.safe_throw_at(throwturf, 1, 4, null, FALSE) /turf/open/space/transit/CanBuildHere() diff --git a/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm b/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm index ad6b36cf428b..11030923668b 100644 --- a/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm +++ b/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm @@ -9,6 +9,7 @@ complementary_color = "#AD6570" blobbernaut_message = "synchronously strikes" message = "The blobs strike you" + reagent = /datum/reagent/blob/synchronous_mesh /datum/blobstrain/reagent/synchronous_mesh/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) if(damage_flag == MELEE || damage_flag == BULLET || damage_flag == LASER) //the cause isn't fire or bombs, so split the damage diff --git a/code/modules/antagonists/blob/blob/overmind.dm b/code/modules/antagonists/blob/blob/overmind.dm index 5515c67e6b32..664db35b1996 100644 --- a/code/modules/antagonists/blob/blob/overmind.dm +++ b/code/modules/antagonists/blob/blob/overmind.dm @@ -72,19 +72,23 @@ GLOBAL_LIST_EMPTY(blob_nodes) forceMove(T) /mob/camera/blob/proc/set_strain(datum/blobstrain/new_strain) - if (ispath(new_strain)) - var/hadstrain = FALSE - if (istype(blobstrain)) - blobstrain.on_lose() - qdel(blobstrain) - hadstrain = TRUE - blobstrain = new new_strain(src) - blobstrain.on_gain() - if (hadstrain) - to_chat(src, "Your strain is now: [blobstrain.name]!") - to_chat(src, "The [blobstrain.name] strain [blobstrain.description]") - if(blobstrain.effectdesc) - to_chat(src, "The [blobstrain.name] strain [blobstrain.effectdesc]") + if(!ispath(new_strain)) + return FALSE + + var/had_strain = FALSE + if(istype(blobstrain)) + blobstrain.on_lose() + qdel(blobstrain) + had_strain = TRUE + + blobstrain = new new_strain(src) + blobstrain.on_gain() + + if(had_strain) + to_chat(src, "Your strain is now: [blobstrain.name]!") + to_chat(src, "The [blobstrain.name] strain [blobstrain.description]") + if(blobstrain.effectdesc) + to_chat(src, "The [blobstrain.name] strain [blobstrain.effectdesc]") /mob/camera/blob/proc/is_valid_turf(turf/T) var/area/A = get_area(T) diff --git a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm index 92225990ddf9..30b11bc8f358 100644 --- a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm +++ b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm @@ -40,6 +40,24 @@ if(!GLOB.ark_of_the_clockwork_justiciar) GLOB.ark_of_the_clockwork_justiciar = src +/obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy() + STOP_PROCESSING(SSprocessing, src) + if(!purpose_fulfilled) + var/area/gate_area = get_area(src) + hierophant_message("An Ark of the Clockwork Justicar has fallen at [gate_area.map_name]!") + send_to_playing_players(sound(null, 0, channel = CHANNEL_JUSTICAR_ARK)) + var/was_stranded = SSshuttle.emergency.mode == SHUTTLE_STRANDED + SSshuttle.clearHostileEnvironment(src) + if(!was_stranded && !purpose_fulfilled) + priority_announce("Massive energy anomaly no longer on short-range scanners, bluespace distortions still detected.","Central Command Higher Dimensional Affairs") + if(glow) + QDEL_NULL(glow) + if(countdown) + QDEL_NULL(countdown) + if(GLOB.ark_of_the_clockwork_justiciar == src) + GLOB.ark_of_the_clockwork_justiciar = null + . = ..() + /obj/structure/destructible/clockwork/massive/celestial_gateway/on_attack_hand(mob/user, act_intent, unarmed_attack_flags) if(!active && is_servant_of_ratvar(user) && user.canUseTopic(src, !issilicon(user), NO_DEXTERY)) if(alert(user, "Are you sure you want to activate the ark? Once enabled, there will be no turning back.", "Enabling the ark", "Activate!", "Cancel") == "Activate!") @@ -125,7 +143,7 @@ L.forceMove(pick(open_turfs)) glow = new(get_turf(src)) var/area/gate_area = get_area(src) - hierophant_message("An Ark of the Clockwork Justicar has been created in [gate_area.map_name]!", FALSE, src) + hierophant_message("An Ark of the Clockwork Justicar has been created in [gate_area?.map_name]!", FALSE, src) /obj/structure/destructible/clockwork/massive/celestial_gateway/proc/initiate_mass_recall() recalling = TRUE @@ -149,23 +167,7 @@ transform = matrix() * 2 animate(src, transform = matrix() * 0.5, time = 30, flags = ANIMATION_END_NOW) -/obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy() - STOP_PROCESSING(SSprocessing, src) - if(!purpose_fulfilled) - var/area/gate_area = get_area(src) - hierophant_message("An Ark of the Clockwork Justicar has fallen at [gate_area.map_name]!") - send_to_playing_players(sound(null, 0, channel = CHANNEL_JUSTICAR_ARK)) - var/was_stranded = SSshuttle.emergency.mode == SHUTTLE_STRANDED - SSshuttle.clearHostileEnvironment(src) - if(!was_stranded && !purpose_fulfilled) - priority_announce("Massive energy anomaly no longer on short-range scanners, bluespace distortions still detected.","Central Command Higher Dimensional Affairs") - if(glow) - qdel(glow) - glow = null - if(countdown) - qdel(countdown) - countdown = null - . = ..() + /obj/structure/destructible/clockwork/massive/celestial_gateway/deconstruct(disassembled = TRUE) if(!(flags_1 & NODECONSTRUCT_1)) diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm index 0efde2d3dbd0..3106573f061f 100644 --- a/code/modules/antagonists/cult/blood_magic.dm +++ b/code/modules/antagonists/cult/blood_magic.dm @@ -357,9 +357,10 @@ /obj/item/melee/blood_magic/Initialize(mapload, spell) . = ..() ADD_TRAIT(src, TRAIT_NODROP, CULT_TRAIT) - source = spell - uses = source.charges - health_cost = source.health_cost + if(spell) + source = spell + uses = source.charges + health_cost = source.health_cost /obj/item/melee/blood_magic/Destroy() @@ -374,7 +375,7 @@ source.desc = source.base_desc source.desc += "
Has [uses] use\s remaining." source.UpdateButtonIcon() - ..() + return ..() /obj/item/melee/blood_magic/attack_self(mob/living/user) afterattack(user, user, TRUE) diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index a52228521059..d31cf6f69d29 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -137,6 +137,11 @@ jaunt = new(src) linked_action = new(src) +/obj/item/cult_bastard/Destroy() + QDEL_NULL(jaunt) + QDEL_NULL(linked_action) + . = ..() + /obj/item/cult_bastard/ComponentInitialize() . = ..() AddComponent(/datum/component/butchering, 50, 80) @@ -740,7 +745,7 @@ /obj/item/cult_spear/Destroy() if(spear_act) qdel(spear_act) - ..() + return ..() /obj/item/cult_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) var/turf/T = get_turf(hit_atom) diff --git a/code/modules/antagonists/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm index 4b2a41db71b7..bdbeb6f00f5e 100644 --- a/code/modules/antagonists/devil/imp/imp.dm +++ b/code/modules/antagonists/devil/imp/imp.dm @@ -45,7 +45,7 @@ of intentionally harming a fellow devil." /mob/living/simple_animal/imp/Initialize(mapload) - ..() + . = ..() boost = world.time + 30 /mob/living/simple_animal/imp/BiologicalLife(delta_time, times_fired) diff --git a/code/modules/antagonists/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm index d524a7edaee7..ff7d253e2dd2 100644 --- a/code/modules/antagonists/devil/true_devil/_true_devil.dm +++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm @@ -29,7 +29,7 @@ create_bodyparts() //initialize bodyparts create_internal_organs() grant_all_languages() - ..() + . = ..() /mob/living/carbon/true_devil/create_internal_organs() internal_organs += new /obj/item/organ/brain diff --git a/code/modules/antagonists/eldritch_cult/eldritch_items.dm b/code/modules/antagonists/eldritch_cult/eldritch_items.dm index 58e0193de583..de0ca6b71599 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_items.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_items.dm @@ -314,6 +314,10 @@ . = ..() linked_action = new(src) +/obj/item/melee/rune_knife/Destroy() + QDEL_NULL(linked_action) + . = ..() + /obj/item/melee/rune_knife/pickup(mob/user) . = ..() linked_action.Grant(user, src) diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index f05321487f28..7f533c87b44e 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -453,7 +453,7 @@ /obj/item/ectoplasm/revenant/Destroy() if(!QDELETED(revenant)) qdel(revenant) - ..() + return ..() /proc/RevenantThrow(over, mob/user, obj/item/throwable) var/mob/living/simple_animal/revenant/spooker = user diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm index d2b698bce449..1c74c0a2c6d9 100644 --- a/code/modules/antagonists/slaughter/slaughter.dm +++ b/code/modules/antagonists/slaughter/slaughter.dm @@ -71,7 +71,7 @@ var/datum/action/cooldown/slam /mob/living/simple_animal/slaughter/Initialize(mapload) - ..() + . = ..() var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new AddSpell(bloodspell) slam = new /datum/action/cooldown/slam diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm index 4c48ad81dd30..eb08d91f7d00 100644 --- a/code/modules/antagonists/swarmer/swarmer.dm +++ b/code/modules/antagonists/swarmer/swarmer.dm @@ -194,9 +194,11 @@ return 0 /obj/item/IntegrateAmount() //returns the amount of resources gained when eating this item + . = ..() + if(!custom_materials) + return if(custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)] || custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)]) return 1 - return ..() /obj/item/gun/swarmer_act()//Stops you from eating the entire armory return FALSE diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm index 5eb1f77fd7ec..de3603ed64c1 100644 --- a/code/modules/assembly/flash.dm +++ b/code/modules/assembly/flash.dm @@ -234,6 +234,10 @@ var/obj/item/organ/cyberimp/arm/flash/I = null var/active_light_strength = 7 +/obj/item/assembly/flash/armimplant/Destroy() + I = null + return ..() + /obj/item/assembly/flash/armimplant/burn_out() if(I && I.owner) to_chat(I.owner, "Your photon projector implant overheats and deactivates!") diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm index 68930ca03ad3..0e9a7c2bc851 100644 --- a/code/modules/awaymissions/capture_the_flag.dm +++ b/code/modules/awaymissions/capture_the_flag.dm @@ -173,7 +173,7 @@ /obj/machinery/capture_the_flag/Destroy() GLOB.poi_list.Remove(src) - ..() + return ..() /obj/machinery/capture_the_flag/process(delta_time) for(var/i in spawned_mobs) @@ -642,7 +642,7 @@ invisibility = 0 /obj/effect/ctf/ammo/Initialize(mapload) - ..() + . = ..() QDEL_IN(src, AMMO_DROP_LIFETIME) /obj/effect/ctf/ammo/Crossed(atom/movable/AM) @@ -681,6 +681,11 @@ for(var/obj/machinery/capture_the_flag/CTF in GLOB.machines) CTF.dead_barricades += src +/obj/effect/ctf/dead_barricade/Destroy(force) + for(var/obj/machinery/capture_the_flag/CTF in GLOB.machines) + CTF.dead_barricades -= src + return ..() + /obj/effect/ctf/dead_barricade/proc/respawn() if(!QDELETED(src)) new /obj/structure/barricade/security/ctf(get_turf(src)) diff --git a/code/modules/awaymissions/mission_code/Cabin.dm b/code/modules/awaymissions/mission_code/Cabin.dm index b5ff23d75b70..c244431b2237 100644 --- a/code/modules/awaymissions/mission_code/Cabin.dm +++ b/code/modules/awaymissions/mission_code/Cabin.dm @@ -44,7 +44,7 @@ var/active = 1 /obj/structure/firepit/Initialize(mapload) - ..() + . = ..() toggleFirepit() /obj/structure/firepit/interact(mob/living/user) diff --git a/code/modules/buildmode/effects/line.dm b/code/modules/buildmode/effects/line.dm index d21c0787fa34..dfcfd86475b8 100644 --- a/code/modules/buildmode/effects/line.dm +++ b/code/modules/buildmode/effects/line.dm @@ -3,6 +3,9 @@ var/client/cl /obj/effect/buildmode_line/New(client/C, atom/atom_a, atom/atom_b, linename) + if(!C || !atom_a || !atom_b) + stack_trace("Buildmode effect created with odd inputs") + return name = linename loc = get_turf(atom_a) I = image('icons/misc/mark.dmi', src, "line", 19.0) diff --git a/code/modules/cargo/gondolapod.dm b/code/modules/cargo/gondolapod.dm index 70431d6447ec..cbc4f088cd30 100644 --- a/code/modules/cargo/gondolapod.dm +++ b/code/modules/cargo/gondolapod.dm @@ -28,6 +28,9 @@ var/obj/structure/closet/supplypod/centcompod/linked_pod /mob/living/simple_animal/pet/gondola/gondolapod/Initialize(mapload, pod) + if(!pod) + stack_trace("Gondola pod created with no pod") + return INITIALIZE_HINT_QDEL linked_pod = pod name = linked_pod.name . = ..() @@ -71,6 +74,6 @@ update_icon() /mob/living/simple_animal/pet/gondola/gondolapod/death() - qdel(linked_pod) //Will cause the open() proc for the linked supplypod to be called with the "broken" parameter set to true, meaning that it will dump its contents on death + QDEL_NULL(linked_pod) //Will cause the open() proc for the linked supplypod to be called with the "broken" parameter set to true, meaning that it will dump its contents on death qdel(src) ..() diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index 48baaa80d86b..b1d4bf2e8ef1 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -538,6 +538,9 @@ /obj/effect/pod_landingzone_effect/Initialize(mapload, obj/structure/closet/supplypod/pod) . = ..() + if(!pod) + stack_trace("Pod landingzone effect created with no pod") + return INITIALIZE_HINT_QDEL transform = matrix() * 1.5 animate(src, transform = matrix()*0.01, time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING]) @@ -556,6 +559,9 @@ /obj/effect/pod_landingzone/Initialize(mapload, podParam, single_order = null, clientman) . = ..() + if(!podParam) + stack_trace("Pod landingzone created with no pod") + return INITIALIZE_HINT_QDEL if (ispath(podParam)) //We can pass either a path for a pod (as expressconsoles do), or a reference to an instantiated pod (as the centcom_podlauncher does) podParam = new podParam() //If its just a path, instantiate it pod = podParam diff --git a/code/modules/clothing/gloves/mittens.dm b/code/modules/clothing/gloves/mittens.dm index 2d00da6780c8..b5cd5e79c723 100644 --- a/code/modules/clothing/gloves/mittens.dm +++ b/code/modules/clothing/gloves/mittens.dm @@ -13,7 +13,7 @@ /obj/item/clothing/gloves/mittens/random /obj/item/clothing/gloves/mittens/random/Initialize(mapload) - ..() + . = ..() var/colours = list("black", "yellow", "lightbrown", "brown", "orange", "red", "purple", "green", "blue", "kitten") var/picked_c = pick(colours) if(picked_c == "kitten") diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index a2d13822f831..3e68c157889f 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -56,7 +56,7 @@ /obj/item/clothing/mask/gas/welding/up /obj/item/clothing/mask/gas/welding/up/Initialize(mapload) - ..() + . = ..() visor_toggling() diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index c946002fa292..274663a3b19e 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -313,6 +313,10 @@ if(istype(loc, /obj/item/clothing/suit/space/hardsuit/syndi)) linkedsuit = loc +/obj/item/clothing/head/helmet/space/hardsuit/syndi/Destroy() + linkedsuit = null + return ..() + /obj/item/clothing/head/helmet/space/hardsuit/syndi/attack_self(mob/user) //Toggle Helmet if(!isturf(user.loc)) to_chat(user, "You cannot toggle your helmet while in this [user.loc]!" ) @@ -526,6 +530,10 @@ . = ..() bomb_radar = new /obj/machinery/doppler_array/integrated(src) +/obj/item/clothing/head/helmet/space/hardsuit/rd/Destroy() + QDEL_NULL(bomb_radar) + return ..() + /obj/item/clothing/head/helmet/space/hardsuit/rd/equipped(mob/living/carbon/human/user, slot) ..() if (slot == ITEM_SLOT_HEAD) @@ -700,6 +708,10 @@ . = ..() bomb_radar = new /obj/machinery/doppler_array/integrated(src) +/obj/item/clothing/head/helmet/space/hardsuit/ancient/mason/Destroy() + QDEL_NULL(bomb_radar) + return ..() + /obj/item/clothing/head/helmet/space/hardsuit/ancient/mason/equipped(mob/living/carbon/human/user, slot) ..() if (slot == ITEM_SLOT_HEAD) @@ -965,7 +977,7 @@ var/energy_color = "#35FFF0" /obj/item/clothing/suit/space/hardsuit/lavaknight/Initialize(mapload) - ..() + . = ..() light_color = energy_color set_light(1) update_icon() diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm index 25cbf27cf674..864e3ff8086d 100644 --- a/code/modules/clothing/suits/toggles.dm +++ b/code/modules/clothing/suits/toggles.dm @@ -153,8 +153,8 @@ /obj/item/clothing/suit/space/hardsuit/Destroy() if(helmet) helmet.suit = null - qdel(helmet) - qdel(jetpack) + QDEL_NULL(helmet) + QDEL_NULL(jetpack) return ..() /obj/item/clothing/head/helmet/space/hardsuit/Destroy() diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index 215be905aa6a..4c9dea45b7a1 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -12,8 +12,8 @@ icon_state = "random_jumpsuit" /obj/item/clothing/under/color/random/Initialize(mapload) - ..() - var/obj/item/clothing/under/color/C = pick(subtypesof(/obj/item/clothing/under/color) - subtypesof(/obj/item/clothing/under/color/jumpskirt) - /obj/item/clothing/under/color/random - /obj/item/clothing/under/color/grey/glorf - /obj/item/clothing/under/color/black/ghost) + . = ..() + var/obj/item/clothing/under/color/C = pick(typesof(/obj/item/clothing/under/color) - subtypesof(/obj/item/clothing/under/color/jumpskirt) - /obj/item/clothing/under/color/random - /obj/item/clothing/under/color/grey/glorf - /obj/item/clothing/under/color/black/ghost) if(ishuman(loc)) var/mob/living/carbon/human/H = loc @@ -26,7 +26,7 @@ icon_state = "random_jumpsuit" //Skirt variant needed /obj/item/clothing/under/color/jumpskirt/random/Initialize(mapload) - ..() + . = ..() var/obj/item/clothing/under/color/jumpskirt/C = pick(subtypesof(/obj/item/clothing/under/color/jumpskirt) - /obj/item/clothing/under/color/jumpskirt/random) if(ishuman(loc)) var/mob/living/carbon/human/H = loc diff --git a/code/modules/detectivework/detective_work.dm b/code/modules/detectivework/detective_work.dm index 3b1b00fc3a49..81eaa8b70d42 100644 --- a/code/modules/detectivework/detective_work.dm +++ b/code/modules/detectivework/detective_work.dm @@ -67,7 +67,7 @@ //Set ignoregloves to add prints irrespective of the mob having gloves on. /atom/proc/add_fingerprint(mob/living/M, ignoregloves = FALSE) - if(!M || !M.key) + if(!istype(M)) return add_hiddenprint(M) diff --git a/code/modules/events/travelling_trader.dm b/code/modules/events/travelling_trader.dm index ac9d103f5f4d..acc5324dc5d6 100644 --- a/code/modules/events/travelling_trader.dm +++ b/code/modules/events/travelling_trader.dm @@ -118,7 +118,7 @@ smoke.set_up(1, loc) smoke.start() visible_message("[src] disappears in a puff of smoke, leaving something on the ground!") - ..() + return ..() //travelling trader subtypes (the types that can actually spawn) //so far there's: cook / botanist / bartender / animal hunter / artifact dealer / surgeon (6 types!) @@ -144,7 +144,7 @@ requested_item = result else requested_item = /obj/item/reagent_containers/food/snacks/copypasta - ..() + . = ..() //botanist /mob/living/carbon/human/dummy/travelling_trader/gardener @@ -164,7 +164,7 @@ requested_item = pick(subtypesof(/obj/item/reagent_containers/food/snacks/grown) - list(/obj/item/reagent_containers/food/snacks/grown/shell, /obj/item/reagent_containers/food/snacks/grown/shell/gatfruit, /obj/item/reagent_containers/food/snacks/grown/cherry_bomb)) - ..() + . = ..() //animal hunter /mob/living/carbon/human/dummy/travelling_trader/animal_hunter @@ -280,7 +280,7 @@ /mob/living/carbon/human/dummy/travelling_trader/artifact_dealer/Initialize(mapload) possible_rewards += list(pick(subtypesof(/obj/item/clothing/head/collectable)) = 1) //this is slightly lower because it's absolutely useless - ..() + . = ..() /datum/outfit/artifact_dealer name = "Artifact Dealer" diff --git a/code/modules/events/wizard/greentext.dm b/code/modules/events/wizard/greentext.dm index 1864ad6d200d..281c4ba002aa 100644 --- a/code/modules/events/wizard/greentext.dm +++ b/code/modules/events/wizard/greentext.dm @@ -39,6 +39,26 @@ roundend_callback = CALLBACK(src,.proc/check_winner) SSticker.OnRoundend(roundend_callback) +/obj/item/greentext/Destroy(force) + if(!(resistance_flags & ON_FIRE) && !force) + return QDEL_HINT_LETMELIVE + + SSticker.round_end_events -= roundend_callback + GLOB.poi_list.Remove(src) + roundend_callback = null + for(var/i in GLOB.player_list) + var/mob/M = i + var/message = "A dark temptation has passed from this world" + if(M in color_altered_mobs) + message += " and you're finally able to forgive yourself" + if(M.color == "#FF0000" || M.color == "#00FF00") + M.remove_atom_colour(ADMIN_COLOUR_PRIORITY) + message += "..." + // can't skip the mob check as it also does the decolouring + if(!quiet) + to_chat(M, message) + . = ..() + /obj/item/greentext/equipped(mob/living/user as mob) to_chat(user, "So long as you leave this place with greentext in hand you know will be happy...") var/list/other_objectives = user.mind.get_all_objectives() @@ -80,24 +100,7 @@ last_holder.add_atom_colour("#FF0000", ADMIN_COLOUR_PRIORITY) last_holder = new_holder //long live the king -/obj/item/greentext/Destroy(force) - if(!(resistance_flags & ON_FIRE) && !force) - return QDEL_HINT_LETMELIVE - SSticker.round_end_events -= roundend_callback - GLOB.poi_list.Remove(src) - for(var/i in GLOB.player_list) - var/mob/M = i - var/message = "A dark temptation has passed from this world" - if(M in color_altered_mobs) - message += " and you're finally able to forgive yourself" - if(M.color == "#FF0000" || M.color == "#00FF00") - M.remove_atom_colour(ADMIN_COLOUR_PRIORITY) - message += "..." - // can't skip the mob check as it also does the decolouring - if(!quiet) - to_chat(M, message) - . = ..() /obj/item/greentext/quiet quiet = TRUE diff --git a/code/modules/fields/infinite_void.dm b/code/modules/fields/infinite_void.dm index 8a60976b439e..06b656a3a9b0 100644 --- a/code/modules/fields/infinite_void.dm +++ b/code/modules/fields/infinite_void.dm @@ -34,7 +34,8 @@ INVOKE_ASYNC(src, .proc/domain_expansion) /obj/effect/domain_expansion/Destroy() - qdel(chronofield) + QDEL_NULL(chronofield) + target = null return ..() /obj/effect/domain_expansion/proc/domain_expansion() diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 5920c33df1b2..20f22c82cab3 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -106,6 +106,9 @@ GLOBAL_LIST_INIT(hallucination_list, list( /obj/effect/hallucination/simple/Initialize(mapload, var/mob/living/carbon/T) . = ..() + if(!T) + stack_trace("A hallucination was created with no target") + return INITIALIZE_HINT_QDEL target = T current_image = GetImage() if(target.client) diff --git a/code/modules/food_and_drinks/food/snacks/meat.dm b/code/modules/food_and_drinks/food/snacks/meat.dm index 20bd5880ff7f..adeef7f91d13 100644 --- a/code/modules/food_and_drinks/food/snacks/meat.dm +++ b/code/modules/food_and_drinks/food/snacks/meat.dm @@ -278,7 +278,7 @@ visible_message("[src] finishes cooking!") new /obj/item/reagent_containers/food/snacks/meat/steak/goliath(loc) qdel(src) - + /obj/item/reagent_containers/food/snacks/meat/slab/dragon name = "ash drake meat" desc = "Meat from an ash drake. It's probably not a good idea to eat this raw." @@ -408,7 +408,7 @@ trash = null tastes = list("meat" = 1, "rock" = 1) foodtype = MEAT - + /obj/item/reagent_containers/food/snacks/meat/steak/dragon name = "dragon steak" desc = "Spicy." diff --git a/code/modules/holiday/halloween/halloween.dm b/code/modules/holiday/halloween/halloween.dm index 59a7fcce0c03..28709d0a0587 100644 --- a/code/modules/holiday/halloween/halloween.dm +++ b/code/modules/holiday/halloween/halloween.dm @@ -43,7 +43,7 @@ var/mob/trapped_mob /obj/structure/closet/Initialize(mapload) - ..() + . = ..() if(prob(30)) set_spooky_trap() diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm index a0d96658e62f..085b5e06c10e 100644 --- a/code/modules/instruments/songs/_song.dm +++ b/code/modules/instruments/songs/_song.dm @@ -146,6 +146,8 @@ stop_playing() SSinstruments.on_song_del(src) lines = null + if(using_instrument) + using_instrument.songs_using -= src using_instrument = null allowed_instrument_ids = null parent = null diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index 547036f08a13..c66a51a3104b 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -654,18 +654,19 @@ /obj/item/integrated_circuit/input/signaler/Initialize(mapload) . = ..() - spawn(40) - set_frequency(frequency) - // Set the pins so when someone sees them, they won't show as null - set_pin_data(IC_INPUT, 1, frequency) - set_pin_data(IC_INPUT, 2, code) + addtimer(CALLBACK(src, .proc/init_frequency), 4 SECONDS) /obj/item/integrated_circuit/input/signaler/Destroy() SSradio.remove_object(src,frequency) - frequency = 0 return ..() +/obj/item/integrated_circuit/input/signaler/proc/init_frequency() + set_frequency(frequency) + // Set the pins so when someone sees them, they won't show as null + set_pin_data(IC_INPUT, 1, frequency) + set_pin_data(IC_INPUT, 2, code) + /obj/item/integrated_circuit/input/signaler/on_data_written() var/new_freq = get_pin_data(IC_INPUT, 1) var/new_code = get_pin_data(IC_INPUT, 2) diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm index 0f97ff8e087c..249379c3324a 100644 --- a/code/modules/integrated_electronics/subtypes/smart.dm +++ b/code/modules/integrated_electronics/subtypes/smart.dm @@ -177,7 +177,7 @@ /obj/item/integrated_circuit/input/mmi_tank/Destroy() RemoveBrain() - ..() + return ..() /obj/item/integrated_circuit/input/mmi_tank/relaymove(var/n,var/dir) set_pin_data(IC_OUTPUT, 2, dir) @@ -320,7 +320,7 @@ /obj/item/integrated_circuit/input/pAI_connector/Destroy() RemovepAI() - ..() + return ..() /obj/item/integrated_circuit/input/pAI_connector/proc/RemovepAI() if(installed_pai) diff --git a/code/modules/library/random_books.dm b/code/modules/library/random_books.dm index be5e9ea6b516..137d1d7b243d 100644 --- a/code/modules/library/random_books.dm +++ b/code/modules/library/random_books.dm @@ -2,7 +2,7 @@ icon_state = "random_book" /obj/item/book/manual/random/Initialize(mapload) - ..() + . = ..() var/static/banned_books = list(/obj/item/book/manual/random, /obj/item/book/manual/nuclear, /obj/item/book/manual/wiki) var/newtype = pick(subtypesof(/obj/item/book/manual) - banned_books) new newtype(loc) diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 68ca1c979d1d..8b39a854bbdb 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -270,6 +270,11 @@ active_style = new /datum/gauntlet_style/brawler active_style.on_apply(src) +/obj/item/kinetic_crusher/glaive/gauntlets/Destroy() + QDEL_NULL(active_style) + current_target = null + return ..() + /obj/item/kinetic_crusher/glaive/gauntlets/examine(mob/living/user) . = ..() . += "According to a very small display, the currently loaded style is \"[active_style.name]\"." diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm index 18f816699cac..83439a766461 100644 --- a/code/modules/mining/fulton.dm +++ b/code/modules/mining/fulton.dm @@ -168,7 +168,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons) /obj/structure/extraction_point/Destroy() GLOB.total_extraction_beacons -= src - ..() + return ..() /obj/effect/extraction_holder name = "extraction holder" diff --git a/code/modules/mining/lavaland/ash_tree.dm b/code/modules/mining/lavaland/ash_tree.dm index 5dca7a8e2fa2..e1996d693cba 100644 --- a/code/modules/mining/lavaland/ash_tree.dm +++ b/code/modules/mining/lavaland/ash_tree.dm @@ -25,7 +25,7 @@ var/sap_amount /obj/structure/flora/ashtree/Initialize(mapload) - ..() + . = ..() if(prob(50)) sap = TRUE icon_state = sap_icon_state diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 1986a8f9c146..c42d098cd38f 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -434,6 +434,12 @@ var/obj/item/warp_cube/linked var/teleporting = FALSE +/obj/item/warp_cube/Destroy() + if(!QDELETED(linked)) + linked.linked = null + QDEL_NULL(linked) + return ..() + /obj/item/warp_cube/attack_self(mob/user) if(!linked) to_chat(user, "[src] fizzles uselessly.") @@ -480,6 +486,12 @@ linked = blue blue.linked = src +/obj/item/warp_cube/red/Destroy() + if(!QDELETED(linked)) + linked.linked = null + QDEL_NULL(linked) + return ..() + /obj/effect/warp_cube mouse_opacity = MOUSE_OPACITY_TRANSPARENT anchored = TRUE diff --git a/code/modules/mob/dead/new_player/login.dm b/code/modules/mob/dead/new_player/login.dm index dbcc2cb2b452..e40b467a791b 100644 --- a/code/modules/mob/dead/new_player/login.dm +++ b/code/modules/mob/dead/new_player/login.dm @@ -5,7 +5,7 @@ if(!mind) mind = new /datum/mind(key) mind.active = 1 - mind.current = src + mind.set_current(src) ..() diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 9b9cceee8451..4f47c374bd44 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -695,7 +695,7 @@ mind.late_joiner = TRUE mind.active = 0 //we wish to transfer the key manually mind.transfer_to(H) //won't transfer key since the mind is not active - mind.original_character = H + mind.set_original_character(H) H.name = real_name client.init_verbs() diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 8d5bd40acca1..d5233d0da746 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -346,6 +346,8 @@ var/obj/effect/decal/cleanable/blood/B = locate() in T if(!B) B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses()) + if(QDELETED(B)) //Give it up + return if(B.bloodiness < MAX_SHOE_BLOODINESS) //add more blood, up to a limit B.bloodiness += BLOOD_AMOUNT_PER_DECAL B.transfer_mob_blood_dna(src) //give blood info to the blood decal. diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm index ca9edead4855..69dc59f1f82d 100644 --- a/code/modules/mob/living/brain/brain.dm +++ b/code/modules/mob/living/brain/brain.dm @@ -34,10 +34,11 @@ if(stat!=DEAD) //If not dead. death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA if(mind) //You aren't allowed to return to brains that don't exist - mind.current = null + mind.set_current(null) mind.active = FALSE //No one's using it anymore. ghostize() //Ghostize checks for key so nothing else is necessary. container = null + QDEL_NULL(stored_dna) return ..() /mob/living/brain/update_mobility() diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index c31b566cf3c8..a917b2e9862b 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -282,6 +282,8 @@ if(brainmob) QDEL_NULL(brainmob) QDEL_LIST(traumas) + if(owner?.mind) + owner.mind.set_current(null) return ..() //other types of brains diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index 71b1d09fadc9..a63aa11962a7 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -17,8 +17,8 @@ var/drooling = 0 //For Neruotoxic spit overlays bodyparts = list(/obj/item/bodypart/chest/alien, /obj/item/bodypart/head/alien, /obj/item/bodypart/l_arm/alien, /obj/item/bodypart/r_arm/alien, /obj/item/bodypart/r_leg/alien, /obj/item/bodypart/l_leg/alien) - can_ventcrawl = TRUE + var/obj/effect/proc_holder/alien/regurgitate/regurg GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list( /datum/strippable_item/hand/left, @@ -29,7 +29,13 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list( //This is fine right now, if we're adding organ specific damage this needs to be updated /mob/living/carbon/alien/humanoid/Initialize(mapload) - AddAbility(new/obj/effect/proc_holder/alien/regurgitate(null)) + regurg = new(null) + AddAbility(regurg) + . = ..() + +/mob/living/carbon/alien/humanoid/Destroy() + RemoveAbility(regurg) + QDEL_NULL(regurg) . = ..() /mob/living/carbon/alien/humanoid/ComponentInitialize() diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 7e1c669e49b1..4205b4b9931e 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -29,6 +29,7 @@ health = 400 icon_state = "alienq" var/datum/action/small_sprite/smallsprite = new/datum/action/small_sprite/queen() + var/obj/effect/proc_holder/alien/royal/queen/promote/promote /mob/living/carbon/alien/humanoid/royal/queen/Initialize(mapload) //there should only be one queen @@ -44,10 +45,17 @@ real_name = src.name AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/xeno(src)) - AddAbility(new/obj/effect/proc_holder/alien/royal/queen/promote()) + promote = new(null) + AddAbility(promote) smallsprite.Grant(src) return ..() +/mob/living/carbon/alien/humanoid/royal/queen/Destroy() + RemoveAbility(promote) + QDEL_NULL(promote) + QDEL_NULL(small_sprite) + return ..() + /mob/living/carbon/alien/humanoid/royal/queen/create_internal_organs() internal_organs += new /obj/item/organ/alien/plasmavessel/large/queen internal_organs += new /obj/item/organ/alien/resinspinner diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm index 2db912fe7eec..f452c4997d77 100644 --- a/code/modules/mob/living/carbon/alien/organs.dm +++ b/code/modules/mob/living/carbon/alien/organs.dm @@ -12,6 +12,9 @@ alien_powers += new A(src) /obj/item/organ/alien/Destroy() + if(owner) + Remove(TRUE) + owner = null QDEL_LIST(alien_powers) return ..() diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 6f48d9022db2..e1d0e68a2dcd 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -446,6 +446,14 @@ var/datum/action/innate/use_extract/major/extract_major var/extract_cooldown = 0 +/datum/species/jelly/luminescent/Destroy(force) + current_extract = null + QDEL_NULL(glow) + QDEL_NULL(extract_major) + QDEL_NULL(integrate_extract) + QDEL_NULL(extract_minor) + return ..() + /datum/species/jelly/luminescent/on_species_loss(mob/living/carbon/C) ..() if(current_extract) @@ -620,6 +628,14 @@ if(link_minds) link_minds.Remove(C) +//Species datums don't normally implement destroy, but JELLIES SUCK ASS OUT OF A STEEL STRAW ~LemonInTheDark, Tsurupeta +/datum/species/jelly/stargazer/Destroy() + QDEL_NULL(project_thought) + QDEL_NULL(link_minds) + QDEL_LIST(linked_actions) + slimelink_owner = null + return ..() + /datum/species/jelly/stargazer/spec_death(gibbed, mob/living/carbon/human/H) ..() for(var/M in linked_mobs) @@ -672,6 +688,10 @@ ..() species = _species +/datum/action/innate/linked_speech/Destroy() + species = null + return ..() + /datum/action/innate/linked_speech/Activate() var/mob/living/carbon/human/H = owner if(!species || !(H in species.linked_mobs)) @@ -749,6 +769,10 @@ ..() species = _species +/datum/action/innate/linked_speech/Destroy() + species = null + return ..() + /datum/action/innate/link_minds/Activate() var/mob/living/carbon/human/H = owner if(!is_species(H, /datum/species/jelly/stargazer)) diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index bb35ff082bf5..b7f37426ce07 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -183,6 +183,9 @@ GLOBAL_LIST_INIT(strippable_monkey_items, create_strippable_list(list( /mob/living/carbon/monkey/angry/Initialize(mapload) . = ..() if(prob(10)) - var/obj/item/clothing/head/helmet/justice/escape/helmet = new(src) - equip_to_slot_or_del(helmet,ITEM_SLOT_HEAD) - INVOKE_ASYNC(helmet, /obj/item.proc/attack_self, src) // todo encapsulate toggle + INVOKE_ASYNC(src, PROC_REF(give_ape_escape_helmet)) + +/mob/living/carbon/monkey/angry/proc/give_ape_escape_helmet() + var/obj/item/clothing/head/helmet/justice/escape/helmet = new(src) + equip_to_slot_or_del(helmet,ITEM_SLOT_HEAD) + helmet.attack_self(src) // todo encapsulate toggle diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index be6508dd966c..b2661c3bb51d 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -35,6 +35,7 @@ if(buckled) buckled.unbuckle_mob(src,force=1) QDEL_LIST_ASSOC_VAL(ability_actions) + QDEL_LIST(abilities) remove_from_all_data_huds() GLOB.mob_living_list -= src diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 08098b818fc4..f4296de5a783 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -187,6 +187,7 @@ // TODO: Why these no work? // QDEL_NULL(robot_control) QDEL_NULL(aiMulti) + QDEL_NULL(aiPDA) // QDEL_NULL(alert_control) malfhack = null current = null @@ -1036,9 +1037,9 @@ return /mob/living/silicon/ai/spawned/Initialize(mapload, datum/ai_laws/L, mob/target_ai) - . = ..() if(!target_ai) target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct + . = ..() /mob/living/silicon/ai/proc/camera_visibility(mob/camera/aiEye/moved_eye) GLOB.cameranet.visibility(moved_eye, client, all_eyes, USE_STATIC_OPAQUE) diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 435d1853f68e..17875235785f 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -85,12 +85,15 @@ var/icon/custom_holoform_icon /mob/living/silicon/pai/Destroy() + QDEL_NULL(signaler) + QDEL_NULL(pda) QDEL_NULL(internal_instrument) if (loc != card) card.forceMove(drop_location()) card.pai = null card.cut_overlays() card.add_overlay("pai-off") + card = null GLOB.pai_list -= src return ..() @@ -111,10 +114,9 @@ //PDA pda = new(src) - spawn(5) - pda.ownjob = "pAI Messenger" - pda.owner = text("[]", src) - pda.name = pda.owner + " (" + pda.ownjob + ")" + pda.ownjob = "pAI Messenger" + pda.owner = text("[]", src) + pda.name = pda.owner + " (" + pda.ownjob + ")" possible_chassis = typelist(NAMEOF(src, possible_chassis), list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE, "fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "borgi" = FALSE , diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 232c5b4195aa..c35b27f7c947 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -116,17 +116,19 @@ if(connected_ai) set_connected_ai(null) if(shell) //??? why would you give an ai radio keys? - GLOB.available_ai_shells -= src + revert_shell() else if(T && istype(radio) && istype(radio.keyslot)) radio.keyslot.forceMove(T) radio.keyslot = null + QDEL_LIST(upgrades) QDEL_NULL(wires) QDEL_NULL(module) QDEL_NULL(eye_lights) QDEL_NULL(inv1) QDEL_NULL(inv2) QDEL_NULL(inv3) + QDEL_NULL(aiPDA) cell = null return ..() @@ -618,7 +620,7 @@ /mob/living/silicon/robot/proc/SetLockdown(state = TRUE) // They stay locked down if their wire is cut. - if(wires.is_cut(WIRE_LOCKDOWN)) + if(wires?.is_cut(WIRE_LOCKDOWN)) state = TRUE if(state) throw_alert("locked", /atom/movable/screen/alert/locked) @@ -692,7 +694,7 @@ // set_light_color(COLOR_RED) //This should only matter for doomsday borgs, as any other time the lamp will be off and the color not seen // set_light_range(1) //Again, like above, this only takes effect when the light is forced on by doomsday mode. lamp_enabled = FALSE - lampButton.update_icon() + lampButton?.update_icon() update_icons() return set_light(lamp_intensity, l_color = (lamp_doom? COLOR_RED : lamp_color)) @@ -700,7 +702,7 @@ // set_light_color(lamp_doom? COLOR_RED : lamp_color) //Red for doomsday killborgs, borg's choice otherwise // set_light_on(TRUE) lamp_enabled = TRUE - lampButton.update_icon() + lampButton?.update_icon() update_icons() /mob/living/silicon/robot/proc/deconstruct() @@ -1139,6 +1141,7 @@ for(var/obj/item/borg/upgrade/ai/boris in src) //A player forced reset of a borg would drop the module before this is called, so this is for catching edge cases qdel(boris) + upgrades -= boris shell = FALSE GLOB.available_ai_shells -= src name = "Unformatted Cyborg-[ident]" diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index c3ae3898ddc8..cde12b545937 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -53,21 +53,21 @@ lasercolor = created_lasercolor icon_state = "[lasercolor]ed209[on]" set_weapon() //giving it the right projectile and firing sound. - spawn(3) - var/datum/job/detective/J = new/datum/job/detective - access_card.access += J.get_access() - prev_access = access_card.access - if(lasercolor) - shot_delay = 6//Longer shot delay because JESUS CHRIST - check_records = 0//Don't actively target people set to arrest - arrest_type = 1//Don't even try to cuff - bot_core.req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) - arrest_type = 1 - if((lasercolor == "b") && (name == "\improper ED-209 Security Robot"))//Picks a name if there isn't already a custome one - name = pick("BLUE BALLER","SANIC","BLUE KILLDEATH MURDERBOT") - if((lasercolor == "r") && (name == "\improper ED-209 Security Robot")) - name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT") + var/datum/job/detective/J = new /datum/job/detective + access_card.access += J.get_access() + prev_access = access_card.access + + if(lasercolor) + shot_delay = 6//Longer shot delay because JESUS CHRIST + check_records = 0//Don't actively target people set to arrest + arrest_type = 1//Don't even try to cuff + bot_core.req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) + arrest_type = 1 + if((lasercolor == "b") && (name == "\improper ED-209 Security Robot"))//Picks a name if there isn't already a custome one + name = pick("BLUE BALLER","SANIC","BLUE KILLDEATH MURDERBOT") + if((lasercolor == "r") && (name == "\improper ED-209 Security Robot")) + name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT") //SECHUD var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] diff --git a/code/modules/mob/living/simple_animal/bot/firebot.dm b/code/modules/mob/living/simple_animal/bot/firebot.dm index 0b97a553b2fe..b681fa895f87 100644 --- a/code/modules/mob/living/simple_animal/bot/firebot.dm +++ b/code/modules/mob/living/simple_animal/bot/firebot.dm @@ -45,9 +45,12 @@ var/datum/job/engineer/J = new/datum/job/engineer access_card.access += J.get_access() prev_access = access_card.access - create_extinguisher() +/mob/living/simple_animal/bot/firebot/Destroy() + QDEL_NULL(internal_ext) + return ..() + /mob/living/simple_animal/bot/firebot/bot_reset() create_extinguisher() diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index ed47611a8f03..1bccdb199236 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -68,8 +68,8 @@ /mob/living/simple_animal/bot/mulebot/Destroy() unload(0) - qdel(wires) - wires = null + QDEL_NULL(wires) + QDEL_NULL(cell) return ..() /mob/living/simple_animal/bot/mulebot/proc/set_id(new_id) diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 7893f7b26b3a..ed2bdf75af18 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -202,7 +202,7 @@ /mob/living/simple_animal/cow/random/Initialize(mapload) milk_reagent = get_random_reagent_id() //this has a blacklist so don't worry about romerol cows, etc - ..() + . = ..() //Wisdom cow, speaks and bestows great wisdoms /mob/living/simple_animal/cow/wisdom diff --git a/code/modules/mob/living/simple_animal/guardian/types/support.dm b/code/modules/mob/living/simple_animal/guardian/types/support.dm index 78225a458c73..5243905b66ff 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/support.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/support.dm @@ -98,7 +98,7 @@ /obj/structure/receiving_pad/New(loc, mob/living/simple_animal/hostile/guardian/healer/G) . = ..() - if(G.guardiancolor) + if(G?.guardiancolor) add_atom_colour(G.guardiancolor, FIXED_COLOUR_PRIORITY) /obj/structure/receiving_pad/proc/disappear() diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index 23a0d44d56f8..337334e15cbd 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -396,10 +396,6 @@ action_icon_state = "wrap_0" action_background_icon_state = "bg_alien" -/obj/effect/proc_holder/wrap/Initialize(mapload) - . = ..() - action = new(src) - /obj/effect/proc_holder/wrap/update_icon() action.button_icon_state = "wrap_[active]" action.UpdateButtonIcon() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 3369588915b6..16d380064dbe 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -62,6 +62,10 @@ Difficulty: Medium internal = new/obj/item/gps/internal/miner(src) miner_saw = new(src) +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Destroy() + QDEL_NULL(miner_saw) + return ..() + /datum/action/innate/megafauna_attack/dash name = "Dash To Target" icon_icon = 'icons/mob/actions/actions_items.dmi' diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 9e8497f96819..70faca799489 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -252,6 +252,8 @@ Difficulty: Very Hard var/list/stored_items = list() var/list/blacklist = list() +GLOBAL_VAR(blackbox_smartfridge) + /obj/machinery/smartfridge/black_box/ComponentInitialize() . = ..() AddElement(/datum/element/update_icon_blocker) @@ -266,11 +268,10 @@ Difficulty: Very Hard /obj/machinery/smartfridge/black_box/Initialize(mapload) . = ..() - var/static/obj/machinery/smartfridge/black_box/current - if(current && current != src) + if(GLOB.blackbox_smartfridge && GLOB.blackbox_smartfridge != src) qdel(src, force=TRUE) return - current = src + GLOB.blackbox_smartfridge = src ReadMemory() /obj/machinery/smartfridge/black_box/process() @@ -317,6 +318,8 @@ Difficulty: Very Hard if(force) for(var/thing in src) qdel(thing) + if(GLOB.blackbox_smartfridge == src) + GLOB.blackbox_smartfridge = null return ..() else return QDEL_HINT_LETMELIVE diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm index 06b10a34c29e..dd38a80f12bf 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm @@ -57,7 +57,8 @@ /mob/living/simple_animal/hostile/asteroid/curseblob/proc/check_for_target() if(QDELETED(set_target) || set_target.stat != CONSCIOUS || z != set_target.z) - qdel(src) + if(!QDELETED(src)) + qdel(src) return TRUE /mob/living/simple_animal/hostile/asteroid/curseblob/GiveTarget(new_target) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm index 7b21ce6a6282..fae2e1fedffe 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm @@ -204,7 +204,7 @@ var/mob/living/simple_animal/hostile/asteroid/elite/herald/my_master = null /mob/living/simple_animal/hostile/asteroid/elite/herald/mirror/Initialize(mapload) - ..() + . = ..() toggle_ai(AI_OFF) /mob/living/simple_animal/hostile/asteroid/elite/herald/mirror/Destroy() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm index 87a5010dceca..a3ad13316a7f 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm @@ -130,11 +130,6 @@ /obj/item/udder/gutlunch name = "nutrient sac" -/obj/item/udder/gutlunch/Initialize(mapload) - . = ..() - reagents = new(50) - reagents.my_atom = src - /obj/item/udder/gutlunch/generateMilk() if(prob(60)) reagents.add_reagent(/datum/reagent/consumable/cream, rand(2, 5)) diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm index 97f4a0a5fc41..4cfba0c74c7e 100644 --- a/code/modules/mob/living/simple_animal/hostile/wizard.dm +++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm @@ -60,6 +60,12 @@ blink.outer_tele_radius = 3 AddSpell(blink) +/mob/living/simple_animal/hostile/wizard/Destroy() + QDEL_NULL(fireball) + QDEL_NULL(mm) + QDEL_NULL(blink) + return ..() + /mob/living/simple_animal/hostile/wizard/handle_automated_action() . = ..() INVOKE_ASYNC(src, .proc/AutomatedCast) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index f80866d97e21..f286922af8ec 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -23,6 +23,7 @@ remove_from_mob_list() remove_from_dead_mob_list() remove_from_alive_mob_list() + QDEL_LIST(mob_spell_list) GLOB.all_clockwork_mobs -= src focus = null LAssailant = null @@ -39,6 +40,8 @@ qdel(cc) client_colours = null ghostize() + if(mind?.current == src) //Let's just be safe yeah? This will occasionally be cleared, but not always. Can't do it with ghostize without changing behavior + mind.set_current(null) ..() return QDEL_HINT_HARDDEL diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index e2345a638457..e1733bb6f997 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -71,7 +71,6 @@ for(var/H in all_components) var/obj/item/computer_hardware/CH = all_components[H] if(CH.holder == src) - CH.on_remove(src) CH.holder = null all_components.Remove(CH.device_type) qdel(CH) diff --git a/code/modules/modular_computers/file_system/programs/signaler.dm b/code/modules/modular_computers/file_system/programs/signaler.dm index dfbef9f6d6df..bf309ecdf463 100644 --- a/code/modules/modular_computers/file_system/programs/signaler.dm +++ b/code/modules/modular_computers/file_system/programs/signaler.dm @@ -19,6 +19,11 @@ set_frequency(signal_frequency) return ..() +/datum/computer_file/program/signaler/Destroy() + SSradio.remove_object(src, signal_frequency) + radio_connection = null + return ..() + /datum/computer_file/program/signaler/ui_data(mob/user) var/list/data = get_header_data() data["frequency"] = signal_frequency diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm index 8740b59b3523..455202a41415 100644 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -10,6 +10,10 @@ var/obj/item/aicard/stored_card var/locked = FALSE +/obj/item/computer_hardware/ai_slot/Destroy() + QDEL_NULL(stored_card) + return ..() + ///What happens when the intellicard is removed (or deleted) from the module, through try_eject() or not. /obj/item/computer_hardware/ai_slot/Exited(atom/movable/gone, direction) if(stored_card == gone) @@ -55,7 +59,7 @@ if(Adjacent(user)) user.put_in_hands(stored_card) else - stored_card.forceMove(drop_location()) + stored_card.forceMove(get_turf(src)) return TRUE return FALSE diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 27d3546ca24f..355e5049e10e 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -16,7 +16,7 @@ battery = new battery_type(src) /obj/item/computer_hardware/battery/Destroy() - battery = null + QDEL_NULL(battery) return ..() ///What happens when the battery is removed (or deleted) from the module, through try_eject() or not. @@ -59,7 +59,7 @@ user.put_in_hands(battery) to_chat(user, span_notice("You detach \the [battery] from \the [src].")) else - battery.forceMove(drop_location()) + battery.forceMove(get_turf(src)) return TRUE /obj/item/stock_parts/cell/computer diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index 13f1b3bbc9b3..0a4c9cf8e1df 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -94,7 +94,7 @@ if(user && !issilicon(user) && in_range(src, user)) user.put_in_hands(stored_card) else - stored_card.forceMove(drop_location()) + stored_card.forceMove(get_turf(src)) to_chat(user, span_notice("You remove the card from \the [src].")) playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) diff --git a/code/modules/ninja/suit/suit.dm b/code/modules/ninja/suit/suit.dm index 44ff5c709893..4ed9b2fa2526 100644 --- a/code/modules/ninja/suit/suit.dm +++ b/code/modules/ninja/suit/suit.dm @@ -84,12 +84,14 @@ cell.icon_state = "bscell" /obj/item/clothing/suit/space/space_ninja/Initialize(mapload) - START_PROCESSING(SSobj, src) - return ..() + START_PROCESSING(SSobj, src) + return ..() /obj/item/clothing/suit/space/space_ninja/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() + QDEL_NULL(spark_system) + QDEL_NULL(cell) + STOP_PROCESSING(SSobj, src) + return ..() // Power usage /obj/item/clothing/suit/space/space_ninja/process(delta_time) diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm index f46d4bf02947..d19fe23da654 100644 --- a/code/modules/paperwork/contract.dm +++ b/code/modules/paperwork/contract.dm @@ -92,6 +92,8 @@ /obj/item/paper/contract/infernal/New(atom/loc, mob/living/nTarget, datum/mind/nOwner) ..() + if(!nOwner || !nTarget) + return owner = nOwner devil_datum = owner.has_antag_datum(/datum/antagonist/devil) target = nTarget diff --git a/code/modules/pool/pool_controller.dm b/code/modules/pool/pool_controller.dm index f2a3b23e5715..942fb2eeb63a 100644 --- a/code/modules/pool/pool_controller.dm +++ b/code/modules/pool/pool_controller.dm @@ -80,6 +80,7 @@ linked_turfs.Cut() mobs_in_pool.Cut() mist_off() + QDEL_NULL(wires) return ..() /obj/machinery/pool/controller/proc/scan_things() diff --git a/code/modules/pool/pool_drain.dm b/code/modules/pool/pool_drain.dm index 09afe09cd1f1..9843b5b24996 100644 --- a/code/modules/pool/pool_drain.dm +++ b/code/modules/pool/pool_drain.dm @@ -31,8 +31,9 @@ /obj/machinery/pool/drain/Destroy() STOP_PROCESSING(SSfastprocess, src) - controller.linked_drain = null - controller = null + if(controller) + controller.linked_drain = null + controller = null whirling_mobs = null return ..() @@ -129,8 +130,9 @@ var/obj/machinery/pool/controller/controller /obj/machinery/pool/filter/Destroy() - controller.linked_filter = null - controller = null + if(controller) + controller.linked_filter = null + controller = null return ..() /obj/machinery/pool/filter/emag_act(mob/living/user) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 9896cc4088bb..f0fd7ebb46b9 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -303,17 +303,18 @@ if(malfai && operating) malfai.malf_picker.processing_time = clamp(malfai.malf_picker.processing_time - 10,0,1000) - area.power_light = FALSE - area.power_equip = FALSE - area.power_environ = FALSE - area.power_change() - area.poweralert(FALSE, src) + if(area) + area.power_light = FALSE + area.power_equip = FALSE + area.power_environ = FALSE + area.power_change() + area.poweralert(FALSE, src) if(occupier) malfvacate(1) - qdel(wires) - wires = null + if(wires) + QDEL_NULL(wires) if(cell) - qdel(cell) + QDEL_NULL(cell) if(terminal) disconnect_terminal() . = ..() diff --git a/code/modules/power/reactor/rbmk.dm b/code/modules/power/reactor/rbmk.dm index cea10c6116cb..18f9a9cf555c 100644 --- a/code/modules/power/reactor/rbmk.dm +++ b/code/modules/power/reactor/rbmk.dm @@ -552,6 +552,11 @@ The reactor CHEWS through moderator. It does not do this slowly. Be very careful . = ..() addtimer(CALLBACK(src, .proc/link_to_reactor), 10 SECONDS) +/obj/machinery/computer/reactor/Destroy() + reactor = null + return ..() + + /obj/machinery/computer/reactor/wrench_act(mob/living/user, obj/item/I) to_chat(user, "You start [anchored ? "un" : ""]securing [name]...") if(I.use_tool(src, user, 40, volume=75)) @@ -728,6 +733,11 @@ The reactor CHEWS through moderator. It does not do this slowly. Be very careful . = ..() radio_connection = SSradio.add_object(src, FREQ_RBMK_CONTROL,filter=RADIO_ATMOSIA) +/obj/machinery/computer/reactor/pump/Destroy() + SSradio.remove_object(src, FREQ_RBMK_CONTROL) + radio_connection = null + return ..() + /obj/machinery/computer/reactor/pump/proc/signal(power, set_output_pressure=null) var/datum/signal/signal if(!set_output_pressure) //Yes this is stupid, but technically if you pass through "set_output_pressure" onto the signal, it'll always try and set its output pressure and yeahhh... diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index 7c6b1cc92248..f9ed50bb98d5 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -13,12 +13,16 @@ interaction_flags_machine = NONE light_range = 4 layer = ABOVE_OBJ_LAYER - var/obj/machinery/field/generator/FG1 = null - var/obj/machinery/field/generator/FG2 = null + var/obj/machinery/field/generator/field_gen_1 = null + var/obj/machinery/field/generator/field_gen_2 = null /obj/machinery/field/containment/Destroy() - FG1.fields -= src - FG2.fields -= src + if(field_gen_1) + field_gen_1.fields -= src + field_gen_1 = null + if(field_gen_2) + field_gen_2.fields -= src + field_gen_2 = null return ..() /obj/machinery/field/containment/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) @@ -46,12 +50,12 @@ return FALSE /obj/machinery/field/containment/attack_animal(mob/living/simple_animal/M) - if(!FG1 || !FG2) + if(!field_gen_1 || !field_gen_2) qdel(src) return if(ismegafauna(M)) M.visible_message("[M] glows fiercely as the containment field flickers out!") - FG1.calc_power(INFINITY) //rip that 'containment' field + field_gen_1.calc_power(INFINITY) //rip that 'containment' field M.adjustHealth(-M.obj_damage) else ..() @@ -68,12 +72,12 @@ /obj/machinery/field/containment/proc/set_master(master1,master2) if(!master1 || !master2) return FALSE - FG1 = master1 - FG2 = master2 + field_gen_1 = master1 + field_gen_2 = master2 return TRUE /obj/machinery/field/containment/shock(mob/living/user) - if(!FG1 || !FG2) + if(!field_gen_1 || !field_gen_2) qdel(src) return FALSE ..() diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 8f9a7805c624..338e37e4ee1a 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -110,6 +110,7 @@ log_game("Emitter deleted at [AREACOORD(T)]") investigate_log("deleted at [AREACOORD(T)]", INVESTIGATE_SINGULO) QDEL_NULL(sparks) + QDEL_NULL(wires) return ..() /obj/machinery/power/emitter/update_icon_state() diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index c32bbc0c8684..c0505e2a9baf 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -28,6 +28,12 @@ wires = new /datum/wires/tesla_coil(src) linked_techweb = SSresearch.science_tech +/obj/machinery/power/tesla_coil/Destroy() + QDEL_NULL(wires) + linked_techweb = null + return ..() + + /obj/machinery/power/tesla_coil/RefreshParts() var/power_multiplier = 0 zap_cooldown = 100 diff --git a/code/modules/projectiles/ammunition/energy/portal.dm b/code/modules/projectiles/ammunition/energy/portal.dm index 7bb10da0cc24..749ac8c3a414 100644 --- a/code/modules/projectiles/ammunition/energy/portal.dm +++ b/code/modules/projectiles/ammunition/energy/portal.dm @@ -2,8 +2,8 @@ projectile_type = /obj/item/projectile/beam/wormhole e_cost = 0 fire_sound = 'sound/weapons/pulse3.ogg' - var/obj/item/gun/energy/wormhole_projector/gun = null select_name = "blue" + var/datum/weakref/gun /obj/item/ammo_casing/energy/wormhole/orange projectile_type = /obj/item/projectile/beam/wormhole/orange @@ -11,7 +11,7 @@ /obj/item/ammo_casing/energy/wormhole/Initialize(mapload, obj/item/gun/energy/wormhole_projector/wh) . = ..() - gun = wh + gun = WEAKREF(wh) /obj/item/ammo_casing/energy/wormhole/throw_proj() . = ..() diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 236143dc43b7..8f47514038d5 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -150,7 +150,7 @@ AddComponent(/datum/component/automatic_fire, fire_delay) /obj/item/gun/Destroy() - if(pin) + if(isobj(pin)) QDEL_NULL(pin) if(gun_light) QDEL_NULL(gun_light) @@ -162,6 +162,8 @@ QDEL_NULL(azoom) if(firemode_action) QDEL_NULL(firemode_action) + if(isatom(suppressed)) + QDEL_NULL(suppressed) return ..() /obj/item/gun/examine(mob/user) diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index 53ba6d6a9211..8635cc7ff501 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -49,9 +49,9 @@ /obj/item/gun/ballistic/automatic/pistol/modular/update_overlays() . = ..() if(magazine && suppressed) - . += "[unique_reskin[current_skin]["icon_state"]]-magazine-sup" //Yes, this means the default iconstate can't have a magazine overlay + . += "[current_skin ? unique_reskin[current_skin]["icon_state"] : initial(icon_state)]-magazine-sup" //Yes, this means the default iconstate can't have a magazine overlay else if (magazine) - . += "[unique_reskin[current_skin]["icon_state"]]-magazine" + . += "[current_skin ? unique_reskin[current_skin]["icon_state"] : initial(icon_state)]-magazine" /obj/item/gun/ballistic/automatic/pistol/m1911 name = "\improper M1911" diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 03dfc46713d6..b6915303330b 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -69,6 +69,8 @@ AddElement(/datum/element/update_icon_updates_onmob) /obj/item/gun/energy/Destroy() + if(cell) + QDEL_NULL(cell) STOP_PROCESSING(SSobj, src) return ..() diff --git a/code/modules/projectiles/guns/energy/laser_gatling.dm b/code/modules/projectiles/guns/energy/laser_gatling.dm index 65d525b6382a..3eb932529a7c 100644 --- a/code/modules/projectiles/guns/energy/laser_gatling.dm +++ b/code/modules/projectiles/guns/energy/laser_gatling.dm @@ -23,6 +23,9 @@ START_PROCESSING(SSfastprocess, src) /obj/item/minigunpack/Destroy() + if(!QDELETED(gun)) + qdel(gun) + gun = null STOP_PROCESSING(SSfastprocess, src) return ..() @@ -120,6 +123,12 @@ return ..() +/obj/item/gun/energy/minigun/Destroy() + if(!QDELETED(ammo_pack)) + qdel(ammo_pack) + ammo_pack = null + return ..() + /obj/item/gun/energy/minigun/attack_self(mob/living/user) return diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 67e7b58bd3e5..35d5d213e26a 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -243,10 +243,10 @@ for(var/i in 1 to ammo_type.len) var/obj/item/ammo_casing/energy/wormhole/W = ammo_type[i] if(istype(W)) - W.gun = src + W.gun = WEAKREF(src) var/obj/item/projectile/beam/wormhole/WH = W.BB if(istype(WH)) - WH.gun = src + WH.gun = WEAKREF(src) /obj/item/gun/energy/wormhole_projector/process_chamber() ..() diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index 4192f038c23b..2628ddce8615 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -52,7 +52,8 @@ /obj/item/gun/magic/Initialize(mapload) . = ..() charges = max_charges - chambered = new ammo_type(src) + if(ammo_type) + chambered = new ammo_type(src) if(can_charge) START_PROCESSING(SSobj, src) diff --git a/code/modules/projectiles/guns/magic/motivation.dm b/code/modules/projectiles/guns/magic/motivation.dm index db4c222619e4..e9e5c0ab3ad6 100644 --- a/code/modules/projectiles/guns/magic/motivation.dm +++ b/code/modules/projectiles/guns/magic/motivation.dm @@ -27,6 +27,10 @@ . = ..() judgementcut = new(src) +/obj/item/gun/magic/staff/motivation/Destroy() + QDEL_NULL(judgementcut) + . = ..() + //lets the user know that their judgement cuts are recharging /obj/item/gun/magic/staff/motivation/shoot_with_empty_chamber(mob/living/user as mob|obj) to_chat(user, "Judgement Cut is recharging.") diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm index 6f7099883412..41bff2ece4b3 100644 --- a/code/modules/projectiles/guns/misc/beam_rifle.dm +++ b/code/modules/projectiles/guns/misc/beam_rifle.dm @@ -507,8 +507,8 @@ duration = 0 . = ..() if(!generation) //first one - QDEL_LIST(gun.current_tracers) - gun.current_tracers += . + QDEL_LIST(gun?.current_tracers) + gun?.current_tracers += . /obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam tracer_type = /obj/effect/projectile/tracer/tracer/aiming diff --git a/code/modules/projectiles/projectile/special/curse.dm b/code/modules/projectiles/projectile/special/curse.dm index 89b1ecba4c6b..0609ba849696 100644 --- a/code/modules/projectiles/projectile/special/curse.dm +++ b/code/modules/projectiles/projectile/special/curse.dm @@ -20,6 +20,10 @@ handedness = prob(50) icon_state = "cursehand[handedness]" +/obj/item/projectile/curse_hand/Destroy() + QDEL_NULL(arm) + . = ..() + /obj/item/projectile/curse_hand/update_icon_state() icon_state = "[initial(icon_state)][handedness]" @@ -43,9 +47,10 @@ for(var/obj/effect/temp_visual/dir_setting/curse/grasp_portal/G in starting) qdel(G) new /obj/effect/temp_visual/dir_setting/curse/grasp_portal/fading(starting, dir) - var/datum/beam/D = starting.Beam(T, icon_state = "curse[handedness]", time = 32, maxdistance = INFINITY, beam_type=/obj/effect/ebeam/curse_arm, beam_sleep_time = 1) - for(var/b in D.elements) - var/obj/effect/ebeam/B = b - animate(B, alpha = 0, time = 32) + var/datum/beam/D = starting?.Beam(T, icon_state = "curse[handedness]", time = 32, maxdistance = INFINITY, beam_type=/obj/effect/ebeam/curse_arm, beam_sleep_time = 1) + if(D) + for(var/b in D.elements) + var/obj/effect/ebeam/B = b + animate(B, alpha = 0, time = 32) return ..() diff --git a/code/modules/projectiles/projectile/special/gravity.dm b/code/modules/projectiles/projectile/special/gravity.dm index ba21ecb28c08..f5572f2f1bb1 100644 --- a/code/modules/projectiles/projectile/special/gravity.dm +++ b/code/modules/projectiles/projectile/special/gravity.dm @@ -13,7 +13,7 @@ . = ..() var/obj/item/ammo_casing/energy/gravity/G = loc if(istype(G)) - power = min(G.gun.power, 15) + power = min(G.gun?.power, 15) /obj/item/projectile/gravity/on_hit() . = ..() diff --git a/code/modules/projectiles/projectile/special/hallucination.dm b/code/modules/projectiles/projectile/special/hallucination.dm index 19fd13857aba..8ad149c8d5e5 100644 --- a/code/modules/projectiles/projectile/special/hallucination.dm +++ b/code/modules/projectiles/projectile/special/hallucination.dm @@ -28,7 +28,7 @@ hal_target.client.images += fake_icon /obj/item/projectile/hallucination/Destroy() - if(hal_target.client) + if(hal_target?.client) hal_target.client.images -= fake_icon QDEL_NULL(fake_icon) return ..() diff --git a/code/modules/projectiles/projectile/special/wormhole.dm b/code/modules/projectiles/projectile/special/wormhole.dm index aaf9f542d38e..99410d38e4a2 100644 --- a/code/modules/projectiles/projectile/special/wormhole.dm +++ b/code/modules/projectiles/projectile/special/wormhole.dm @@ -5,12 +5,13 @@ damage = 0 nodamage = TRUE pass_flags = PASSGLASS | PASSTABLE | PASSGRILLE | PASSMOB - var/obj/item/gun/energy/wormhole_projector/gun color = "#33CCFF" tracer_type = /obj/effect/projectile/tracer/wormhole impact_type = /obj/effect/projectile/impact/wormhole muzzle_type = /obj/effect/projectile/muzzle/wormhole hitscan = TRUE + //Weakref to the thing that shot us + var/datum/weakref/gun /obj/item/projectile/beam/wormhole/orange name = "orange bluespace beam" @@ -23,7 +24,8 @@ /obj/item/projectile/beam/wormhole/on_hit(atom/target) - if(!gun) + var/obj/item/gun/energy/wormhole_projector/projector = gun.resolve() + if(!projector) qdel(src) return BULLET_ACT_BLOCK - gun.create_portal(src, get_turf(src)) + projector.create_portal(src, get_turf(src)) diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 77d713047a94..a33785549773 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -112,7 +112,6 @@ value_multiplier = new_value /datum/reagents/Destroy() - . = ..() //We're about to delete all reagents, so lets cleanup addiction_list.Cut() var/list/cached_reagents = reagent_list @@ -124,6 +123,7 @@ if(my_atom && my_atom.reagents == src) my_atom.reagents = null my_atom = null + return ..() // Used in attack logs for reagents in pills and such /datum/reagents/proc/log_list() diff --git a/code/modules/reagents/reagent_containers/borghypo.dm b/code/modules/reagents/reagent_containers/borghypo.dm index 0b3e5bcf75ac..10fe47f443bc 100644 --- a/code/modules/reagents/reagent_containers/borghypo.dm +++ b/code/modules/reagents/reagent_containers/borghypo.dm @@ -43,6 +43,7 @@ Borg Hypospray START_PROCESSING(SSobj, src) /obj/item/reagent_containers/borghypo/Destroy() + QDEL_LIST(reagent_list) STOP_PROCESSING(SSobj, src) return ..() diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index 50db4f061013..df396c61cf4c 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -203,7 +203,7 @@ /obj/item/reagent_containers/pill/neurine name = "neurine pill" desc = "Used to treat non-severe mental traumas." - list_reagents = list("neurine" = 10) + list_reagents = list(/datum/reagent/medicine/neurine = 10) icon_state = "pill22" roundstart = TRUE diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm index dc07a5a1846e..6d43912d4db0 100644 --- a/code/modules/research/techweb/_techweb.dm +++ b/code/modules/research/techweb/_techweb.dm @@ -212,7 +212,6 @@ /datum/techweb/proc/add_design(datum/design/design, custom = FALSE) if(!istype(design)) return FALSE - researched_designs[design.id] = design researched_designs[design.id] = TRUE if(custom) custom_designs[design.id] = TRUE diff --git a/code/modules/ruins/lavalandruin_code/puzzle.dm b/code/modules/ruins/lavalandruin_code/puzzle.dm index 136acc7da780..2afdb1f101a6 100644 --- a/code/modules/ruins/lavalandruin_code/puzzle.dm +++ b/code/modules/ruins/lavalandruin_code/puzzle.dm @@ -242,7 +242,8 @@ /obj/structure/puzzle_element/Moved() . = ..() - source.validate() + if(source) + source.validate() //Admin abuse version so you can pick the icon before it sets up /obj/effect/sliding_puzzle/admin diff --git a/code/modules/smithing/anvil.dm b/code/modules/smithing/anvil.dm index 197b39272d1b..6ecc087f48c6 100644 --- a/code/modules/smithing/anvil.dm +++ b/code/modules/smithing/anvil.dm @@ -69,7 +69,7 @@ RECIPE_PIKE = /obj/item/smithing/pikehead) /obj/structure/anvil/Initialize(mapload) - ..() + . = ..() currentquality = anvilquality /obj/structure/anvil/attackby(obj/item/I, mob/user) diff --git a/code/modules/smithing/finished_items.dm b/code/modules/smithing/finished_items.dm index 97618df8fc2a..3b139bb61d0a 100644 --- a/code/modules/smithing/finished_items.dm +++ b/code/modules/smithing/finished_items.dm @@ -8,6 +8,7 @@ material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS total_mass = TOTAL_MASS_MEDIEVAL_WEAPON //yeah ok slot_flags = ITEM_SLOT_BELT + obj_flags = UNIQUE_RENAME w_class = WEIGHT_CLASS_NORMAL force = 6 lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' @@ -19,7 +20,7 @@ var/wield_force = 15 /obj/item/melee/smith/Initialize(mapload) - ..() + . = ..() if(desc == "cringe") desc = "A handmade [name]." overlay = mutable_appearance(icon, overlay_state) @@ -56,7 +57,7 @@ sharpness = SHARP_POINTY//it doesnt have a blade it has a point /obj/item/mining_scanner/prospector/Initialize(mapload) - ..() + . = ..() var/mutable_appearance/overlay desc = "A handmade [name]." overlay = mutable_appearance(icon, "minihandle") @@ -74,7 +75,7 @@ sharpness = SHARP_POINTY /obj/item/pickaxe/smithed/Initialize(mapload) - ..() + . = ..() desc = "A handmade [name]." var/mutable_appearance/overlay overlay = mutable_appearance(icon, "stick") @@ -95,7 +96,7 @@ sharpness = SHARP_EDGED //it cuts through the earth /obj/item/shovel/smithed/Initialize(mapload) - ..() + . = ..() desc = "A handmade [name]." var/mutable_appearance/overlay overlay = mutable_appearance(icon, "shovelhandle") diff --git a/code/modules/smithing/furnace.dm b/code/modules/smithing/furnace.dm index 70bff3203006..952f185550fe 100644 --- a/code/modules/smithing/furnace.dm +++ b/code/modules/smithing/furnace.dm @@ -11,13 +11,13 @@ /obj/structure/furnace/Initialize(mapload) - ..() + . = ..() create_reagents(250, TRANSPARENT) START_PROCESSING(SSobj, src) /obj/structure/furnace/Destroy() - ..() STOP_PROCESSING(SSobj, src) + return ..() /obj/structure/furnace/process() if(debug) diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 4be592b9198a..fc427757ea03 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -20,6 +20,12 @@ if(has_action) action = new base_action(src) +/obj/effect/proc_holder/Destroy() + QDEL_NULL(action) + if(ranged_ability_user) + remove_ranged_ability() + return ..() + /obj/effect/proc_holder/proc/on_gain(mob/living/user) return @@ -34,12 +40,6 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for the badmin verb for now -/obj/effect/proc_holder/Destroy() - QDEL_NULL(action) - if(ranged_ability_user) - remove_ranged_ability() - return ..() - /obj/effect/proc_holder/singularity_act() return diff --git a/code/modules/spells/spell_types/lichdom.dm b/code/modules/spells/spell_types/lichdom.dm index bf6051c87c9f..524d66da46d6 100644 --- a/code/modules/spells/spell_types/lichdom.dm +++ b/code/modules/spells/spell_types/lichdom.dm @@ -87,6 +87,9 @@ /obj/item/phylactery/Initialize(mapload, datum/mind/newmind) . = ..() + if(!newmind) + stack_trace("A phylactery was created with no target mind") + return INITIALIZE_HINT_QDEL mind = newmind name = "phylactery of [mind.name]" diff --git a/code/modules/spells/spell_types/shapeshift.dm b/code/modules/spells/spell_types/shapeshift.dm index 67c2e3e9411e..d7c6607f9bdc 100644 --- a/code/modules/spells/spell_types/shapeshift.dm +++ b/code/modules/spells/spell_types/shapeshift.dm @@ -95,7 +95,8 @@ src.source = source shape = loc if(!istype(shape)) - CRASH("shapeshift holder created outside mob/living") + stack_trace("shapeshift holder created outside mob/living") + return INITIALIZE_HINT_QDEL stored = caster if(stored.mind) stored.mind.transfer_to(shape) diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm index a23e16cf881c..d989a6009599 100644 --- a/code/modules/spells/spell_types/touch_attacks.dm +++ b/code/modules/spells/spell_types/touch_attacks.dm @@ -7,6 +7,14 @@ include_user = 1 range = -1 + +/obj/effect/proc_holder/spell/targeted/touch/Destroy() + remove_hand() + if(action?.owner) + var/mob/guy_who_needs_to_know = action.owner + to_chat(guy_who_needs_to_know, span_notice("The power of the spell dissipates from your hand.")) + return ..() + /obj/effect/proc_holder/spell/targeted/touch/proc/remove_hand(recharge = FALSE) QDEL_NULL(attached_hand) if(recharge) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index c4ab435ee0cf..5e4fb22904d0 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -636,6 +636,7 @@ /obj/item/bodypart/proc/update_limb(dropping_limb, mob/living/carbon/source) body_markings_list = list() var/mob/living/carbon/C + owner.create_weakref() if(source) C = source if(!original_owner) @@ -646,6 +647,9 @@ C = owner no_update = FALSE + if(!C) + return + if(HAS_TRAIT(C, TRAIT_HUSK) && is_organic_limb()) species_id = "husk" //overrides species_id dmg_overlay_type = "" //no damage overlay shown when husked diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 998473abe31b..215bd2370d4e 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -24,6 +24,11 @@ for(var/obj/item/I in contents) add_item(I) +/obj/item/organ/cyberimp/arm/Destroy() + QDEL_LIST(items_list) + QDEL_NULL(holder) + return ..() + /obj/item/organ/cyberimp/arm/proc/add_item(obj/item/I) if(I in items_list) return diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index d4a26633741f..5ab663934554 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -360,7 +360,7 @@ desc = "Something hecked up" /obj/item/organ/random/Initialize(mapload) - ..() + . = ..() var/list = list(/obj/item/organ/tongue, /obj/item/organ/brain, /obj/item/organ/heart, /obj/item/organ/liver, /obj/item/organ/ears, /obj/item/organ/eyes, /obj/item/organ/tail, /obj/item/organ/stomach) var/newtype = pick(list) new newtype(loc) diff --git a/code/modules/tcg/cards.dm b/code/modules/tcg/cards.dm index 7717a44410fd..8308c6ca9998 100644 --- a/code/modules/tcg/cards.dm +++ b/code/modules/tcg/cards.dm @@ -114,12 +114,15 @@ . = ..() if(!special) datum_type = new_datum - card_datum = new datum_type + if(datum_type) + card_datum = new datum_type + illegal = illegal_card + if(!card_datum) + return icon = card_datum.pack icon_state = card_datum.icon_state name = card_datum.name desc = card_datum.desc - illegal = illegal_card switch(card_datum.rarity) if("Common") @@ -378,8 +381,8 @@ var/static/radial_pickup = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_pickup") /obj/item/tcgcard_deck/Initialize(mapload) - . = ..() LoadComponent(/datum/component/storage/concrete/tcg) + . = ..() /obj/item/tcgcard_deck/ComponentInitialize() . = ..() diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index 1b6d5935b043..5a6954ba7c89 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -20,6 +20,9 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) // /obj/machinery/restaurant_portal, //Template type /obj/effect/mob_spawn, + /obj/effect/mob_spawn/alien, + /obj/effect/mob_spawn/alien/corpse, + /obj/effect/mob_spawn/alien/corpse/humanoid, //Template type // /obj/structure/holosign/robot_seat, //Singleton @@ -30,24 +33,43 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) // /obj/merge_conflict_marker, //briefcase launchpads erroring /obj/machinery/launchpad/briefcase, + // Needs mind + /obj/item/phylactery, + //Template type + /obj/item/genital_equipment, + //No ID to pass in + /obj/effect/spawner/structure/window/reinforced/tinted/electrochromatic, + // Needs proper args + /obj/effect/buildmode_line, + //Spawns it in the wall and shuttle controller runtimes (actually not caught in unit test) + /obj/effect/landmark/latejoin, + //Those DAMN SWARMERS ARE EATING EVERYTHING WHILE TEST IS RUNNING + /mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon, + // Randomly causes test to fail because of random movement + /obj/item/grenade/clusterbuster/segment, + // With 10% Spawns `while() ... sleep()` proc that causes her hat to harddel // TODO rewrite helmet code attack_self() and port modern /tg/ helmet code + /mob/living/carbon/monkey/angry, ) //Say it with me now, type template ignore += typesof(/obj/effect/mapping_helpers) //This turf existing is an error in and of itself ignore += typesof(/turf/baseturf_skipover) ignore += typesof(/turf/baseturf_bottom) + // Messes with test results by teleporting stuff out of location + ignore += typesof(/turf/open/space/transit) //This demands a borg, so we'll let if off easy - // ignore += typesof(/obj/item/modular_computer/pda/silicon) + ignore += typesof(/obj/item/modular_computer/tablet/integrated) //This one demands a computer, ditto ignore += typesof(/obj/item/modular_computer/processor) //Very finiky, blacklisting to make things easier ignore += typesof(/obj/item/poster/wanted) //This expects a seed, we can't pass it - // ignore += typesof(/obj/item/food/grown) + ignore += typesof(/obj/item/reagent_containers/food/snacks/grown) //Needs clients / mobs to observe it to exist. Also includes hallucinations. // ignore += typesof(/obj/effect/client_image_holder) + ignore += typesof(/obj/effect/hallucination) //Same to above. Needs a client / mob / hallucination to observe it to exist. - // ignore += typesof(/obj/projectile/hallucination) + ignore += typesof(/obj/item/projectile/hallucination) // ignore += typesof(/obj/item/hallucinated) //Can't pass in a thing to glow ignore += typesof(/obj/effect/abstract/eye_lighting) @@ -56,22 +78,30 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) ignore += typesof(/obj/effect/pod_landingzone) //We have a baseturf limit of 10, adding more than 10 baseturf helpers will kill CI, so here's a future edge case to fix. ignore += typesof(/obj/effect/baseturf_helper) + //No host to pass in + ignore += typesof(/obj/effect/abstract/proximity_checker) + //No owner to pass in + ignore += typesof(/obj/effect/abstract/parry) //No tauma to pass in ignore += typesof(/mob/camera/imaginary_friend) + //There's no shapeshift to hold + ignore += typesof(/obj/shapeshift_holder) //No pod to gondola ignore += typesof(/mob/living/simple_animal/pet/gondola/gondolapod) //No heart to give // ignore += typesof(/obj/structure/ethereal_crystal) //No linked console - // ignore += typesof(/mob/camera/ai_eye/remote/base_construction) + ignore += typesof(/mob/camera/aiEye/remote/base_construction) //See above - // ignore += typesof(/mob/camera/ai_eye/remote/shuttle_docker) + ignore += typesof(/mob/camera/aiEye/remote/shuttle_docker) //Hangs a ref post invoke async, which we don't support. Could put a qdeleted check but it feels hacky ignore += typesof(/obj/effect/anomaly/grav/high) //See above ignore += typesof(/obj/effect/timestop) + // See above + ignore += typesof(/obj/effect/domain_expansion) //Invoke async in init, skippppp - // ignore += typesof(/mob/living/silicon/robot/model) + ignore += typesof(/mob/living/silicon/robot/modules) //This lad also sleeps ignore += typesof(/obj/item/hilbertshotel) //this boi spawns turf changing stuff, and it stacks and causes pain. Let's just not @@ -100,6 +130,10 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) // ignore += typesof(/obj/structure/industrial_lift) // Runtimes if the associated machinery does not exist, but not the base type // ignore += subtypesof(/obj/machinery/airlock_controller) + // All of them sleep with CHECK_TICK and hang refs. //TODO: Port modern /tg/ techwebs + ignore += typesof(/obj/machinery/rnd/production) + // This one sleeps too in it's AI code + ignore += typesof(/mob/living/simple_animal/hostile/swarmer) var/list/cached_contents = spawn_at.contents.Copy() var/original_turf_type = spawn_at.type diff --git a/code/modules/vehicles/atv.dm b/code/modules/vehicles/atv.dm index cb32b1108386..1edb33b9e726 100644 --- a/code/modules/vehicles/atv.dm +++ b/code/modules/vehicles/atv.dm @@ -41,25 +41,29 @@ /obj/vehicle/ridden/atv/turret/Moved() . = ..() - if(turret) - turret.forceMove(get_turf(src)) - switch(dir) - if(NORTH) - turret.pixel_x = 0 - turret.pixel_y = 4 - turret.layer = ABOVE_MOB_LAYER - if(EAST) - turret.pixel_x = -12 - turret.pixel_y = 4 - turret.layer = OBJ_LAYER - if(SOUTH) - turret.pixel_x = 0 - turret.pixel_y = 4 - turret.layer = OBJ_LAYER - if(WEST) - turret.pixel_x = 12 - turret.pixel_y = 4 - turret.layer = OBJ_LAYER + if(!turret) + return + var/turf/our_turf = get_turf(src) + if(!our_turf) + return + turret.forceMove(our_turf) + switch(dir) + if(NORTH) + turret.pixel_x = 0 + turret.pixel_y = 4 + turret.layer = ABOVE_MOB_LAYER + if(EAST) + turret.pixel_x = -12 + turret.pixel_y = 4 + turret.layer = OBJ_LAYER + if(SOUTH) + turret.pixel_x = 0 + turret.pixel_y = 4 + turret.layer = OBJ_LAYER + if(WEST) + turret.pixel_x = 12 + turret.pixel_y = 4 + turret.layer = OBJ_LAYER /obj/vehicle/ridden/atv/snowmobile name = "snowmobile" diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm index f4ffda302e6e..ae76fd877d6c 100644 --- a/code/modules/vehicles/mecha/_mecha.dm +++ b/code/modules/vehicles/mecha/_mecha.dm @@ -871,7 +871,7 @@ if(pilot_mob && pilot_mob.Adjacent(src)) if(LAZYLEN(occupants)) return - LAZYADD(occupants, src) + LAZYADD(occupants, pilot_mob) pilot_mob.mecha = src pilot_mob.forceMove(src) update_icon() diff --git a/code/modules/vehicles/mecha/mech_fabricator.dm b/code/modules/vehicles/mecha/mech_fabricator.dm index 29cc88a56367..5d835995a92d 100644 --- a/code/modules/vehicles/mecha/mech_fabricator.dm +++ b/code/modules/vehicles/mecha/mech_fabricator.dm @@ -72,6 +72,11 @@ RefreshParts() //Recalculating local material sizes if the fab isn't linked return ..() +/obj/machinery/mecha_part_fabricator/Destroy() + QDEL_NULL(stored_research) + rmat = null + return ..() + /obj/machinery/mecha_part_fabricator/RefreshParts() var/T = 0 diff --git a/code/modules/vending/cola.dm b/code/modules/vending/cola.dm index f60c3fc2fa64..c2f2b16fe342 100644 --- a/code/modules/vending/cola.dm +++ b/code/modules/vending/cola.dm @@ -37,7 +37,7 @@ desc = "Uh oh!" /obj/machinery/vending/cola/random/Initialize(mapload) - ..() + . = ..() var/T = pick(subtypesof(/obj/machinery/vending/cola) - /obj/machinery/vending/cola/random) new T(loc) return INITIALIZE_HINT_QDEL diff --git a/code/modules/vending/snack.dm b/code/modules/vending/snack.dm index efb667049603..c85ec8a594c2 100644 --- a/code/modules/vending/snack.dm +++ b/code/modules/vending/snack.dm @@ -42,7 +42,7 @@ desc = "Uh oh!" /obj/machinery/vending/snack/random/Initialize(mapload) - ..() + . = ..() var/T = pick(subtypesof(/obj/machinery/vending/snack) - /obj/machinery/vending/snack/random) new T(loc) return INITIALIZE_HINT_QDEL From 692e2a51381d2f9ef2a36b35bc06d38ad7dfce8e Mon Sep 17 00:00:00 2001 From: KrissKr0ss <123323882+KrissKr0ss@users.noreply.github.com> Date: Fri, 18 Aug 2023 19:21:00 -0500 Subject: [PATCH 08/12] some fixes --- code/modules/clothing/spacesuits/chronosuit.dm | 4 ++-- code/modules/mob/living/carbon/alien/humanoid/queen.dm | 1 - .../mob/living/simple_animal/hostile/megafauna/bubblegum.dm | 4 ++-- code/modules/unit_tests/create_and_destroy.dm | 4 ---- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/code/modules/clothing/spacesuits/chronosuit.dm b/code/modules/clothing/spacesuits/chronosuit.dm index 5ec0fad2e632..064bdefd9ac4 100644 --- a/code/modules/clothing/spacesuits/chronosuit.dm +++ b/code/modules/clothing/spacesuits/chronosuit.dm @@ -32,8 +32,8 @@ var/teleporting = 0 var/phase_timer_id -/obj/item/clothing/suit/space/chronos/New() - ..() +/obj/item/clothing/suit/space/chronos/Initialize(mapload) + . = ..() teleport_now.chronosuit = src teleport_now.target = src diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 4205b4b9931e..31ed8821ba3d 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -53,7 +53,6 @@ /mob/living/carbon/alien/humanoid/royal/queen/Destroy() RemoveAbility(promote) QDEL_NULL(promote) - QDEL_NULL(small_sprite) return ..() /mob/living/carbon/alien/humanoid/royal/queen/create_internal_organs() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index def5dc8c76e1..6080161b9749 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -457,11 +457,11 @@ Difficulty: Hard deathsound = 'sound/effects/splat.ogg' /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Initialize(mapload) - ..() + . = ..() toggle_ai(AI_OFF) /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/charge(atom/chargeat = target, delay = 3, chargepast = 2) - ..() + . = ..() qdel(src) /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Destroy() diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index 5a6954ba7c89..06fbc678c659 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -35,8 +35,6 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) /obj/machinery/launchpad/briefcase, // Needs mind /obj/item/phylactery, - //Template type - /obj/item/genital_equipment, //No ID to pass in /obj/effect/spawner/structure/window/reinforced/tinted/electrochromatic, // Needs proper args @@ -116,8 +114,6 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) ignore += typesof(/obj/item/pinpointer/shuttle) //This spawns beams as a part of init, which can sleep past an async proc. This hangs a ref, and fucks us. It's only a problem here because the beam sleeps with CHECK_TICK // ignore += typesof(/obj/structure/alien/resin/flower_bud) - //Needs a linked mecha - // ignore += typesof(/obj/effect/skyfall_landingzone) //Expects a mob to holderize, we have nothing to give ignore += typesof(/obj/item/clothing/head/mob_holder) //Needs cards passed into the initilazation args From e985cf67c4593d1b78668b8ce730e26575f057f9 Mon Sep 17 00:00:00 2001 From: KrissKr0ss <123323882+KrissKr0ss@users.noreply.github.com> Date: Fri, 18 Aug 2023 19:29:09 -0500 Subject: [PATCH 09/12] Revert "some fixes" This reverts commit 692e2a51381d2f9ef2a36b35bc06d38ad7dfce8e. --- code/modules/clothing/spacesuits/chronosuit.dm | 4 ++-- code/modules/mob/living/carbon/alien/humanoid/queen.dm | 1 + .../mob/living/simple_animal/hostile/megafauna/bubblegum.dm | 4 ++-- code/modules/unit_tests/create_and_destroy.dm | 4 ++++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/code/modules/clothing/spacesuits/chronosuit.dm b/code/modules/clothing/spacesuits/chronosuit.dm index 064bdefd9ac4..5ec0fad2e632 100644 --- a/code/modules/clothing/spacesuits/chronosuit.dm +++ b/code/modules/clothing/spacesuits/chronosuit.dm @@ -32,8 +32,8 @@ var/teleporting = 0 var/phase_timer_id -/obj/item/clothing/suit/space/chronos/Initialize(mapload) - . = ..() +/obj/item/clothing/suit/space/chronos/New() + ..() teleport_now.chronosuit = src teleport_now.target = src diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 31ed8821ba3d..4205b4b9931e 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -53,6 +53,7 @@ /mob/living/carbon/alien/humanoid/royal/queen/Destroy() RemoveAbility(promote) QDEL_NULL(promote) + QDEL_NULL(small_sprite) return ..() /mob/living/carbon/alien/humanoid/royal/queen/create_internal_organs() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 6080161b9749..def5dc8c76e1 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -457,11 +457,11 @@ Difficulty: Hard deathsound = 'sound/effects/splat.ogg' /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Initialize(mapload) - . = ..() + ..() toggle_ai(AI_OFF) /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/charge(atom/chargeat = target, delay = 3, chargepast = 2) - . = ..() + ..() qdel(src) /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Destroy() diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index 06fbc678c659..5a6954ba7c89 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -35,6 +35,8 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) /obj/machinery/launchpad/briefcase, // Needs mind /obj/item/phylactery, + //Template type + /obj/item/genital_equipment, //No ID to pass in /obj/effect/spawner/structure/window/reinforced/tinted/electrochromatic, // Needs proper args @@ -114,6 +116,8 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) ignore += typesof(/obj/item/pinpointer/shuttle) //This spawns beams as a part of init, which can sleep past an async proc. This hangs a ref, and fucks us. It's only a problem here because the beam sleeps with CHECK_TICK // ignore += typesof(/obj/structure/alien/resin/flower_bud) + //Needs a linked mecha + // ignore += typesof(/obj/effect/skyfall_landingzone) //Expects a mob to holderize, we have nothing to give ignore += typesof(/obj/item/clothing/head/mob_holder) //Needs cards passed into the initilazation args From 91163860e23ad6e2f4145d92ad9429c32e0dad37 Mon Sep 17 00:00:00 2001 From: KrissKr0ss <123323882+KrissKr0ss@users.noreply.github.com> Date: Fri, 18 Aug 2023 19:30:37 -0500 Subject: [PATCH 10/12] bruh --- code/modules/mob/living/carbon/alien/humanoid/queen.dm | 1 - code/modules/unit_tests/create_and_destroy.dm | 4 ---- 2 files changed, 5 deletions(-) diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 4205b4b9931e..31ed8821ba3d 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -53,7 +53,6 @@ /mob/living/carbon/alien/humanoid/royal/queen/Destroy() RemoveAbility(promote) QDEL_NULL(promote) - QDEL_NULL(small_sprite) return ..() /mob/living/carbon/alien/humanoid/royal/queen/create_internal_organs() diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index 5a6954ba7c89..06fbc678c659 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -35,8 +35,6 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) /obj/machinery/launchpad/briefcase, // Needs mind /obj/item/phylactery, - //Template type - /obj/item/genital_equipment, //No ID to pass in /obj/effect/spawner/structure/window/reinforced/tinted/electrochromatic, // Needs proper args @@ -116,8 +114,6 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) ignore += typesof(/obj/item/pinpointer/shuttle) //This spawns beams as a part of init, which can sleep past an async proc. This hangs a ref, and fucks us. It's only a problem here because the beam sleeps with CHECK_TICK // ignore += typesof(/obj/structure/alien/resin/flower_bud) - //Needs a linked mecha - // ignore += typesof(/obj/effect/skyfall_landingzone) //Expects a mob to holderize, we have nothing to give ignore += typesof(/obj/item/clothing/head/mob_holder) //Needs cards passed into the initilazation args From ac4bb514b3a28f4717f1ff4524e38ed6e1aa36a3 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Tue, 12 Sep 2023 17:20:32 -0300 Subject: [PATCH 11/12] gets rid of some issues, maybe --- code/__DEFINES/machines.dm | 12 + code/__DEFINES/mobs.dm | 2 + .../game/machinery/computer/communications.dm | 14 +- code/game/machinery/status_display.dm | 252 +++++++++--------- code/game/objects/items/devices/PDA/cart.dm | 4 +- .../modules/clothing/spacesuits/chronosuit.dm | 9 +- .../mob/living/silicon/ai/_preferences.dm | 19 ++ .../hostile/megafauna/bubblegum.dm | 4 +- tgstation.dme | 1 + .../tgui/interfaces/CommunicationsConsole.js | 16 +- 10 files changed, 178 insertions(+), 155 deletions(-) create mode 100644 code/modules/mob/living/silicon/ai/_preferences.dm diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index b25ef763cf41..419fc1e11521 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -128,3 +128,15 @@ #define CLONEPOD_GET_MIND 1 #define CLONEPOD_POLL_MIND 2 #define CLONEPOD_NO_MIND 3 + +/// Max length of a status line in the status display +#define MAX_STATUS_LINE_LENGTH 40 + +/// Blank Status Display +#define SD_BLANK 0 +/// Shows the emergency shuttle timer +#define SD_EMERGENCY 1 +/// Shows an arbitrary message, user-set +#define SD_MESSAGE 2 +/// Shows an alert picture (e.g. red alert, radiation, etc.) +#define SD_PICTURE 3 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index cd4e609e7a73..216a42f88f04 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -366,6 +366,8 @@ #define AI_EMOTION_BLUE_GLOW "Blue Glow" #define AI_EMOTION_RED_GLOW "Red Glow" +/// Icon state to use for ai displays that just turns them off +#define AI_DISPLAY_DONT_GLOW "ai_off" // / Breathing types. Lungs can access either by these or by a string, which will be considered a gas ID. #define BREATH_OXY /datum/breathing_class/oxygen #define BREATH_PLASMA /datum/breathing_class/plasma diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index f1ed801af1d8..533adaae91b2 100755 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,5 +1,4 @@ #define IMPORTANT_ACTION_COOLDOWN (60 SECONDS) -#define MAX_STATUS_LINE_LENGTH 40 #define STATE_BUYING_SHUTTLE "buying_shuttle" #define STATE_CHANGING_STATUS "changing_status" @@ -296,9 +295,8 @@ if ("setStatusMessage") if (!authenticated(usr)) return - var/line_one = reject_bad_text(params["lineOne"] || "", MAX_STATUS_LINE_LENGTH) - var/line_two = reject_bad_text(params["lineTwo"] || "", MAX_STATUS_LINE_LENGTH) - post_status("alert", "blank") + var/line_one = reject_bad_text(params["upperText"] || "", MAX_STATUS_LINE_LENGTH) + var/line_two = reject_bad_text(params["lowerText"] || "", MAX_STATUS_LINE_LENGTH) post_status("message", line_one, line_two) last_status_display = list(line_one, line_two) playsound(src, "terminal_type", 50, FALSE) @@ -472,8 +470,8 @@ data["budget"] = bank_account.account_balance data["shuttles"] = shuttles if (STATE_CHANGING_STATUS) - data["lineOne"] = last_status_display ? last_status_display[1] : "" - data["lineTwo"] = last_status_display ? last_status_display[2] : "" + data["upperText"] = last_status_display ? last_status_display[1] : "" + data["lowerText"] = last_status_display ? last_status_display[2] : "" return data @@ -567,8 +565,8 @@ var/datum/signal/status_signal = new(list("command" = command)) switch(command) if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 + status_signal.data["top_text"] = data1 + status_signal.data["bottom_text"] = data2 if("alert") status_signal.data["picture_state"] = data1 diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index c550f729468b..527e481b428f 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -1,18 +1,14 @@ // Status display -// (formerly Countdown timer display) -#define CHARS_PER_LINE 5 -#define FONT_SIZE "5pt" -#define FONT_COLOR "#09f" -#define FONT_STYLE "Small Fonts" +#define MAX_STATIC_WIDTH 22 +#define FONT_STYLE "12pt 'TinyUnicode'" #define SCROLL_RATE (0.04 SECONDS) // time per pixel -#define LINE1_Y -8 -#define LINE2_Y -15 - -#define SD_BLANK 0 // 0 = Blank -#define SD_EMERGENCY 1 // 1 = Emergency Shuttle timer -#define SD_MESSAGE 2 // 2 = Arbitrary message(s) -#define SD_PICTURE 3 // 3 = alert picture +#define SCROLL_PADDING 2 // how many pixels we chop to make a smooth loop +#define LINE1_X 1 +#define LINE1_Y -4 +#define LINE2_X 1 +#define LINE2_Y -11 +#define STATUS_DISPLAY_FONT_DATUM /datum/font/tiny_unicode/size_12pt /// Status display which can show images and scrolling text. /obj/machinery/status_display @@ -20,7 +16,6 @@ desc = null icon = 'icons/obj/status_display.dmi' icon_state = "frame" - base_icon_state = "unanchoredstatusdisplay" verb_say = "beeps" verb_ask = "beeps" verb_exclaim = "beeps" @@ -29,11 +24,19 @@ var/obj/effect/overlay/status_display_text/message1_overlay var/obj/effect/overlay/status_display_text/message2_overlay - var/mutable_appearance/ai_vtuber_overlay var/current_picture = "" var/current_mode = SD_BLANK var/message1 = "" var/message2 = "" + + /// Normal text color + var/text_color = "#09F" + /// Color for headers, eg. "- ETA -" + var/header_text_color = "#2CF" + + /// AI image to display + var/mutable_appearance/ai_vtuber_overlay + /// AI controlling this display var/mob/living/silicon/ai/master /obj/item/wallframe/status_display @@ -44,6 +47,12 @@ result_path = /obj/machinery/status_display/evac pixel_shift = -32 +//makes it go on the wall when built +/obj/machinery/status_display/Initialize(mapload, ndir, building) + . = ..() + update_appearance() + register_context() + /obj/machinery/status_display/wrench_act(mob/living/user, obj/item/tool) . = ..() balloon_alert(user, "[anchored ? "un" : ""]securing...") @@ -92,13 +101,17 @@ line1 = uppertext(line1) line2 = uppertext(line2) - if(line1 != message1) + var/message_changed = FALSE + if(line1 != message1 || isnull(message1_overlay)) message1 = line1 + message_changed = TRUE - if(line2 != message2) + if(line2 != message2 || isnull(message2_overlay)) message2 = line2 + message_changed = TRUE - update_appearance() + if(message_changed) + update_appearance() /** * Remove both message objs and null the fields. @@ -117,10 +130,11 @@ * Arguments: * * overlay - the current /obj/effect/overlay/status_display_text instance * * line_y - The Y offset to render the text. + * * x_offset - Used to offset the text on the X coordinates, not usually needed. * * message - the new message text. * Returns new /obj/effect/overlay/status_display_text or null if unchanged. */ -/obj/machinery/status_display/proc/update_message(obj/effect/overlay/status_display_text/overlay, line_y, message) +/obj/machinery/status_display/proc/update_message(obj/effect/overlay/status_display_text/overlay, line_y, message, x_offset, line_pair) if(overlay && message == overlay.message) return null @@ -131,7 +145,10 @@ if(overlay) qdel(overlay) - var/obj/effect/overlay/status_display_text/new_status_display_text = new(src, line_y, message) + var/obj/effect/overlay/status_display_text/new_status_display_text = new(src, line_y, message, text_color, header_text_color, x_offset, line_pair) + // Draw our object visually "in front" of this display, taking advantage of sidemap + new_status_display_text.pixel_y = -32 + new_status_display_text.pixel_z = 32 vis_contents += new_status_display_text return new_status_display_text @@ -144,9 +161,9 @@ ) set_light(0) return - set_light(1.4, 0.7, LIGHT_COLOR_BLUE) // blue light + set_light(1.5, 0.7, LIGHT_COLOR_BLUE) // blue light -/obj/machinery/status_display/update_overlays() +/obj/machinery/status_display/update_overlays(updates) . = ..() if(stat & (NOPOWER|BROKEN)) @@ -165,11 +182,21 @@ if(SD_PICTURE) remove_messages() . += mutable_appearance(icon, current_picture) + if(current_picture == AI_DISPLAY_DONT_GLOW) // If the thing's off, don't display the emissive yeah? + return . else - var/overlay = update_message(message1_overlay, LINE1_Y, message1) + var/line1_metric + var/line2_metric + var/line_pair + var/datum/font/display_font = new STATUS_DISPLAY_FONT_DATUM() + line1_metric = display_font.get_metrics(message1) + line2_metric = display_font.get_metrics(message2) + line_pair = (line1_metric > line2_metric ? line1_metric : line2_metric) + + var/overlay = update_message(message1_overlay, LINE1_Y, message1, LINE1_X, line_pair) if(overlay) message1_overlay = overlay - overlay = update_message(message2_overlay, LINE2_Y, message2) + overlay = update_message(message2_overlay, LINE2_Y, message2, LINE2_X, line_pair) if(overlay) message2_overlay = overlay @@ -177,7 +204,7 @@ if(message1 == "" && message2 == "") return - . += emissive_appearance(icon, "outline", alpha = src.alpha) + . += emissive_appearance(icon, "outline", src, alpha = src.alpha) // Timed process - performs nothing in the base class /obj/machinery/status_display/process() @@ -205,7 +232,7 @@ /obj/machinery/status_display/examine(mob/user) . = ..() - if (current_mode == SD_MESSAGE && (message1_overlay?.message || message2_overlay?.message)) + if (message1_overlay || message2_overlay) . += "The display says:" if (message1_overlay.message) . += "\t[html_encode(message1_overlay.message)]" @@ -216,31 +243,17 @@ /obj/machinery/status_display/proc/display_shuttle_status(obj/docking_port/mobile/shuttle) if(!shuttle) // the shuttle is missing - no processing - set_messages("shutl?","") + set_messages("shutl","not in service") return PROCESS_KILL else if(shuttle.timer) - var/line1 = "-[shuttle.getModeStr()]-" + var/line1 = "<<< [shuttle.getModeStr()]" var/line2 = shuttle.getTimerStr() - if(length_char(line2) > CHARS_PER_LINE) - line2 = "error" set_messages(line1, line2) else // don't kill processing, the timer might turn back on set_messages("", "") -/obj/machinery/status_display/proc/examine_shuttle(mob/user, obj/docking_port/mobile/shuttle) - if (shuttle) - var/modestr = shuttle.getModeStr() - if (modestr) - if (shuttle.timer) - modestr = "
\t[modestr]: [shuttle.getTimerStr()]" - else - modestr = "
\t[modestr]" - return "The display says:
\t[shuttle.name][modestr]" - else - return "The display says:
\tShuttle missing!" - /obj/machinery/status_display/Destroy() remove_messages() return ..() @@ -252,39 +265,56 @@ icon = 'icons/obj/status_display.dmi' vis_flags = VIS_INHERIT_LAYER | VIS_INHERIT_PLANE | VIS_INHERIT_ID + /// The message this overlay is displaying. var/message -/obj/effect/overlay/status_display_text/Initialize(mapload, yoffset, line) + // If the line is short enough to not marquee, and it matches this, it's a header. + var/static/regex/header_regex = regex("^-.*-$") + +/obj/effect/overlay/status_display_text/Initialize(mapload, yoffset, line, text_color, header_text_color, xoffset = 0, line_pair) . = ..() maptext_y = yoffset message = line - var/line_length = length_char(line) + var/datum/font/display_font = new STATUS_DISPLAY_FONT_DATUM() + var/line_width = display_font.get_metrics(line) - if(line_length > CHARS_PER_LINE) + if(line_width > MAX_STATIC_WIDTH) // Marquee text - var/marquee_message = "[line] • [line] • [line]" - var/marquee_length = line_length * 3 + 6 - maptext = generate_text(marquee_message, center = FALSE) - maptext_width = 6 * marquee_length - maptext_x = 32 + var/marquee_message = "[line] [line] [line]" + + // Width of full content. Must of these is never revealed unless the user inputted a single character. + var/full_marquee_width = display_font.get_metrics("[marquee_message] ") + // We loop after only this much has passed. + var/looping_marquee_width = (display_font.get_metrics("[line] ]") - SCROLL_PADDING) + + maptext = generate_text(marquee_message, center = FALSE, text_color = text_color) + maptext_width = full_marquee_width + maptext_x = 0 // Mask off to fit in screen. add_filter("mask", 1, alpha_mask_filter(icon = icon(icon, "outline"))) // Scroll. - var/width = 4 * marquee_length - var/time = (width + 32) * SCROLL_RATE - animate(src, maptext_x = -width, time = time, loop = -1) - animate(maptext_x = 32, time = 0) + var/time = line_pair * SCROLL_RATE + animate(src, maptext_x = (-looping_marquee_width) + MAX_STATIC_WIDTH, time = time, loop = -1) + animate(maptext_x = MAX_STATIC_WIDTH, time = 0) else // Centered text - maptext = generate_text(line, center = TRUE) - maptext_x = 0 + var/color = header_regex.Find(line) ? header_text_color : text_color + maptext = generate_text(line, center = TRUE, text_color = color) + maptext_x = xoffset //Defaults to 0, this would be centered unless overided -/obj/effect/overlay/status_display_text/proc/generate_text(text, center) - return {"
[text]
"} +/** + * Generate the actual maptext. + * Arguments: + * * text - the text to display + * * center - center the text if TRUE, otherwise right-align (the direction the text is coming from) + * * text_color - the text color + */ +/obj/effect/overlay/status_display_text/proc/generate_text(text, center, text_color) + return {"
[text]
"} /// Evac display which shows shuttle timer or message set by Command. /obj/machinery/status_display/evac @@ -295,12 +325,6 @@ // MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/evac, 32) -//makes it go on the wall when built -/obj/machinery/status_display/Initialize(mapload, ndir, building) - . = ..() - update_appearance() - register_context() - /obj/machinery/status_display/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) . = ..() if(isAI(user)) @@ -347,13 +371,6 @@ set_picture(last_picture) return PROCESS_KILL -/obj/machinery/status_display/evac/examine(mob/user) - . = ..() - if(current_mode == SD_EMERGENCY) - . += examine_shuttle(user, SSshuttle.emergency) - else if(!message1 && !message2) - . += "The display is blank." - /obj/machinery/status_display/evac/receive_signal(datum/signal/signal) switch(signal.data["command"]) if("blank") @@ -364,7 +381,7 @@ set_messages("", "") if("message") current_mode = SD_MESSAGE - set_messages(signal.data["msg1"] || "", signal.data["msg2"] || "") + set_messages(signal.data["top_text"] || "", signal.data["bottom_text"] || "") if("alert") current_mode = SD_PICTURE last_picture = signal.data["picture_state"] @@ -378,6 +395,8 @@ /obj/machinery/status_display/supply name = "supply display" current_mode = SD_MESSAGE + text_color = "#F90" + header_text_color = "#FC2" /obj/machinery/status_display/supply/process() if(stat & NOPOWER) @@ -390,8 +409,8 @@ if(!SSshuttle.supply) // Might be missing in our first update on initialize before shuttles // have loaded. Cross our fingers that it will soon return. - line1 = "CARGO" - line2 = "shutl?" + line1 = "shutl" + line2 = "not in service" else if(SSshuttle.supply.mode == SHUTTLE_IDLE) if(is_station_level(SSshuttle.supply.z)) line1 = "CARGO" @@ -400,26 +419,10 @@ line1 = "" line2 = "" else - line1 = "CARGO" + line1 = "<<< [SSshuttle.supply.getModeStr()]" line2 = SSshuttle.supply.getTimerStr() - if(length_char(line2) > CHARS_PER_LINE) - line2 = "Error" set_messages(line1, line2) -/obj/machinery/status_display/supply/examine(mob/user) - . = ..() - var/obj/docking_port/mobile/shuttle = SSshuttle.supply - var/shuttleMsg = null - if (shuttle.mode == SHUTTLE_IDLE) - if (is_station_level(shuttle.z)) - shuttleMsg = "Docked" - else - shuttleMsg = "[shuttle.getModeStr()]: [shuttle.getTimerStr()]" - if (shuttleMsg) - . += "The display says:
\t[shuttleMsg]" - else - . += "The display is blank." - /// General-purpose shuttle status display. /obj/machinery/status_display/shuttle @@ -427,6 +430,9 @@ current_mode = SD_MESSAGE var/shuttle_id + text_color = "#0F5" + header_text_color = "#2FC" + /obj/machinery/status_display/shuttle/process() if(!shuttle_id || (stat & NOPOWER)) // No power, no processing. @@ -435,13 +441,6 @@ return display_shuttle_status(SSshuttle.getShuttle(shuttle_id)) -/obj/machinery/status_display/shuttle/examine(mob/user) - . = ..() - if(shuttle_id) - . += examine_shuttle(user, SSshuttle.getShuttle(shuttle_id)) - else - . += "The display is blank." - /obj/machinery/status_display/shuttle/vv_edit_var(var_name, var_value) . = ..() if(!.) @@ -462,29 +461,9 @@ desc = "A small screen which the AI can use to present itself." current_mode = SD_PICTURE + /// Current AI emotion to display var/emotion = AI_EMOTION_BLANK - /// A mapping between AI_EMOTION_* string constants, which also double as user readable descriptions, and the name of the iconfile. - var/static/list/emotion_map = list( - AI_EMOTION_BLANK = "ai_off", - AI_EMOTION_VERY_HAPPY = "ai_veryhappy", - AI_EMOTION_HAPPY = "ai_happy", - AI_EMOTION_NEUTRAL = "ai_neutral", - AI_EMOTION_UNSURE = "ai_unsure", - AI_EMOTION_CONFUSED = "ai_confused", - AI_EMOTION_SAD = "ai_sad", - AI_EMOTION_BSOD = "ai_bsod", - AI_EMOTION_PROBLEMS = "ai_trollface", - AI_EMOTION_AWESOME = "ai_awesome", - AI_EMOTION_DORFY = "ai_urist", - AI_EMOTION_THINKING = "ai_thinking", - AI_EMOTION_FACEPALM = "ai_facepalm", - AI_EMOTION_FRIEND_COMPUTER = "ai_friend", - AI_EMOTION_BLUE_GLOW = "ai_sal", - AI_EMOTION_RED_GLOW = "ai_hal", - ) - - // MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/ai, 32) /obj/machinery/status_display/ai/Initialize(mapload) @@ -499,8 +478,8 @@ if(!isAI(user)) return var/list/choices = list() - for(var/emotion_const in emotion_map) - var/icon_state = emotion_map[emotion_const] + for(var/emotion_const in GLOB.ai_status_display_emotes) + var/icon_state = GLOB.ai_status_display_emotes[emotion_const] choices[emotion_const] = image(icon = 'icons/obj/status_display.dmi', icon_state = icon_state) var/emotion_result = show_radial_menu(user, src, choices, tooltips = TRUE) @@ -510,6 +489,14 @@ user.emote(initial(emote.key)) break +/obj/machinery/status_display/ai/process() + if(stat & NOPOWER) + update_appearance() + return PROCESS_KILL + + set_picture(GLOB.ai_status_display_emotes[emotion]) + return PROCESS_KILL + // ai vtuber moment /obj/machinery/status_display/AICtrlClick(mob/living/silicon/ai/user) if(!isAI(user) || master || (user.current && !istype(user.current, /obj/machinery/status_display))) // don't let two AIs control the same one, don't let AI control two things at once @@ -546,14 +533,6 @@ if(master && !radio_freq && master.controlled_display == src) master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mods) -/obj/machinery/status_display/ai/process() - if(stat & NOPOWER) - update_appearance() - return PROCESS_KILL - - set_picture(emotion_map[emotion]) - return PROCESS_KILL - /* /obj/item/circuit_component/status_display display_name = "Status Display" @@ -584,9 +563,13 @@ var/static/list/picture_options = list( "Default" = "default", + "Delta Alert" = "deltaalert", "Red Alert" = "redalert", + "Blue Alert" = "bluealert", + "Green Alert" = "greenalert", "Biohazard" = "biohazard", "Lockdown" = "lockdown", + "Radiation" = "radiation", "Happy" = "ai_happy", "Neutral" = "ai_neutral", "Very Happy" = "ai_veryhappy", @@ -623,18 +606,21 @@ var/datum/signal/status_signal = new(list("command" = command_value)) switch(command_value) if("message") - status_signal.data["msg1"] = message1.value - status_signal.data["msg2"] = message2.value + status_signal.data["top_text"] = message1.value + status_signal.data["bottom_text"] = message2.value if("alert") status_signal.data["picture_state"] = picture_map[picture.value] connected_display.receive_signal(status_signal) */ -#undef CHARS_PER_LINE -#undef FONT_SIZE -#undef FONT_COLOR +#undef MAX_STATIC_WIDTH #undef FONT_STYLE #undef SCROLL_RATE +#undef LINE1_X #undef LINE1_Y +#undef LINE2_X #undef LINE2_Y +#undef STATUS_DISPLAY_FONT_DATUM + +#undef SCROLL_PADDING diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 34f8202898c5..9f54ab5953c2 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -212,8 +212,8 @@ var/datum/signal/status_signal = new(list("command" = command)) switch(command) if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 + status_signal.data["top_text"] = data1 + status_signal.data["bottom_text"] = data2 if("alert") status_signal.data["picture_state"] = data1 diff --git a/code/modules/clothing/spacesuits/chronosuit.dm b/code/modules/clothing/spacesuits/chronosuit.dm index 5ec0fad2e632..b1ec754cbff3 100644 --- a/code/modules/clothing/spacesuits/chronosuit.dm +++ b/code/modules/clothing/spacesuits/chronosuit.dm @@ -32,11 +32,16 @@ var/teleporting = 0 var/phase_timer_id -/obj/item/clothing/suit/space/chronos/New() - ..() +/obj/item/clothing/suit/space/chronos/Initialize(mapload) + . = ..() teleport_now.chronosuit = src teleport_now.target = src +/obj/item/clothing/suit/space/chronos/Destroy() + QDEL_NULL(teleport_now) + QDEL_NULL(camera) + . = ..() + /obj/item/clothing/suit/space/chronos/proc/new_camera(mob/user) if(camera) qdel(camera) diff --git a/code/modules/mob/living/silicon/ai/_preferences.dm b/code/modules/mob/living/silicon/ai/_preferences.dm new file mode 100644 index 000000000000..1e7ada4dbffc --- /dev/null +++ b/code/modules/mob/living/silicon/ai/_preferences.dm @@ -0,0 +1,19 @@ +// A mapping between AI_EMOTION_* string constants, which also double as user readable descriptions, and the name of the iconfile. (used for /obj/machinery/status_display/ai ) +GLOBAL_LIST_INIT(ai_status_display_emotes, list( + AI_EMOTION_AWESOME = "ai_awesome", + AI_EMOTION_BLANK = AI_DISPLAY_DONT_GLOW, + AI_EMOTION_BLUE_GLOW = "ai_sal", + AI_EMOTION_BSOD = "ai_bsod", + AI_EMOTION_CONFUSED = "ai_confused", + AI_EMOTION_DORFY = "ai_urist", + AI_EMOTION_FACEPALM = "ai_facepalm", + AI_EMOTION_FRIEND_COMPUTER = "ai_friend", + AI_EMOTION_HAPPY = "ai_happy", + AI_EMOTION_NEUTRAL = "ai_neutral", + AI_EMOTION_PROBLEMS = "ai_trollface", + AI_EMOTION_RED_GLOW = "ai_hal", + AI_EMOTION_SAD = "ai_sad", + AI_EMOTION_THINKING = "ai_thinking", + AI_EMOTION_UNSURE = "ai_unsure", + AI_EMOTION_VERY_HAPPY = "ai_veryhappy", +)) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index def5dc8c76e1..c2e3e07eeb29 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -457,7 +457,7 @@ Difficulty: Hard deathsound = 'sound/effects/splat.ogg' /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Initialize(mapload) - ..() + . = ..() toggle_ai(AI_OFF) /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/charge(atom/chargeat = target, delay = 3, chargepast = 2) @@ -466,7 +466,7 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Destroy() new /obj/effect/decal/cleanable/blood(get_turf(src)) - . = ..() + return ..() /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/CanAllowThrough(atom/movable/mover, turf/target) . = ..() diff --git a/tgstation.dme b/tgstation.dme index 48fe8ae6eb7c..854019602ec2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2779,6 +2779,7 @@ #include "code\modules\mob\living\silicon\silicon.dm" #include "code\modules\mob\living\silicon\silicon_defense.dm" #include "code\modules\mob\living\silicon\silicon_movement.dm" +#include "code\modules\mob\living\silicon\ai\_preferences.dm" #include "code\modules\mob\living\silicon\ai\ai.dm" #include "code\modules\mob\living\silicon\ai\ai_defense.dm" #include "code\modules\mob\living\silicon\ai\ai_portrait_picker.dm" diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole.js b/tgui/packages/tgui/interfaces/CommunicationsConsole.js index a1b7bb4ff365..fd8e8739c4fc 100644 --- a/tgui/packages/tgui/interfaces/CommunicationsConsole.js +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole.js @@ -200,8 +200,8 @@ const PageChangingStatus = (props, context) => { const { act, data } = useBackend(context); const { maxStatusLineLength } = data; - const [lineOne, setLineOne] = useLocalState(context, "lineOne", data.lineOne); - const [lineTwo, setLineTwo] = useLocalState(context, "lineTwo", data.lineTwo); + const [upperText, setupperText] = useLocalState(context, "upperText", data.upperText); + const [lowerText, setlowerText] = useLocalState(context, "lowerText", data.lowerText); return ( @@ -263,18 +263,18 @@ const PageChangingStatus = (props, context) => { setLineOne(value)} + onChange={(_, value) => setupperText(value)} /> setLineTwo(value)} + onChange={(_, value) => setlowerText(value)} /> @@ -283,8 +283,8 @@ const PageChangingStatus = (props, context) => { icon="comment-o" content="Message" onClick={() => act("setStatusMessage", { - lineOne, - lineTwo, + upperText, + lowerText, })} /> From 7d2b7d2cac820ff2f0995ad02cc8619b7a9ddb8d Mon Sep 17 00:00:00 2001 From: SandPoot Date: Sat, 13 Apr 2024 17:27:43 -0300 Subject: [PATCH 12/12] proc_ref --- code/_onclick/hud/radial.dm | 2 +- code/modules/integrated_electronics/subtypes/input.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index 381ce29af6df..c35e693ea6f8 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -14,7 +14,7 @@ GLOBAL_LIST_EMPTY(radial_menus) UnregisterSignal(parent, COMSIG_PARENT_QDELETING) parent = new_value if(parent) - RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/handle_parent_del) + RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(handle_parent_del)) /atom/movable/screen/radial/proc/handle_parent_del() SIGNAL_HANDLER diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index a0ede6503a65..77ea2ae2f7ef 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -654,7 +654,7 @@ /obj/item/integrated_circuit/input/signaler/Initialize(mapload) . = ..() - addtimer(CALLBACK(src, .proc/init_frequency), 4 SECONDS) + addtimer(CALLBACK(src, PROC_REF(init_frequency)), 4 SECONDS) /obj/item/integrated_circuit/input/signaler/Destroy() SSradio.remove_object(src,frequency)