From a08aa3413851e767e4ff0b5c75e12b2b56da3696 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Fri, 25 Oct 2024 01:03:14 +0000 Subject: [PATCH 1/6] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-3482.yml | 6 ----- html/changelogs/AutoChangeLog-pr-3526.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3592.yml | 7 ------ html/changelogs/AutoChangeLog-pr-3600.yml | 5 ----- html/changelogs/AutoChangeLog-pr-3608.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3613.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3615.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3618.yml | 5 ----- html/changelogs/AutoChangeLog-pr-3622.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3629.yml | 4 ---- html/changelogs/archive/2024-10.yml | 27 +++++++++++++++++++++++ 11 files changed, 27 insertions(+), 47 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-3482.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3526.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3592.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3600.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3608.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3613.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3615.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3618.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3622.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3629.yml diff --git a/html/changelogs/AutoChangeLog-pr-3482.yml b/html/changelogs/AutoChangeLog-pr-3482.yml deleted file mode 100644 index 32a1e2cc7499..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3482.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: thgvr -changes: - - {balance: Removed Kepori damage modifiers.} - - {balance: Decreased Kepori move speed modifier. (They are still a bit faster than - average)} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3526.yml b/html/changelogs/AutoChangeLog-pr-3526.yml deleted file mode 100644 index f21948b36459..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3526.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: FalloutFalcon -changes: - - {refactor: refactored some mapping stuff including random spawners!} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3592.yml b/html/changelogs/AutoChangeLog-pr-3592.yml deleted file mode 100644 index 8bd7da1fe492..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3592.yml +++ /dev/null @@ -1,7 +0,0 @@ -author: SomeguyManperson -changes: - - {balance: simple humans now have as much health as normal humans! This makes them - weaker!} - - {balance: simple humans now also benefit from their armor as much as normal humans! - This makes them stronger!} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3600.yml b/html/changelogs/AutoChangeLog-pr-3600.yml deleted file mode 100644 index 4213f8bb5ba9..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3600.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: zimon9 -changes: - - {rscadd: Added plastitanium shards} - - {bugfix: fixed some artifacts in the plasma glass shard sprites} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3608.yml b/html/changelogs/AutoChangeLog-pr-3608.yml deleted file mode 100644 index 3f69cc679412..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3608.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Sadhorizon -changes: - - {tweak: Medical stacks now work on corpses.} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3613.yml b/html/changelogs/AutoChangeLog-pr-3613.yml deleted file mode 100644 index faa1257caa79..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3613.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Erikafox -changes: - - {bugfix: Newscasters can now read IDs through a wallet.} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3615.yml b/html/changelogs/AutoChangeLog-pr-3615.yml deleted file mode 100644 index de17ffbc1571..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3615.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Gristlebee -changes: - - {rscadd: Vote sound changed to announce_dig.ogg} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3618.yml b/html/changelogs/AutoChangeLog-pr-3618.yml deleted file mode 100644 index 75cf1806ad47..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3618.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: MemeSnorfer and Moffball -changes: - - {imageadd: Snouted balaclava sprites for both Sarathi and Elzuose} - - {code_imp: 'Elzuose snout type, for use with mask sprites'} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3622.yml b/html/changelogs/AutoChangeLog-pr-3622.yml deleted file mode 100644 index 5d7609e22c05..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3622.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: generalthrax -changes: - - {bugfix: Design disks work again} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3629.yml b/html/changelogs/AutoChangeLog-pr-3629.yml deleted file mode 100644 index 3f1925061720..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3629.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: zimon9 -changes: - - {bugfix: fixed the orbit menu search function so that names work again} -delete-after: true diff --git a/html/changelogs/archive/2024-10.yml b/html/changelogs/archive/2024-10.yml index 2e75ce1afc4e..c09dbea576a2 100644 --- a/html/changelogs/archive/2024-10.yml +++ b/html/changelogs/archive/2024-10.yml @@ -333,3 +333,30 @@ - balance: Fireaxes on the black market now cost less. Thanks Kiirv-Waha! FalloutFalcon: - code_imp: better pr labeling for ships and ruins +2024-10-25: + Erikafox: + - bugfix: Newscasters can now read IDs through a wallet. + FalloutFalcon: + - refactor: refactored some mapping stuff including random spawners! + Gristlebee: + - rscadd: Vote sound changed to announce_dig.ogg + MemeSnorfer and Moffball: + - imageadd: Snouted balaclava sprites for both Sarathi and Elzuose + - code_imp: Elzuose snout type, for use with mask sprites + Sadhorizon: + - tweak: Medical stacks now work on corpses. + SomeguyManperson: + - balance: simple humans now have as much health as normal humans! This makes them + weaker! + - balance: simple humans now also benefit from their armor as much as normal humans! + This makes them stronger! + generalthrax: + - bugfix: Design disks work again + thgvr: + - balance: Removed Kepori damage modifiers. + - balance: Decreased Kepori move speed modifier. (They are still a bit faster than + average) + zimon9: + - bugfix: fixed the orbit menu search function so that names work again + - rscadd: Added plastitanium shards + - bugfix: fixed some artifacts in the plasma glass shard sprites From a171fa2f8bf4eb1fa7eb281609da4887a8eb4ef4 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Fri, 25 Oct 2024 01:05:25 -0500 Subject: [PATCH 2/6] Qdel Log Hotfix (#3633) ## About The Pull Request #3558 accidentally ported the JSON log support for qdel logs, despite the fact we still use old normal logs. This moves us back to the version we support, and also adds support ## Why It's Good For The Game qdel logs are nice to have for gauging prevalence of harddels ## Changelog :cl: server: qdel logs work again /:cl: --- code/controllers/subsystem/garbage.dm | 50 ++++++++++++++------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index 60372d39d95b..1bb1d185c84f 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -94,38 +94,37 @@ SUBSYSTEM_DEF(garbage) /datum/controller/subsystem/garbage/Shutdown() //Adds the del() log to the qdel log file - var/list/del_log = list() + var/list/dellog = list() //sort by how long it's wasted hard deleting sortTim(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE) for(var/path in items) var/datum/qdel_item/I = items[path] - var/list/entry = list() - del_log[path] = entry - + dellog += "Path: [path]" if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG) - entry["SUSPENDED FOR LAG"] = TRUE + dellog += "\tSUSPENDED FOR LAG" if (I.failures) - entry["Failures"] = I.failures - entry["qdel() Count"] = I.qdels - entry["Destroy() Cost (ms)"] = I.destroy_time - + dellog += "\tFailures: [I.failures]" + dellog += "\tqdel() Count: [I.qdels]" + dellog += "\tDestroy() Cost: [I.destroy_time]ms" if (I.hard_deletes) - entry["Total Hard Deletes"] = I.hard_deletes - entry["Time Spend Hard Deleting (ms)"] = I.hard_delete_time - entry["Highest Time Spend Hard Deleting (ms)"] = I.hard_delete_max + dellog += "\tTotal Hard Deletes: [I.hard_deletes]" + dellog += "\tTime Spent Hard Deleting: [I.hard_delete_time]ms" + dellog += "\tHighest Time Spent Hard Deleting: [I.hard_delete_max]ms" if (I.hard_deletes_over_threshold) - entry["Hard Deletes Over Threshold"] = I.hard_deletes_over_threshold + dellog += "\tHard Deletes Over Threshold: [I.hard_deletes_over_threshold]" if (I.slept_destroy) - entry["Total Sleeps"] = I.slept_destroy + dellog += "\tSleeps: [I.slept_destroy]" if (I.no_respect_force) - entry["Total Ignored Force"] = I.no_respect_force + dellog += "\tIgnored force: [I.no_respect_force] times" if (I.no_hint) - entry["Total No Hint"] = I.no_hint - if(LAZYLEN(I.extra_details)) - entry["Deleted Metadata"] = I.extra_details - - log_qdel("", del_log) + dellog += "\tNo hint: [I.no_hint] times" + if (LAZYLEN(I.extra_details)) + dellog += "\tDeleted Metadata:" + dellog += I.extra_details.Join("\n\t") + if (I.most_refs) + dellog += "\tMost Refs After qdel(): [I.most_refs]" + log_qdel(dellog.Join("\n")) /datum/controller/subsystem/garbage/fire() //the fact that this resets its processing each fire (rather then resume where it left off) is intentional. @@ -191,8 +190,10 @@ SUBSYSTEM_DEF(garbage) var/datum/D = L[GC_QUEUE_ITEM_REF] + var/remaining_refs = refcount(D) - REFS_WE_EXPECT + // If that's all we've got, send er off - if (refcount(D) == REFS_WE_EXPECT) + if (!remaining_refs) ++gcedlasttick ++totalgcs pass_counts[level]++ @@ -213,9 +214,8 @@ SUBSYSTEM_DEF(garbage) switch (level) if (GC_QUEUE_CHECK) #ifdef REFERENCE_TRACKING - // Decides how many refs to look for (potentially) + // Decides how many refs to look for (potentially) with remaining_refs // Based off the remaining and the ones we can account for - var/remaining_refs = refcount(D) - REFS_WE_EXPECT if(reference_find_on_fail[text_ref(D)]) INVOKE_ASYNC(D, TYPE_PROC_REF(/datum,find_references), remaining_refs) ref_searching = TRUE @@ -230,8 +230,9 @@ SUBSYSTEM_DEF(garbage) var/datum/qdel_item/I = items[type] var/message = "## TESTING: GC: -- [text_ref(D)] | [type] was unable to be GC'd --" - message = "[message] (ref count of [refcount(D)])" + message = "[message] (ref count of [remaining_refs])" log_world(message) + I.most_refs = max(I.most_refs, remaining_refs) var/detail = D.dump_harddel_info() if(detail) @@ -341,6 +342,7 @@ SUBSYSTEM_DEF(garbage) var/hard_delete_time = 0 //!Total amount of milliseconds spent hard deleting this type. var/hard_delete_max = 0 //!Highest time spent hard_deleting this in ms. var/hard_deletes_over_threshold = 0 //!Number of times hard deletes took longer than the configured threshold + var/most_refs = 0 //!The highest amount of refs its had after failing to qdel var/no_respect_force = 0 //!Number of times it's not respected force=TRUE var/no_hint = 0 //!Number of times it's not even bother to give a qdel hint var/slept_destroy = 0 //!Number of times it's slept in its destroy From 01721e38d641e10af9fe729a46bc3c986fdb91f7 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Fri, 25 Oct 2024 01:26:05 -0500 Subject: [PATCH 3/6] Automatic changelog generation for PR #3633 [ci skip] --- html/changelogs/AutoChangeLog-pr-3633.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3633.yml diff --git a/html/changelogs/AutoChangeLog-pr-3633.yml b/html/changelogs/AutoChangeLog-pr-3633.yml new file mode 100644 index 000000000000..927efb100651 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3633.yml @@ -0,0 +1,4 @@ +author: MarkSuckerberg +changes: + - {server: qdel logs work again} +delete-after: true From edabc4dd191071b5e5d0bf24c116e9c5effa56a2 Mon Sep 17 00:00:00 2001 From: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com> Date: Fri, 25 Oct 2024 08:18:11 -0500 Subject: [PATCH 4/6] Tweaks and improvments to SSblackbox logging (#3561) ## About The Pull Request updates a few of the logs to be more useful and ands a few new ones ## Why It's Good For The Game ## Changelog :cl: add: Faction statistics are now logged to the blackbox del: Removes some cruft, red and blue team radios, out date/useless database logging station trash cleaned /:cl: --------- Co-authored-by: Sun-Soaked <45698967+Sun-Soaked@users.noreply.github.com> --- code/__DEFINES/radio.dm | 10 +---- code/controllers/subsystem/blackbox.dm | 4 -- code/game/communications.dm | 12 ++---- code/game/machinery/autolathe.dm | 11 ++--- code/game/machinery/teambuilder.dm | 2 - code/game/objects/effects/decals/cleanable.dm | 7 +-- .../game/objects/items/devices/radio/radio.dm | 2 +- .../game/objects/items/stacks/sheets/glass.dm | 9 ++-- code/game/objects/items/trash.dm | 7 +-- code/game/say.dm | 6 +-- code/game/turfs/closed/minerals.dm | 3 +- code/modules/awaymissions/capture_the_flag.dm | 2 - code/modules/faction/faction_datum.dm | 43 +++++++++++++------ .../mining/equipment/kinetic_crusher.dm | 1 + .../modules/mob/dead/new_player/new_player.dm | 1 + .../mob/dead/new_player/ship_select.dm | 3 +- code/modules/mob/living/brain/brain_item.dm | 3 +- .../projectiles/ammunition/_ammo_casing.dm | 3 -- 18 files changed, 58 insertions(+), 71 deletions(-) diff --git a/code/__DEFINES/radio.dm b/code/__DEFINES/radio.dm index 96e7ab6e0578..f6686232993d 100644 --- a/code/__DEFINES/radio.dm +++ b/code/__DEFINES/radio.dm @@ -22,7 +22,6 @@ #define RADIO_KEY_CENTCOM "e" #define RADIO_TOKEN_CENTCOM ":e" -//WS Begin - SGR, Overmaps #define RADIO_CHANNEL_SOLGOV "SolGov" #define RADIO_KEY_SOLGOV "s" #define RADIO_TOKEN_SOLGOV ":s" @@ -48,18 +47,11 @@ #define RADIO_TOKEN_PIRATE ":y" #define RADIO_CHANNEL_WIDEBAND "Wideband" -//WS End - -#define RADIO_CHANNEL_CTF_RED "Red Team" -#define RADIO_CHANNEL_CTF_BLUE "Blue Team" - #define MIN_FREE_FREQ 1201 // ------------------------------------------------- // Frequencies are always odd numbers and range from 1201 to 1599. #define FREQ_SYNDICATE 1213 // Nuke op comms frequency, dark brown -#define FREQ_CTF_RED 1215 // CTF red team comms frequency, red -#define FREQ_CTF_BLUE 1217 // CTF blue team comms frequency, blue #define FREQ_CENTCOM 1337 // CentCom comms frequency, gray #define FREQ_SOLGOV 1345 // SolGov comms frequency, dark blue WS ADDITION #define FREQ_INTEQ 1347 // Inteq comms frequency, light brown @@ -91,7 +83,7 @@ #define MAX_FREQ 1489 // ------------------------------------------------------ -#define FREQ_WIDEBAND 1501 // Subspace/shuttle comms frequency, dark blue WS ADDITION +#define FREQ_WIDEBAND 1501 // sector wide communication #define MAX_FREE_FREQ 1599 // ------------------------------------------------- diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index 2ac0b06b74d6..ab325d49ebe7 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -150,10 +150,6 @@ SUBSYSTEM_DEF(blackbox) record_feedback("tally", "radio_usage", 1, "solgov") //WS Edit - SolGov Rep if(FREQ_AI_PRIVATE) record_feedback("tally", "radio_usage", 1, "ai private") - if(FREQ_CTF_RED) - record_feedback("tally", "radio_usage", 1, "CTF red team") - if(FREQ_CTF_BLUE) - record_feedback("tally", "radio_usage", 1, "CTF blue team") else record_feedback("tally", "radio_usage", 1, "other") diff --git a/code/game/communications.dm b/code/game/communications.dm index 94afdbf364f5..2ee368eaa891 100644 --- a/code/game/communications.dm +++ b/code/game/communications.dm @@ -94,8 +94,8 @@ GLOBAL_LIST_INIT(radiochannels, list( RADIO_CHANNEL_COMMON = FREQ_COMMON, RADIO_CHANNEL_COMMAND = FREQ_COMMAND, RADIO_CHANNEL_CENTCOM = FREQ_CENTCOM, - RADIO_CHANNEL_SOLGOV = FREQ_SOLGOV, //WS Edit - SolGov Rep - RADIO_CHANNEL_WIDEBAND = FREQ_WIDEBAND, //WS Edit - Overmap + RADIO_CHANNEL_SOLGOV = FREQ_SOLGOV, + RADIO_CHANNEL_WIDEBAND = FREQ_WIDEBAND, RADIO_CHANNEL_SYNDICATE = FREQ_SYNDICATE, RADIO_CHANNEL_NANOTRASEN = FREQ_NANOTRASEN, RADIO_CHANNEL_MINUTEMEN = FREQ_MINUTEMEN, @@ -103,16 +103,14 @@ GLOBAL_LIST_INIT(radiochannels, list( RADIO_CHANNEL_INTEQ = FREQ_INTEQ, RADIO_CHANNEL_PIRATE = FREQ_PIRATE, RADIO_CHANNEL_AI_PRIVATE = FREQ_AI_PRIVATE, - RADIO_CHANNEL_CTF_RED = FREQ_CTF_RED, - RADIO_CHANNEL_CTF_BLUE = FREQ_CTF_BLUE )) GLOBAL_LIST_INIT(reverseradiochannels, list( "[FREQ_COMMON]" = RADIO_CHANNEL_COMMON, "[FREQ_COMMAND]" = RADIO_CHANNEL_COMMAND, "[FREQ_CENTCOM]" = RADIO_CHANNEL_CENTCOM, - "[FREQ_SOLGOV]" = RADIO_CHANNEL_SOLGOV, //WS Edit - SolGov Rep - "[FREQ_WIDEBAND]" = RADIO_CHANNEL_WIDEBAND, //WS Edit - SolGov Rep + "[FREQ_SOLGOV]" = RADIO_CHANNEL_SOLGOV, + "[FREQ_WIDEBAND]" = RADIO_CHANNEL_WIDEBAND, "[FREQ_SYNDICATE]" = RADIO_CHANNEL_SYNDICATE, "[FREQ_NANOTRASEN]" = RADIO_CHANNEL_NANOTRASEN, "[FREQ_MINUTEMEN]" = RADIO_CHANNEL_MINUTEMEN, @@ -120,8 +118,6 @@ GLOBAL_LIST_INIT(reverseradiochannels, list( "[FREQ_INTEQ]" = RADIO_CHANNEL_INTEQ, "[FREQ_PIRATE]" = RADIO_CHANNEL_PIRATE, "[FREQ_AI_PRIVATE]" = RADIO_CHANNEL_AI_PRIVATE, - "[FREQ_CTF_RED]" = RADIO_CHANNEL_CTF_RED, - "[FREQ_CTF_BLUE]" = RADIO_CHANNEL_CTF_BLUE )) /datum/radio_frequency diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index e63b3f75fb2b..452cd39b5d58 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -339,14 +339,15 @@ materials.use_materials(materials_used) if(is_stack) - var/obj/item/stack/N = new being_built.build_path(A, multiplier, FALSE) - N.update_appearance() - N.autolathe_crafted(src) + var/obj/item/stack/new_item = new being_built.build_path(A, multiplier, FALSE) + new_item.update_appearance() + new_item.autolathe_crafted(src) + SSblackbox.record_feedback("nested tally", "item_printed", 1, list("[type]", "[new_item.type]")) else for(var/i=1, i<=multiplier, i++) var/obj/item/new_item = new being_built.build_path(A) new_item.autolathe_crafted(src) - + SSblackbox.record_feedback("nested tally", "item_printed", 1, list("[type]", "[new_item.type]")) if(length(picked_materials)) new_item.set_custom_materials(picked_materials, 1 / multiplier) //Ensure we get the non multiplied amount for(var/x in picked_materials) @@ -446,5 +447,5 @@ //Called when the object is constructed by an autolathe //Has a reference to the autolathe so you can do !!FUN!! things with hacked lathes -/obj/item/proc/autolathe_crafted(obj/machinery/autolathe/A) +/obj/item/proc/autolathe_crafted(obj/machinery/autolathe/lathe) return diff --git a/code/game/machinery/teambuilder.dm b/code/game/machinery/teambuilder.dm index 153035a39374..402ea5aa5c6b 100644 --- a/code/game/machinery/teambuilder.dm +++ b/code/game/machinery/teambuilder.dm @@ -43,11 +43,9 @@ desc = "A machine that, when passed, colors you based on the color of your team. Go red team!" color = "#ff0000" team_color = "#ff0000" - team_radio = FREQ_CTF_RED /obj/machinery/teambuilder/blue name = "Teambuilding Machine (Blue)" desc = "A machine that, when passed, colors you based on the color of your team. Go blue team!" color = "#0000ff" team_color = "#0000ff" - team_radio = FREQ_CTF_BLUE diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index 3ec6f58aa7b1..0be76dd1fac6 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -32,11 +32,8 @@ AddElement(/datum/element/connect_loc, loc_connections) AddElement(/datum/element/beauty, beauty) - SSblackbox.record_feedback("tally", "station_mess_created", 1, name) - -/obj/effect/decal/cleanable/Destroy() - SSblackbox.record_feedback("tally", "station_mess_destroyed", 1, name) - return ..() + if(!mapload) + SSblackbox.record_feedback("tally", "station_mess_created", 1, name) /obj/effect/decal/cleanable/proc/replace_decal(obj/effect/decal/cleanable/C) // Returns true if we should give up in favor of the pre-existing decal if(mergeable_decal) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index cc528f103fe2..1caf2861a448 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -254,7 +254,7 @@ var/datum/signal/subspace/vocal/signal = new(src, freq, speaker, language, message, spans, message_mods) // Independent radios, on the CentCom frequency, reach all independent radios - if (independent && (freq == FREQ_CENTCOM || freq == FREQ_WIDEBAND || freq == FREQ_CTF_RED || freq == FREQ_CTF_BLUE)) //WS Edit - SolGov Rep + if (independent && (freq == FREQ_CENTCOM || freq == FREQ_WIDEBAND)) //WS Edit - SolGov Rep signal.data["compression"] = 0 signal.transmission_method = TRANSMISSION_SUPERSPACE signal.map_zones = list(0) // reaches all Z-levels diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 40de06d6f9cb..666e927eaf3e 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -282,7 +282,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( var/obj/item/stack/sheet/weld_material = /obj/item/stack/sheet/glass embedding = list("embed_chance" = 65) -/obj/item/shard/Initialize() +/obj/item/shard/Initialize(mapload) . = ..() AddComponent(/datum/component/caltrop, force) AddComponent(/datum/component/butchering, 150, 65) @@ -300,17 +300,14 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( if (icon_prefix) icon_state = "[icon_prefix][icon_state]" - SSblackbox.record_feedback("tally", "station_mess_created", 1, name) + if(!mapload) + SSblackbox.record_feedback("tally", "station_mess_created", 1, name) var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered), ) AddElement(/datum/element/connect_loc, loc_connections) -/obj/item/shard/Destroy() - . = ..() - SSblackbox.record_feedback("tally", "station_mess_destroyed", 1, name) - /obj/item/shard/afterattack(atom/A as mob|obj, mob/user, proximity) . = ..() if(!proximity || !(src in user)) diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index bdcb89796bc4..cc67c6795783 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -8,11 +8,8 @@ resistance_flags = FLAMMABLE /obj/item/trash/Initialize(mapload) - SSblackbox.record_feedback("tally", "station_mess_created", 1, name) - return ..() - -/obj/item/trash/Destroy() - SSblackbox.record_feedback("tally", "station_mess_destroyed", 1, name) + if(!mapload) + SSblackbox.record_feedback("tally", "station_mess_created", 1, name) return ..() /obj/item/trash/raisins diff --git a/code/game/say.dm b/code/game/say.dm index cac8bafe5365..c92b984dbb77 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -13,10 +13,8 @@ GLOBAL_LIST_INIT(freqtospan, list( "[FREQ_AI_PRIVATE]" = "aiprivradio", "[FREQ_SYNDICATE]" = "syndradio", "[FREQ_CENTCOM]" = "centcomradio", - "[FREQ_SOLGOV]" = "solgovradio", //WS Edit - SolGov Rep - "[FREQ_WIDEBAND]" = "widebandradio", //WS Edit - Overmaps - "[FREQ_CTF_RED]" = "redteamradio", - "[FREQ_CTF_BLUE]" = "blueteamradio" + "[FREQ_SOLGOV]" = "solgovradio", + "[FREQ_WIDEBAND]" = "widebandradio", )) GLOBAL_LIST_INIT(freqcolor, list()) diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm index d3d2cc076ef2..7b8d68b7b2f9 100644 --- a/code/game/turfs/closed/minerals.dm +++ b/code/game/turfs/closed/minerals.dm @@ -113,7 +113,8 @@ visible_message(span_warning("The ore was completely ruined!")) else new mineralType(src, mineralAmt) - SSblackbox.record_feedback("tally", "ore_mined", mineralAmt, mineralType) + if(ishuman(user)) + SSblackbox.record_feedback("tally", "ore_mined", mineralAmt, mineralType) if(ishuman(user)) var/mob/living/carbon/human/H = user if(give_exp) diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm index cde1ad6a4de1..037ae0add3e6 100644 --- a/code/modules/awaymissions/capture_the_flag.dm +++ b/code/modules/awaymissions/capture_the_flag.dm @@ -590,7 +590,6 @@ /datum/outfit/ctf/red/post_equip(mob/living/carbon/human/H) ..() var/obj/item/radio/R = H.ears - R.set_frequency(FREQ_CTF_RED) R.freqlock = TRUE R.independent = TRUE H.dna.species.stunmod = 0 @@ -598,7 +597,6 @@ /datum/outfit/ctf/blue/post_equip(mob/living/carbon/human/H) ..() var/obj/item/radio/R = H.ears - R.set_frequency(FREQ_CTF_BLUE) R.freqlock = TRUE R.independent = TRUE H.dna.species.stunmod = 0 diff --git a/code/modules/faction/faction_datum.dm b/code/modules/faction/faction_datum.dm index 450ee9688953..017c324f5011 100644 --- a/code/modules/faction/faction_datum.dm +++ b/code/modules/faction/faction_datum.dm @@ -1,62 +1,77 @@ /datum/faction var/name + /// Primarly to be used for backend stuff. + var/short_name var/parent_faction + /// List of prefixes that ships of this faction uses var/list/prefixes +/datum/faction/New() + if(!short_name) + short_name = name + /datum/faction/syndicate name = FACTION_SYNDICATE parent_faction = /datum/faction/syndicate - prefixes = list("SEV", "SSV") + prefixes = PREFIX_SYNDICATE /datum/faction/syndicate/ngr name = FACTION_NGR - prefixes = list("NGRV") + short_name = "NGR" + prefixes = PREFIX_NGR /datum/faction/syndicate/cybersun name = FACTION_CYBERSUN - prefixes = list("CSSV") + prefixes = PREFIX_CYBERSUN /datum/faction/syndicate/suns name = FACTION_SUNS - prefixes = list("SUNS") + short_name = "SUNS" + prefixes = PREFIX_SUNS /datum/faction/solgov name = FACTION_SOLGOV - prefixes = list("SCSV") + prefixes = PREFIX_SOLGOV /datum/faction/srm name = FACTION_SRM - prefixes = list("SRSV") + short_name = "SRM" + prefixes = PREFIX_SRM /datum/faction/inteq name = FACTION_INTEQ - prefixes = list("IRMV") + short_name = "INTEQ" + prefixes = PREFIX_INTEQ /datum/faction/clip name = FACTION_CLIP - prefixes = list("CMSV", "CMGSV") + short_name = "CLIP" + prefixes = PREFIX_CLIP /datum/faction/nt name = FACTION_NT + short_name = "NT" parent_faction = /datum/faction/nt - prefixes = list("NTSV") + prefixes = PREFIX_NT /datum/faction/nt/ns_logi name = FACTION_NS_LOGI - prefixes = list("NSSV") + prefixes = PREFIX_NS_LOGI /datum/faction/nt/vigilitas name = FACTION_VIGILITAS - prefixes = list("VISV") + prefixes = PREFIX_VIGILITAS /datum/faction/frontier name = FACTION_FRONTIER - prefixes = list("FFV") + prefixes = PREFIX_FRONTIER /datum/faction/pgf name = FACTION_PGF - prefixes = list("PGF", "PGFMC", "PGFN") + short_name = "PGF" + prefixes = PREFIX_PGF /datum/faction/independent name = FACTION_INDEPENDENT - prefixes = list("SV", "IMV", "ISV") + short_name = "Indie" + prefixes = PREFIX_INDEPENDENT diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 05e81421ac4f..d110fe901082 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -138,6 +138,7 @@ L.apply_status_effect(STATUS_EFFECT_CRUSHERMARK, hammer_synced) var/target_turf = get_turf(target) if(ismineralturf(target_turf)) + SSblackbox.record_feedback("tally", "pick_used_mining", 1, src.type) var/turf/closed/mineral/M = target_turf new /obj/effect/temp_visual/kinetic_blast(M) ..() diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 2dbbd9d4b65b..3ff72083450a 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -345,6 +345,7 @@ log_manifest(character.mind.key, character.mind, character, TRUE) + SSblackbox.record_feedback("tally", "player_joined_faction", 1, ship.get_faction()) if(length(ship.job_slots) > 1 && ship.job_slots[1] == job) // if it's the "captain" equivalent job of the ship. checks to make sure it's not a one-job ship minor_announce("[job.name] [character.real_name] on deck!", zlevel = ship.shuttle_port.virtual_z()) return TRUE diff --git a/code/modules/mob/dead/new_player/ship_select.dm b/code/modules/mob/dead/new_player/ship_select.dm index e130f6e404ce..dd60c13ce2cd 100644 --- a/code/modules/mob/dead/new_player/ship_select.dm +++ b/code/modules/mob/dead/new_player/ship_select.dm @@ -100,7 +100,8 @@ to_chat(spawnee, "There was an error loading the ship. Please contact admins!") spawnee.new_player_panel() return - SSblackbox.record_feedback("tally", "ship_purchased", 1, template.name) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + SSblackbox.record_feedback("tally", "ship_purchased", 1, template.name) + SSblackbox.record_feedback("tally", "faction_ship_purchased", 1, template.faction_datum.name) // Try to spawn as the first listed job in the job slots (usually captain) // Playtime checks are overridden, to ensure the player gets to join the ship they spawned. if(!spawnee.AttemptLateSpawn(target.job_slots[1], target, FALSE)) diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 8629d01650d4..5fe96021461b 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -381,7 +381,8 @@ if(resilience) actual_trauma.resilience = resilience . = actual_trauma - SSblackbox.record_feedback("tally", "traumas", 1, actual_trauma.type) + if(owner?.client) + SSblackbox.record_feedback("tally", "traumas", 1, actual_trauma.type) //Add a random trauma of a certain subtype /obj/item/organ/brain/proc/gain_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience, natural_gain = FALSE) diff --git a/code/modules/projectiles/ammunition/_ammo_casing.dm b/code/modules/projectiles/ammunition/_ammo_casing.dm index 580410f6683f..1c259d622931 100644 --- a/code/modules/projectiles/ammunition/_ammo_casing.dm +++ b/code/modules/projectiles/ammunition/_ammo_casing.dm @@ -171,11 +171,8 @@ /obj/item/ammo_casing/Destroy() . = ..() - if(BB) QDEL_NULL(BB) - else - SSblackbox.record_feedback("tally", "station_mess_destroyed", 1, name) /obj/item/ammo_casing/update_icon_state() icon_state = "[initial(icon_state)][BB ? (bullet_skin ? "-[bullet_skin]" : "") : "-empty"]" From 05242e0f62e4bbe4530b2c08b7c9e520d7a32773 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Fri, 25 Oct 2024 08:29:02 -0500 Subject: [PATCH 5/6] Automatic changelog generation for PR #3561 [ci skip] --- html/changelogs/AutoChangeLog-pr-3561.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3561.yml diff --git a/html/changelogs/AutoChangeLog-pr-3561.yml b/html/changelogs/AutoChangeLog-pr-3561.yml new file mode 100644 index 000000000000..d12adfca6b82 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3561.yml @@ -0,0 +1,6 @@ +author: FalloutFalcon +changes: + - {rscadd: Faction statistics are now logged to the blackbox} + - {rscdel: 'Removes some cruft, red and blue team radios, out date/useless database + logging station trash cleaned'} +delete-after: true From c38a3d26fc873d5eb21d0a660e8f04d353d891e9 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Fri, 25 Oct 2024 09:54:40 -0500 Subject: [PATCH 6/6] Opendream-detected Errors, More Harddels, Slight CI Improvements (#3572) ## About The Pull Request I didn't get them all in my last PR, apparently. Also throws in some minor tweaks for ambiguity and such detected by the OpenDream parser since I was messing around with that at the time. Also adds OpenDream linting to CI because why not. Ports: tgstation/tgstation#81892 (which is in turn from Para and Goon) tgstation/tgstation#82029 beestation/beestation-hornet#11464 tgstation/tgstation#86510 tgstation/tgstation#83255 tgstation/tgstation#78225 tgstation/tgstation#78265 Fixes: #3530 ## Why It's Good For The Game Harddels still bad, OpenDream good --------- Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: san7890 Co-authored-by: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Co-authored-by: Jordan Dominion Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Co-authored-by: distributivgesetz Co-authored-by: Aleksej Komarov --- .github/workflows/autowiki.yml | 2 +- .github/workflows/ci_suite.yml | 140 ++++++++++++------ .github/workflows/codeowner_reviews.yml | 1 + .github/workflows/rerun_flaky_tests.yml | 34 ++--- .github/workflows/run_integration_tests.yml | 2 + .github/workflows/tgs_test.yml | 9 +- .gitignore | 1 + __odlint.dm | 10 ++ check_regex.yaml | 2 +- code/__DEFINES/misc.dm | 4 + code/__HELPERS/_logging.dm | 12 +- code/__HELPERS/type2type.dm | 8 +- code/_compile_options.dm | 15 +- code/controllers/subsystem/acid.dm | 3 +- code/datums/components/_component.dm | 18 +-- code/datums/components/food/edible.dm | 6 +- code/datums/components/radioactive.dm | 1 - code/game/machinery/computer/medical.dm | 6 - code/game/machinery/computer/security.dm | 1 - code/game/objects/items/crayons.dm | 4 +- code/game/objects/items/devices/PDA/PDA.dm | 2 +- code/game/objects/items/devices/mines.dm | 2 +- code/game/objects/items/pet_carrier.dm | 6 +- code/game/objects/items/storage/guncases.dm | 2 + code/game/objects/obj_defense.dm | 7 +- .../crates_lockers/closets/utility_closets.dm | 2 +- code/game/objects/structures/crateshelf.dm | 1 + code/modules/admin/permissionedit.dm | 2 +- .../admin/view_variables/debug_variables.dm | 17 ++- code/modules/client/loadout/_loadout.dm | 6 +- .../client/loadout/loadout_accessories.dm | 3 + code/modules/client/loadout/loadout_hat.dm | 3 + code/modules/client/loadout/loadout_suit.dm | 3 + code/modules/client/preferences.dm | 2 +- code/modules/clothing/head/jobs.dm | 2 +- code/modules/clothing/head/misc.dm | 2 +- code/modules/clothing/suits/cloaks.dm | 1 - code/modules/clothing/under/accessories.dm | 5 +- code/modules/food_and_drinks/drinks/drinks.dm | 1 + .../modules/mob/dead/new_player/new_player.dm | 2 +- .../living/simple_animal/hostile/hostile.dm | 2 +- code/modules/mob/mob.dm | 2 - code/modules/mob/mob_helpers.dm | 4 +- code/modules/projectiles/gun.dm | 2 +- .../projectiles/guns/ballistic/assault.dm | 9 +- .../projectiles/guns/ballistic/revolver.dm | 2 + .../projectiles/guns/ballistic/shotgun.dm | 6 +- code/modules/projectiles/guns/energy.dm | 1 + code/modules/projectiles/guns/energy/laser.dm | 2 - .../manufacturer/scarborough/ballistics.dm | 2 +- .../chemistry/reagents/drink_reagents.dm | 2 +- .../chemistry/reagents/food_reagents.dm | 2 +- code/modules/surgery/bodyparts/bodyparts.dm | 2 +- code/modules/unit_tests/_unit_tests.dm | 4 +- code/modules/unit_tests/create_and_destroy.dm | 10 +- code/modules/unit_tests/icons/inhands.dm | 6 +- code/modules/unit_tests/icons/worn_icons.dm | 31 ++-- code/modules/unit_tests/outfit_sanity.dm | 4 +- icons/mob/inhands/weapons/guns_lefthand.dmi | Bin 42172 -> 39557 bytes .../mob/inhands/weapons/swords_righthand.dmi | Bin 31326 -> 31324 bytes shiptest.dme | 1 + tgui/public/tgui.html | 8 +- tools/ci/annotate_dm.sh | 4 + tools/ci/annotate_od.sh | 4 + tools/ci/od_lints.dm | 58 ++++++++ tools/dm_annotator/__main__.py | 59 ++++++++ tools/od_annotator/__main__.py | 50 +++++++ tools/requirements.txt | 6 +- tools/tgs_test/Program.cs | 4 +- 69 files changed, 454 insertions(+), 183 deletions(-) create mode 100644 __odlint.dm create mode 100755 tools/ci/annotate_dm.sh create mode 100644 tools/ci/annotate_od.sh create mode 100644 tools/ci/od_lints.dm create mode 100644 tools/dm_annotator/__main__.py create mode 100644 tools/od_annotator/__main__.py diff --git a/.github/workflows/autowiki.yml b/.github/workflows/autowiki.yml index c6f85bade91d..10d3cea1e676 100644 --- a/.github/workflows/autowiki.yml +++ b/.github/workflows/autowiki.yml @@ -43,7 +43,7 @@ jobs: sudo apt update || true sudo apt install -o APT::Immediate-configure=false libssl-dev:i386 bash tools/ci/install_rust_g.sh - + - name: Cache dependencies if: steps.secrets_set.outputs.SECRETS_ENABLED uses: actions/cache@v3 diff --git a/.github/workflows/ci_suite.yml b/.github/workflows/ci_suite.yml index 915b6021e98c..33af3581a22b 100644 --- a/.github/workflows/ci_suite.yml +++ b/.github/workflows/ci_suite.yml @@ -3,17 +3,34 @@ on: push: branches: - master + - "project/**" pull_request: branches: - master + - "project/**" merge_group: branches: - master + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: + start_gate: + if: ( !contains(github.event.head_commit.message, '[ci skip]') ) + name: Start Gate + runs-on: ubuntu-latest + steps: + - name: Mandatory Empty Step + run: exit 0 + run_linters: - if: ${{ ! contains(github.event.head_commit.message, '[ci skip]') }} name: Run Linters + needs: start_gate runs-on: ubuntu-22.04 + timeout-minutes: 5 + steps: - uses: actions/checkout@v3 with: @@ -53,40 +70,62 @@ jobs: run: | pip3 install setuptools bash tools/ci/install_node.sh - bash tools/ci/install_spaceman_dmm.sh dreamchecker cargo install ripgrep --features pcre2 tools/bootstrap/python -c '' - - name: Run Linters + - name: Give Linters A Go + id: linter-setup + run: exit 0 + - name: Run Grep Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_grep.sh + - name: Run DreamChecker + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: ~/dreamchecker 2>&1 | bash tools/ci/annotate_dm.sh + - name: Run Map Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() run: | - tools/bootstrap/python -m tools.maplint.source --github - tools/build/build --ci lint tgui-test - bash tools/ci/check_filedirs.sh shiptest.dme - bash tools/ci/check_changelogs.sh - bash tools/ci/check_misc.sh - bash tools/ci/check_grep.sh - tools/bootstrap/python -m dmi.test tools/bootstrap/python -m mapmerge2.dmm_test - ~/dreamchecker > ${GITHUB_WORKSPACE}/output-annotations.txt 2>&1 - - - name: Annotate Lints - if: always() - uses: yogstation13/DreamAnnotate@v2 - with: - outputFile: output-annotations.txt - - - name: Run Check Regex + tools/bootstrap/python -m tools.maplint.source + - name: Run DMI Tests + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/bootstrap/python -m dmi.test + - name: Check File Directories + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_filedirs.sh shiptest.dme + - name: Check Changelogs + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_changelogs.sh + - name: Check Miscellaneous Files + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_misc.sh + - name: Run TGUI Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/build/build --ci lint tgui-test + - name: Run Regex Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() run: | tools/bootstrap/python -m ci.check_regex --log-changes-only --github-actions + cat check_regex_output.txt + + - name: Install OpenDream + uses: robinraju/release-downloader@v1.9 + with: + repository: "OpenDreamProject/OpenDream" + tag: "latest" + fileName: "DMCompiler_linux-x64.tar.gz" + extract: true - - name: Annotate Regex Matches + - name: Run OpenDream Linter run: | - cat check_regex_output.txt + ./DMCompiler_linux-x64/DMCompiler shiptest.dme --suppress-unimplemented --define=CIBUILDING | bash tools/ci/annotate_od.sh compile_all_maps: - if: ${{ ! contains(github.event.head_commit.message, '[ci skip]') }} name: Compile Maps + needs: start_gate runs-on: ubuntu-latest + timeout-minutes: 5 + steps: - uses: actions/checkout@v3 @@ -106,39 +145,41 @@ jobs: tools/build/build --ci dm -DCIBUILDING -DCITESTING -DALL_MAPS -DFULL_INIT run_all_tests: - if: ${{ ! contains(github.event.head_commit.message, '[ci skip]') }} name: Integration Tests + needs: start_gate strategy: fail-fast: false matrix: - arg: [ - "BASIC_TESTS", - "CREATE_AND_DESTROY_TEST", - "PLANET_GEN_TEST", - "RUIN_PLACEMENT_TEST", - "SHIP_PLACEMENT_TEST" - ] + arg: + [ + "BASIC_TESTS", + "CREATE_AND_DESTROY_TEST", + "PLANET_GEN_TEST", + "RUIN_PLACEMENT_TEST", + "SHIP_PLACEMENT_TEST", + ] uses: ./.github/workflows/run_integration_tests.yml with: arg: ${{ matrix.arg }} -# run_alternate_tests: -# if: "!contains(github.event.head_commit.message, '[ci skip]')" -# name: Alternate Tests -# strategy: -# fail-fast: false -# matrix: -# major: [515] -# minor: [1614] -# uses: ./.github/workflows/run_integration_tests.yml -# with: -# major: ${{ matrix.major }} -# minor: ${{ matrix.minor }} + # run_alternate_tests: + # if: "!contains(github.event.head_commit.message, '[ci skip]')" + # name: Alternate Tests + # strategy: + # fail-fast: false + # matrix: + # major: [515] + # minor: [1614] + # uses: ./.github/workflows/run_integration_tests.yml + # with: + # major: ${{ matrix.major }} + # minor: ${{ matrix.minor }} test_windows: - if: ${{ ! contains(github.event.head_commit.message, '[ci skip]') }} name: Windows Build + needs: start_gate runs-on: windows-latest + timeout-minutes: 5 steps: - uses: actions/checkout@v3 @@ -163,7 +204,18 @@ jobs: bash tools/deploy.sh ./deploy - name: Deploy artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: deploy path: deploy + + completion_gate: # Serves as a non-moving target for branch rulesets + if: always() && !cancelled() + name: Completion Gate + needs: [test_windows, compile_all_maps, run_linters, run_all_tests] + runs-on: ubuntu-latest + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} diff --git a/.github/workflows/codeowner_reviews.yml b/.github/workflows/codeowner_reviews.yml index 753f575f90d7..6799c5d14b37 100644 --- a/.github/workflows/codeowner_reviews.yml +++ b/.github/workflows/codeowner_reviews.yml @@ -6,6 +6,7 @@ on: pull_request_target jobs: assign-users: runs-on: ubuntu-latest + timeout-minutes: 5 steps: # Checks-out your repository under $GITHUB_WORKSPACE, so the job can access it diff --git a/.github/workflows/rerun_flaky_tests.yml b/.github/workflows/rerun_flaky_tests.yml index e3cbda05749b..b705735a0dfb 100644 --- a/.github/workflows/rerun_flaky_tests.yml +++ b/.github/workflows/rerun_flaky_tests.yml @@ -3,7 +3,7 @@ on: workflow_run: workflows: [Checks] types: - - completed + - completed permissions: actions: write @@ -15,23 +15,23 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.run_attempt == 1 }} steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Rerun flaky tests - uses: actions/github-script@v6 - with: - script: | - const { rerunFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js') - await rerunFlakyTests({ github, context }) + - name: Checkout + uses: actions/checkout@v3 + - name: Rerun flaky tests + uses: actions/github-script@v6 + with: + script: | + const { rerunFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js') + await rerunFlakyTests({ github, context }) report_flaky_tests: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.run_attempt == 2 }} steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Report flaky tests - uses: actions/github-script@v6 - with: - script: | - const { reportFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js') - await reportFlakyTests({ github, context }) + - name: Checkout + uses: actions/checkout@v3 + - name: Report flaky tests + uses: actions/github-script@v6 + with: + script: | + const { reportFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js') + await reportFlakyTests({ github, context }) diff --git a/.github/workflows/run_integration_tests.yml b/.github/workflows/run_integration_tests.yml index 9c83d6ab013d..699151fe6f99 100644 --- a/.github/workflows/run_integration_tests.yml +++ b/.github/workflows/run_integration_tests.yml @@ -14,9 +14,11 @@ on: required: false default: ALL_TESTS type: string + jobs: run_integration_tests: runs-on: ubuntu-latest + timeout-minutes: 15 services: mysql: image: mysql:latest diff --git a/.github/workflows/tgs_test.yml b/.github/workflows/tgs_test.yml index a92b6cac76a3..4b7853aa77cf 100644 --- a/.github/workflows/tgs_test.yml +++ b/.github/workflows/tgs_test.yml @@ -3,7 +3,9 @@ on: push: branches: - master + - 'project/**' - 'gh-readonly-queue/master/**' + - 'gh-readonly-queue/project/**' paths: - '.tgs.yml' - '.github/workflows/tgs_test.yml' @@ -12,11 +14,13 @@ on: - 'code/__DEFINES/tgs.dm' - 'code/game/world.dm' - 'code/modules/tgs/**' + - 'tools/bootstrap/**' - 'tools/tgs_scripts/**' - 'tools/tgs_test/**' pull_request: branches: - master + - 'project/**' paths: - '.tgs.yml' - '.github/workflows/tgs_test.yml' @@ -25,6 +29,7 @@ on: - 'code/__DEFINES/tgs.dm' - 'code/game/world.dm' - 'code/modules/tgs/**' + - 'tools/bootstrap/**' - 'tools/tgs_scripts/**' - 'tools/tgs_test/**' merge_group: @@ -54,12 +59,12 @@ jobs: - 5000:5000 #Can't use env here for some reason steps: - name: Setup dotnet - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x - name: Checkout Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Test TGS Integration run: dotnet run -c Release --project tools/tgs_test ${{ github.repository }} /tgs_instances/tgstation ${{ env.TGS_API_PORT }} ${{ github.event.pull_request.head.sha || github.sha }} ${{ secrets.GITHUB_TOKEN }} ${{ env.PR_NUMBER }} diff --git a/.gitignore b/.gitignore index 94713bc82e1c..196353141536 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ *.lk *.int *.backup +/shiptest.json ### https://raw.github.com/github/gitignore/cc542de017c606138a87ee4880e5f06b3a306def/Global/Linux.gitignore *~ diff --git a/__odlint.dm b/__odlint.dm new file mode 100644 index 000000000000..b7c120514a1d --- /dev/null +++ b/__odlint.dm @@ -0,0 +1,10 @@ +// This file is included right at the start of the DME. +// Its purpose is to enable multiple lints (pragmas) that are supported by OpenDream to better validate the codebase +// These are essentially nitpicks the DM compiler should pick up on but doesnt + +#if !defined(SPACEMAN_DMM) && defined(OPENDREAM) +// This is in a separate file as a hack to avoid SpacemanDMM +// evaluating the #pragma lines, even if its outside a block it cares about +// (Also so people can code-own it. Shoutout to AA) +#include "tools/ci/od_lints.dm" +#endif diff --git a/check_regex.yaml b/check_regex.yaml index a9ed6b699d7f..95d3738da5dc 100644 --- a/check_regex.yaml +++ b/check_regex.yaml @@ -57,7 +57,7 @@ standards: - no_more: [ - 34, + 32, "indentions inside defines", '^(\s*)#define (\w*)( {2,}| ?\t+)(?!(\/\/|\/\*))', ] diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 68ef8b65b127..384b7fcc46c7 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -447,3 +447,7 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE #define ROUND_END_NOT_DELAYED 0 #define ROUND_END_DELAYED 1 #define ROUND_END_TGS 2 + +/// A null statement to guard against EmptyBlock lint without necessitating the use of pass() +/// Used to avoid proc-call overhead. But use sparingly. Probably pointless in most places. +#define EMPTY_BLOCK_GUARD ; diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 1ab889987695..16de5230a2bb 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -10,14 +10,22 @@ #define WRITE_LOG(log, text) rustg_log_write(log, text, "true") #define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") -//print a warning message to world.log +#ifdef UNIT_TESTS +#define WARNING(MSG) log_world("::warning file=[__FILE__],line=[__LINE__]::[MSG] src: [UNLINT(src)] usr: [usr].") +#else #define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].") +#endif +/// Print a warning message to world.log /proc/warning(msg) msg = "## WARNING: [msg]" log_world(msg) -//not an error or a warning, but worth to mention on the world log, just in case. +#ifdef UNIT_TESTS +#define NOTICE(MSG) log_world("::notice file=[__FILE__],line=[__LINE__]::[MSG] src: [UNLINT(src)] usr: [usr].") +#else #define NOTICE(MSG) notice(MSG) +#endif +///not an error or a warning, but worth to mention on the world log, just in case. /proc/notice(msg) msg = "## NOTICE: [msg]" log_world(msg) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 452a522870a0..3770b4e847ad 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -94,8 +94,8 @@ return "northwest" if(SOUTHWEST) return "southwest" - else - return + + return NONE //Turns text into proper directions /proc/text2dir(direction) @@ -116,8 +116,8 @@ return SOUTHEAST if("SOUTHWEST") return SOUTHWEST - else - return + + return NONE //Converts an angle (degrees) into an ss13 direction GLOBAL_LIST_INIT(modulo_angle_to_dir, list(NORTH,NORTHEAST,EAST,SOUTHEAST,SOUTH,SOUTHWEST,WEST,NORTHWEST)) diff --git a/code/_compile_options.dm b/code/_compile_options.dm index ee7638ea853d..9ff2cbe896ae 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -63,7 +63,7 @@ #warn compiling in TESTING mode. testing() debug messages will be visible. #endif -#ifdef CIBUILDING +#if defined(CIBUILDING) && !defined(OPENDREAM) #define UNIT_TESTS #endif @@ -90,3 +90,16 @@ // A reasonable number of maximum overlays an object needs // If you think you need more, rethink it #define MAX_ATOM_OVERLAYS 100 + +#if defined(OPENDREAM) + #if !defined(CIBUILDING) + #warn You are building with OpenDream. Remember to build TGUI manually. + #warn You can do this by running tgui-build.cmd from the bin directory. + #endif +#else + #if !defined(CBT) && !defined(SPACEMAN_DMM) + #warn Building with Dream Maker is no longer supported and will result in errors. + #warn In order to build, run BUILD.cmd in the root directory. + #warn Consider switching to VSCode editor instead, where you can press Ctrl+Shift+B to build. + #endif +#endif diff --git a/code/controllers/subsystem/acid.dm b/code/controllers/subsystem/acid.dm index 0ea8967e263c..efbc5e7d260e 100644 --- a/code/controllers/subsystem/acid.dm +++ b/code/controllers/subsystem/acid.dm @@ -33,8 +33,7 @@ SUBSYSTEM_DEF(acid) return continue - if(O.acid_level && O.acid_processing()) - else + if(!O.acid_level || !O.acid_processing()) O.update_appearance() processing -= O diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 6c15d00869f2..695b6519f9c7 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -364,17 +364,17 @@ */ /datum/proc/GetExactComponent(datum/component/c_type) RETURN_TYPE(c_type) - if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE) + var/initial_type_mode = initial(c_type.dupe_mode) + if(initial_type_mode == COMPONENT_DUPE_ALLOWED || initial_type_mode == COMPONENT_DUPE_SELECTIVE) stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]") - var/list/dc = datum_components - if(!dc) + var/list/all_components = datum_components + if(!all_components) return null - var/datum/component/C = dc[c_type] - if(C) - if(length(C)) - C = C[1] - if(C.type == c_type) - return C + var/datum/component/potential_component + if(length(all_components)) + potential_component = all_components[c_type] + if(potential_component?.type == c_type) + return potential_component return null /** diff --git a/code/datums/components/food/edible.dm b/code/datums/components/food/edible.dm index 729c50f2349f..f5129fb761b1 100644 --- a/code/datums/components/food/edible.dm +++ b/code/datums/components/food/edible.dm @@ -153,9 +153,9 @@ Behavior that's still missing from this component that original food items had t SIGNAL_HANDLER if(!(food_flags & FOOD_IN_CONTAINER)) - switch (bitecount) - if (0) - return + switch(bitecount) + if(0) + EMPTY_BLOCK_GUARD if(1) examine_list += "[parent] was bitten by someone!" if(2,3) diff --git a/code/datums/components/radioactive.dm b/code/datums/components/radioactive.dm index 9306f6aae899..26d98b99e80e 100644 --- a/code/datums/components/radioactive.dm +++ b/code/datums/components/radioactive.dm @@ -55,7 +55,6 @@ var/filter = master.get_filter("rad_glow") if(filter) animate(filter, alpha = 110, time = 15, loop = -1) - animate(alpha = 40, time = 25) /datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, _strength, _source, _half_life, _can_contaminate) if(!i_am_original) diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 17c5dc2ef39c..9bd38b62c9fa 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -168,7 +168,6 @@ else dat += "
[bdat]" - else else dat += "{Log In}" var/datum/browser/popup = new(user, "med_rec", "Medical Records Console", 600, 400) @@ -375,7 +374,6 @@ if(istype(active1.fields["photo_side"], /obj/item/photo)) var/obj/item/photo/P = active1.fields["photo_side"] P.show(usr) - else else if(href_list["p_stat"]) if(active1) @@ -488,16 +486,12 @@ for(var/datum/data/record/R in GLOB.data_core.medical) if((lowertext(R.fields["name"]) == t1 || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))) active2 = R - else - //Foreach continue //goto(3229) if(!(active2)) temp = text("Could not locate record [].", sanitize(t1)) else for(var/datum/data/record/E in GLOB.data_core.general) if((E.fields["name"] == active2.fields["name"] || E.fields["id"] == active2.fields["id"])) active1 = E - else - //Foreach continue //goto(3334) screen = 4 else if(href_list["print_p"]) diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 37759d04b13e..cdfad1556187 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -228,7 +228,6 @@ dat += "New Security Record

" dat += "Delete Record (ALL)
Print Record
Print Wanted Poster
Print Missing Persons Poster
Back

" dat += "{Log Out}" - else else dat += "{Log In}" var/datum/browser/popup = new(user, "secure_rec", "Security Records Console", 600, 400) diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index d703ae86ca7d..cd6fafd4dbef 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -235,7 +235,7 @@ . = TRUE if("select_stencil") var/stencil = params["item"] - if(stencil in all_drawables + randoms) + if(stencil in (all_drawables + randoms)) drawtype = stencil . = TRUE text_buffer = "" @@ -320,7 +320,7 @@ temp = "symbol" else if(drawing in drawings) temp = "drawing" - else if(drawing in graffiti|oriented) + else if(drawing in (graffiti|oriented)) temp = "graffiti" var/graf_rot diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index e259e56ccd0e..70871e2ddc77 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -410,7 +410,7 @@ GLOBAL_LIST_EMPTY(PDAs) //BASIC FUNCTIONS=================================== if("Refresh")//Refresh, goes to the end of the proc. - + EMPTY_BLOCK_GUARD if("Return")//Return if(mode<=9) mode = 0 diff --git a/code/game/objects/items/devices/mines.dm b/code/game/objects/items/devices/mines.dm index 6319cc1a638d..4f2169350d79 100644 --- a/code/game/objects/items/devices/mines.dm +++ b/code/game/objects/items/devices/mines.dm @@ -469,7 +469,7 @@ shrapnel_magnitude = 4 /obj/item/mine/pressure/explosive/fire/mine_effect(mob/victim) - if(victim && victim.is_holding(src))//in case it's been picked up + if(victim?.is_holding(src))//in case it's been picked up for(var/turf/T in view(4,victim)) T.IgniteTurf(15) new /obj/effect/hotspot(T) diff --git a/code/game/objects/items/pet_carrier.dm b/code/game/objects/items/pet_carrier.dm index a08b1398aad8..0882034b165a 100644 --- a/code/game/objects/items/pet_carrier.dm +++ b/code/game/objects/items/pet_carrier.dm @@ -31,13 +31,13 @@ /obj/item/pet_carrier/Exited(atom/movable/occupant) . = ..() - if(occupant in occupants && isliving(occupant)) + if((occupant in occupants) && isliving(occupant)) var/mob/living/L = occupant occupants -= occupant occupant_weight -= L.mob_size /obj/item/pet_carrier/handle_atom_del(atom/A) - if(A in occupants && isliving(A)) + if((A in occupants) && isliving(A)) var/mob/living/L = A occupants -= L occupant_weight -= L.mob_size @@ -178,7 +178,7 @@ add_occupant(target) /obj/item/pet_carrier/proc/add_occupant(mob/living/occupant) - if(occupant in occupants || !istype(occupant)) + if((occupant in occupants) || !istype(occupant)) return occupant.forceMove(src) occupants += occupant diff --git a/code/game/objects/items/storage/guncases.dm b/code/game/objects/items/storage/guncases.dm index c846dd36b050..760a84f4e3aa 100644 --- a/code/game/objects/items/storage/guncases.dm +++ b/code/game/objects/items/storage/guncases.dm @@ -3,6 +3,8 @@ desc = "A large box designed for holding firearms and magazines safely." icon = 'icons/obj/guncase.dmi' icon_state = "guncase" + lefthand_file = 'icons/mob/inhands/equipment/toolbox_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/toolbox_righthand.dmi' item_state = "infiltrator_case" force = 12 throwforce = 12 diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index e0e115b81d01..363a83d965a3 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -24,11 +24,8 @@ /obj/proc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir, armour_penetration = 0) if(damage_flag == "melee" && damage_amount < damage_deflection) return 0 - switch(damage_type) - if(BRUTE) - if(BURN) - else - return 0 + if(damage_type != BRUTE && damage_type != BURN) + return 0 var/armor_protection = 0 if(damage_flag) armor_protection = armor.getRating(damage_flag) diff --git a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm index 089b6f8f792c..fa4fe485015d 100644 --- a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm @@ -50,7 +50,7 @@ new /obj/item/clothing/mask/breath(src) if ("nothing") - // doot + EMPTY_BLOCK_GUARD // teehee if ("delete") diff --git a/code/game/objects/structures/crateshelf.dm b/code/game/objects/structures/crateshelf.dm index 3b1387f5490b..0bf1cfa64c4f 100644 --- a/code/game/objects/structures/crateshelf.dm +++ b/code/game/objects/structures/crateshelf.dm @@ -123,6 +123,7 @@ crate.SpinAnimation(rand(4,7), 1) // Spin the crates around a little as they fall. Randomness is applied so it doesn't look weird. switch(pick(1, 1, 1, 1, 2, 2, 3)) // Randomly pick whether to do nothing, open the crate, or break it open. if(1) // Believe it or not, this does nothing. + EMPTY_BLOCK_GUARD if(2) // Open the crate! if(crate.open()) // Break some open, cause a little chaos. crate.visible_message(span_warning("[crate]'s lid falls open!")) diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm index 5e354e0f6550..0046d353dc5c 100644 --- a/code/modules/admin/permissionedit.dm +++ b/code/modules/admin/permissionedit.dm @@ -214,7 +214,7 @@ . = ckey(admin_key) if(!.) return FALSE - if(!admin_ckey && (. in GLOB.admin_datums+GLOB.deadmins)) + if(!admin_ckey && (. in (GLOB.admin_datums+GLOB.deadmins))) to_chat(usr, "[admin_key] is already an admin.", confidential = TRUE) return FALSE if(use_db) diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm index 68d2b4c2ec64..60528592f4e0 100644 --- a/code/modules/admin/view_variables/debug_variables.dm +++ b/code/modules/admin/view_variables/debug_variables.dm @@ -1,23 +1,24 @@ #define VV_HTML_ENCODE(thing) (sanitize ? html_encode(thing) : thing) /// Get displayed variable in VV variable list -/proc/debug_variable(name, value, level, datum/D, sanitize = TRUE) //if D is a list, name will be index, and value will be assoc value. +/proc/debug_variable(name, value, level, datum/owner, sanitize = TRUE) //if D is a list, name will be index, and value will be assoc value. var/header - if(D) - if(islist(D)) + if(owner) + if(islist(owner)) + var/list/owner_list = owner var/index = name if (value) - name = D[name] //name is really the index until this line + name = owner_list[name] //name is really the index until this line else - value = D[name] - header = "
  • ([VV_HREF_TARGET_1V(D, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(D, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(D, VV_HK_LIST_REMOVE, "-", index)]) " + value = owner_list[name] + header = "
  • ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_REMOVE, "-", index)]) " else - header = "
  • ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_MASSEDIT, "M", name)]) " + header = "
  • ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_MASSEDIT, "M", name)]) " else header = "
  • " var/item var/name_part = VV_HTML_ENCODE(name) - if(level > 0 || islist(D)) //handling keys in assoc lists + if(level > 0 || islist(owner)) //handling keys in assoc lists if(istype(name,/datum)) name_part = "[VV_HTML_ENCODE(name)] [REF(name)]" else if(islist(name)) diff --git a/code/modules/client/loadout/_loadout.dm b/code/modules/client/loadout/_loadout.dm index a0e5d6cab3c3..44c1cff4ffa5 100644 --- a/code/modules/client/loadout/_loadout.dm +++ b/code/modules/client/loadout/_loadout.dm @@ -20,11 +20,11 @@ GLOBAL_LIST_EMPTY(gear_datums) if(G == initial(G.subtype_path)) continue - if(!use_name) - WARNING("Loadout - Missing display name: [G]") + if(!use_name && initial(G.path)) + WARNING("Loadout gear [G] is missing display name") continue if(!initial(G.path) && use_category != "OOC") //OOC category does not contain actual items - WARNING("Loadout - Missing path definition: [G]") + WARNING("Loadout gear [G] is missing path definition") continue if(!GLOB.loadout_categories[use_category]) diff --git a/code/modules/client/loadout/loadout_accessories.dm b/code/modules/client/loadout/loadout_accessories.dm index a8acc1544654..d52c9a8b58fc 100644 --- a/code/modules/client/loadout/loadout_accessories.dm +++ b/code/modules/client/loadout/loadout_accessories.dm @@ -98,6 +98,9 @@ subtype_path = /datum/gear/accessory/mask slot = ITEM_SLOT_MASK +/datum/gear/accessory/mask/bandana + subtype_path = /datum/gear/accessory/mask/bandana + /datum/gear/accessory/mask/bandana/red display_name = "bandana, red" path = /obj/item/clothing/mask/bandana/red diff --git a/code/modules/client/loadout/loadout_hat.dm b/code/modules/client/loadout/loadout_hat.dm index 2f7e59c288b7..f660d35f676a 100644 --- a/code/modules/client/loadout/loadout_hat.dm +++ b/code/modules/client/loadout/loadout_hat.dm @@ -80,6 +80,9 @@ //Soft caps +/datum/gear/hat/softcap + subtype_path = /datum/gear/hat/softcap + /datum/gear/hat/softcap/red display_name = "cap, red" path = /obj/item/clothing/head/soft/red diff --git a/code/modules/client/loadout/loadout_suit.dm b/code/modules/client/loadout/loadout_suit.dm index 1d11857663ad..1edeed63530a 100644 --- a/code/modules/client/loadout/loadout_suit.dm +++ b/code/modules/client/loadout/loadout_suit.dm @@ -88,6 +88,9 @@ path = /obj/item/clothing/suit/toggle/hazard //Suspenders +/datum/gear/suit/suspenders + subtype_path = /datum/gear/suit/suspenders + /datum/gear/suit/suspenders/red display_name = "suspenders, red" path = /obj/item/clothing/suit/toggle/suspenders diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 9954d785cc74..18c9a5374443 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -893,7 +893,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) mutant_category = 0 // end generic adjective - if("wings" in pref_species.default_features && GLOB.r_wings_list.len >1) + if(("wings" in pref_species.default_features) && GLOB.r_wings_list.len >1) if(!mutant_category) dat += APPEARANCE_CATEGORY_COLUMN diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 416fa20df39c..c5d7e6ec98f9 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -184,7 +184,7 @@ . = ..() UnregisterSignal(M, COMSIG_MOB_SAY) -/obj/item/clothing/head/warden/drill/proc/handle_speech(datum/source, mob/speech_args) +/obj/item/clothing/head/warden/drill/proc/handle_speech(datum/source, list/speech_args) var/message = speech_args[SPEECH_MESSAGE] if(message[1] != "*") switch (mode) diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index b2636b02871b..1271717fc537 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -201,7 +201,7 @@ . = ..() UnregisterSignal(M, COMSIG_MOB_SAY) -/obj/item/clothing/head/frenchberet/proc/handle_speech(datum/source, mob/speech_args) +/obj/item/clothing/head/frenchberet/proc/handle_speech(datum/source, list/speech_args) var/message = speech_args[SPEECH_MESSAGE] if(message[1] != "*") message = " [message]" diff --git a/code/modules/clothing/suits/cloaks.dm b/code/modules/clothing/suits/cloaks.dm index 982b3804f586..1524a4aa6466 100644 --- a/code/modules/clothing/suits/cloaks.dm +++ b/code/modules/clothing/suits/cloaks.dm @@ -5,7 +5,6 @@ desc = "It's a cape that can be worn around your neck." icon = 'icons/obj/clothing/cloaks.dmi' icon_state = "qmcloak" - item_state = "qmcloak" w_class = WEIGHT_CLASS_SMALL body_parts_covered = CHEST|GROIN|LEGS|ARMS flags_inv = HIDESUITSTORAGE diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm index 89cce9c24d64..81e5da317553 100644 --- a/code/modules/clothing/under/accessories.dm +++ b/code/modules/clothing/under/accessories.dm @@ -103,7 +103,7 @@ name = "waistcoat" desc = "For some classy, murderous fun." icon_state = "waistcoat" - item_state = "waistcoat" + item_state = "det_suit" minimize_when_attached = FALSE attachment_slot = null @@ -119,13 +119,11 @@ name = "syndicate maid apron" desc = "Practical? No. Tactical? Also no. Cute? Most definitely yes." icon_state = "maidapronsynd" - item_state = "maidapronsynd" /obj/item/clothing/accessory/maidapron/inteq name = "inteq maid apron" desc = "A 'tactical' apron to protect you from all sorts of spills, from dough to blood!" icon_state = "inteqmaidapron" - item_state = "inteqmaidapron" ////////// //Medals// @@ -408,7 +406,6 @@ name = "shoulder holster" desc = "A holster to carry a handgun and ammo. WARNING: Badasses only." icon_state = "holster" - item_state = "holster" pocket_storage_component_path = /datum/component/storage/concrete/pockets/holster attachment_slot = null diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm index ec10f7dfb0f2..90b180587638 100644 --- a/code/modules/food_and_drinks/drinks/drinks.dm +++ b/code/modules/food_and_drinks/drinks/drinks.dm @@ -593,6 +593,7 @@ broh.losebreath++ switch(broh.losebreath) if(-INFINITY to 0) + EMPTY_BLOCK_GUARD if(1 to 2) if(prob(30)) user.visible_message("[broh]'s eyes water as [broh.p_they()] chug the can of [src]!") diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 3ff72083450a..406c59ae0d04 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -433,7 +433,7 @@ mind.active = FALSE //we wish to transfer the key manually mind.original_character_slot_index = client.prefs.default_slot mind.transfer_to(H) //won't transfer key since the mind is not active - mind.set_original_character(H) + H.mind.set_original_character(H) H.name = real_name client.init_verbs() diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 5005930855e4..beb44db426de 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -712,7 +712,7 @@ if(ignite_turfs) T.IgniteTurf(power,flame_color) for(var/mob/living/L in T.contents) - if(L in hit_list || L == source) + if((L in hit_list) || L == source) continue hit_list += L L.adjustFireLoss(20) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index c800acc46786..d1b28067a804 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -452,8 +452,6 @@ else client.eye = client.mob client.perspective = MOB_PERSPECTIVE - else - //Do nothing else //Reset to common defaults: mob if on turf, otherwise current loc if(isturf(loc)) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index db9a8db4ec2c..e8d5f5c508c6 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -147,7 +147,7 @@ if(20) newletter += "[newletter][newletter]" else - // do nothing + EMPTY_BLOCK_GUARD . += "[newletter]" return sanitize(.) @@ -192,7 +192,7 @@ if(5) newletter = "glor" else - // do nothing + EMPTY_BLOCK_GUARD . += newletter return sanitize(.) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index ea8d5fcbfff2..32d91294bc84 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -337,7 +337,7 @@ /// triggered on wield of two handed item /obj/item/gun/proc/on_wield(obj/item/source, mob/user) wielded = TRUE - INVOKE_ASYNC(src, .proc.do_wield, user) + INVOKE_ASYNC(src, PROC_REF(do_wield), user) /obj/item/gun/proc/do_wield(mob/user) user.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/gun, multiplicative_slowdown = wield_slowdown) diff --git a/code/modules/projectiles/guns/ballistic/assault.dm b/code/modules/projectiles/guns/ballistic/assault.dm index 091c1aba92ba..0141667d1ce6 100644 --- a/code/modules/projectiles/guns/ballistic/assault.dm +++ b/code/modules/projectiles/guns/ballistic/assault.dm @@ -230,17 +230,10 @@ return secondary.screwdriver_act(user, attack_obj,) return ..() - -/obj/item/gun/ballistic/automatic/assault/e40/can_shoot() - var/current_firemode = gun_firemodes[firemode_index] - if(current_firemode != FIREMODE_OTHER) - return ..() - return secondary.can_shoot() - /obj/item/gun/ballistic/automatic/assault/e40/on_wield(obj/item/source, mob/user) wielded = TRUE secondary.wielded = TRUE - INVOKE_ASYNC(src, .proc.do_wield, user) + INVOKE_ASYNC(src, PROC_REF(do_wield), user) /obj/item/gun/ballistic/automatic/assault/e40/do_wield(mob/user) . = ..() diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index 2c7b664ff3b5..752af51a57e5 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -463,6 +463,7 @@ desc = "A small law enforcement firearm. Originally commissioned by Nanotrasen for their Private Investigation division, it has become extremely popular among independent civilians as a cheap, compact sidearm. Uses .38 Special rounds." fire_sound = 'sound/weapons/gun/revolver/shot_light.ogg' icon_state = "detective" + item_state = "hp_generic" icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' @@ -572,6 +573,7 @@ EMPTY_GUN_HELPER(revolver/viper) mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' icon_state = "montagne" + item_state = "hp_generic" manufacturer = MANUFACTURER_HUNTERSPRIDE spread_unwielded = 15 recoil = 0 diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 26c796555ebd..30ccb528a4e9 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -153,6 +153,8 @@ EMPTY_GUN_HELPER(shotgun/hellfire) desc = "A semi-automatic shotgun with tactical furniture and six-shell capacity underneath." icon_state = "cshotgun" item_state = "shotgun_combat" + lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi' + righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi' fire_delay = 0.5 SECONDS default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/com allowed_ammo_types = list( @@ -398,8 +400,8 @@ EMPTY_GUN_HELPER(shotgun/doublebarrel) name = "improvised shotgun" desc = "A length of pipe and miscellaneous bits of scrap fashioned into a rudimentary single-shot shotgun." icon = 'icons/obj/guns/projectile.dmi' - lefthand_file = GUN_LEFTHAND_ICON - righthand_file = GUN_RIGHTHAND_ICON + lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi' + righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi' mob_overlay_icon = null base_icon_state = "ishotgun" diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 9c75aa56edcc..4eba20701ac7 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -3,6 +3,7 @@ desc = "A basic energy-based gun." icon = 'icons/obj/guns/energy.dmi' icon_state = "laser" + item_state = "spur" muzzleflash_iconstate = "muzzle_flash_laser" muzzle_flash_color = COLOR_SOFT_RED diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index f5f82ff43fb9..c17c1cb8a005 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -2,8 +2,6 @@ name = "SL L-204 laser gun" desc = "A basic energy-based laser gun that fires concentrated beams of light which pass through glass and thin metal." - icon_state = "laser" - item_state = "laser" w_class = WEIGHT_CLASS_NORMAL custom_materials = list(/datum/material/iron=2000) ammo_type = list(/obj/item/ammo_casing/energy/lasergun) diff --git a/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm b/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm index 6d1dff192407..dc599c84bb7f 100644 --- a/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm +++ b/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm @@ -886,7 +886,7 @@ NO_MAG_GUN_HELPER(automatic/assault/hydra/dmr) /obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/on_wield(obj/item/source, mob/user) wielded = TRUE secondary.wielded = TRUE - INVOKE_ASYNC(src, .proc.do_wield, user) + INVOKE_ASYNC(src, PROC_REF(do_wield), user) /obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/do_wield(mob/user) . = ..() diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index d37048250e62..bb6bafc9e74c 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -63,7 +63,7 @@ M.adjust_blindness(-1) switch(current_cycle) if(1 to 20) - //nothing + EMPTY_BLOCK_GUARD //nothing if(21 to INFINITY) if(prob(current_cycle-10)) M.cure_nearsighted(list(EYE_DAMAGE)) diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 9e4b2120774e..82bda376fc3c 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -119,7 +119,7 @@ F.fry(volume) F.reagents.add_reagent(/datum/reagent/consumable/cooking_oil, reac_volume) -/datum/reagent/consumable/cooking_oil/expose_mob(mob/living/M, method = TOUCH, method = SMOKE, reac_volume, show_message = 1, touch_protection = 0) +/datum/reagent/consumable/cooking_oil/expose_mob(mob/living/M, method = TOUCH, reac_volume, show_message = 1, touch_protection = 0) if(!istype(M)) return var/boiling = FALSE diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index 97196cf64ecc..ebfc08db3a48 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -776,7 +776,7 @@ ///obj/item/bodypart/proc/break_bone_feedback() owner.visible_message("You hear a cracking sound coming from [owner]'s [name].", "You feel something crack in your [name]!", "You hear an awful cracking sound.") - playsound(owner, list('sound/health/bone/bone_break1.ogg','sound/health/bone/bone_break2.ogg','sound/health/bone/bone_break3.ogg','sound/health/bone/bone_break4.ogg','sound/health/bone/bone_break5.ogg','sound/health/bone/bone_break6.ogg'), 100, FALSE, -1) + playsound(owner, pick(list('sound/health/bone/bone_break1.ogg','sound/health/bone/bone_break2.ogg','sound/health/bone/bone_break3.ogg','sound/health/bone/bone_break4.ogg','sound/health/bone/bone_break5.ogg','sound/health/bone/bone_break6.ogg')), 100, FALSE, -1) /obj/item/bodypart/proc/fix_bone() // owner.update_inv_splints() breaks diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 16e76901f957..5364a69c3483 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -45,6 +45,8 @@ /// Use this when something shouldn't happen and is of note, but shouldn't block CI. /// Does not mark the test as failed. #define TEST_NOTICE(source, message) source.log_for_test((##message), "notice", __FILE__, __LINE__) +/// TEST_NOTICE but more important +#define TEST_WARNING(source, message) source.log_for_test((##message), "warning", __FILE__, __LINE__) /// Constants indicating unit test completion status #define UNIT_TEST_PASSED 0 @@ -70,7 +72,7 @@ #ifdef BASIC_TESTS -//#include "icons/inhands.dm" +#include "icons/inhands.dm" #include "icons/missing_icons.dm" #include "icons/spritesheets.dm" #include "icons/worn_icons.dm" diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index b7c1b924fd53..ed3d9c6ed0a7 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -141,14 +141,12 @@ // Drastically lower the amount of time it takes to GC, since we don't have clients that can hold it up. SSgarbage.collection_timeout[GC_QUEUE_CHECK] = 10 SECONDS - //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() var/list/queues_we_care_about = list() - // All up to harddel - for(var/i in 1 to GC_QUEUE_HARDDELETE - 1) + // All of em, I want hard deletes too, since we rely on the debug info from them + for(var/i in 1 to GC_QUEUE_HARDDELETE) queues_we_care_about += i //Now that we've qdel'd everything, let's sleep until the gc has processed all the shit we care about @@ -158,6 +156,7 @@ time_needed += SSgarbage.collection_timeout[index] var/start_time = world.time + var/real_start_time = REALTIMEOFDAY var/garbage_queue_processed = FALSE sleep(time_needed) @@ -179,7 +178,7 @@ 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 + if(REALTIMEOFDAY > real_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 @@ -215,4 +214,3 @@ SSticker.delay_end = FALSE //This shouldn't be needed, but let's be polite SSgarbage.collection_timeout[GC_QUEUE_CHECK] = GC_CHECK_QUEUE - SSgarbage.collection_timeout[GC_QUEUE_HARDDELETE] = GC_DEL_QUEUE diff --git a/code/modules/unit_tests/icons/inhands.dm b/code/modules/unit_tests/icons/inhands.dm index 858c6d2f2840..dc05295203e6 100644 --- a/code/modules/unit_tests/icons/inhands.dm +++ b/code/modules/unit_tests/icons/inhands.dm @@ -53,7 +53,7 @@ match_message += (match_message ? " & '[file_place]'" : " - Matching sprite found in: '[file_place]'") if(!(skip_left || skip_right) && !lefthand_file && !righthand_file) - TEST_FAIL("Missing both icon files for [item_path].\n\titem_state = \"[item_state]\"[match_message]") + TEST_NOTICE(src, "Missing both icon files for [item_path].\n\titem_state = \"[item_state]\"[match_message]") continue var/missing_left @@ -80,7 +80,7 @@ if(!match_message && right_fallback && left_fallback) fallback_log_message += "\n\t[item_path] has invalid value, using fallback icon.\n\titem_state = \"[item_state]\"" continue - TEST_FAIL("Missing inhand sprites for [item_path] in both '[lefthand_file]' & '[righthand_file]'.\n\titem_state = \"[item_state]\"[match_message]") + TEST_NOTICE(src, "Missing inhand sprites for [item_path] in both '[lefthand_file]' & '[righthand_file]'.\n\titem_state = \"[item_state]\"[match_message]") else if(missing_left) TEST_FAIL("Missing left inhand sprite for [item_path] in '[lefthand_file]'[left_fallback ? ", using fallback icon" : null].\n\titem_state = \"[item_state]\"[match_message]") else if(missing_right) @@ -90,5 +90,5 @@ TEST_FAIL("Invalid item_state values should be set to null if there isn't a valid icon.[fallback_log_message]") if(unset_inhand_var_message) - log_test("\tNotice - Possible inhand icon matches found. It is best to be explicit with inhand sprite values.[unset_inhand_var_message]") + TEST_NOTICE(src, "Possible inhand icon matches found. It is best to be explicit with inhand sprite values.[unset_inhand_var_message]") diff --git a/code/modules/unit_tests/icons/worn_icons.dm b/code/modules/unit_tests/icons/worn_icons.dm index 3dba4d7c8e03..31c5d432dfa0 100644 --- a/code/modules/unit_tests/icons/worn_icons.dm +++ b/code/modules/unit_tests/icons/worn_icons.dm @@ -5,6 +5,7 @@ /// Make sure this location is also present in tools/deploy.sh /// If you need additional paths ontop of this second one, you can add another generate_possible_icon_states_list("your/folder/path/") below the if(additional_icon_location) block in Run(), and make sure to add that path to tools/deploy.sh as well. var/additional_icon_location = null + var/required_test = TRUE /datum/unit_test/mob_overlay_icons/proc/generate_possible_icon_states_list(directory_path) if(!directory_path) @@ -16,6 +17,9 @@ else possible_icon_states += generate_possible_icon_states_list("[directory_path][file_path]") +/datum/unit_test/mob_overlay_icons/proc/types_to_search() + return subtypesof(/obj/item/clothing) + /datum/unit_test/mob_overlay_icons/Run() generate_possible_icon_states_list() if(additional_icon_location) @@ -23,7 +27,7 @@ var/list/already_warned_icons = list() - for(var/obj/item/item_path as anything in (subtypesof(/obj/item/clothing))) + for(var/obj/item/item_path as anything in types_to_search()) var/cached_slot_flags = initial(item_path.slot_flags) if(!cached_slot_flags || (cached_slot_flags & ITEM_SLOT_LPOCKET) || (cached_slot_flags & ITEM_SLOT_RPOCKET) || initial(item_path.item_flags) & ABSTRACT) continue @@ -45,7 +49,10 @@ if(mob_overlay_icon) //easiest to check since we override everything. this automatically includes downstream support. if(!(icon_state in icon_states(mob_overlay_icon, 1))) - TEST_FAIL("[item_path] using invalid [mob_overlay_state ? "mob_overlay_state" : "icon_state"], \"[icon_state]\" in mob_overlay_icon override file, '[mob_overlay_icon]'[match_message]") + if(required_test) + TEST_FAIL("[item_path] using invalid [mob_overlay_state ? "mob_overlay_state" : "icon_state"], \"[icon_state]\" in mob_overlay_icon override file, '[mob_overlay_icon]'[match_message]") + else + TEST_NOTICE(src, "[item_path] using invalid [mob_overlay_state ? "mob_overlay_state" : "icon_state"], \"[icon_state]\" in mob_overlay_icon override file, '[mob_overlay_icon]'[match_message]") continue var/icon_file //checks against all the default icon locations if one isn't defined. @@ -61,15 +68,6 @@ fail_reasons += "[item_path] using invalid [mob_overlay_state ? "mob_overlay_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]" spacer = "\n\t" - /* - if(cached_slot_flags & ITEM_SLOT_ID) - icon_file = 'icons/mob/clothing/id.dmi' - if(!(icon_state in icon_states(icon_file, 1))) - already_warned_icons += icon_state - fail_reasons += "[spacer][item_path] using invalid [mob_overlay_state ? "mob_overlay_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]" - spacer = "\n\t" - */ - if(cached_slot_flags & ITEM_SLOT_GLOVES) icon_file = 'icons/mob/clothing/hands.dmi' if(!(icon_state in icon_states(icon_file, 1))) @@ -113,4 +111,13 @@ spacer = "\n\t" if(fail_reasons) - TEST_FAIL(fail_reasons) + if(required_test) + TEST_FAIL(fail_reasons) + else + TEST_NOTICE(src, fail_reasons) + +/datum/unit_test/mob_overlay_icons/not_clothing + required_test = FALSE + +/datum/unit_test/mob_overlay_icons/not_clothing/types_to_search() + return (subtypesof(/obj/item) - subtypesof(/obj/item/clothing)) diff --git a/code/modules/unit_tests/outfit_sanity.dm b/code/modules/unit_tests/outfit_sanity.dm index a09395d42103..8e85797e713e 100644 --- a/code/modules/unit_tests/outfit_sanity.dm +++ b/code/modules/unit_tests/outfit_sanity.dm @@ -6,7 +6,7 @@ if (outfit.random != TRUE) \ TEST_FAIL("[outfit.name]'s [#outfit_key] is invalid! Could not equip a [outfit.##outfit_key] into that slot."); \ else \ - log_test("[outfit.name]'s [#outfit_key] is invalid! Could not equip a [outfit.##outfit_key] into that slot."); \ + TEST_NOTICE(src, "[outfit.name]'s [#outfit_key] is invalid! Could not equip a [outfit.##outfit_key] into that slot."); \ } \ } @@ -72,7 +72,7 @@ if (outfit.random != TRUE) TEST_FAIL("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.") else - log_test("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.") + TEST_NOTICE(src, "[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.") #undef CHECK_OUTFIT_SLOT diff --git a/icons/mob/inhands/weapons/guns_lefthand.dmi b/icons/mob/inhands/weapons/guns_lefthand.dmi index ff71ba99e3c082d3f95180e18c164d474a17f18d..fd76394a835a214a7673411d42e41c08bdfa5215 100644 GIT binary patch literal 39557 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

    PTNXyo}*OukxtPLUfViCa|9bzx|1m1( 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 X_Giq8J%j_+Xt4`6DC=s=>v4YpY0pY_ literal 42172 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%0ADEFUW9q)A10mm#gLQu&-7oO$Mw&NAlgkAL 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" ); }; - -// Signal tgui that we're ready to receive updates -Byond.sendMessage('ready');