diff --git a/code/__DEFINES/_spacemandmm.dm b/code/__DEFINES/_spacemandmm.dm
index 0abcd8bba9a..8112a60eaec 100644
--- a/code/__DEFINES/_spacemandmm.dm
+++ b/code/__DEFINES/_spacemandmm.dm
@@ -1,15 +1,22 @@
// This file contains all the defines related to the spacemanDMM linter (dreamchecker). When running under normal conditions, BYOND will ignore all these
+// Read https://github.com/SpaceManiac/SpacemanDMM/tree/master/crates/dreamchecker for more info.
#ifdef SPACEMAN_DMM
#define RETURN_TYPE(X) set SpacemanDMM_return_type = X
#define SHOULD_CALL_PARENT(X) set SpacemanDMM_should_call_parent = X
#define UNLINT(X) SpacemanDMM_unlint(X)
#define SHOULD_NOT_OVERRIDE(X) set SpacemanDMM_should_not_override = X
#define SHOULD_NOT_SLEEP(X) set SpacemanDMM_should_not_sleep = X
+ /// A "pure" proc does not make any external changes, or to its output.
#define SHOULD_BE_PURE(X) set SpacemanDMM_should_be_pure = X
+ /// Private procs can only be called by things of exactly the same type. This also prevents overriding of the proc.
#define PRIVATE_PROC(X) set SpacemanDMM_private_proc = X
+ /// Protected procs can only be call by things of the same type or subtypes
#define PROTECTED_PROC(X) set SpacemanDMM_protected_proc = X
+ /// Final vars forbid overriding their value by types that inherit it.
#define VAR_FINAL var/SpacemanDMM_final
+ /// Private vars can only be called by things of exactly the same type
#define VAR_PRIVATE var/SpacemanDMM_private
+ /// Protected vars can only be call by things of the same type or subtypes
#define VAR_PROTECTED var/SpacemanDMM_protected
#else
#define RETURN_TYPE(X)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 182545fa871..4777409d0af 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -488,11 +488,13 @@
/// Updates the name of the atom
/atom/proc/update_name(updates=ALL)
SHOULD_CALL_PARENT(TRUE)
+ PROTECTED_PROC(TRUE)
return SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_NAME, updates)
/// Updates the description of the atom
/atom/proc/update_desc(updates=ALL)
SHOULD_CALL_PARENT(TRUE)
+ PROTECTED_PROC(TRUE)
return SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_DESC, updates)
/// Updates the icon of the atom
@@ -525,10 +527,12 @@
/// Updates the icon state of the atom
/atom/proc/update_icon_state()
+ PROTECTED_PROC(TRUE)
return
/// Updates the overlays of the atom. It has to return a list of overlays if it can't call the parent to create one. The list can contain anything that would be valid for the add_overlay proc: Images, mutable appearances, icon states...
/atom/proc/update_overlays()
+ PROTECTED_PROC(TRUE)
return list()
/atom/proc/relaymove()
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index a260b16f531..19b6d021711 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1531,8 +1531,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
DA.polarized_glass = polarized_glass
DA.state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS
DA.created_name = name
- DA.update_name()
- DA.update_icon()
+ DA.update_appearance(UPDATE_NAME|UPDATE_ICON)
if(!disassembled)
if(DA)
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index 20c2c4e3f3d..9a2a29a93f9 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -197,8 +197,7 @@
DA = new /obj/structure/door_assembly(loc)
if(glass)
DA.glass = TRUE
- DA.update_icon()
- DA.update_name()
+ DA.update_appearance(UPDATE_NAME|UPDATE_ICON)
qdel(src)
/obj/machinery/door/airlock/plasma/attackby(obj/item/C, mob/user, params)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 071811f3137..1c1451bb566 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -610,6 +610,6 @@
blockage.max_integrity += 25
foam_level++
blockage.icon_state = "foamed_[foam_level]"
- blockage.update_icon_state()
+ blockage.update_icon(UPDATE_ICON_STATE)
#undef MAX_FOAM_LEVEL
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 18cc38eac8b..f31a573e270 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -383,7 +383,7 @@ GLOBAL_LIST_EMPTY(allRequestConsoles)
/obj/machinery/requests_console/proc/print_label(tag_name, tag_index)
var/obj/item/shippingPackage/sp = new /obj/item/shippingPackage(get_turf(src))
sp.sortTag = tag_index
- sp.update_desc()
+ sp.update_appearance(UPDATE_DESC)
print_cooldown = world.time + 600 //1 minute cooldown before you can print another label, but you can still configure the next one during this time
/obj/machinery/requests_console/proc/view_messages()
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index d04a540066a..bd96797ad52 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -159,7 +159,7 @@
qdel(V)
set_light(lights_range_ambient, lights_power_ambient)
- update_overlays()
+ update_icon(UPDATE_OVERLAYS)
/obj/mecha/update_overlays()
. = ..()
@@ -1037,7 +1037,7 @@
AI.forceMove(src)
occupant = AI
icon_state = reset_icon(icon_state)
- update_overlays()
+ update_icon(UPDATE_OVERLAYS)
playsound(src, 'sound/machines/windowdoor.ogg', 50, 1)
if(!hasInternalDamage())
SEND_SOUND(occupant, sound(nominalsound, volume = 50))
@@ -1195,7 +1195,7 @@
H.throw_alert("locked", /atom/movable/screen/alert/mech_maintenance)
if(connected_port)
H.throw_alert("mechaport_d", /atom/movable/screen/alert/mech_port_disconnect)
- update_overlays()
+ update_icon(UPDATE_OVERLAYS)
return TRUE
else
return FALSE
@@ -1251,7 +1251,7 @@
Move(loc)
icon_state = reset_icon()
dir = dir_in
- update_overlays()
+ update_icon(UPDATE_OVERLAYS)
log_message("[mmi_as_oc] moved in as pilot.")
if(!hasInternalDamage())
SEND_SOUND(occupant, sound(nominalsound, volume = 50))
@@ -1341,7 +1341,7 @@
if(ishuman(L))
var/mob/living/carbon/human/H = L
H.regenerate_icons() // workaround for 14457
- update_overlays()
+ update_icon(UPDATE_OVERLAYS)
/obj/mecha/force_eject_occupant(mob/target)
go_out()
diff --git a/code/game/objects/items/scratch_card.dm b/code/game/objects/items/scratch_card.dm
index 2a6f372fe7d..5864b207e7e 100644
--- a/code/game/objects/items/scratch_card.dm
+++ b/code/game/objects/items/scratch_card.dm
@@ -27,7 +27,7 @@
icon_state = "scard_loser"
playsound(user, 'sound/items/scratching.ogg', 25, TRUE)
scratched = TRUE
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
/obj/item/scratch/attack_obj(obj/O, mob/living/user, params)
if(winner && istype(O, /obj/machinery/economy/atm))
diff --git a/code/game/objects/items/weapons/chemical_flamethrower/chemical_flamethrower.dm b/code/game/objects/items/weapons/chemical_flamethrower/chemical_flamethrower.dm
index 9ec74e6b2f2..ae130040936 100644
--- a/code/game/objects/items/weapons/chemical_flamethrower/chemical_flamethrower.dm
+++ b/code/game/objects/items/weapons/chemical_flamethrower/chemical_flamethrower.dm
@@ -39,7 +39,7 @@
if(should_start_with_canisters && !length(canisters))
canisters += new /obj/item/chemical_canister
update_canister_stats()
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
/obj/item/chemical_flamethrower/Destroy()
QDEL_LIST_CONTENTS(canisters)
diff --git a/code/game/objects/items/weapons/melee/melee_misc.dm b/code/game/objects/items/weapons/melee/melee_misc.dm
index c374a870e6b..99c3652f28e 100644
--- a/code/game/objects/items/weapons/melee/melee_misc.dm
+++ b/code/game/objects/items/weapons/melee/melee_misc.dm
@@ -174,11 +174,11 @@
/obj/item/melee/breach_cleaver/proc/wield(obj/item/source, mob/living/carbon/human/user)
to_chat(user, "You heave [src] up in both hands.")
user.apply_status_effect(STATUS_EFFECT_BREACH_AND_CLEAVE)
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
/obj/item/melee/breach_cleaver/proc/unwield(obj/item/source, mob/living/carbon/human/user)
user.remove_status_effect(STATUS_EFFECT_BREACH_AND_CLEAVE)
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
/obj/item/melee/breach_cleaver/attack_obj(obj/O, mob/living/user, params)
if(!HAS_TRAIT(src, TRAIT_WIELDED)) // Only works good when wielded
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index 5a1aed227aa..fadff4057ac 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -431,7 +431,7 @@
slowdown = 1
if(antidrop_on_zip)
flags ^= NODROP
- update_icon_state(UPDATE_ICON_STATE)
+ update_icon(UPDATE_ICON_STATE)
return
slowdown = 0
@@ -440,7 +440,7 @@
container.hide_from_all() // Hide everything inside the bag too
if(antidrop_on_zip)
flags |= NODROP
- update_icon_state(UPDATE_ICON_STATE)
+ update_icon(UPDATE_ICON_STATE)
/obj/item/storage/backpack/duffel/update_icon_state()
. = ..()
diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm
index 6e3bcbd9171..3511f2bb5e4 100644
--- a/code/game/objects/structures/door_assembly.dm
+++ b/code/game/objects/structures/door_assembly.dm
@@ -285,8 +285,7 @@
if(electronics)
target.electronics = source.electronics
source.electronics.forceMove(target)
- target.update_icon(UPDATE_OVERLAYS)
- target.update_name()
+ target.update_appearance(UPDATE_NAME|UPDATE_OVERLAYS)
qdel(source)
/obj/structure/door_assembly/deconstruct(disassembled = TRUE)
diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm
index dde36ebb6ce..de327bca7e2 100644
--- a/code/game/objects/structures/mineral_doors.dm
+++ b/code/game/objects/structures/mineral_doors.dm
@@ -243,6 +243,6 @@
blockage.max_integrity += 25
foam_level++
blockage.icon_state = "foamed_[foam_level]"
- blockage.update_icon_state()
+ blockage.update_icon(UPDATE_ICON_STATE)
#undef MAX_FOAM_LEVEL
diff --git a/code/game/objects/structures/transit_tubes/transit_tube_construction.dm b/code/game/objects/structures/transit_tubes/transit_tube_construction.dm
index bfc860d6bd5..9836fb7343f 100644
--- a/code/game/objects/structures/transit_tubes/transit_tube_construction.dm
+++ b/code/game/objects/structures/transit_tubes/transit_tube_construction.dm
@@ -37,7 +37,7 @@
return
flipped = !flipped
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
/obj/structure/transit_tube_construction/screwdriver_act(mob/living/user, obj/item/I)
. = TRUE
diff --git a/code/modules/atmospherics/machinery/atmospherics.dm b/code/modules/atmospherics/machinery/atmospherics.dm
index af620e41e27..21f7103bab2 100644
--- a/code/modules/atmospherics/machinery/atmospherics.dm
+++ b/code/modules/atmospherics/machinery/atmospherics.dm
@@ -297,7 +297,7 @@ Pipelines + Other Objects -> Pipe network
level = (T.intact || !can_be_undertile) ? 2 : 1
else
level = 2
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
add_fingerprint(usr)
if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets
SSair.machinery_to_construct.Add(src)
diff --git a/code/modules/hydroponics/compost_bin.dm b/code/modules/hydroponics/compost_bin.dm
index 027b0d03f9e..d42c6f74b95 100644
--- a/code/modules/hydroponics/compost_bin.dm
+++ b/code/modules/hydroponics/compost_bin.dm
@@ -96,7 +96,7 @@
to_chat(user, "You have very nearly saturated the contents of [src] with potassium.")
SStgui.update_uis(src)
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
return TRUE
if(istype(O, /obj/item/food/grown))
@@ -110,7 +110,7 @@
make_biomass(O)
to_chat(user, "You put [O] in [src].")
SStgui.update_uis(src)
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
return TRUE
if(istype(O, /obj/item/reagent_containers))
var/proportion = 0
@@ -154,7 +154,7 @@
to_chat(user, "[src] has been nearly filled with potash.")
SStgui.update_uis(src)
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
return TRUE
@@ -199,7 +199,7 @@
biomass -= conversion_amount + potash_saltpetre_conversion + potassium_saltpetre_conversion
compost += conversion_amount
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
SStgui.update_uis(src)
// Makes soil from compost
@@ -209,7 +209,7 @@
return
new /obj/item/stack/sheet/soil(loc, amount)
compost -= SOIL_COST * amount
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
SStgui.update_uis(src)
/obj/machinery/compost_bin/attack_hand(mob/user)
diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm
index fdbed68d139..24521ccb0f8 100644
--- a/code/modules/hydroponics/gene_modder.dm
+++ b/code/modules/hydroponics/gene_modder.dm
@@ -451,7 +451,7 @@
else
core_gene.value = min(core_gene.value, genemod_var)
- disk.update_name()
+ disk.update_appearance(UPDATE_NAME)
QDEL_NULL(seed)
update_icon(UPDATE_OVERLAYS)
update_genes()
@@ -493,7 +493,7 @@
var/datum/plant_gene/core/C = gene.Copy()
disk.core_genes += C
- disk.update_name()
+ disk.update_appearance(UPDATE_NAME)
QDEL_NULL(seed)
update_icon(UPDATE_OVERLAYS)
update_genes()
diff --git a/code/modules/mob/living/simple_animal/slime/slime_death.dm b/code/modules/mob/living/simple_animal/slime/slime_death.dm
index 3136f8f0770..fcd7eb8d2b4 100644
--- a/code/modules/mob/living/simple_animal/slime/slime_death.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime_death.dm
@@ -22,7 +22,7 @@
E.Grant(src)
revive()
regenerate_icons()
- update_name()
+ update_appearance(UPDATE_NAME)
return
set_stat(DEAD) //Temporarily set to dead for icon updates
diff --git a/code/modules/mob/living/simple_animal/slime/slime_mob.dm b/code/modules/mob/living/simple_animal/slime/slime_mob.dm
index 304f9ed74c0..08682a4717a 100644
--- a/code/modules/mob/living/simple_animal/slime/slime_mob.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime_mob.dm
@@ -113,7 +113,7 @@
/mob/living/simple_animal/slime/proc/set_colour(new_colour)
colour = new_colour
- update_name()
+ update_appearance(UPDATE_NAME)
slime_mutation = mutation_table(colour)
var/sanitizedcolour = replacetext(colour, " ", "")
coretype = text2path("/obj/item/slime_extract/[sanitizedcolour]")
diff --git a/code/modules/mob/living/simple_animal/slime/slime_powers.dm b/code/modules/mob/living/simple_animal/slime/slime_powers.dm
index 1fef3f12c22..cb536acb6bb 100644
--- a/code/modules/mob/living/simple_animal/slime/slime_powers.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime_powers.dm
@@ -132,7 +132,7 @@
for(var/datum/action/innate/slime/evolve/E in actions)
E.Remove(src)
regenerate_icons()
- update_name()
+ update_appearance(UPDATE_NAME)
else
to_chat(src, "I am not ready to evolve yet...")
else
diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm
index 93e7c2100f2..5dd2e207e10 100644
--- a/code/modules/mod/mod_activation.dm
+++ b/code/modules/mod/mod_activation.dm
@@ -223,7 +223,7 @@
module.on_suit_deactivation()
STOP_PROCESSING(SSobj, src)
update_speed()
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
wearer.regenerate_icons()
/// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits.
diff --git a/code/modules/mod/mod_construction.dm b/code/modules/mod/mod_construction.dm
index 77fedf4c529..d526ae37a71 100644
--- a/code/modules/mod/mod_construction.dm
+++ b/code/modules/mod/mod_construction.dm
@@ -250,7 +250,7 @@
if(part.use_tool(src, user, 0, volume = 30))
to_chat(user, "Assembly unscrewed.")
construction_step = SCREWED_ASSEMBLY_STEP
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
/obj/item/mod/construction/shell/update_icon_state()
. = ..()
diff --git a/code/modules/mod/modules/_modules.dm b/code/modules/mod/modules/_modules.dm
index 35f6b60db82..6ba416447e6 100644
--- a/code/modules/mod/modules/_modules.dm
+++ b/code/modules/mod/modules/_modules.dm
@@ -362,7 +362,7 @@
return
var/core_path = pick(accepted_anomalies)
core = new core_path(src)
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
/obj/item/mod/module/anomaly_locked/Destroy()
QDEL_NULL(core)
@@ -407,7 +407,7 @@
core = item
to_chat(user, "You install [item].")
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
core.forceMove(src)
else
return ..()
@@ -424,7 +424,7 @@
if(Adjacent(user) && !issilicon(user))
user.put_in_hands(core)
core = null
- update_icon_state()
+ update_icon(UPDATE_ICON_STATE)
/obj/item/mod/module/anomaly_locked/update_icon_state()
icon_state = initial(icon_state) + (core ? "-core" : "")
diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm
index 80dba5fd47e..4bd0eef2f6a 100644
--- a/code/modules/paperwork/paper_bundle.dm
+++ b/code/modules/paperwork/paper_bundle.dm
@@ -268,7 +268,7 @@
. += sheet
. += "clip"
- update_desc()
+ update_appearance(UPDATE_DESC)
/obj/item/paper_bundle/proc/get_page(page_override)
if(page_override)
diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm
index ff397b39e9c..5a0fbf434ff 100644
--- a/code/modules/projectiles/ammunition.dm
+++ b/code/modules/projectiles/ammunition.dm
@@ -128,7 +128,7 @@
var/list/stored_ammo = list()
var/ammo_type = /obj/item/ammo_casing
var/max_ammo = 7
- var/multi_sprite_step = AMMO_BOX_MULTI_SPRITE_STEP_NONE // see update_icon_state() for details
+ var/multi_sprite_step = AMMO_BOX_MULTI_SPRITE_STEP_NONE // see update_icon_state for details
var/caliber
var/multiload = 1
var/slow_loading = FALSE
diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm
index 5e1c9074958..ad7350420cd 100644
--- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm
+++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm
@@ -463,7 +463,7 @@
if(slime.docile) //Undoes docility, but doesn't make rabid.
slime.visible_message("[slime] forgets its training, becoming wild once again!")
slime.docile = FALSE
- slime.update_name()
+ slime.update_appearance(UPDATE_NAME)
continue
slime.rabid = TRUE
slime.visible_message("[slime] is driven into a frenzy!")
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
index 9351978962e..c7a23b33c67 100644
--- a/code/modules/recycling/sortingmachinery.dm
+++ b/code/modules/recycling/sortingmachinery.dm
@@ -450,7 +450,7 @@
to_chat(user, "You seal [src], preparing it for delivery.")
icon_state = "shippack_sealed"
sealed = 1
- update_desc()
+ update_appearance(UPDATE_DESC)
else
if(tgui_alert(user, "Do you want to tear up the package?", "Shipping", list("Yes", "No")) == "Yes")
to_chat(user, "You shred [src].")
diff --git a/tools/ci/check_grep2.py b/tools/ci/check_grep2.py
index 1bbc9d8c249..996def27bf2 100644
--- a/tools/ci/check_grep2.py
+++ b/tools/ci/check_grep2.py
@@ -159,6 +159,16 @@ def check_empty_list_whitespace(idx, line):
if EMPTY_LIST_WHITESPACE.search(line):
return [(idx + 1, "Empty list declarations should not have any whitespace within their parentheses.")]
+IGNORE_ATOM_ICON_FILE = "atoms.dm"
+NO_MANUAL_ICON_UPDATES = re.compile(r"([\s.])(update_icon_state|update_desc|update_overlays|update_name)\(.*\)")
+def check_manual_icon_updates(idx, line):
+ if result := NO_MANUAL_ICON_UPDATES.search(line):
+ proc_result = result.group(2)
+ target = "update_icon"
+ if(proc_result == "update_name" or proc_result == "update_desc"):
+ target = "update_appearance"
+ return [(idx + 1, f"{proc_result}() should not be called manually. Use {target}({proc_result.upper()}) instead.")]
+
CODE_CHECKS = [
check_space_indentation,
check_mixed_indentation,
@@ -196,6 +206,8 @@ if __name__ == "__main__":
extra_checks = []
if filename != IGNORE_515_PROC_MARKER_FILENAME:
extra_checks.append(check_515_proc_syntax)
+ if filename != IGNORE_ATOM_ICON_FILE:
+ extra_checks.append(check_manual_icon_updates)
last_line = None
for idx, line in enumerate(code):