[MIRROR] unhardcodes modsuit parts (#27777)

* unhardcodes modsuit parts

* Fixes

* Fix custom skins

* Should be now

---------

Co-authored-by: Fikou <23585223+Fikou@users.noreply.github.com>
Co-authored-by: SpaceLoveSs13 <68121607+SpaceLoveSs13@users.noreply.github.com>
This commit is contained in:
SkyratBot
2024-06-07 14:31:33 +02:00
committed by GitHub
parent 8d9e764e04
commit 3dbe0ce79c
44 changed files with 1319 additions and 956 deletions

View File

@@ -65,20 +65,10 @@
var/activation_step_time = MOD_ACTIVATION_STEP_TIME
/// Extended description of the theme.
var/extended_desc
/// MOD helmet.
var/obj/item/clothing/head/mod/helmet
/// MOD chestplate.
var/obj/item/clothing/suit/mod/chestplate
/// MOD gauntlets.
var/obj/item/clothing/gloves/mod/gauntlets
/// MOD boots.
var/obj/item/clothing/shoes/mod/boots
/// MOD core.
var/obj/item/mod/core/core
/// Associated list of parts (helmet, chestplate, gauntlets, boots) to their unsealed worn layer.
/// List of MODsuit part datums.
var/list/mod_parts = list()
/// Associated list of parts that can overslot to their overslot (overslot means the part can cover another layer of clothing).
var/list/overslotting_parts = list()
/// Modules the MOD currently possesses.
var/list/modules = list()
/// Currently used module.
@@ -103,43 +93,14 @@
if(new_theme)
theme = new_theme
theme = GLOB.mod_themes[theme]
slot_flags = theme.slot_flags
extended_desc = theme.extended_desc
slowdown_inactive = theme.slowdown_inactive
slowdown_active = theme.slowdown_active
activation_step_time = theme.activation_step_time
complexity_max = theme.complexity_max
ui_theme = theme.ui_theme
charge_drain = theme.charge_drain
theme.set_up_parts(src, new_skin)
for(var/obj/item/part as anything in get_parts())
RegisterSignal(part, COMSIG_ATOM_DESTRUCTION, PROC_REF(on_part_destruction))
RegisterSignal(part, COMSIG_QDELETING, PROC_REF(on_part_deletion))
set_wires(new /datum/wires/mod(src))
if(length(req_access))
locked = TRUE
new_core?.install(src)
helmet = new /obj/item/clothing/head/mod(src)
mod_parts += helmet
chestplate = new /obj/item/clothing/suit/mod(src)
chestplate.allowed += theme.allowed_suit_storage
mod_parts += chestplate
gauntlets = new /obj/item/clothing/gloves/mod(src)
mod_parts += gauntlets
boots = new /obj/item/clothing/shoes/mod(src)
mod_parts += boots
var/list/all_parts = mod_parts + src
for(var/obj/item/part as anything in all_parts)
part.name = "[theme.name] [part.name]"
part.desc = "[part.desc] [theme.desc]"
part.set_armor(theme.armor_type)
part.resistance_flags = theme.resistance_flags
part.flags_1 |= theme.atom_flags //flags like initialization or admin spawning are here, so we cant set, have to add
part.heat_protection = NONE
part.cold_protection = NONE
part.max_heat_protection_temperature = theme.max_heat_protection_temperature
part.min_cold_protection_temperature = theme.min_cold_protection_temperature
part.siemens_coefficient = theme.siemens_coefficient
for(var/obj/item/part as anything in mod_parts)
RegisterSignal(part, COMSIG_ATOM_DESTRUCTION, PROC_REF(on_part_destruction))
RegisterSignal(part, COMSIG_QDELETING, PROC_REF(on_part_deletion))
set_mod_skin(new_skin || theme.default_skin)
update_speed()
RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
RegisterSignal(src, COMSIG_SPEED_POTION_APPLIED, PROC_REF(on_potion))
@@ -152,46 +113,23 @@
STOP_PROCESSING(SSobj, src)
for(var/obj/item/mod/module/module as anything in modules)
uninstall(module, deleting = TRUE)
for(var/obj/item/part as anything in mod_parts)
overslotting_parts -= part
var/atom/deleting_atom
if(!QDELETED(helmet))
deleting_atom = helmet
helmet = null
mod_parts -= deleting_atom
qdel(deleting_atom)
if(!QDELETED(chestplate))
deleting_atom = chestplate
chestplate = null
mod_parts -= deleting_atom
qdel(deleting_atom)
if(!QDELETED(gauntlets))
deleting_atom = gauntlets
gauntlets = null
mod_parts -= deleting_atom
qdel(deleting_atom)
if(!QDELETED(boots))
deleting_atom = boots
boots = null
mod_parts -= deleting_atom
qdel(deleting_atom)
if(core)
QDEL_NULL(core)
QDEL_NULL(wires)
QDEL_NULL(mod_link)
for(var/datum/mod_part/part_datum as anything in get_part_datums(all = TRUE))
part_datum.part_item = null
part_datum.overslotting = null
return ..()
/obj/item/mod/control/atom_destruction(damage_flag)
if(wearer)
wearer.visible_message(span_danger("[src] fall[p_s()] apart, completely destroyed!"), vision_distance = COMBAT_MESSAGE_RANGE)
clean_up()
for(var/obj/item/mod/module/module as anything in modules)
for(var/obj/item/item in module)
item.forceMove(drop_location())
uninstall(module)
for(var/obj/item/part as anything in mod_parts)
if(!overslotting_parts[part])
continue
var/obj/item/overslot = overslotting_parts[part]
overslot.forceMove(drop_location())
overslotting_parts[part] = null
if(ai_assistant)
if(ispAI(ai_assistant))
INVOKE_ASYNC(src, PROC_REF(remove_pai), /* user = */ null, /* forced = */ TRUE) // async to appease spaceman DMM because the branch we don't run has a do_after
@@ -248,11 +186,10 @@
subtract_charge((charge_drain + malfunctioning_charge_drain) * seconds_per_tick)
for(var/obj/item/mod/module/module as anything in modules)
if(malfunctioning && module.active && SPT_PROB(5, seconds_per_tick))
module.on_deactivation(display_message = TRUE)
module.deactivate(display_message = TRUE)
module.on_process(seconds_per_tick)
/obj/item/mod/control/equipped(mob/user, slot)
..()
/obj/item/mod/control/visual_equipped(mob/user, slot, initial = FALSE) //needs to be visual because we wanna show it in select equipment
if(slot & slot_flags)
set_wearer(user)
else if(wearer)
@@ -284,7 +221,7 @@
/obj/item/mod/control/allow_attack_hand_drop(mob/user)
if(user != wearer)
return ..()
for(var/obj/item/part as anything in mod_parts)
for(var/obj/item/part as anything in get_parts())
if(part.loc != src)
balloon_alert(user, "retract parts first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
@@ -293,7 +230,7 @@
/obj/item/mod/control/MouseDrop(atom/over_object)
if(usr != wearer || !istype(over_object, /atom/movable/screen/inventory/hand))
return ..()
for(var/obj/item/part as anything in mod_parts)
for(var/obj/item/part as anything in get_parts())
if(part.loc != src)
balloon_alert(wearer, "retract parts first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
@@ -446,16 +383,12 @@
to_chat(wearer, span_notice("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!"))
if(. & EMP_PROTECT_CONTENTS)
return
selected_module?.on_deactivation(display_message = TRUE)
selected_module?.deactivate(display_message = TRUE)
wearer.apply_damage(5 / severity, BURN, spread_damage=TRUE)
to_chat(wearer, span_danger("You feel [src] heat up from the EMP, burning you slightly."))
if(wearer.stat < UNCONSCIOUS && prob(10))
wearer.emote("scream")
/obj/item/mod/control/visual_equipped(mob/user, slot, initial = FALSE)
if(slot & slot_flags)
set_wearer(user)
/obj/item/mod/control/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot)
. = ..()
quick_activation()
@@ -463,7 +396,7 @@
/obj/item/mod/control/doStrip(mob/stripper, mob/owner)
if(active && !toggle_activate(stripper, force_deactivate = TRUE))
return
for(var/obj/item/part as anything in mod_parts)
for(var/obj/item/part as anything in get_parts())
if(part.loc == src)
continue
retract(null, part)
@@ -473,14 +406,44 @@
icon_state = "[skin]-[base_icon_state][active ? "-sealed" : ""]"
return ..()
/obj/item/mod/control/proc/get_parts(all = FALSE)
. = list()
for(var/key in mod_parts)
var/datum/mod_part/part = mod_parts[key]
if(!all && part.part_item == src)
continue
. += part.part_item
/obj/item/mod/control/proc/get_part_datums(all = FALSE)
. = list()
for(var/key in mod_parts)
var/datum/mod_part/part = mod_parts[key]
if(!all && part.part_item == src)
continue
. += part
/obj/item/mod/control/proc/get_part_datum(obj/item/part)
RETURN_TYPE(/datum/mod_part)
var/datum/mod_part/potential_part = mod_parts["[part.slot_flags]"]
if(potential_part?.part_item == part)
return potential_part
for(var/datum/mod_part/mod_part in get_part_datums())
if(mod_part.part_item == part)
return mod_part
CRASH("get_part_datum called with incorrect item [part] passed.")
/obj/item/mod/control/proc/get_part_from_slot(slot)
slot = "[slot]"
for(var/part_slot in mod_parts)
if(slot != part_slot)
continue
var/datum/mod_part/part = mod_parts[part_slot]
return part.part_item
/obj/item/mod/control/proc/set_wearer(mob/living/carbon/human/user)
if (wearer == user)
// This should also not happen.
// This path is hit when equipping an outfit with visualsOnly, but only sometimes, and this eventually gets called twice.
// I'm not sure this proc should ever be being called by visualsOnly, but it is,
// and this was an emergency patch.
return
else if (!isnull(wearer))
if(wearer == user)
CRASH("set_wearer() was called with the new wearer being the current wearer: [wearer]")
else if(!isnull(wearer))
stack_trace("set_wearer() was called with a new wearer without unset_wearer() being called")
wearer = user
@@ -500,17 +463,20 @@
wearer = null
/obj/item/mod/control/proc/clean_up()
if(QDELING(src))
unset_wearer()
return
if(active || activating)
for(var/obj/item/mod/module/module as anything in modules)
if(!module.active)
continue
module.on_deactivation(display_message = FALSE)
for(var/obj/item/part as anything in mod_parts)
seal_part(part, seal = FALSE)
for(var/obj/item/part as anything in mod_parts)
module.deactivate(display_message = FALSE)
for(var/obj/item/part as anything in get_parts())
seal_part(part, is_sealed = FALSE)
for(var/obj/item/part as anything in get_parts())
retract(null, part)
if(active)
finish_activation(on = FALSE)
finish_activation(is_on = FALSE)
mod_link?.end_call()
var/mob/old_wearer = wearer
unset_wearer()
@@ -519,8 +485,7 @@
/obj/item/mod/control/proc/on_species_gain(datum/source, datum/species/new_species, datum/species/old_species)
SIGNAL_HANDLER
var/list/all_parts = mod_parts + src
for(var/obj/item/part in all_parts)
for(var/obj/item/part in get_parts(all = TRUE))
if(!(new_species.no_equip_flags & part.slot_flags) || is_type_in_list(new_species, part.species_exception))
continue
forceMove(drop_location())
@@ -578,6 +543,11 @@
balloon_alert(user, "[new_module] would make [src] too complex!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
if(!new_module.has_required_parts(mod_parts))
if(user)
balloon_alert(user, "[new_module] incompatible with [src]'s parts!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
new_module.forceMove(src)
modules += new_module
complexity += new_module.complexity
@@ -600,7 +570,7 @@
if(active)
old_module.on_suit_deactivation(deleting = deleting)
if(old_module.active)
old_module.on_deactivation(display_message = !deleting, deleting = deleting)
old_module.deactivate(display_message = !deleting, deleting = deleting)
old_module.UnregisterSignal(src, COMSIG_ITEM_GET_WORN_OVERLAYS)
old_module.on_uninstall(deleting = deleting)
QDEL_LIST_ASSOC_VAL(old_module.pinned_to)
@@ -656,9 +626,8 @@
wearer.update_spacesuit_hud_icon(state_to_use || "0")
/obj/item/mod/control/proc/update_speed()
var/list/all_parts = mod_parts + src
for(var/obj/item/part as anything in all_parts)
part.slowdown = (active ? slowdown_active : slowdown_inactive) / length(all_parts)
for(var/obj/item/part as anything in get_parts(all = TRUE))
part.slowdown = (active ? slowdown_active : slowdown_inactive) / length(mod_parts)
wearer?.update_equipment_speed_mods()
/obj/item/mod/control/proc/power_off()
@@ -666,54 +635,10 @@
toggle_activate(wearer, force_deactivate = TRUE)
/obj/item/mod/control/proc/set_mod_color(new_color)
var/list/all_parts = mod_parts + src
for(var/obj/item/part as anything in all_parts)
for(var/obj/item/part as anything in get_parts(all = TRUE))
part.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
part.add_atom_colour(new_color, FIXED_COLOUR_PRIORITY)
wearer?.regenerate_icons()
/obj/item/mod/control/proc/set_mod_skin(new_skin)
if(active)
CRASH("[src] tried to set skin while active!")
skin = new_skin
var/list/used_skin = theme.skins[new_skin]
if(used_skin[CONTROL_LAYER])
alternate_worn_layer = used_skin[CONTROL_LAYER]
var/list/skin_updating = mod_parts + src
for(var/obj/item/part as anything in skin_updating)
part.icon = used_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi'
part.worn_icon = used_skin[MOD_WORN_ICON_OVERRIDE] || 'icons/mob/clothing/modsuit/mod_clothing.dmi'
part.icon_state = "[skin]-[part.base_icon_state]"
for(var/obj/item/clothing/part as anything in mod_parts)
var/used_category
if(part == helmet)
used_category = HELMET_FLAGS
if(part == chestplate)
used_category = CHESTPLATE_FLAGS
if(part == gauntlets)
used_category = GAUNTLETS_FLAGS
if(part == boots)
used_category = BOOTS_FLAGS
var/list/category = used_skin[used_category]
part.clothing_flags = category[UNSEALED_CLOTHING] || NONE
part.visor_flags = category[SEALED_CLOTHING] || NONE
part.flags_inv = category[UNSEALED_INVISIBILITY] || NONE
part.visor_flags_inv = category[SEALED_INVISIBILITY] || NONE
part.flags_cover = category[UNSEALED_COVER] || NONE
part.visor_flags_cover = category[SEALED_COVER] || NONE
part.alternate_worn_layer = category[UNSEALED_LAYER]
mod_parts[part] = part.alternate_worn_layer
/* SKYRAT EDIT START - All MODsuit parts can be worn as overslots.
if(!category[CAN_OVERSLOT])
if(overslotting_parts[part])
var/obj/item/overslot = overslotting_parts[part]
overslot.forceMove(drop_location())
overslotting_parts -= part
continue
*/ // SKYRAT EDIT END
overslotting_parts |= part
wearer?.regenerate_icons()
/obj/item/mod/control/proc/on_exit(datum/source, atom/movable/part, direction)
SIGNAL_HANDLER
@@ -727,7 +652,9 @@
if(part in modules)
uninstall(part)
return
if(part in mod_parts)
if(part in get_parts())
if(isnull(part.loc))
return
if(!wearer)
part.forceMove(src)
return
@@ -738,27 +665,25 @@
/obj/item/mod/control/proc/on_part_destruction(obj/item/part, damage_flag)
SIGNAL_HANDLER
if(overslotting_parts[part])
var/obj/item/overslot = overslotting_parts[part]
overslot.forceMove(drop_location())
overslotting_parts[part] = null
if(QDELETED(src))
if(QDELING(src))
return
atom_destruction(damage_flag)
/obj/item/mod/control/proc/on_part_deletion(obj/item/part) //the part doesnt count as being qdeleted, so our destroying does an infinite loop, fix later
/obj/item/mod/control/proc/on_part_deletion(obj/item/part)
SIGNAL_HANDLER
if(QDELETED(src))
if(QDELING(src))
return
part.moveToNullspace()
qdel(src)
/obj/item/mod/control/proc/on_overslot_exit(datum/source, atom/movable/overslot, direction)
/obj/item/mod/control/proc/on_overslot_exit(obj/item/part, atom/movable/overslot, direction)
SIGNAL_HANDLER
if(overslot != overslotting_parts[source])
var/datum/mod_part/part_datum = get_part_datum(part)
if(overslot != part_datum.overslotting)
return
overslotting_parts[source] = null
part_datum.overslotting = null
/obj/item/mod/control/proc/on_potion(atom/movable/source, obj/item/slimepotion/speed/speed_potion, mob/living/user)
SIGNAL_HANDLER