/// MODsuits, trade-off between armor and utility /obj/item/mod name = "Base MOD" desc = "You should not see this, yell at a coder!" icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'// figure out how to work with 2 of these icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi' /obj/item/mod/control name = "MOD control unit" desc = "The control unit of a Modular Outerwear Device, a powered suit that protects against various environments." icon_state = "standard-control" icon_state = "mod_control" item_state = "mod_control" base_icon_state = "control" w_class = WEIGHT_CLASS_BULKY slot_flags = SLOT_FLAG_BACK strip_delay = 10 SECONDS armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0) actions_types = list( /datum/action/item_action/mod/deploy, /datum/action/item_action/mod/activate, /datum/action/item_action/mod/panel, /datum/action/item_action/mod/module, ) resistance_flags = NONE max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT siemens_coefficient = 0.5 var/datum/wires/mod/wires /// The MOD's theme, decides on some stuff like armor and statistics. var/datum/mod_theme/theme = /datum/mod_theme/standard /// Looks of the MOD. var/skin = "standard" /// Theme of the MOD TGUI var/ui_theme = "ntos" /// If the suit is deployed and turned on. var/active = FALSE /// If the suit wire/module hatch is open. var/open = FALSE /// If the suit is ID locked. var/locked = FALSE /// If the suit is malfunctioning. var/malfunctioning = FALSE /// If the suit is currently activating/deactivating. var/activating = FALSE /// How long the MOD is electrified for. var/seconds_electrified = 0 /// If the suit interface is broken. var/interface_break = FALSE /// How much module complexity can this MOD carry. var/complexity_max = DEFAULT_MAX_COMPLEXITY /// How much module complexity this MOD is carrying. var/complexity = 0 /// Power usage of the MOD. var/charge_drain = DEFAULT_CHARGE_DRAIN /// Slowdown of the MOD when not active. var/slowdown_inactive = 1.25 /// Slowdown of the MOD when active. var/slowdown_active = 0.75 /// How long this MOD takes each part to seal. 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. 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. var/obj/item/mod/module/selected_module /// Person wearing the MODsuit. var/mob/living/carbon/human/wearer /// Internal storage in a modsuit var/obj/item/storage/backpack/modstorage/bag /// Is it EMP proof? var/emp_proof = FALSE /// List of overlays the mod has. Needs to be cut onremoval / module deactivation var/list/mod_overlays = list() /// Is the jetpack on so we should make ion effects? var/jetpack_active = FALSE /obj/item/mod/control/serialize() var/list/data = ..() var/list/modules_list = list() for(var/obj/item/mod/module/mod as anything in modules) modules_list.Add(list(mod.serialize())) data["modules"] = modules_list return data /obj/item/mod/control/deserialize(list/data) if(data["modules"]) for(var/old_mods in modules) uninstall(old_mods, deleting = TRUE) for(var/obj/item/mod/module/module as anything in data["modules"]) module = list_to_object(module, src) install(module) ..() /obj/item/mod/control/Initialize(mapload, datum/mod_theme/new_theme, new_skin, obj/item/mod/core/new_core) . = ..() 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 complexity_max = theme.complexity_max ui_theme = theme.ui_theme charge_drain = theme.charge_drain 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.armor = part.armor.attachArmor(theme.armor_type_2.armor) part.resistance_flags = theme.resistance_flags part.flags |= 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_OBJ_DECONSTRUCT, PROC_REF(on_part_destruction)) //look into RegisterSignal(part, COMSIG_PARENT_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)) for(var/obj/item/mod/module/module as anything in theme.inbuilt_modules) module = new module(src) install(module) /obj/item/mod/control/Destroy() if(active) 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 if(!QDELETED(helmet)) mod_parts -= helmet QDEL_NULL(helmet) if(!QDELETED(chestplate)) mod_parts -= chestplate QDEL_NULL(chestplate) if(!QDELETED(gauntlets)) mod_parts -= gauntlets QDEL_NULL(gauntlets) if(!QDELETED(boots)) mod_parts -= boots QDEL_NULL(boots) if(core) QDEL_NULL(core) QDEL_NULL(wires) wearer = null selected_module = null bag = null modules = null return ..() /obj/item/mod/control/examine(mob/user) . = ..() if(active) . += "Charge: [core ? "[get_charge_percent()]%" : "No core"]." . += "Selected module: [selected_module || "None"]." if(!open && !active) if(!wearer) . += "You could equip it to turn it on." . += "You could open the cover with a screwdriver." else if(open) . += "You could close the cover with a screwdriver." . += "You could use modules on it to install them." . += "You could remove modules with a crowbar." . += "You could update the access lock with an ID." . += "You could access the wire panel with a wire tool." if(core) . += "You could remove [core] with a wrench." else . += "You could use a MOD core on it to install one." /obj/item/mod/control/examine_more(mob/user) . = ..() . += "[extended_desc]" /obj/item/mod/control/process() if(seconds_electrified > 0) seconds_electrified-- if(get_charge() <= 10 && active && !activating) //Sometimes we get power being funky, this should fix it. power_off() return PROCESS_KILL var/malfunctioning_charge_drain = 0 if(malfunctioning) malfunctioning_charge_drain = rand(1, 20) subtract_charge((charge_drain + malfunctioning_charge_drain)) update_charge_alert() for(var/obj/item/mod/module/module as anything in modules) if(malfunctioning && module.active && prob(5)) module.on_deactivation(display_message = TRUE) module.on_process() /obj/item/mod/control/equipped(mob/user, slot) ..() if(slot == SLOT_HUD_BACK) set_wearer(user) else if(wearer) unset_wearer() /obj/item/mod/control/item_action_slot_check(slot) if(slot == SLOT_HUD_BACK) return TRUE /obj/item/mod/control/on_mob_move(direction, mob/user) if(!jetpack_active) return var/turf/T = get_step(src, GetOppositeDir(direction)) if(!has_gravity(T)) new /obj/effect/particle_effect/ion_trails(T, direction) /obj/item/mod/control/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) . = ..() if(!wearer || old_loc != wearer || loc == wearer) return clean_up() /obj/item/mod/control/MouseDrop(atom/over_object) if(iscarbon(usr)) var/mob/M = usr if(get_dist(usr, src) > 1) //1 as we want to access it if beside the user return if(!over_object) return if(ismecha(M.loc)) return if(!M.restrained() && !M.stat) playsound(loc, "rustle", 50, TRUE, -5) if(istype(over_object, /obj/screen/inventory/hand)) for(var/obj/item/part as anything in mod_parts) if(part.loc != src) to_chat(wearer, "Retract parts first!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE) return if(!M.unEquip(src, silent = TRUE)) return M.put_in_active_hand(src) else if(bag) bag.forceMove(usr) bag.show_to(usr) add_fingerprint(M) /obj/item/mod/control/wrench_act(mob/living/user, obj/item/wrench) if(..()) return TRUE if(seconds_electrified && get_charge() && shock(user)) return TRUE if(open) if(!core) to_chat(user, "There is no core!") return TRUE wrench.play_tool_sound(src, 100) if(!wrench.use_tool(src, user, 3 SECONDS) || !open) return TRUE if(!core) return TRUE wrench.play_tool_sound(src, 100) core.forceMove(drop_location()) update_charge_alert() return TRUE return ..() /obj/item/mod/control/screwdriver_act(mob/living/user, obj/item/screwdriver) if(..()) return TRUE if(active || activating || locate(/mob/living/silicon/ai) in src) to_chat(user, "Deactivate the suit first!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE to_chat(user, "[open ? "Closing" : "Opening"] cover...") screwdriver.play_tool_sound(src, 100) if(screwdriver.use_tool(src, user, 1 SECONDS)) if(active || activating) to_chat(user, "Deactivate the suit first!") screwdriver.play_tool_sound(src, 100) to_chat(user, "Cover [open ? "closed" : "opened"]") open = !open return TRUE /obj/item/mod/control/crowbar_act(mob/living/user, obj/item/crowbar) . = ..() if(!open) to_chat(user, "Open the cover first!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE if(!allowed(user)) to_chat(user, "Insufficient access!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return if(seconds_electrified && get_charge() && shock(user)) return FALSE if(SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVAL, user) & MOD_CANCEL_REMOVAL) playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE if(length(modules)) var/list/removable_modules = list() for(var/obj/item/mod/module/module as anything in modules) if(!module.removable) continue removable_modules += module var/obj/item/mod/module/module_to_remove = tgui_input_list(user, "Which module do you want to pry out?", "Module Removal", removable_modules) if(!module_to_remove?.mod) return FALSE uninstall(module_to_remove) module_to_remove.forceMove(drop_location()) crowbar.play_tool_sound(src, 100) SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVED, user) return TRUE to_chat(user, "No modules!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE /obj/item/mod/control/attackby(obj/item/attacking_item, mob/living/user, params) if(istype(attacking_item, /obj/item/mod/module)) if(!open) to_chat(user, "Open the cover first!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE install(attacking_item, user) SEND_SIGNAL(src, COMSIG_MOD_MODULE_ADDED, user) return TRUE else if(istype(attacking_item, /obj/item/mod/core)) if(!open) to_chat(user, "Open the cover first!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE if(core) to_chat(user, "Core already installed!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE var/obj/item/mod/core/attacking_core = attacking_item playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) user.drop_item() attacking_core.install(src) update_charge_alert() return TRUE else if(istype(attacking_item, /obj/item/multitool) && open) if(seconds_electrified && get_charge() && shock(user)) return TRUE wires.Interact(user) return TRUE else if(open && attacking_item.GetID()) update_access(user, attacking_item.GetID()) return TRUE else if(istype(attacking_item, /obj/item/stock_parts/cell)) if(!core) to_chat(user, "There is no core installed!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE core.on_attackby(attacking_item, user, params) else if(istype(attacking_item, /obj/item/stack/ore/plasma) || istype(attacking_item, /obj/item/stack/sheet/mineral/plasma)) if(!core) to_chat(user, "There is no core installed!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE core.on_attackby(attacking_item, user, params) else if(istype(attacking_item, /obj/item/mod/skin_applier)) return ..() else if(bag && istype(attacking_item)) bag.forceMove(user) bag.attackby(attacking_item, user, params) return ..() /obj/item/mod/control/attack_hand(mob/living/carbon/user) if(!iscarbon(user)) return if(loc == user && user.back && user.back == src) if(bag) bag.forceMove(user) bag.show_to(user) else ..() /obj/item/mod/control/AltClick(mob/user) . = ..() if(ishuman(user) && Adjacent(user) && !user.incapacitated(FALSE, TRUE) && bag) bag.forceMove(user) bag.show_to(user) playsound(loc, "rustle", 50, TRUE, -5) add_fingerprint(user) /obj/item/mod/control/proc/can_be_inserted(I, stop_messages) if(bag) return bag.can_be_inserted(I, stop_messages) return FALSE /obj/item/mod/control/proc/handle_item_insertion(I, prevent_warning) if(bag) bag.handle_item_insertion(I, prevent_warning) /obj/item/mod/control/get_cell() if(!open) return var/obj/item/stock_parts/cell/cell = get_charge_source() if(!istype(cell)) return return cell /obj/item/mod/control/GetAccess() if(locate(/mob/living/silicon/ai) in src) return req_access.Copy() return ..() /obj/item/mod/control/emag_act(mob/user) locked = !locked to_chat(user, "Suit access [locked ? "locked" : "unlocked"]") return TRUE /obj/item/mod/control/emp_act(severity) . = ..() if(!active || !wearer) return to_chat(wearer, "[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!") if(emp_proof) return selected_module?.on_deactivation(display_message = TRUE) wearer.apply_damage(10 / severity, BURN, spread_damage = TRUE) //Test this with ion shotguns. to_chat(wearer, "You feel [src] heat up from the EMP, burning you slightly!") if(wearer.stat < UNCONSCIOUS && prob(10)) wearer.emote("scream") core.emp_act(severity) if(prob(50 / severity)) wires.emp_pulse() //3 wires get pulsed. Dangerous to a mod user. for(var/obj/item/mod/module/holster/H in modules) H.holstered?.emp_act(severity) if(bag) bag.emp_act(severity) /obj/item/mod/control/dropped(mob/user) . = ..() if(!wearer) return clean_up() update_mod_overlays(TRUE) if(active && !toggle_activate(force_deactivate = TRUE)) return for(var/obj/item/part as anything in mod_parts) if(part.loc == src) continue retract(null, part) return ..() /obj/item/mod/control/update_icon_state() icon_state = "[skin]-[base_icon_state][active ? "-sealed" : ""]" return ..() /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)) stack_trace("set_wearer() was called with a new wearer without unset_wearer() being called") wearer = user SEND_SIGNAL(src, COMSIG_MOD_WEARER_SET, wearer) RegisterSignal(wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit)) update_charge_alert() for(var/obj/item/clothing/C in mod_parts) C.refit_for_species(wearer.dna.species.sprite_sheet_name) update_mod_overlays() for(var/obj/item/mod/module/module as anything in modules) module.on_equip() /obj/item/mod/control/proc/unset_wearer() for(var/obj/item/mod/module/module as anything in modules) module.on_unequip() UnregisterSignal(wearer, list(COMSIG_ATOM_EXITED, COMSIG_SPECIES_GAIN)) wearer.clear_alert("mod_charge") SEND_SIGNAL(src, COMSIG_MOD_WEARER_UNSET, wearer) wearer = null /obj/item/mod/control/proc/clean_up() 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) retract(null, part) if(active) finish_activation(on = FALSE) var/mob/old_wearer = wearer unset_wearer() old_wearer.drop_item() /obj/item/mod/control/proc/quick_module(mob/user) if(!length(modules)) return var/list/display_names = list() var/list/items = list() for(var/obj/item/mod/module/module as anything in modules) if(module.module_type == MODULE_PASSIVE) continue display_names[module.name] = module var/image/module_image = image(icon = module.icon, icon_state = module.icon_state) if(module == selected_module) module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_selected") else if(module.active) module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_active") if(!COOLDOWN_FINISHED(module, cooldown_timer)) module_image.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_cooldown")) items += list(module.name = module_image) if(!length(items)) return var/radial_anchor = src var/pick = show_radial_menu(user, radial_anchor, items, custom_check = FALSE, require_near = TRUE) if(!pick) return var/module_reference = display_names[pick] var/obj/item/mod/module/picked_module = module_reference picked_module.on_select() /obj/item/mod/control/proc/shock(mob/living/user) if(!istype(user) || get_charge() < 1) return FALSE do_sparks(5, TRUE, src) var/check_range = TRUE return electrocute_mob(user, get_charge_source(), src, 1, check_range) /obj/item/mod/control/proc/install(obj/item/mod/module/new_module, mob/user) for(var/obj/item/mod/module/old_module as anything in modules) if(is_type_in_list(new_module, old_module.incompatible_modules) || is_type_in_list(old_module, new_module.incompatible_modules)) if(user) to_chat(user, "[new_module] incompatible with [old_module]!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return var/complexity_with_module = complexity complexity_with_module += new_module.complexity if(complexity_with_module > complexity_max) if(user) to_chat(user, "[new_module] would make [src] too complex!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return if(user) if(!user.drop_item()) to_chat(user, "[new_module] is stuck to your hand!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return new_module.forceMove(src) modules += new_module complexity += new_module.complexity new_module.mod = src new_module.on_install() if(wearer) new_module.on_equip() if(active) new_module.on_suit_activation() if(user) to_chat(user, "[new_module] added!") playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) /obj/item/mod/control/proc/uninstall(obj/item/mod/module/old_module, deleting = FALSE) modules -= old_module complexity -= old_module.complexity if(wearer) old_module.on_unequip() if(active) old_module.on_suit_deactivation(deleting = deleting) if(old_module.active) old_module.on_deactivation(display_message = !deleting, deleting = deleting) old_module.on_uninstall(deleting = deleting) QDEL_LIST_ASSOC_VAL(old_module.pinned_to) old_module.mod = null /obj/item/mod/control/proc/update_access(mob/user, obj/item/card/id/card) if(!allowed(user)) to_chat(user, "Insufficient access!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return req_access = card.access.Copy() to_chat(user, "Access updated!") /obj/item/mod/control/proc/update_mod_overlays(full_removal = FALSE) if(!wearer) return for(var/I in mod_overlays) wearer.cut_overlay(I) mod_overlays -= I if(full_removal) return for(var/obj/item/mod/module/M in modules) M.add_module_overlay(wearer) /obj/item/mod/control/proc/get_charge_source() return core?.charge_source() /obj/item/mod/control/proc/get_charge() return core?.charge_amount() || 0 /obj/item/mod/control/proc/get_max_charge() return core?.max_charge_amount() || 1 //avoid dividing by 0 /obj/item/mod/control/proc/get_charge_percent() return ((get_charge() / get_max_charge()) * 100) /obj/item/mod/control/proc/add_charge(amount) return core?.add_charge(amount) || FALSE /obj/item/mod/control/proc/subtract_charge(amount) return core?.subtract_charge(amount) || FALSE /obj/item/mod/control/proc/check_charge(amount) return core?.check_charge(amount) || FALSE /obj/item/mod/control/proc/update_charge_alert() if(!wearer) return if(!core) wearer.throw_alert("mod_charge", /obj/screen/alert/nocell) return core.update_charge_alert() /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) /obj/item/mod/control/proc/power_off() to_chat(wearer, "Power cells depleted!") 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) 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] 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.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.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 if(!category[CAN_OVERSLOT]) if(overslotting_parts[part]) var/obj/item/overslot = overslotting_parts[part] overslot.forceMove(drop_location()) overslotting_parts -= part continue overslotting_parts |= part wearer?.regenerate_icons() /obj/item/mod/control/proc/on_exit(datum/source, atom/movable/part, direction) SIGNAL_HANDLER if(part.loc == src) return if(part == core) core.uninstall() update_charge_alert() return if(part.loc == wearer) return if(part in modules) uninstall(part) return if(part in mod_parts) if(!wearer) part.forceMove(src) return retract(wearer, part, TRUE) if(active) INVOKE_ASYNC(src, PROC_REF(toggle_activate), wearer, TRUE) /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)) return obj_destruction(damage_flag) /obj/item/mod/control/proc/on_overslot_exit(datum/source, atom/movable/overslot, direction) SIGNAL_HANDLER if(overslot != overslotting_parts[source]) return overslotting_parts[source] = null /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 SIGNAL_HANDLER if(QDELETED(src)) return qdel(src) /obj/item/mod/control/water_act(volume, temperature, source, method) if(HAS_TRAIT(src, TRAIT_OIL_SLICKED)) //Overide base to work right slowdown_active = theme.slowdown_active slowdown_inactive = theme.slowdown_inactive update_speed() remove_atom_colour(FIXED_COLOUR_PRIORITY) REMOVE_TRAIT(src, TRAIT_OIL_SLICKED, "potion") if(ishuman(loc)) var/mob/living/carbon/human/H = loc H.regenerate_icons() /obj/item/mod/control/extinguish_light(force) . = ..() for(var/obj/item/mod/module/module as anything in modules) module.extinguish_light(force)