Refactors MODsuit slowdown calculations (#90116)

## About The Pull Request

MODsuits now calculate their slowdown by querying modules by sending a
signal on themselves instead of having modules try and keep up with
control unit's speed updates (which broke in a fabulous fashion after
MODsuits were allowed to deploy by piece)
Also added a separate trait to prevent objects from being speed
potion-ed to combat stabilized red crossbreed issues, and removed a
duplicate list helper (40 lines down in the same file lol)

Closes #89979
Closes #90036

## Changelog
🆑
fix: MODsuits should now be affected by stabilized red crossbreeds
fix: MODsuit slowdowns should no longer behave weirdly with ash
accretion/magboots/armor booster modules.
refactor: Refactored MODsuit slowdown calculations to be query-based
instead of modules directly modifying part speed values.
/🆑
This commit is contained in:
SmArtKar
2025-03-26 17:55:32 +01:00
committed by GitHub
parent c0204da3da
commit b7383bc503
15 changed files with 87 additions and 52 deletions
+14 -4
View File
@@ -9,11 +9,11 @@
body_parts_covered = HEAD
heat_protection = HEAD
cold_protection = HEAD
item_flags = IMMUTABLE_SLOW
// Even without a hat stabilizer, hats can be worn - however, they'll fall off very easily
/obj/item/clothing/head/mod/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NO_SPEED_POTION, INNATE_TRAIT)
AddComponent(/datum/component/hat_stabilizer, loose_hat = TRUE)
/obj/item/clothing/suit/mod
@@ -33,9 +33,12 @@
body_parts_covered = CHEST|GROIN
heat_protection = CHEST|GROIN
cold_protection = CHEST|GROIN
item_flags = IMMUTABLE_SLOW
drop_sound = null
/obj/item/clothing/suit/mod/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NO_SPEED_POTION, INNATE_TRAIT)
/obj/item/clothing/gloves/mod
name = "MOD gauntlets"
desc = "A pair of gauntlets for a MODsuit."
@@ -47,11 +50,14 @@
body_parts_covered = HANDS|ARMS
heat_protection = HANDS|ARMS
cold_protection = HANDS|ARMS
item_flags = IMMUTABLE_SLOW
equip_sound = null
pickup_sound = null
drop_sound = null
/obj/item/clothing/gloves/mod/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NO_SPEED_POTION, INNATE_TRAIT)
/obj/item/clothing/shoes/mod
name = "MOD boots"
desc = "A pair of boots for a MODsuit."
@@ -63,6 +69,10 @@
body_parts_covered = FEET|LEGS
heat_protection = FEET|LEGS
cold_protection = FEET|LEGS
item_flags = IGNORE_DIGITIGRADE | IMMUTABLE_SLOW
item_flags = IGNORE_DIGITIGRADE
fastening_type = SHOES_SLIPON
equip_sound = null
/obj/item/clothing/shoes/mod/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NO_SPEED_POTION, INNATE_TRAIT)
+17 -5
View File
@@ -684,9 +684,19 @@
wearer.update_spacesuit_hud_icon(state_to_use || "0")
/obj/item/mod/control/proc/update_speed()
for(var/obj/item/part as anything in get_parts(all = TRUE))
part.slowdown = slowdown_deployed / length(mod_parts)
var/datum/mod_part/part_datum = get_part_datum(part)
var/total_slowdown = 0
var/prevent_slowdown = HAS_TRAIT(src, TRAIT_SPEED_POTIONED)
if (!prevent_slowdown)
total_slowdown += slowdown_deployed
var/list/module_slowdowns = list()
SEND_SIGNAL(src, COMSIG_MOD_UPDATE_SPEED, module_slowdowns, prevent_slowdown)
for (var/module_slow in module_slowdowns)
total_slowdown += module_slow
for(var/datum/mod_part/part_datum as anything in get_part_datums(all = TRUE))
var/obj/item/part = part_datum.part_item
part.slowdown = total_slowdown / length(mod_parts)
if (!part_datum.sealed)
part.slowdown = max(part.slowdown, 0)
wearer?.update_equipment_speed_mods()
@@ -747,15 +757,17 @@
/obj/item/mod/control/proc/on_potion(atom/movable/source, obj/item/slimepotion/speed/speed_potion, mob/living/user)
SIGNAL_HANDLER
if(slowdown_deployed <= 0)
if(HAS_TRAIT(src, TRAIT_SPEED_POTIONED))
to_chat(user, span_warning("[src] has already been coated with red, that's as fast as it'll go!"))
return SPEED_POTION_STOP
if(active)
to_chat(user, span_warning("It's too dangerous to smear [speed_potion] on [src] while it's active!"))
return SPEED_POTION_STOP
to_chat(user, span_notice("You slather the red gunk over [src], making it faster."))
set_mod_color(color_transition_filter(COLOR_RED))
slowdown_deployed = 0
ADD_TRAIT(src, TRAIT_SPEED_POTIONED, SLIME_POTION_TRAIT)
update_speed()
qdel(speed_potion)
return SPEED_POTION_STOP
+5 -3
View File
@@ -119,6 +119,7 @@
part_datum.part_item = mod_part
mod.mod_parts["[mod_part.slot_flags]"] = part_datum
parts += mod_part
for(var/obj/item/part as anything in parts)
part.name = "[name] [part.name]"
part.desc = "[part.desc] [desc]"
@@ -130,6 +131,7 @@
part.max_heat_protection_temperature = max_heat_protection_temperature
part.min_cold_protection_temperature = min_cold_protection_temperature
part.siemens_coefficient = siemens_coefficient
set_skin(mod, skin || default_skin)
/datum/mod_theme/proc/set_skin(obj/item/mod/control/mod, skin)
@@ -1156,7 +1158,7 @@
complexity_max = DEFAULT_MAX_COMPLEXITY + 3
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
siemens_coefficient = 0
slowdown_deployed = 0.5
slowdown_deployed = 0
ui_theme = "syndicate"
resistance_flags = FIRE_PROOF
inbuilt_modules = list(/obj/item/mod/module/armor_booster)
@@ -1263,7 +1265,7 @@
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
complexity_max = DEFAULT_MAX_COMPLEXITY + 3
siemens_coefficient = 0
slowdown_deployed = 0.5
slowdown_deployed = 0
ui_theme = "syndicate"
inbuilt_modules = list(/obj/item/mod/module/armor_booster)
allowed_suit_storage = list(
@@ -1696,7 +1698,7 @@
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
complexity_max = DEFAULT_MAX_COMPLEXITY + 3
siemens_coefficient = 0
slowdown_deployed = 0.5
slowdown_deployed = 0
ui_theme = "ntos_terminal"
inbuilt_modules = list(/obj/item/mod/module/armor_booster)
allowed_suit_storage = list(
+14 -12
View File
@@ -18,17 +18,15 @@
mask_worn_overlay = TRUE
/// Whether or not this module removes pressure protection.
var/remove_pressure_protection = TRUE
/// Speed added to the control unit.
var/speed_added = 0.5
/// Speed that we actually added.
var/actual_speed_added = 0
/// Slowdown added to the control unit while this module is disabled
var/space_slowdown = 0.5
/// Armor values added to the suit parts.
var/datum/armor/armor_mod = /datum/armor/mod_module_armor_boost
/// List of parts of the suit that are spaceproofed, for giving them back the pressure protection.
var/list/spaceproofed = list()
/obj/item/mod/module/armor_booster/no_speedbost
speed_added = 0
space_slowdown = 0
/datum/armor/mod_module_armor_boost
melee = 25
@@ -37,36 +35,37 @@
energy = 15
/obj/item/mod/module/armor_booster/on_part_activation()
RegisterSignal(mod, COMSIG_MOD_UPDATE_SPEED, PROC_REF(on_update_speed))
var/obj/item/clothing/head_cover = mod.get_part_from_slot(ITEM_SLOT_HEAD) || mod.get_part_from_slot(ITEM_SLOT_MASK) || mod.get_part_from_slot(ITEM_SLOT_EYES)
if(istype(head_cover))
head_cover.flash_protect = FLASH_PROTECTION_WELDER
mod.update_speed()
/obj/item/mod/module/armor_booster/on_part_deactivation(deleting = FALSE)
if(deleting)
return
UnregisterSignal(mod, COMSIG_MOD_UPDATE_SPEED)
var/obj/item/clothing/head_cover = mod.get_part_from_slot(ITEM_SLOT_HEAD) || mod.get_part_from_slot(ITEM_SLOT_MASK) || mod.get_part_from_slot(ITEM_SLOT_EYES)
if(istype(head_cover))
head_cover.flash_protect = initial(head_cover.flash_protect)
mod.update_speed()
/obj/item/mod/module/armor_booster/on_activation()
playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
balloon_alert(mod.wearer, "armor boosted, EVA lost")
actual_speed_added = max(0, min(mod.slowdown_deployed, speed_added))
var/datum/mod_part/head_cover = mod.get_part_datum_from_slot(ITEM_SLOT_HEAD) || mod.get_part_datum_from_slot(ITEM_SLOT_MASK) || mod.get_part_datum_from_slot(ITEM_SLOT_EYES)
if(head_cover)
RegisterSignal(mod, COMSIG_MOD_PART_SEALED, PROC_REF(seal_helmet))
seal_helmet(mod, head_cover)
var/list/mod_parts = mod.get_parts(all = TRUE)
for(var/obj/item/part as anything in mod.get_parts(all = TRUE))
part.set_armor(part.get_armor().add_other_armor(armor_mod))
part.slowdown -= speed_added / length(mod_parts)
if(!remove_pressure_protection || !isclothing(part))
continue
var/obj/item/clothing/clothing_part = part
if(clothing_part.clothing_flags & STOPSPRESSUREDAMAGE)
clothing_part.clothing_flags &= ~STOPSPRESSUREDAMAGE
spaceproofed[clothing_part] = TRUE
mod.wearer.update_equipment_speed_mods()
mod.update_speed()
/obj/item/mod/module/armor_booster/on_deactivation(display_message = TRUE, deleting = FALSE)
if(!deleting)
@@ -76,18 +75,21 @@
if(head_cover)
UnregisterSignal(mod, COMSIG_MOD_PART_SEALED)
REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, REF(src))
var/list/mod_parts = mod.get_parts(all = TRUE)
for(var/obj/item/part as anything in mod.get_parts(all = TRUE))
part.set_armor(part.get_armor().subtract_other_armor(armor_mod))
part.slowdown += speed_added / length(mod_parts)
if(!remove_pressure_protection || !isclothing(part))
continue
var/obj/item/clothing/clothing_part = part
if(spaceproofed[clothing_part])
clothing_part.clothing_flags |= STOPSPRESSUREDAMAGE
mod.wearer.update_equipment_speed_mods()
mod.update_speed()
spaceproofed = list()
/obj/item/mod/module/armor_booster/proc/on_update_speed(datum/source, list/module_slowdowns, prevent_slowdown)
SIGNAL_HANDLER
if (!active)
module_slowdowns += space_slowdown
/obj/item/mod/module/armor_booster/generate_worn_overlay(mutable_appearance/standing)
overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]"
@@ -61,15 +61,24 @@
/// A list of traits to add to the wearer when we're active (see: Magboots)
var/list/active_traits = list(TRAIT_NO_SLIP_WATER, TRAIT_NO_SLIP_ICE, TRAIT_NO_SLIP_SLIDE, TRAIT_NEGATES_GRAVITY)
/obj/item/mod/module/magboot/on_install()
RegisterSignal(mod, COMSIG_MOD_UPDATE_SPEED, PROC_REF(on_update_speed))
/obj/item/mod/module/magboot/on_uninstall(deleting)
UnregisterSignal(mod, COMSIG_MOD_UPDATE_SPEED)
/obj/item/mod/module/magboot/on_activation()
mod.wearer.add_traits(active_traits, REF(src))
mod.slowdown += slowdown_active
mod.wearer.update_equipment_speed_mods()
mod.update_speed()
/obj/item/mod/module/magboot/on_deactivation(display_message = TRUE, deleting = FALSE)
mod.wearer.remove_traits(active_traits, REF(src))
mod.slowdown -= slowdown_active
mod.wearer.update_equipment_speed_mods()
mod.update_speed()
/obj/item/mod/module/magboot/proc/on_update_speed(datum/source, list/module_slowdowns, prevent_slowdown)
SIGNAL_HANDLER
if (!prevent_slowdown && active)
module_slowdowns += slowdown_active
/obj/item/mod/module/magboot/advanced
name = "MOD advanced magnetic stability module"
+12 -11
View File
@@ -369,9 +369,7 @@
/// Armor values per tile.
var/datum/armor/armor_mod = /datum/armor/mod_ash_accretion
/// Speed added when you're fully covered in ash.
var/speed_added = 0.5
/// Speed that we actually added.
var/actual_speed_added = 0
var/speed_added = -0.5
/// Turfs that let us accrete ash.
var/static/list/accretion_turfs
/// Turfs that let us keep ash.
@@ -409,24 +407,30 @@
/obj/item/mod/module/ash_accretion/on_part_activation()
mod.wearer.add_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), REF(src))
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
RegisterSignal(mod, COMSIG_MOD_UPDATE_SPEED, PROC_REF(on_update_speed))
/obj/item/mod/module/ash_accretion/on_part_deactivation(deleting = FALSE)
mod.wearer.remove_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), REF(src))
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
UnregisterSignal(mod, COMSIG_MOD_UPDATE_SPEED)
if(!traveled_tiles)
return
var/datum/armor/to_remove = get_armor_by_type(armor_mod)
for(var/obj/item/part as anything in mod.get_parts(all = TRUE))
part.set_armor(part.get_armor().subtract_other_armor(to_remove.generate_new_with_multipliers(list(ARMOR_ALL = traveled_tiles))))
if(traveled_tiles == max_traveled_tiles)
mod.slowdown += speed_added
mod.wearer.update_equipment_speed_mods()
mod.update_speed()
traveled_tiles = 0
/obj/item/mod/module/ash_accretion/generate_worn_overlay(mutable_appearance/standing)
overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
return ..()
/obj/item/mod/module/ash_accretion/proc/on_update_speed(datum/source, list/module_slowdowns, prevent_slowdown)
SIGNAL_HANDLER
if (traveled_tiles == max_traveled_tiles)
module_slowdowns += speed_added
/obj/item/mod/module/ash_accretion/proc/on_move(atom/source, atom/oldloc, dir, forced)
if(!isturf(mod.wearer.loc)) //dont lose ash from going in a locker
return
@@ -443,18 +447,15 @@
mod.wearer.color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,3) //make them super light
animate(mod.wearer, 1 SECONDS, color = null, flags = ANIMATION_PARALLEL)
playsound(src, 'sound/effects/sparks/sparks1.ogg', 100, TRUE)
actual_speed_added = max(0, min(mod.slowdown_deployed, speed_added))
mod.slowdown -= actual_speed_added
mod.wearer.update_equipment_speed_mods()
mod.update_speed()
else if(is_type_in_typecache(mod.wearer.loc, keep_turfs))
return
else
if(traveled_tiles <= 0)
return
if(traveled_tiles == max_traveled_tiles)
mod.slowdown += actual_speed_added
mod.wearer.update_equipment_speed_mods()
traveled_tiles--
if(traveled_tiles == max_traveled_tiles - 1) // Just lost our speed buff
mod.update_speed()
for(var/obj/item/part as anything in mod.get_parts(all = TRUE))
part.set_armor(part.get_armor().subtract_other_armor(armor_mod))
if(traveled_tiles <= 0)