diff --git a/RUN_SERVER.cmd b/RUN_SERVER.cmd new file mode 100644 index 0000000000000..70e60a5276da3 --- /dev/null +++ b/RUN_SERVER.cmd @@ -0,0 +1,2 @@ +@echo off +call "%~dp0\tools\build\build.bat" --wait-on-error server %* diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index 811ea64f9b4c1..03a999b509995 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -76062,6 +76062,7 @@ }, /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/directional/north, +/mob/living/basic/spider/giant/sgt_araneus, /turf/open/floor/stone, /area/station/command/heads_quarters/hos) "yaG" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index cc0e0ed7842f1..18925cfd927a0 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -32093,7 +32093,6 @@ }, /obj/item/pen, /obj/effect/turf_decal/tile/blue/anticorner/contrasted, -/obj/machinery/door/airlock, /turf/open/floor/iron, /area/station/command/bridge) "jJV" = ( diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index f1218d4d70732..d592135038d8c 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -19251,6 +19251,7 @@ /obj/effect/turf_decal/tile/bar{ dir = 1 }, +/obj/structure/window/spawner/directional/south, /turf/open/floor/iron, /area/station/hallway/secondary/service) "hcv" = ( diff --git a/_maps/map_files/Mining/Lavaland.dmm b/_maps/map_files/Mining/Lavaland.dmm index 72fe092d5caa1..f2d6eb671e38f 100644 --- a/_maps/map_files/Mining/Lavaland.dmm +++ b/_maps/map_files/Mining/Lavaland.dmm @@ -645,6 +645,15 @@ }, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) +"ey" = ( +/obj/structure/lattice/catwalk/mining, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/item/toy/plush/shark{ + desc = "A plushie depicting a somewhat cartoonish shark. The tag calls it a 'hákarl', noting that it was made by an obscure furniture manufacturer in old Scandinavia. This one seems to have some cable wiring sticking out of its mouth." + }, +/turf/open/lava/smooth/lava_land_surface, +/area/lavaland/surface/outdoors) "ez" = ( /obj/structure/chair/comfy/teal{ dir = 4 @@ -2869,7 +2878,7 @@ /area/mine/laborcamp/production) "ps" = ( /obj/structure/cable, -/obj/machinery/power/smes/full, +/obj/machinery/power/smes/super/full, /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/mine/maintenance/service) @@ -5038,7 +5047,7 @@ /turf/open/floor/plating/lavaland_atmos, /area/lavaland/surface/outdoors) "Ee" = ( -/obj/machinery/power/smes/full, +/obj/machinery/power/smes/super/full, /obj/structure/cable, /turf/open/floor/plating, /area/mine/maintenance/labor) @@ -29901,7 +29910,7 @@ Xw aj aj aj -cU +ey aj aj aj diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm index ef254589129c4..325116eb5b869 100644 --- a/code/__DEFINES/access.dm +++ b/code/__DEFINES/access.dm @@ -467,6 +467,7 @@ #define REGION_RESEARCH "Research" /// Used to seed the accesses_by_region list in SSid_access. A list of all research regional accesses that are overseen by the RD. #define REGION_ACCESS_RESEARCH list( \ + ACCESS_AI_UPLOAD, \ ACCESS_GENETICS, \ ACCESS_MECH_SCIENCE, \ ACCESS_MINISAT, \ diff --git a/code/__DEFINES/ai/ai_blackboard.dm b/code/__DEFINES/ai/ai_blackboard.dm index 1a2b19740f612..a65b5ebd40758 100644 --- a/code/__DEFINES/ai/ai_blackboard.dm +++ b/code/__DEFINES/ai/ai_blackboard.dm @@ -19,6 +19,11 @@ ///can this mob heal? #define BB_BASIC_MOB_HEALER "BB_basic_mob_healer" +///the owner we will try to play with +#define BB_OWNER_TARGET "BB_owner_target" +///the list of interactions we can have with the owner +#define BB_INTERACTIONS_WITH_OWNER "BB_interactions_with_owner" + /// Store a single or list of emotes at this key #define BB_EMOTE_KEY "BB_emotes" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_spawner.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_spawner.dm index 6ff8b1e8d61d4..2e1b157ec11f8 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_spawner.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_spawner.dm @@ -2,5 +2,8 @@ /// called when a spawner spawns a mob #define COMSIG_SPAWNER_SPAWNED "spawner_spawned" +/// Called when a spawner spawns a mob in a turf peel, but we need to use the default case. +#define COMSIG_SPAWNER_SPAWNED_DEFAULT "spawner_spawned_default" + /// called when a ghost clicks a spawner role: (mob/living) #define COMSIG_GHOSTROLE_SPAWNED "ghostrole_spawned" diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm index c4007d1296910..d3439cf857291 100644 --- a/code/__DEFINES/dcs/signals/signals_mod.dm +++ b/code/__DEFINES/dcs/signals/signals_mod.dm @@ -1,10 +1,14 @@ //MODsuit signals /// Called when a module is selected to be the active one from on_select(obj/item/mod/module/module) #define COMSIG_MOD_MODULE_SELECTED "mod_module_selected" -/// Called when a MOD deploys one or more of its parts. +/// Called when a MOD user deploys one or more of its parts. #define COMSIG_MOD_DEPLOYED "mod_deployed" -/// Called when a MOD retracts one or more of its parts. +/// Called when a MOD user retracts one or more of its parts. #define COMSIG_MOD_RETRACTED "mod_retracted" +/// Called when a MOD deploys a part. +#define COMSIG_MOD_PART_DEPLOYED "mod_part_deployed" +/// Called when a MOD retracts a part. +#define COMSIG_MOD_PART_RETRACTED "mod_part_retracted" /// Called when a MOD is finished toggling itself. #define COMSIG_MOD_TOGGLED "mod_toggled" /// Called when a MOD activation is called from toggle_activate(mob/user) diff --git a/code/__DEFINES/mod.dm b/code/__DEFINES/mod.dm index be59793927f07..8257e1969bedb 100644 --- a/code/__DEFINES/mod.dm +++ b/code/__DEFINES/mod.dm @@ -4,7 +4,7 @@ /// The default cell drain of a modsuit. The standard modsuit active power usage drains this much energy per modsuit second. #define DEFAULT_CHARGE_DRAIN (0.005 * STANDARD_CELL_CHARGE) // A standard cell lasts 200 seconds with this on active power usage, while a high power one lasts 2,000 seconds. -/// Default time for a part to seal +/// Default time for a part of the suit to seal. #define MOD_ACTIVATION_STEP_TIME (2 SECONDS) /// Passive module, just acts when put in naturally. @@ -23,14 +23,8 @@ /// This module can be used while the suit is off #define MODULE_ALLOW_INACTIVE (1<<2) -//Defines used by the theme for clothing flags and similar -#define CONTROL_LAYER "control_layer" -#define HELMET_FLAGS "helmet_flags" -#define CHESTPLATE_FLAGS "chestplate_flags" -#define GAUNTLETS_FLAGS "gauntlets_flags" -#define BOOTS_FLAGS "boots_flags" - #define UNSEALED_LAYER "unsealed_layer" +#define SEALED_LAYER "sealed_layer" #define UNSEALED_CLOTHING "unsealed_clothing" #define SEALED_CLOTHING "sealed_clothing" #define UNSEALED_INVISIBILITY "unsealed_invisibility" @@ -38,6 +32,8 @@ #define UNSEALED_COVER "unsealed_cover" #define SEALED_COVER "sealed_cover" #define CAN_OVERSLOT "can_overslot" +#define UNSEALED_MESSAGE "unsealed_message" +#define SEALED_MESSAGE "sealed_message" //Defines used to override MOD clothing's icon and worn icon files in the skin. #define MOD_ICON_OVERRIDE "mod_icon_override" @@ -49,6 +45,16 @@ #define MODLINK_FREQ_CHARLIE "CHRL" #define MODLINK_FREQ_CENTCOM "CC" +//Default text for different messages for the user. +#define HELMET_UNSEAL_MESSAGE "hisses open" +#define HELMET_SEAL_MESSAGE "hisses closed" +#define CHESTPLATE_UNSEAL_MESSAGE "releases your chest" +#define CHESTPLATE_SEAL_MESSAGE "cinches tightly around your chest" +#define GAUNTLET_UNSEAL_MESSAGE "become loose around your fingers" +#define GAUNTLET_SEAL_MESSAGE "tighten around your fingers and wrists" +#define BOOT_UNSEAL_MESSAGE "relax their grip on your legs" +#define BOOT_SEAL_MESSAGE "seal around your feet" + /// Global list of all /datum/mod_theme GLOBAL_LIST_INIT(mod_themes, setup_mod_themes()) /// Global list of all ids associated to a /datum/mod_link instance diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index c7daddd2970ae..2989310fb9b73 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -35,7 +35,7 @@ #define CHAT_GHOSTLAWS (1<<11) #define CHAT_LOGIN_LOGOUT (1<<12) -#define TOGGLES_DEFAULT_CHAT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_PULLR|CHAT_GHOSTWHISPER|CHAT_GHOSTPDA|CHAT_GHOSTRADIO|CHAT_BANKCARD|CHAT_GHOSTLAWS|CHAT_LOGIN_LOGOUT) +#define TOGGLES_DEFAULT_CHAT (CHAT_OOC|CHAT_DEAD|CHAT_PRAYER|CHAT_PULLR|CHAT_GHOSTPDA|CHAT_GHOSTRADIO|CHAT_BANKCARD|CHAT_GHOSTLAWS|CHAT_LOGIN_LOGOUT) #define PARALLAX_INSANE "Insane" #define PARALLAX_HIGH "High" diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm index cada3f9eb21aa..42e00045761e5 100644 --- a/code/__DEFINES/surgery.dm +++ b/code/__DEFINES/surgery.dm @@ -26,6 +26,8 @@ #define ORGAN_HIDDEN (1<<9) /// Has the organ already been inserted inside someone #define ORGAN_VIRGIN (1<<10) +/// ALWAYS show this when scanned by advanced scanners, even if it is totally healthy +#define ORGAN_PROMINENT (1<<11) /// Helper to figure out if a limb is organic #define IS_ORGANIC_LIMB(limb) (limb.bodytype & BODYTYPE_ORGANIC) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index d623a707b8855..eb7fa2c3aa36b 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -632,6 +632,49 @@ GLOBAL_LIST_INIT(skin_tone_names, list( else return precise_zone +///Returns a list of strings for a given slot flag. +/proc/parse_slot_flags(slot_flags) + var/list/slot_strings = list() + if(slot_flags & ITEM_SLOT_BACK) + slot_strings += "back" + if(slot_flags & ITEM_SLOT_MASK) + slot_strings += "mask" + if(slot_flags & ITEM_SLOT_NECK) + slot_strings += "neck" + if(slot_flags & ITEM_SLOT_HANDCUFFED) + slot_strings += "handcuff" + if(slot_flags & ITEM_SLOT_LEGCUFFED) + slot_strings += "legcuff" + if(slot_flags & ITEM_SLOT_BELT) + slot_strings += "belt" + if(slot_flags & ITEM_SLOT_ID) + slot_strings += "id" + if(slot_flags & ITEM_SLOT_EARS) + slot_strings += "ear" + if(slot_flags & ITEM_SLOT_EYES) + slot_strings += "glasses" + if(slot_flags & ITEM_SLOT_GLOVES) + slot_strings += "glove" + if(slot_flags & ITEM_SLOT_HEAD) + slot_strings += "head" + if(slot_flags & ITEM_SLOT_FEET) + slot_strings += "shoe" + if(slot_flags & ITEM_SLOT_OCLOTHING) + slot_strings += "oversuit" + if(slot_flags & ITEM_SLOT_ICLOTHING) + slot_strings += "undersuit" + if(slot_flags & ITEM_SLOT_SUITSTORE) + slot_strings += "suit storage" + if(slot_flags & (ITEM_SLOT_LPOCKET|ITEM_SLOT_RPOCKET)) + slot_strings += "pocket" + if(slot_flags & ITEM_SLOT_HANDS) + slot_strings += "hand" + if(slot_flags & ITEM_SLOT_DEX_STORAGE) + slot_strings += "dextrous storage" + if(slot_flags & ITEM_SLOT_BACKPACK) + slot_strings += "backpack" + return slot_strings + ///Returns the direction that the initiator and the target are facing /proc/check_target_facings(mob/living/initiator, mob/living/target) /*This can be used to add additional effects on interactions between mobs depending on how the mobs are facing each other, such as adding a crit damage to blows to the back of a guy's head. diff --git a/code/__HELPERS/spatial_info.dm b/code/__HELPERS/spatial_info.dm index 98e11b483bab7..c23e3a408a53c 100644 --- a/code/__HELPERS/spatial_info.dm +++ b/code/__HELPERS/spatial_info.dm @@ -468,5 +468,8 @@ if(possible_spawn in inner) continue peel += possible_spawn + + if(!length(peel)) + return center //Offer the center only as a default case when we don't have a valid circle. return peel diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 13b0e5c3c1335..ec76dee9c8e22 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -255,10 +255,8 @@ return CLICK_ACTION_SUCCESS /obj/machinery/power/apc/attack_ai_secondary(mob/living/silicon/user, list/modifiers) - if(!can_use(user, loud = TRUE)) - return - - togglelock(user) + if(can_use(user, loud = TRUE)) + togglelock(user) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /* AI Turrets */ diff --git a/code/datums/ai/basic_mobs/basic_subtrees/play_with_owners.dm b/code/datums/ai/basic_mobs/basic_subtrees/play_with_owners.dm new file mode 100644 index 0000000000000..e27e984e70649 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/play_with_owners.dm @@ -0,0 +1,21 @@ +/datum/ai_planning_subtree/find_and_hunt_target/play_with_owner + target_key = BB_OWNER_TARGET + hunting_behavior = /datum/ai_behavior/hunt_target/play_with_owner + finding_behavior = /datum/ai_behavior/find_hunt_target/find_owner + hunt_targets = list(/mob/living) + hunt_chance = 80 + hunt_range = 9 + +/datum/ai_behavior/find_hunt_target/find_owner + action_cooldown = 1 MINUTES + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/find_hunt_target/find_owner/valid_dinner(mob/living/source, atom/friend, radius, datum/ai_controller/controller, seconds_per_tick) + return (friend != source) && (source.faction.Find(REF(friend))) && can_see(source, friend, radius) + +/datum/ai_behavior/hunt_target/play_with_owner + +/datum/ai_behavior/hunt_target/play_with_owner/target_caught(mob/living/hunter, atom/hunted) + var/list/interactions_list = hunter.ai_controller.blackboard[BB_INTERACTIONS_WITH_OWNER] + var/interaction_message = length(interactions_list) ? pick(interactions_list) : "Plays with" + hunter.manual_emote("[interaction_message] [hunted]!") diff --git a/code/datums/components/echolocation.dm b/code/datums/components/echolocation.dm index 13ef8f4ed2431..60a6d0fa8aa3c 100644 --- a/code/datums/components/echolocation.dm +++ b/code/datums/components/echolocation.dm @@ -1,8 +1,8 @@ /datum/component/echolocation /// Radius of our view. var/echo_range = 4 - /// Time between echolocations. - var/cooldown_time = 1.8 SECONDS + /// Time between echolocations. IMPORTANT!! The effective time in local and the effective time in live are very different. The second is noticeably slower, + var/cooldown_time = 1 SECONDS /// Time for the image to start fading out. var/image_expiry_time = 1.4 SECONDS /// Time for the image to fade in. diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm index d6c07d434237a..a808784b815ec 100644 --- a/code/datums/components/riding/riding_mob.dm +++ b/code/datums/components/riding/riding_mob.dm @@ -97,7 +97,7 @@ return ..() /datum/component/riding/creature/driver_move(atom/movable/movable_parent, mob/living/user, direction) - if(!COOLDOWN_FINISHED(src, vehicle_move_cooldown)) + if(!COOLDOWN_FINISHED(src, vehicle_move_cooldown) || !Process_Spacemove()) return COMPONENT_DRIVER_BLOCK_MOVE if(!keycheck(user)) if(ispath(keytype, /obj/item)) diff --git a/code/datums/components/spawner.dm b/code/datums/components/spawner.dm index 7c85c1a5b2b70..29767d11ce6d1 100644 --- a/code/datums/components/spawner.dm +++ b/code/datums/components/spawner.dm @@ -72,6 +72,8 @@ picked_spot = pick(turf_peel(spawn_distance, spawn_distance_exclude, spawner.loc, view_based = TRUE)) if(!picked_spot) picked_spot = pick(circle_range_turfs(spawner.loc, spawn_distance)) + if(picked_spot == spawner.loc) + SEND_SIGNAL(spawner, COMSIG_SPAWNER_SPAWNED_DEFAULT) created = new chosen_mob_type(picked_spot) else if (spawn_distance >= 1) picked_spot = pick(circle_range_turfs(spawner.loc, spawn_distance)) diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm index 0e54c21e70234..e6cf8f2d3d80e 100644 --- a/code/datums/mood_events/generic_negative_events.dm +++ b/code/datums/mood_events/generic_negative_events.dm @@ -469,7 +469,7 @@ mood_change = -3 timeout = 5 MINUTES -/datum/mood_event/amulette_insanity +/datum/mood_event/amulet_insanity description = "I sEe THe LiGHt, It mUsT BE stOPPed" mood_change = -6 timeout = 5 MINUTES diff --git a/code/datums/quirks/positive_quirks/night_vision.dm b/code/datums/quirks/positive_quirks/night_vision.dm deleted file mode 100644 index 808a213db514b..0000000000000 --- a/code/datums/quirks/positive_quirks/night_vision.dm +++ /dev/null @@ -1,28 +0,0 @@ -/datum/quirk/night_vision - name = "Night Vision" - desc = "You can see slightly more clearly in full darkness than most people." - icon = FA_ICON_MOON - value = 4 - mob_trait = TRAIT_NIGHT_VISION - gain_text = span_notice("The shadows seem a little less dark.") - lose_text = span_danger("Everything seems a little darker.") - medical_record_text = "Patient's eyes show above-average acclimation to darkness." - mail_goodies = list( - /obj/item/flashlight/flashdark, - /obj/item/food/grown/mushroom/glowshroom/shadowshroom, - /obj/item/skillchip/light_remover, - ) - -/datum/quirk/night_vision/add(client/client_source) - refresh_quirk_holder_eyes() - -/datum/quirk/night_vision/remove() - refresh_quirk_holder_eyes() - -/datum/quirk/night_vision/proc/refresh_quirk_holder_eyes() - var/mob/living/carbon/human/human_quirk_holder = quirk_holder - var/obj/item/organ/internal/eyes/eyes = human_quirk_holder.get_organ_by_type(/obj/item/organ/internal/eyes) - if(!eyes || eyes.lighting_cutoff) - return - // We've either added or removed TRAIT_NIGHT_VISION before calling this proc. Just refresh the eyes. - eyes.refresh() diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm index c081ee4845fa5..a48c0ccf59431 100644 --- a/code/datums/wounds/_wounds.dm +++ b/code/datums/wounds/_wounds.dm @@ -215,13 +215,13 @@ var/msg = span_danger("[victim]'s [limb.plaintext_zone] [occur_text]!") var/vis_dist = COMBAT_MESSAGE_RANGE - if(severity > WOUND_SEVERITY_MODERATE) + if(severity > WOUND_SEVERITY_SEVERE) msg = "[msg]" vis_dist = DEFAULT_MESSAGE_RANGE victim.visible_message(msg, span_userdanger("Your [limb.plaintext_zone] [occur_text]!"), vision_distance = vis_dist) if(sound_effect) - playsound(L.owner, sound_effect, sound_volume + (20 * severity), TRUE) + playsound(L.owner, sound_effect, sound_volume + (20 * severity), TRUE, falloff_exponent = SOUND_FALLOFF_EXPONENT + 2, ignore_walls = FALSE, falloff_distance = 0) wound_injury(old_wound, attack_direction = attack_direction) if(!demoted) diff --git a/code/game/machinery/computer/orders/order_items/cook/order_milk_eggs.dm b/code/game/machinery/computer/orders/order_items/cook/order_milk_eggs.dm index fcd0737675bec..9e5413f854863 100644 --- a/code/game/machinery/computer/orders/order_items/cook/order_milk_eggs.dm +++ b/code/game/machinery/computer/orders/order_items/cook/order_milk_eggs.dm @@ -134,3 +134,13 @@ name = "Mothic Pantry Pack" item_path = /obj/item/storage/box/mothic_cans_sauces cost_per_order = 120 + +/datum/orderable_item/milk_eggs/armorfish + name = "Cleaned Armorfish" + item_path = /obj/item/food/fishmeat/armorfish + cost_per_order = 30 + +/datum/orderable_item/milk_eggs/moonfish + name = "Moonfish" + item_path = /obj/item/food/fishmeat/moonfish + cost_per_order = 30 diff --git a/code/game/machinery/computer/orders/order_items/cook/order_reagents.dm b/code/game/machinery/computer/orders/order_items/cook/order_reagents.dm index d6b4728d10496..39fb38df550ef 100644 --- a/code/game/machinery/computer/orders/order_items/cook/order_reagents.dm +++ b/code/game/machinery/computer/orders/order_items/cook/order_reagents.dm @@ -95,3 +95,13 @@ name = "Grounding Solution" item_path = /obj/item/reagent_containers/condiment/grounding_solution cost_per_order = 30 + +/datum/orderable_item/reagents/honey + name = "Honey" + item_path = /obj/item/reagent_containers/condiment/honey + cost_per_order = 125 //its high quality honey :) + +/datum/orderable_item/reagents/mayonnaise + name = "Mayonnaise" + item_path = /obj/item/reagent_containers/condiment/mayonnaise + cost_per_order = 30 diff --git a/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm b/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm index 506920986dd2c..f96562724d27d 100644 --- a/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm +++ b/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm @@ -89,3 +89,91 @@ name = "Pickled Voltvine" item_path = /obj/item/food/pickled_voltvine cost_per_order = 5 + +/datum/orderable_item/veggies/chili + name = "Chili" + item_path = /obj/item/food/grown/chili + +/datum/orderable_item/veggies/berries + name = "Berries" + item_path = /obj/item/food/grown/berries + +/datum/orderable_item/veggies/pineapple + name = "Pineapple" + item_path = /obj/item/food/grown/pineapple + +/datum/orderable_item/veggies/peas + name = "Peas" + item_path = /obj/item/food/grown/peas + +/datum/orderable_item/veggies/korta_nut //nanotrasen does not devote as much of their resources to pathetic lizard crops + name = "Korta Nut" + item_path = /obj/item/food/grown/korta_nut + cost_per_order = 15 + +/datum/orderable_item/veggies/parsnip + name = "Parsnip" + item_path = /obj/item/food/grown/parsnip + +/datum/orderable_item/veggies/redbeet + name = "Red Beet" + item_path = /obj/item/food/grown/redbeet + +/datum/orderable_item/veggies/orange + name = "Orange" + item_path = /obj/item/food/grown/citrus/orange + +/datum/orderable_item/veggies/vanillapod + name = "Vanilla" + item_path = /obj/item/food/grown/vanillapod + cost_per_order = 25 //food items that are treated as mutations in game should be more expensive. groceries shouldnt include ACTUAL mutations but i think real foods are ok + +/datum/orderable_item/veggies/sweetkorta + name = "Sweet Korta Nut" + item_path = /obj/item/food/grown/korta_nut/sweet + cost_per_order = 30 + +/datum/orderable_item/veggies/redonion + name = "Red Onion" + item_path = /obj/item/food/grown/onion/red + cost_per_order = 25 + +/datum/orderable_item/veggies/peanut + name = "Peanut" + item_path = /obj/item/food/grown/peanut + +/datum/orderable_item/veggies/sweetpotato + name = "Sweet Potato" + item_path = /obj/item/food/grown/potato/sweet + cost_per_order = 25 + +/datum/orderable_item/veggies/oat + name = "Oat" + item_path = /obj/item/food/grown/oat + +/datum/orderable_item/veggies/trumpet + name = "Spaceman's Trumpet" + item_path = /obj/item/food/grown/trumpet + cost_per_order = 25 + +/datum/orderable_item/veggies/banana + name = "Banana" + item_path = /obj/item/food/grown/banana + +/datum/orderable_item/veggies/ghostchili + name = "Ghost Chili" + item_path = /obj/item/food/grown/ghost_chili + cost_per_order = 25 + +/datum/orderable_item/veggies/lemon + name = "Lemon" + item_path = /obj/item/food/grown/citrus/lemon + +/datum/orderable_item/veggies/lime + name = "Lime" + item_path = /obj/item/food/grown/citrus/lime + +/datum/orderable_item/veggies/toechtauese + name = "Töchtaüse berries" + item_path = /obj/item/food/grown/toechtauese + cost_per_order = 15 diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index c1be0917bf352..52ae94e48a2d6 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -8,7 +8,7 @@ #define LINE1_Y -4 #define LINE2_X 1 #define LINE2_Y -11 -#define STATUS_DISPLAY_FONT_DATUM /datum/font/tiny_unicode/size_12pt +GLOBAL_DATUM_INIT(status_font, /datum/font, new /datum/font/tiny_unicode/size_12pt()) /// Status display which can show images and scrolling text. /obj/machinery/status_display @@ -22,8 +22,11 @@ density = FALSE layer = ABOVE_WINDOW_LAYER - var/obj/effect/overlay/status_display_text/message1_overlay - var/obj/effect/overlay/status_display_text/message2_overlay + // We store overlays as keys, so multiple displays can use the same object safely + /// String key we use to index the first effect overlay displayed on us + var/message_key_1 + /// String key we use to index the second effect overlay displayed on us + var/message_key_2 var/current_picture = "" var/current_mode = SD_BLANK var/message1 = "" @@ -109,10 +112,26 @@ * Don't call this in subclasses. */ /obj/machinery/status_display/proc/remove_messages() - if(message1_overlay) - QDEL_NULL(message1_overlay) - if(message2_overlay) - QDEL_NULL(message2_overlay) + var/obj/effect/overlay/status_display_text/overlay_1 = get_status_text(message_key_1) + message_key_1 = null + overlay_1?.disown(src) + var/obj/effect/overlay/status_display_text/overlay_2 = get_status_text(message_key_2) + message_key_2 = null + overlay_2?.disown(src) + +// List in the form key -> status display that shows said key +GLOBAL_LIST_EMPTY(key_to_status_display) + +/proc/generate_status_text(line_y, message, x_offset, text_color, header_text_color, line_pair) + var/key = "[line_y]-[message]-[x_offset]-[text_color]-[header_text_color]-[line_pair]" + var/obj/effect/overlay/status_display_text/new_overlay = GLOB.key_to_status_display[key] + if(!new_overlay) + new_overlay = new(null, line_y, message, text_color, header_text_color, x_offset, line_pair, key) + GLOB.key_to_status_display[key] = new_overlay + return new_overlay + +/proc/get_status_text(key) + return GLOB.key_to_status_display[key] /** * Create/update message overlay. @@ -125,19 +144,16 @@ * * message - the new message text. * Returns new /obj/effect/overlay/status_display_text or null if unchanged. */ -/obj/machinery/status_display/proc/update_message(obj/effect/overlay/status_display_text/overlay, line_y, message, x_offset, line_pair) - if(overlay && message == overlay.message) - return null +/obj/machinery/status_display/proc/update_message(current_key, line_y, message, x_offset, line_pair) + var/obj/effect/overlay/status_display_text/current_overlay = get_status_text(current_key) + var/obj/effect/overlay/status_display_text/new_overlay = generate_status_text(line_y, message, text_color, header_text_color, x_offset, line_pair) - if(overlay) - qdel(overlay) + if(current_overlay == new_overlay) + return current_key - var/obj/effect/overlay/status_display_text/new_status_display_text = new(src, line_y, message, text_color, header_text_color, x_offset, line_pair) - // Draw our object visually "in front" of this display, taking advantage of sidemap - new_status_display_text.pixel_y = -32 - new_status_display_text.pixel_z = 32 - vis_contents += new_status_display_text - return new_status_display_text + current_overlay?.disown(src) + new_overlay.own(src) + return new_overlay.status_key /obj/machinery/status_display/update_appearance(updates=ALL) . = ..() @@ -171,17 +187,12 @@ var/line1_metric var/line2_metric var/line_pair - var/datum/font/display_font = new STATUS_DISPLAY_FONT_DATUM() - line1_metric = display_font.get_metrics(message1) - line2_metric = display_font.get_metrics(message2) + line1_metric = GLOB.status_font.get_metrics(message1) + line2_metric = GLOB.status_font.get_metrics(message2) line_pair = (line1_metric > line2_metric ? line1_metric : line2_metric) - var/overlay = update_message(message1_overlay, LINE1_Y, message1, LINE1_X, line_pair) - if(overlay) - message1_overlay = overlay - overlay = update_message(message2_overlay, LINE2_Y, message2, LINE2_X, line_pair) - if(overlay) - message2_overlay = overlay + message_key_1 = update_message(message_key_1, LINE1_Y, message1, LINE1_X, line_pair) + message_key_2 = update_message(message_key_2, LINE2_Y, message2, LINE2_X, line_pair) // Turn off backlight if message is blank if(message1 == "" && message2 == "") @@ -215,6 +226,8 @@ /obj/machinery/status_display/examine(mob/user) . = ..() + var/obj/effect/overlay/status_display_text/message1_overlay = get_status_text(message_key_1) + var/obj/effect/overlay/status_display_text/message2_overlay = get_status_text(message_key_2) if (message1_overlay || message2_overlay) . += "The display says:" if (message1_overlay.message) @@ -247,30 +260,37 @@ /obj/effect/overlay/status_display_text icon = 'icons/obj/machines/status_display.dmi' vis_flags = VIS_INHERIT_LAYER | VIS_INHERIT_PLANE | VIS_INHERIT_ID + // physically shift down to render correctly + pixel_y = -32 + pixel_z = 32 /// The message this overlay is displaying. var/message + /// Amount of usage this overlay is getting + var/use_count = 0 + /// The status key we represent + var/status_key // If the line is short enough to not marquee, and it matches this, it's a header. var/static/regex/header_regex = regex("^-.*-$") -/obj/effect/overlay/status_display_text/Initialize(mapload, yoffset, line, text_color, header_text_color, xoffset = 0, line_pair) +/obj/effect/overlay/status_display_text/Initialize(mapload, maptext_y, message, text_color, header_text_color, xoffset = 0, line_pair, status_key) . = ..() - maptext_y = yoffset - message = line + src.maptext_y = maptext_y + src.message = message + src.status_key = status_key - var/datum/font/display_font = new STATUS_DISPLAY_FONT_DATUM() - var/line_width = display_font.get_metrics(line) + var/line_width = GLOB.status_font.get_metrics(message) if(line_width > MAX_STATIC_WIDTH) // Marquee text - var/marquee_message = "[line] [line] [line]" + var/marquee_message = "[message] [message] [message]" // Width of full content. Must of these is never revealed unless the user inputted a single character. - var/full_marquee_width = display_font.get_metrics("[marquee_message] ") + var/full_marquee_width = GLOB.status_font.get_metrics("[marquee_message] ") // We loop after only this much has passed. - var/looping_marquee_width = (display_font.get_metrics("[line] ]") - SCROLL_PADDING) + var/looping_marquee_width = (GLOB.status_font.get_metrics("[message] ]") - SCROLL_PADDING) maptext = generate_text(marquee_message, center = FALSE, text_color = text_color) maptext_width = full_marquee_width @@ -285,10 +305,28 @@ animate(maptext_x = MAX_STATIC_WIDTH, time = 0) else // Centered text - var/color = header_regex.Find(line) ? header_text_color : text_color - maptext = generate_text(line, center = TRUE, text_color = color) + var/color = header_regex.Find(message) ? header_text_color : text_color + maptext = generate_text(message, center = TRUE, text_color = color) maptext_x = xoffset //Defaults to 0, this would be centered unless overided +/obj/effect/overlay/status_display_text/Destroy(force) + GLOB.key_to_status_display -= status_key + return ..() + +/// Status displays are static, shared by everyone who needs them +/// This marks us as being used by one more guy +/obj/effect/overlay/status_display_text/proc/own(atom/movable/owned_by) + owned_by.vis_contents += src + use_count += 1 + +/// Status displays are static, shared by everyone who needs them +/// This marks us as no longer being used by a guy +/obj/effect/overlay/status_display_text/proc/disown(atom/movable/disowned_by) + disowned_by.vis_contents -= src + use_count -= 1 + if(use_count <= 0) + qdel(src) + /** * Generate the actual maptext. * Arguments: @@ -562,6 +600,4 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/random_message, 32) #undef LINE1_Y #undef LINE2_X #undef LINE2_Y -#undef STATUS_DISPLAY_FONT_DATUM - #undef SCROLL_PADDING diff --git a/code/game/objects/items/choice_beacon.dm b/code/game/objects/items/choice_beacon.dm index 06385b765cfab..75a3f35c80b97 100644 --- a/code/game/objects/items/choice_beacon.dm +++ b/code/game/objects/items/choice_beacon.dm @@ -139,7 +139,7 @@ icon_state = "self_delivery" inhand_icon_state = "self_delivery" company_source = "S.E.L.F." - company_message = span_bold("Request status: Recieved. Package status: Delivered. Notes: To assure optimal value, use supplied Interdyne-brand autosurgeons to change implantment status.") + company_message = span_bold("Request status: Received. Package status: Delivered. Notes: To assure optimal value, use supplied Interdyne-brand autosurgeons to change implantment status.") /obj/item/choice_beacon/augments/generate_display_names() var/static/list/augment_list diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index 10c301af1c63d..c8c76c583e103 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -279,7 +279,7 @@ Status" for(var/obj/item/organ/organ as anything in humantarget.organs) - var/status = organ.get_status_text() + var/status = organ.get_status_text(advanced) if (status != "") render = TRUE toReport += "[organ.name]:\ diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm index b40a81f3642c3..c85bd5fe9615d 100644 --- a/code/game/objects/items/religion.dm +++ b/code/game/objects/items/religion.dm @@ -418,8 +418,8 @@ /obj/item/claymore/weak desc = "This one is rusted." - force = 30 - armour_penetration = 15 + force = 24 + armour_penetration = 10 /obj/item/claymore/weak/ceremonial desc = "A rusted claymore, once at the heart of a powerful scottish clan struck down and oppressed by tyrants, it has been passed down the ages as a symbol of defiance." diff --git a/code/game/objects/items/shrapnel.dm b/code/game/objects/items/shrapnel.dm index c07bc780c9128..9b024cfbb97a2 100644 --- a/code/game/objects/items/shrapnel.dm +++ b/code/game/objects/items/shrapnel.dm @@ -31,6 +31,7 @@ shrapnel_type = /obj/item/shrapnel ricochet_incidence_leeway = 60 hit_prone_targets = TRUE + ignore_range_hit_prone_targets = TRUE sharpness = SHARP_EDGED wound_bonus = 30 embedding = list(embed_chance=70, ignore_throwspeed_threshold=TRUE, fall_chance=1) @@ -73,6 +74,7 @@ /obj/projectile/bullet/pellet/stingball/on_ricochet(atom/A) hit_prone_targets = TRUE // ducking will save you from the first wave, but not the rebounds + ignore_range_hit_prone_targets = TRUE /obj/projectile/bullet/pellet/stingball/mega name = "megastingball pellet" diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 5b42bf3e432d9..7bd009016148e 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -440,6 +440,13 @@ new /obj/item/grenade/empgrenade(src) new /obj/item/implanter/emp(src) +/obj/item/storage/box/syndie_kit/smoke + name = "smoke kit" + +/obj/item/storage/box/syndie_kit/smoke/PopulateContents() + for(var/i in 1 to 5) + new /obj/item/grenade/smokebomb(src) + /obj/item/storage/box/syndie_kit/mail_counterfeit name = "mail counterfeit kit" desc = "A box full of mail counterfeit devices. Nothing stops the mail." diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index b2cc7544e1077..7f0e28c95e670 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -127,10 +127,6 @@ throw_range = 7 force = 0 var/random_color = TRUE - /// the string of the dmi state the balloon has while on the floor. - var/world_state - /// the string of the dmi state the balloon has while in your inventory. - var/storage_state /// the string describing the name of balloon's current colour. var/current_color @@ -159,13 +155,6 @@ list("orange", "purple") = /obj/item/toy/balloon_animal/plasmaman, ) -/obj/item/toy/balloon/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) - . = ..() - if(isturf(loc)) - icon_state = "[world_state]" - else - icon_state = "[storage_state]" - update_appearance() /obj/item/toy/balloon/long/attackby(obj/item/attacking_item, mob/living/user, params) if(!istype(attacking_item, /obj/item/toy/balloon/long) || !HAS_TRAIT(user, TRAIT_BALLOON_SUTRA)) @@ -217,14 +206,30 @@ /obj/item/toy/balloon/Initialize(mapload) . = ..() - if(random_color) - var/chosen_balloon_color = pick(BALLOON_COLORS) - current_color = "[chosen_balloon_color]" - name = "[chosen_balloon_color] [name]" - icon_state = "[icon_state]_[chosen_balloon_color]" - inhand_icon_state = icon_state - world_state = "[icon_state]" - storage_state = "[icon_state]_storage" + AddElement(/datum/element/update_icon_updates_onmob) + if(!random_color) + return + current_color = pick(BALLOON_COLORS) + update_appearance() + +/obj/item/toy/balloon/update_name(updates) + . = ..() + name = "[current_color ? "[current_color] ":null][initial(name)]" + +/obj/item/toy/balloon/vv_edit_var(vname, vval) + . = ..() + if(vname == NAMEOF(src, current_color)) + update_appearance() + +/obj/item/toy/balloon/update_icon_state() + . = ..() + var/new_icon = "[initial(icon_state)][current_color ? "_[current_color]":null]" + inhand_icon_state = new_icon + icon_state = "[new_icon][isturf(loc) ? null : "_storage"]" + +/obj/item/toy/balloon/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + update_appearance() /obj/item/toy/balloon/corgi name = "corgi balloon" @@ -280,7 +285,9 @@ name = "balloon animal" desc = "You shouldn't have this." icon = 'icons/obj/toys/balloons.dmi' - icon_state = "balloon_guy" + inhand_icon_state = "balloon" + lefthand_file = 'icons/mob/inhands/items/balloons_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items/balloons_righthand.dmi' throwforce = 0 throw_speed = 2 throw_range = 5 diff --git a/code/game/objects/structures/deployable_turret.dm b/code/game/objects/structures/deployable_turret.dm index 7b03348288abb..b2bb5fe51c595 100644 --- a/code/game/objects/structures/deployable_turret.dm +++ b/code/game/objects/structures/deployable_turret.dm @@ -213,7 +213,7 @@ /obj/machinery/deployable_turret/hmg name = "heavy machine gun turret" - desc = "A heavy calibre machine gun commonly used by Nanotrasen forces, famed for it's ability to give people on the recieving end more holes than normal." + desc = "A heavy caliber machine gun commonly used by Nanotrasen forces, famed for it's ability to give people on the receiving end more holes than normal." icon_state = "hmg" max_integrity = 250 projectile_type = /obj/projectile/bullet/manned_turret/hmg diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm index c8961a5769ccf..55b160e0109c5 100644 --- a/code/game/objects/structures/lavaland/ore_vent.dm +++ b/code/game/objects/structures/lavaland/ore_vent.dm @@ -82,6 +82,8 @@ icon_state = icon_state_tapped update_appearance(UPDATE_ICON_STATE) add_overlay(mutable_appearance('icons/obj/mining_zones/terrain.dmi', "well", ABOVE_MOB_LAYER)) + + RegisterSignal(src, COMSIG_SPAWNER_SPAWNED_DEFAULT, PROC_REF(anti_cheese)) return ..() /obj/structure/ore_vent/Destroy() @@ -407,6 +409,13 @@ COOLDOWN_START(src, manual_vent_cooldown, 10 SECONDS) return new_rock +/** + * When the ore vent cannot spawn a mob due to being blocked from all sides, we cause some MILD, MILD explosions. + * Explosion matches a gibtonite light explosion, as a way to clear neartby solid structures, with a high likelyhood of breaking the NODE drone. + */ +/obj/structure/ore_vent/proc/anti_cheese() + explosion(src, heavy_impact_range = 1, light_impact_range = 3, flame_range = 0, flash_range = 0, adminlog = FALSE) + //comes with the station, and is already tapped. /obj/structure/ore_vent/starter_resources name = "active ore vent" diff --git a/code/game/objects/structures/syndicate_uplink_beacon.dm b/code/game/objects/structures/syndicate_uplink_beacon.dm index 2106fade55a5c..33e260e8d209a 100644 --- a/code/game/objects/structures/syndicate_uplink_beacon.dm +++ b/code/game/objects/structures/syndicate_uplink_beacon.dm @@ -3,7 +3,7 @@ name = "suspicious beacon" icon = 'icons/obj/machines/telecomms.dmi' icon_state = "relay_traitor" - desc = "This ramshackle device seems capable of recieving and sending signals for some nefarious purpose." + desc = "This ramshackle device seems capable of receiving and sending signals for some nefarious purpose." density = TRUE anchored = TRUE /// Traitor's code that they speak into the radio diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index b65f72f8f4d79..c95fce43dc943 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -38,6 +38,8 @@ desc = "When this pops, things are gonna get more aware around here." var/group_name = "a bunch of giant spiders" var/effect_range = 3 + var/antag_type = null + var/make_antag = FALSE /obj/effect/fun_balloon/sentience/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) @@ -49,6 +51,7 @@ var/list/data = list() data["group_name"] = group_name data["range"] = effect_range + data["antag"] = make_antag return data /obj/effect/fun_balloon/sentience/ui_state(mob/user) @@ -73,6 +76,11 @@ if("effect_range") effect_range = params["updated_range"] + if("select_antag") + var/list/paths = subtypesof(/datum/antagonist) + antag_type = input(usr,"Select antag", "Antagonist selection") as null|anything in sort_list(paths) + make_antag = TRUE + if("pop") if(!popped) popped = TRUE @@ -105,6 +113,9 @@ message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(body)])") body.ghostize(FALSE) body.key = C.key + if (make_antag) + body.mind.add_antag_datum(antag_type) + continue new /obj/effect/temp_visual/gravpush(get_turf(body)) // ----------- Emergency Shuttle Balloon diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 18dce0501ff65..bc22eedbfa189 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -95,7 +95,7 @@ */ /obj/item/antag_spawner/nuke_ops name = "syndicate operative beacon" - desc = "MI13 designed one-use radio for calling immediate backup. Have no regards for safety of whom it summons - they are all inferior clones from Interdyne's genebanks anyway." + desc = "A single-use beacon designed to quickly launch reinforcement operatives into the field." icon = 'icons/obj/devices/voice.dmi' icon_state = "nukietalkie" /// The name of the special role given to the recruit @@ -170,7 +170,7 @@ /obj/item/antag_spawner/nuke_ops/overwatch name = "overwatch support beacon" desc = "Assigns an Overwatch Intelligence Agent to your operation. Stationed at their own remote outpost, they can view station cameras, alarms, and even move the Infiltrator shuttle! \ - Also, all members of your operation will recieve body cameras that they can view your progress from." + Also, all members of your operation will receive body cameras that they can view your progress from." special_role_name = ROLE_OPERATIVE_OVERWATCH outfit = /datum/outfit/syndicate/support use_subtypes = FALSE diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm index 960851280e313..29ea5f1e78502 100644 --- a/code/modules/antagonists/abductor/equipment/gland.dm +++ b/code/modules/antagonists/abductor/equipment/gland.dm @@ -3,7 +3,7 @@ desc = "A nausea-inducing hunk of twisting flesh and metal." icon = 'icons/obj/antags/abductor.dmi' icon_state = "gland" - organ_flags = ORGAN_ROBOTIC // weird? + organ_flags = ORGAN_ROBOTIC | ORGAN_PROMINENT // weird? /// Shows name of the gland as well as a description of what it does upon examination by abductor scientists and observers. var/abductor_hint = "baseline placebo referencer" diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm index ea172fee0134b..c8bbee26481a6 100644 --- a/code/modules/antagonists/changeling/powers/tiny_prick.dm +++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm @@ -77,6 +77,8 @@ VAR_FINAL/datum/changeling_profile/selected_dna /// Duration of the sting var/sting_duration = 8 MINUTES + /// Set this to false via VV to allow golem, plasmaman, or monkey changelings to turn other people into golems, plasmamen, or monkeys + var/verify_valid_species = TRUE /datum/action/changeling/sting/transformation/Grant(mob/grant_to) . = ..() @@ -99,6 +101,9 @@ return if(!new_selected_dna || changeling.chosen_sting || selected_dna) // selected other sting or other DNA while sleeping return + if(verify_valid_species && (TRAIT_NO_DNA_COPY in new_selected_dna.dna.species.inherent_traits)) + user.balloon_alert(user, "dna incompatible!") + return selected_dna = new_selected_dna return ..() diff --git a/code/modules/antagonists/heretic/items/corrupted_organs.dm b/code/modules/antagonists/heretic/items/corrupted_organs.dm index 311e84c805599..3bd3ead7f6094 100644 --- a/code/modules/antagonists/heretic/items/corrupted_organs.dm +++ b/code/modules/antagonists/heretic/items/corrupted_organs.dm @@ -2,6 +2,7 @@ /obj/item/organ/internal/eyes/corrupt name = "corrupt orbs" desc = "These eyes have seen something they shouldn't have." + organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE | ORGAN_VIRGIN | ORGAN_PROMINENT /// The override images we are applying var/list/hallucinations @@ -39,6 +40,7 @@ /obj/item/organ/internal/tongue/corrupt name = "corrupt tongue" desc = "This one tells only lies." + organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE | ORGAN_VIRGIN | ORGAN_PROMINENT /obj/item/organ/internal/tongue/corrupt/Initialize(mapload) . = ..() @@ -65,6 +67,7 @@ /obj/item/organ/internal/liver/corrupt name = "corrupt liver" desc = "After what you've seen you could really go for a drink." + organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE | ORGAN_VIRGIN | ORGAN_PROMINENT /// How much extra ingredients to add? var/amount_added = 5 /// What extra ingredients can we add? @@ -108,6 +111,7 @@ /obj/item/organ/internal/stomach/corrupt name = "corrupt stomach" desc = "This parasite demands an unwholesome diet in order to be satisfied." + organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE | ORGAN_VIRGIN | ORGAN_PROMINENT /// Do we have an unholy thirst? var/thirst_satiated = FALSE /// Timer for when we get thirsty again @@ -173,6 +177,7 @@ /obj/item/organ/internal/heart/corrupt name = "corrupt heart" desc = "What corruption is this spreading along with the blood?" + organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE | ORGAN_VIRGIN | ORGAN_PROMINENT /// How long until the next heart? COOLDOWN_DECLARE(hand_cooldown) @@ -192,6 +197,7 @@ /obj/item/organ/internal/lungs/corrupt name = "corrupt lungs" desc = "Some things SHOULD be drowned in tar." + organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE | ORGAN_VIRGIN | ORGAN_PROMINENT /// How likely are we not to cough every time we take a breath? var/cough_chance = 15 /// How much gas to emit? @@ -226,6 +232,7 @@ /obj/item/organ/internal/appendix/corrupt name = "corrupt appendix" desc = "What kind of dark, cosmic force is even going to bother to corrupt an appendix?" + organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE | ORGAN_VIRGIN | ORGAN_PROMINENT /// How likely are we to spawn worms? var/worm_chance = 2 diff --git a/code/modules/antagonists/heretic/items/heretic_necks.dm b/code/modules/antagonists/heretic/items/heretic_necks.dm index c1f244dfd0e8f..e24c17abdeeba 100644 --- a/code/modules/antagonists/heretic/items/heretic_necks.dm +++ b/code/modules/antagonists/heretic/items/heretic_necks.dm @@ -52,9 +52,9 @@ w_class = WEIGHT_CLASS_SMALL -// The amulette conversion tool used by moon heretics -/obj/item/clothing/neck/heretic_focus/moon_amulette - name = "Moonlight Amulette" +// The amulet conversion tool used by moon heretics +/obj/item/clothing/neck/heretic_focus/moon_amulet + name = "Moonlight Amulet" desc = "A piece of the mind, the soul and the moon. Gazing into it makes your head spin and hear whispers of laughter and joy." icon = 'icons/obj/antags/eldritch.dmi' icon_state = "moon_amulette" @@ -62,11 +62,11 @@ // How much damage does this item do to the targets sanity? var/sanity_damage = 20 -/obj/item/clothing/neck/heretic_focus/moon_amulette/attack(mob/living/target, mob/living/user, params) +/obj/item/clothing/neck/heretic_focus/moon_amulet/attack(mob/living/target, mob/living/user, params) var/mob/living/carbon/human/hit = target if(!IS_HERETIC_OR_MONSTER(user)) user.balloon_alert(user, "you feel a presence watching you") - user.add_mood_event("Moon Amulette Insanity", /datum/mood_event/amulette_insanity) + user.add_mood_event("Moon Amulet Insanity", /datum/mood_event/amulet_insanity) user.mob_mood.set_sanity(user.mob_mood.sanity - 50) return if(hit.can_block_magic()) @@ -75,7 +75,7 @@ return if(hit.mob_mood.sanity_level < SANITY_LEVEL_UNSTABLE) user.balloon_alert(user, "their mind is too strong!") - hit.add_mood_event("Moon Amulette Insanity", /datum/mood_event/amulette_insanity) + hit.add_mood_event("Moon Amulet Insanity", /datum/mood_event/amulet_insanity) hit.mob_mood.set_sanity(hit.mob_mood.sanity - sanity_damage) else user.balloon_alert(user, "their mind bends to see the truth!") diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm index b708bf6584e9c..e2af5390ba810 100644 --- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm @@ -13,7 +13,7 @@ * Mark of Moon * Ritual of Knowledge * Lunar Parade - * Moonlight Amulette + * Moonlight Amulet * > Sidepaths: * Curse of Paralasys * Unfathomable Curio @@ -103,15 +103,15 @@ desc = "Grants you Lunar Parade, a spell that - after a short charge - sends a carnival forward \ when hitting someone they are forced to join the parade and suffer hallucinations." gain_text = "The music like a reflection of the soul compelled them, like moths to a flame they followed" - next_knowledge = list(/datum/heretic_knowledge/moon_amulette) + next_knowledge = list(/datum/heretic_knowledge/moon_amulet) spell_to_add = /datum/action/cooldown/spell/pointed/projectile/moon_parade cost = 1 route = PATH_MOON -/datum/heretic_knowledge/moon_amulette - name = "Moonlight Amulette" - desc = "Allows you to transmute 2 sheets of glass, a heart and a tie to create a Moonlight Amulette. \ +/datum/heretic_knowledge/moon_amulet + name = "Moonlight Amulet" + desc = "Allows you to transmute 2 sheets of glass, a heart and a tie to create a Moonlight Amulet. \ If the item is used on someone with low sanity they go berserk attacking everyone, \ if their sanity isn't low enough it decreases their mood." gain_text = "At the head of the parade he stood, the moon condensed into one mass, a reflection of the soul." @@ -127,7 +127,7 @@ /obj/item/stack/sheet/glass = 2, /obj/item/clothing/neck/tie = 1, ) - result_atoms = list(/obj/item/clothing/neck/heretic_focus/moon_amulette) + result_atoms = list(/obj/item/clothing/neck/heretic_focus/moon_amulet) cost = 1 route = PATH_MOON @@ -171,7 +171,7 @@ Bring 3 corpses with more than 50 brain damage to a transmutation rune to complete the ritual. \ When completed, you become a harbinger of madness gaining and aura of passive sanity decrease, \ confusion increase and, if their sanity is low enough, brain damage and blindness. \ - 1/5th of the crew will turn into acolytes and follow your command, they will all recieve moonlight amulettes." + 1/5th of the crew will turn into acolytes and follow your command, they will all receive moonlight amulets." gain_text = "We dived down towards the crowd, his soul splitting off in search of greater venture \ for where the Ringleader had started the parade, I shall continue it unto the suns demise \ WITNESS MY ASCENSION, THE MOON SMILES ONCE MORE AND FOREVER MORE IT SHALL!" @@ -229,7 +229,7 @@ continue var/datum/antagonist/lunatic/lunatic = crewmate.mind.add_antag_datum(/datum/antagonist/lunatic) lunatic.set_master(user.mind, user) - var/obj/item/clothing/neck/heretic_focus/moon_amulette/amulet = new(crewmate_turf) + var/obj/item/clothing/neck/heretic_focus/moon_amulet/amulet = new(crewmate_turf) var/static/list/slots = list( "neck" = ITEM_SLOT_NECK, "hands" = ITEM_SLOT_HANDS, diff --git a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm index a4810c706c118..f933bbfda1d26 100644 --- a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm +++ b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm @@ -25,7 +25,7 @@ gain_text = "The flesh of humanity is weak. Make them bleed. Show them their fragility." next_knowledge = list( /datum/heretic_knowledge/mad_mask, - /datum/heretic_knowledge/moon_amulette, + /datum/heretic_knowledge/moon_amulet, ) required_atoms = list( /obj/item/bodypart/leg/left = 1, diff --git a/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm b/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm index f1dd564310be5..5ab50a8b84f3c 100644 --- a/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm +++ b/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm @@ -23,7 +23,7 @@ gain_text = "The mansus holds many a curio, some are not meant for the mortal eye." next_knowledge = list( /datum/heretic_knowledge/spell/burglar_finesse, - /datum/heretic_knowledge/moon_amulette, + /datum/heretic_knowledge/moon_amulet, ) required_atoms = list( /obj/item/organ/internal/lungs = 1, @@ -48,7 +48,7 @@ They yearn for mortal eyes again, and I shall grant that wish." next_knowledge = list( /datum/heretic_knowledge/spell/burglar_finesse, - /datum/heretic_knowledge/moon_amulette, + /datum/heretic_knowledge/moon_amulet, ) required_atoms = list(/obj/item/canvas = 1) result_atoms = list(/obj/item/canvas) diff --git a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm index 453b030611bdd..671e01603c5c7 100644 --- a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm +++ b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm @@ -20,7 +20,7 @@ /datum/heretic_knowledge/entropy_pulse name = "Pulse of Entropy" - desc = "Allows you to transmute 10 iron sheets to fill the surrounding vicinity of the rune with rust." + desc = "Allows you to transmute 10 iron sheets and a garbage item to fill the surrounding vicinity of the rune with rust." gain_text = "Reality begins to whisper to me. To give it its entropic end." required_atoms = list( /obj/item/stack/sheet/iron = 10, diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 72cb8982d3436..665834d4085a9 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -22,6 +22,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations) /datum/gateway_destination/proc/get_available_reason() . = "Unreachable" if(world.time - SSticker.round_start_time < wait) + playsound(src, 'sound/effects/gateway_calibrating.ogg', 80, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) . = "Connection desynchronized. Recalibration in progress." /* Check if the movable is allowed to arrive at this destination (exile implants mostly) */ @@ -134,6 +135,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations) /obj/effect/gateway_portal_bumper/Bumped(atom/movable/AM) if(get_dir(src,AM) == SOUTH) + playsound(src, 'sound/effects/gateway_travel.ogg', 70, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) gateway.Transfer(AM) /obj/effect/gateway_portal_bumper/Destroy(force) @@ -198,6 +200,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations) /obj/machinery/gateway/proc/deactivate() var/datum/gateway_destination/dest = target target = null + playsound(src, 'sound/effects/gateway_close.ogg', 140, TRUE, TRUE, SOUND_RANGE) dest.deactivate(src) QDEL_NULL(portal) update_use_power(IDLE_POWER_USE) @@ -260,6 +263,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations) target.activate(destination) portal_visuals.setup_visuals(target) transport_active = TRUE + playsound(src, 'sound/effects/gateway_open.ogg', 140, TRUE, TRUE, SOUND_RANGE) generate_bumper() update_use_power(ACTIVE_POWER_USE) update_appearance() @@ -302,6 +306,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations) if(calibrated) to_chat(user, span_alert("The gate is already calibrated, there is no work for you to do here.")) else + playsound(src, 'sound/effects/gateway_calibrated.ogg', 80, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) to_chat(user, "[span_boldnotice("Recalibration successful!")]: \black This gate's systems have been fine tuned. Travel to this gate will now be on target.") calibrated = TRUE return TRUE diff --git a/code/modules/bitrunning/antagonists/cyber_tac.dm b/code/modules/bitrunning/antagonists/cyber_tac.dm index 26ad05081e89d..a45fdb345d304 100644 --- a/code/modules/bitrunning/antagonists/cyber_tac.dm +++ b/code/modules/bitrunning/antagonists/cyber_tac.dm @@ -29,80 +29,3 @@ var/obj/item/implant/weapons_auth/auth = new(user) auth.implant(user) - -/obj/item/mod/control/pre_equipped/glitch - theme = /datum/mod_theme/glitch - applied_cell = /obj/item/stock_parts/cell/bluespace - applied_modules = list( - /obj/item/mod/module/storage, - /obj/item/mod/module/magnetic_harness, - /obj/item/mod/module/jetpack/advanced, - /obj/item/mod/module/jump_jet, - /obj/item/mod/module/flashlight, - ) - default_pins = list( - /obj/item/mod/module/armor_booster, - /obj/item/mod/module/jetpack/advanced, - /obj/item/mod/module/jump_jet, - ) - starting_frequency = null - -/datum/armor/mod_theme_glitch - melee = 15 - bullet = 20 - laser = 35 - bomb = 65 - bio = 100 - fire = 100 - acid = 100 - wound = 100 - -/datum/mod_theme/glitch - name = "glitch" - desc = "A modsuit outfitted for elite Cyber Authority units to track, capture, and eliminate organic intruders." - extended_desc = "The Cyber Authority function as a digital police force, patrolling the digital realm and enforcing the law. Cyber Tac units are the elite of the elite, outfitted with lethal weaponry and fast mobility specially designed to quell organic uprisings." - default_skin = "glitch" - armor_type = /datum/armor/mod_theme_glitch - resistance_flags = FIRE_PROOF|ACID_PROOF - atom_flags = PREVENT_CONTENTS_EXPLOSION_1 - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - complexity_max = DEFAULT_MAX_COMPLEXITY + 3 - siemens_coefficient = 0 - slowdown_inactive = 1 - slowdown_active = 0.5 - ui_theme = "terminal" - inbuilt_modules = list(/obj/item/mod/module/armor_booster) - allowed_suit_storage = list( - /obj/item/ammo_box, - /obj/item/ammo_casing, - /obj/item/restraints/handcuffs, - /obj/item/assembly/flash, - ) - skins = list( - "glitch" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, - UNSEALED_CLOTHING = SNUG_FIT, - SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, - UNSEALED_INVISIBILITY = HIDEFACIALHAIR, - SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, - ), - CHESTPLATE_FLAGS = list( - UNSEALED_CLOTHING = THICKMATERIAL, - SEALED_CLOTHING = STOPSPRESSUREDAMAGE, - SEALED_INVISIBILITY = HIDEJUMPSUIT, - ), - GAUNTLETS_FLAGS = list( - UNSEALED_CLOTHING = THICKMATERIAL, - SEALED_CLOTHING = STOPSPRESSUREDAMAGE, - CAN_OVERSLOT = TRUE, - ), - BOOTS_FLAGS = list( - UNSEALED_CLOTHING = THICKMATERIAL, - SEALED_CLOTHING = STOPSPRESSUREDAMAGE, - CAN_OVERSLOT = TRUE, - ), - ), - ) - diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index d13f025e0b138..e9ec547755d5e 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -480,7 +480,7 @@ BLIND // can't see anything visor_toggling() - to_chat(user, span_notice("You adjust [src] [up ? "up" : "down"].")) + to_chat(user, span_notice("You push [src] [up ? "out of the way" : "back into place"].")) update_item_action_buttons() diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index 8b80a28dd4af1..fd8512c3eb83f 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -340,7 +340,7 @@ /obj/item/clothing/under/CtrlClick(mob/user) . = ..() - if(!.) + if(.) return if(!can_toggle_sensors(user)) return diff --git a/code/modules/deathmatch/deathmatch_lobby.dm b/code/modules/deathmatch/deathmatch_lobby.dm index 1f4678ba1685d..918808126c69c 100644 --- a/code/modules/deathmatch/deathmatch_lobby.dm +++ b/code/modules/deathmatch/deathmatch_lobby.dm @@ -363,8 +363,8 @@ data["map"]["min_players"] = map.min_players data["map"]["max_players"] = map.max_players - data["mod_menu_open"] = FALSE - data["modifiers"] = has_auth ? list() : get_modifier_list(is_host, mod_menu_open) + data["mod_menu_open"] = mod_menu_open + data["modifiers"] = has_auth ? get_modifier_list(is_host, mod_menu_open) : list() data["observers"] = get_observer_list() data["players"] = get_player_list() data["playing"] = playing diff --git a/code/modules/deathmatch/deathmatch_modifier.dm b/code/modules/deathmatch/deathmatch_modifier.dm index 127700b734f1e..ec5c20cf919b1 100644 --- a/code/modules/deathmatch/deathmatch_modifier.dm +++ b/code/modules/deathmatch/deathmatch_modifier.dm @@ -544,3 +544,13 @@ /datum/deathmatch_modifier/hear_global_chat/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby) player.add_traits(list(TRAIT_SIXTHSENSE, TRAIT_XRAY_HEARING), DEATHMATCH_TRAIT) + +/datum/deathmatch_modifier/apply_quirks + name = "Quirks enabled" + description = "Applies selected quirks to all players" + +/datum/deathmatch_modifier/apply_quirks/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby) + if (!player.client) + return + + SSquirks.AssignQuirks(player, player.client) diff --git a/code/modules/events/immovable_rod/immovable_rod.dm b/code/modules/events/immovable_rod/immovable_rod.dm index e9d2995218d56..e1f0ada0e600c 100644 --- a/code/modules/events/immovable_rod/immovable_rod.dm +++ b/code/modules/events/immovable_rod/immovable_rod.dm @@ -247,10 +247,16 @@ strongman.client?.give_award(/datum/award/achievement/jobs/feat_of_strength, strongman) strongman.visible_message( span_boldwarning("[strongman] suplexes [src] into the ground!"), - span_warning("You suplex [src] into the ground!") + span_warning("As you suplex [src] into the ground, your body ripples with power!") ) new /obj/structure/festivus/anchored(drop_location()) new /obj/effect/anomaly/flux(drop_location()) + + var/is_heavy_gravity = strongman.has_gravity() > STANDARD_GRAVITY //If for some reason you have to suplex the rod in heavy gravity, you get the double experience here as well, why not + var/experience_gained = 100 * num_sentient_mobs_hit * (is_heavy_gravity ? 2 : 1) // We gain more expeirence the more sentient mobs the rod has taken out. The deadlier the rod, the stronger we become. At 25 sentient mobs, we instantly become a legendary athlete. + strongman.mind?.adjust_experience(/datum/skill/athletics, experience_gained) + strongman.apply_status_effect(/datum/status_effect/exercised) //time for a nap, you earned it + qdel(src) return TRUE diff --git a/code/modules/food_and_drinks/machinery/icecream_vat.dm b/code/modules/food_and_drinks/machinery/icecream_vat.dm index cae1b26024933..eba5ff63f3f8c 100644 --- a/code/modules/food_and_drinks/machinery/icecream_vat.dm +++ b/code/modules/food_and_drinks/machinery/icecream_vat.dm @@ -110,26 +110,27 @@ . = ..() if(.) return - if(!beaker || !istype(beaker) || !beaker.reagents || (beaker.item_flags & ABSTRACT) || !beaker.is_open_container()) + if(!istype(beaker) || !beaker.reagents || (beaker.item_flags & ABSTRACT) || !beaker.is_open_container()) return if(custom_ice_cream_beaker) - if(beaker.forceMove(src)) + if(user.transferItemToLoc(beaker, src)) try_put_in_hand(custom_ice_cream_beaker, user) balloon_alert(user, "beakers swapped") custom_ice_cream_beaker = beaker else balloon_alert(user, "beaker slot full!") return - if(beaker.forceMove(src)) - balloon_alert(user, "beaker inserted") - custom_ice_cream_beaker = beaker + if(!user.transferItemToLoc(beaker, src)) + return + balloon_alert(user, "beaker inserted") + custom_ice_cream_beaker = beaker /obj/machinery/icecream_vat/attackby_secondary(obj/item/reagent_containers/beaker, mob/user, params) . = ..() if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN) return - if(!beaker || !istype(beaker) || !beaker.reagents || (beaker.item_flags & ABSTRACT) || !beaker.is_open_container()) + if(!istype(beaker) || !beaker.reagents || (beaker.item_flags & ABSTRACT) || !beaker.is_open_container()) return SECONDARY_ATTACK_CONTINUE_CHAIN var/added_reagents = FALSE for(var/datum/reagent/beaker_reagents in beaker.reagents.reagent_list) @@ -211,9 +212,10 @@ return ice_cream_icon /obj/machinery/icecream_vat/on_deconstruction(disassembled = TRUE) - new /obj/item/stack/sheet/iron(loc, 4) - if(custom_ice_cream_beaker) - custom_ice_cream_beaker.forceMove(loc) + var/atom/drop_location = drop_location() + + new /obj/item/stack/sheet/iron(drop_location, 4) + custom_ice_cream_beaker?.forceMove(drop_location) ///Makes an ice cream cone of the make_type, using ingredients list as reagents used to make it. Puts in user's hand if possible. /obj/machinery/icecream_vat/proc/make_cone(mob/user, make_type, list/ingredients) diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm index dd269bc7e3736..008ddd6df0db5 100644 --- a/code/modules/jobs/job_types/cargo_technician.dm +++ b/code/modules/jobs/job_types/cargo_technician.dm @@ -54,3 +54,4 @@ name = "Cargo Technician (MODsuit)" back = /obj/item/mod/control/pre_equipped/loader + suit = null diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm index e20ef7c19edd9..3f580b8e932a5 100644 --- a/code/modules/jobs/job_types/chief_medical_officer.dm +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -56,7 +56,7 @@ id = /obj/item/card/id/advanced/silver id_trim = /datum/id_trim/job/chief_medical_officer - uniform = /obj/item/clothing/under/rank/medical/chief_medical_officer/scrubs + uniform = /obj/item/clothing/under/rank/medical/chief_medical_officer suit = /obj/item/clothing/suit/toggle/labcoat/cmo suit_store = /obj/item/flashlight/pen/paramedic backpack_contents = list( @@ -64,7 +64,6 @@ ) belt = /obj/item/modular_computer/pda/heads/cmo ears = /obj/item/radio/headset/heads/cmo - head = /obj/item/clothing/head/utility/surgerycap/cmo shoes = /obj/item/clothing/shoes/sneakers/blue l_pocket = /obj/item/laser_pointer/blue r_pocket = /obj/item/pinpointer/crew diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm index a0f2971df2306..ccfcbae1623e6 100644 --- a/code/modules/jobs/job_types/medical_doctor.dm +++ b/code/modules/jobs/job_types/medical_doctor.dm @@ -44,12 +44,11 @@ jobtype = /datum/job/doctor id_trim = /datum/id_trim/job/medical_doctor - uniform = /obj/item/clothing/under/rank/medical/scrubs/blue + uniform = /obj/item/clothing/under/rank/medical/doctor suit = /obj/item/clothing/suit/toggle/labcoat suit_store = /obj/item/flashlight/pen belt = /obj/item/modular_computer/pda/medical ears = /obj/item/radio/headset/headset_med - head = /obj/item/clothing/head/utility/surgerycap shoes = /obj/item/clothing/shoes/sneakers/white l_hand = /obj/item/storage/medkit/surgery diff --git a/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm b/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm index b889073909d61..bff83423be73e 100644 --- a/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm +++ b/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm @@ -1,12 +1,12 @@ /obj/item/skillchip/research_director name = "R.D.S.P.L.X. skillchip" desc = "Knowledge of how to solve the ancient conumdrum; what happens when an unstoppable force meets an immovable object." - auto_traits = list(TRAIT_ROD_SUPLEX) + auto_traits = list(TRAIT_ROD_SUPLEX, TRAIT_STRENGTH) skill_name = "True Strength" skill_description = "The knowledge and strength to resolve the most ancient conumdrum; what happens when an unstoppable force meets an immovable object." skill_icon = "dumbbell" - activate_message = "You realise if you apply the correct force, at the correct angle, it is possible to make the immovable permanently movable." - deactivate_message = "You forget how to permanently anchor a paradoxical object." + activate_message = "You realise if you apply the correct force, at the correct angle, it is possible to make the immovable permanently movable. And... damn, you look huge." + deactivate_message = "You forget how to permanently anchor a paradoxical object. Also, you should really hit the gym..." chip_category = SKILLCHIP_CATEGORY_GENERAL skillchip_flags = NONE slot_use = 1 diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm index 55c1e6426b360..3f678da6910bc 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm @@ -7,6 +7,7 @@ icon_state = "legion_remains" zone = BODY_ZONE_CHEST slot = ORGAN_SLOT_PARASITE_EGG + organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE | ORGAN_VIRGIN | ORGAN_PROMINENT decay_factor = STANDARD_ORGAN_DECAY * 3 // About 5 minutes outside of a host /// What stage of growth the corruption has reached. var/stage = 0 @@ -55,6 +56,10 @@ stage = 0 elapsed_time = 0 +/obj/item/organ/internal/legion_tumour/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) + . = ..() + owner.log_message("has received [src] which will eventually turn them into a Legion.", LOG_VICTIM) + /obj/item/organ/internal/legion_tumour/attack(mob/living/target, mob/living/user, params) if (try_apply(target, user)) qdel(src) @@ -79,6 +84,7 @@ if (!ishuman(target)) return FALSE + log_combat(user, target, "used a Legion Tumour on", src, "as they are in crit, this will turn them into a Legion.") target.visible_message(span_boldwarning("[user] splatters [target] with [src]... and it springs into horrible life!")) var/mob/living/basic/legion_brood/skull = new(target.loc) skull.melee_attack(target) @@ -143,6 +149,7 @@ /obj/item/organ/internal/legion_tumour/proc/infest() if (QDELETED(src) || QDELETED(owner)) return + owner.log_message("has been turned into a Legion by their tumour.", LOG_VICTIM) owner.visible_message(span_boldwarning("Black tendrils burst from [owner]'s flesh, covering them in amorphous flesh!")) var/mob/living/basic/mining/legion/new_legion = new spawn_type(owner.loc) new_legion.consume(owner) diff --git a/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm index d85ad69682c7d..8559fc82b6802 100644 --- a/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm +++ b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm @@ -17,8 +17,8 @@ icon_living = "mining_node_active" icon_dead = "mining_node_active" - maxHealth = 500 - health = 500 + maxHealth = 300 // We adjust the max health based on the vent size in the arrive() proc. + health = 300 density = TRUE pass_flags = PASSTABLE|PASSGRILLE|PASSMOB mob_size = MOB_SIZE_LARGE @@ -74,6 +74,8 @@ /mob/living/basic/node_drone/proc/arrive(obj/structure/ore_vent/parent_vent) attached_vent = parent_vent + maxHealth = 300 + ((attached_vent.boulder_size/BOULDER_SIZE_SMALL) * 100) + health = maxHealth flying_state = FLY_IN_STATE update_appearance(UPDATE_ICON_STATE) pixel_z = 400 diff --git a/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm b/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm index 78c4e9c61ea9c..d79ae5582f696 100644 --- a/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm +++ b/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm @@ -21,7 +21,7 @@ GLOBAL_LIST_EMPTY(raptor_population) /mob/living/basic/mining/raptor name = "raptor" - desc = "A trusty powerful stead. Taming it might prove difficult..." + desc = "A trusty, powerful steed. Taming it might prove difficult..." icon = 'icons/mob/simple/lavaland/raptor_big.dmi' speed = 2 mob_biotypes = MOB_ORGANIC|MOB_BEAST @@ -202,7 +202,7 @@ GLOBAL_LIST_EMPTY(raptor_population) melee_damage_upper = 20 raptor_color = RAPTOR_RED dex_description = "A resilient breed of raptors, battle-tested and bred for the purpose of humbling its foes in combat, \ - This breed demonstrates higher combat capabilities than its peers and oozes rutheless aggression." + This breed demonstrates higher combat capabilities than its peers and oozes ruthless aggression." child_path = /mob/living/basic/mining/raptor/baby_raptor/red /mob/living/basic/mining/raptor/purple @@ -231,7 +231,7 @@ GLOBAL_LIST_EMPTY(raptor_population) health = 460 raptor_color = RAPTOR_GREEN dex_description = "A tough breed of raptor, made to withstand the harshest of punishment and to laugh in the face of pain, \ - This breed is able to withstand more beating than its peers." + this breed is able to withstand more punishment than its peers." child_path = /mob/living/basic/mining/raptor/baby_raptor/green /mob/living/basic/mining/raptor/green/Initialize(mapload) diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm index 40c2d836554b1..323f8422d2fd0 100644 --- a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm @@ -2,6 +2,12 @@ /datum/ai_controller/basic_controller/raptor blackboard = list( + BB_INTERACTIONS_WITH_OWNER = list( + "Pecks", + "Nuzzles", + "Wags tail against", + "Playfully leans against" + ), BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/raptor, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/raptor, BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/mining/raptor), @@ -25,6 +31,7 @@ /datum/ai_planning_subtree/make_babies, /datum/ai_planning_subtree/find_and_hunt_target/raptor_start_trouble, /datum/ai_planning_subtree/express_happiness, + /datum/ai_planning_subtree/find_and_hunt_target/play_with_owner/raptor, ) /datum/ai_controller/basic_controller/raptor/TryPossessPawn(atom/new_pawn) @@ -39,7 +46,7 @@ /datum/targeting_strategy/basic/raptor -//dont attack anyone with the neutral faction. +//dont attack anyone with the neutral faction. /datum/targeting_strategy/basic/raptor/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) return (the_target.faction.Find(FACTION_NEUTRAL) || the_target.faction.Find(FACTION_RAPTOR)) diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm index 2d23268d35c81..9dcb360c65d82 100644 --- a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm @@ -60,3 +60,7 @@ return return ..() +/datum/ai_planning_subtree/find_and_hunt_target/play_with_owner/raptor/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + if(!controller.blackboard[BB_RAPTOR_PLAYFUL]) + return + return ..() diff --git a/code/modules/mob/living/basic/slime/ai/behaviours.dm b/code/modules/mob/living/basic/slime/ai/behaviours.dm index e573bd57354f7..1cd4677994531 100644 --- a/code/modules/mob/living/basic/slime/ai/behaviours.dm +++ b/code/modules/mob/living/basic/slime/ai/behaviours.dm @@ -32,6 +32,10 @@ if(REF(dinner) in hunter.faction) //Don't eat our friends... return + var/static/list/slime_faction = list(FACTION_SLIME) + if(faction_check(slime_faction, dinner.faction)) //Don't try to eat slimy things, no matter how hungry we are. Anyone else can be betrayed. + return + if(!hunter.can_feed_on(dinner, check_adjacent = FALSE)) //Are they tasty to slimes? return diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm index ee2073987dae2..816bb7cd838e1 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm @@ -20,7 +20,6 @@ icon_gib = "carp_gib" gold_core_spawnable = HOSTILE_SPAWN mob_biotypes = MOB_ORGANIC | MOB_BEAST - movement_type = FLYING health = 25 maxHealth = 25 pressure_resistance = 200 diff --git a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm index bbefe37606b96..be31e121249a6 100644 --- a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm +++ b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm @@ -28,7 +28,6 @@ damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0.5, OXY = 1) combat_mode = TRUE speed = 0 - movement_type = FLYING attack_verb_continuous = "chomps" attack_verb_simple = "chomp" attack_sound = 'sound/magic/demon_attack1.ogg' diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 24a35e683e36d..82d51c173cb05 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -1839,7 +1839,7 @@ GLOBAL_LIST_EMPTY(features_by_species) SPECIES_PERK_TYPE = SPECIES_NEUTRAL_PERK, SPECIES_PERK_ICON = "tint", SPECIES_PERK_NAME = initial(exotic_blood.name), - SPECIES_PERK_DESC = "[name] blood is [initial(exotic_blood.name)], which can make recieving medical treatment harder.", + SPECIES_PERK_DESC = "[name] blood is [initial(exotic_blood.name)], which can make receiving medical treatment harder.", )) // Otherwise otherwise, see if they have an exotic bloodtype set @@ -1848,7 +1848,7 @@ GLOBAL_LIST_EMPTY(features_by_species) SPECIES_PERK_TYPE = SPECIES_NEUTRAL_PERK, SPECIES_PERK_ICON = "tint", SPECIES_PERK_NAME = "Exotic Blood", - SPECIES_PERK_DESC = "[plural_form] have \"[exotic_bloodtype]\" type blood, which can make recieving medical treatment harder.", + SPECIES_PERK_DESC = "[plural_form] have \"[exotic_bloodtype]\" type blood, which can make receiving medical treatment harder.", )) return to_add diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index e2ed47300cc14..9ba754569c108 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -335,6 +335,8 @@ fitness_modifier *= 2 if (HAS_TRAIT(src, TRAIT_STRENGTH)) fitness_modifier *= 1.5 + if (HAS_TRAIT(src, TRAIT_ROD_SUPLEX)) + fitness_modifier *= 2 // To be able to suplex a rod, you must possess an incredible amount of power if (HAS_TRAIT(src, TRAIT_EASILY_WOUNDED)) fitness_modifier /= 2 if (HAS_TRAIT(src, TRAIT_GAMER)) diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index 1e8cf9def9463..4866501584a96 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -228,7 +228,7 @@ SPECIES_PERK_ICON = FA_ICON_PERSON_FALLING, SPECIES_PERK_NAME = "Catlike Grace", SPECIES_PERK_DESC = "Felinids have catlike instincts allowing them to land upright on their feet. \ - Instead of being knocked down from falling, you only recieve a short slowdown. \ + Instead of being knocked down from falling, you only receive a short slowdown. \ However, they do not have catlike legs, and the fall will deal additional damage.", ), list( diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 570cf1ec30458..395c4f343dc3d 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -663,13 +663,15 @@ return copytext(sanitize(input("Choose an emote to display.") as text|null), 1, MAX_MESSAGE_LEN) /datum/emote/living/custom/proc/get_custom_emote_type_from_user() - var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable") + var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable", "Both") switch(type) if("Visible") return EMOTE_VISIBLE if("Hearable") return EMOTE_AUDIBLE + if("Both") + return EMOTE_VISIBLE | EMOTE_AUDIBLE else tgui_alert(usr,"Unable to use this emote, must be either hearable or visible.") return FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 4c6605cd8393d..37649ceb3c5cf 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -29,7 +29,6 @@ Difficulty: Medium health_doll_icon = "miner" mob_biotypes = MOB_ORGANIC|MOB_HUMANOID light_color = COLOR_LIGHT_GRAYISH_RED - movement_type = GROUND speak_emote = list("roars") speed = 3 move_to_delay = 3 diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm index e2a840d569b64..7caa4489095ff 100644 --- a/code/modules/mob/mob_say.dm +++ b/code/modules/mob/mob_say.dm @@ -42,6 +42,7 @@ /mob/verb/me_verb(message as text) set name = "Me" set category = "IC" + set desc = "Perform a custom emote. Leave blank to pick between an audible or a visible emote (Defaults to visible)." if(GLOB.say_disabled) //This is here to try to identify lag problems to_chat(usr, span_danger("Speech is currently admin-disabled.")) @@ -49,7 +50,7 @@ message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN)) - QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "me", 1, message, TRUE), SSspeech_controller) + QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "me", EMOTE_VISIBLE|EMOTE_AUDIBLE, message, TRUE), SSspeech_controller) /mob/try_speak(message, ignore_spam = FALSE, forced = null, filterproof = FALSE) var/list/filter_result diff --git a/code/modules/mob_spawn/ghost_roles/mining_roles.dm b/code/modules/mob_spawn/ghost_roles/mining_roles.dm index 53fa001097039..5fb3337b911e7 100644 --- a/code/modules/mob_spawn/ghost_roles/mining_roles.dm +++ b/code/modules/mob_spawn/ghost_roles/mining_roles.dm @@ -34,7 +34,7 @@ if(3) flavour_text += "you were a doctor on one of Nanotrasen's space stations, but you left behind that damn corporation's tyranny and everything it stood for. From a metaphorical hell \ to a literal one, you find yourself nonetheless missing the recycled air and warm floors of what you left behind... but you'd still rather be here than there." - outfit.uniform = /obj/item/clothing/under/rank/medical/scrubs/blue + outfit.uniform = /obj/item/clothing/under/rank/medical/doctor outfit.suit = /obj/item/clothing/suit/toggle/labcoat outfit.back = /obj/item/storage/backpack/medic if(4) diff --git a/code/modules/mod/adding_new_mod.md b/code/modules/mod/adding_new_mod.md index b0bf12486c14a..8252822cf6c25 100644 --- a/code/modules/mod/adding_new_mod.md +++ b/code/modules/mod/adding_new_mod.md @@ -82,16 +82,15 @@ So, now that we have our theme, we want to add a skin to it (or another theme of armor_type = /datum/armor/modtheme_psychological complexity_max = DEFAULT_MAX_COMPLEXITY - 7 charge_drain = DEFAULT_CHARGE_DRAIN * 0.5 - skins = list( + variants = list( "psychological" = list( - HELMET_LAYER = null, - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( ), ), ) @@ -101,8 +100,7 @@ We now have a psychological skin, this will apply the psychological icons to eve For example, if our helmet's icon covers the full head (like the research skin), we want to do something like this. ```dm - HELMET_LAYER = null, - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, @@ -113,8 +111,8 @@ For example, if our helmet's icon covers the full head (like the research skin), Otherwise, with an open helmet that becomes closed (like the engineering skin), we'd do this. ```dm - HELMET_LAYER = NECK_LAYER, - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( + UNSEALED_LAYER = NECK_LAYER UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, @@ -137,47 +135,46 @@ There are specific cases of helmets that semi-cover the head, like the cosmohonk armor_type = /datum/armor/modtheme_psychological complexity_max = DEFAULT_MAX_COMPLEXITY - 7 charge_drain = DEFAULT_CHARGE_DRAIN * 0.5 - skins = list( + variants = list( "psychological" = list( - HELMET_LAYER = NECK_LAYER, - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( + UNSEALED_LAYER = NECK_LAYER UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, ), "psychotherapeutic" = list( - HELMET_LAYER = null, - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, ), @@ -207,7 +204,7 @@ As we want this effect to be on demand, we probably want this to be an usable mo - Usable: You can use these for a one time effect. - Active: You can only have one selected at a time. It gives you a special click effect. -As we have an usable module, we want to set a cooldown time. All modules are also incompatible with themselves, have a specific power cost and complexity varying on how powerful they are, so let's update our definition, and also add a new variable for how much brain damage we'll heal. +As we have an usable module, we want to set a cooldown time. All modules are also incompatible with themselves, have a specific power cost and complexity varying on how powerful they are, and are equippable to certain slots, so let's update our definition, and also add a new variable for how much brain damage we'll heal. ```dm /obj/item/mod/module/neuron_healer @@ -220,25 +217,20 @@ As we have an usable module, we want to set a cooldown time. All modules are als use_energy_cost = DEFAULT_CHARGE_DRAIN incompatible_modules = list(/obj/item/mod/module/neuron_healer) cooldown_time = 15 SECONDS + required_slot = list(ITEM_SLOT_HEAD) var/brain_damage_healed = 25 ``` -Now, we want to override the on_use proc for our new effect. We want to make sure the use checks passed from parent. You can read about most procs and variables by reading [this](modules/_module.dm) +Now, we want to override the on_use proc for our new effect. You can read about most procs and variables by reading [this](modules/_module.dm) ```dm /obj/item/mod/module/neuron_healer/on_use() - . = ..() - if(!.) - return ``` After this, we want to put our special code, a basic effect of healing all mobs nearby for their brain damage and creating a beam to them. ```dm /obj/item/mod/module/neuron_healer/on_use() - . = ..() - if(!.) - return for(var/mob/living/carbon/carbon_mob in range(5, src)) if(carbon_mob == mod.wearer) continue @@ -272,47 +264,46 @@ Now we want to add it to the psychological theme, which is very simple, finishin complexity_max = DEFAULT_MAX_COMPLEXITY - 7 charge_drain = DEFAULT_CHARGE_DRAIN * 0.5 inbuilt_modules = list(/obj/item/mod/module/neuron_healer/advanced) - skins = list( + variants = list( "psychological" = list( - HELMET_LAYER = NECK_LAYER, - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( + UNSEALED_LAYER = NECK_LAYER UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, ), "psychotherapeutic" = list( - HELMET_LAYER = null, - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, ), diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm index 0d150c15fe61a..237e151bcb2c0 100644 --- a/code/modules/mod/mod_activation.dm +++ b/code/modules/mod/mod_activation.dm @@ -6,7 +6,8 @@ return var/list/display_names = list() var/list/items = list() - for(var/obj/item/part as anything in mod_parts) + var/list/parts = get_parts() + for(var/obj/item/part as anything in parts) display_names[part.name] = REF(part) var/image/part_image = image(icon = part.icon, icon_state = part.icon_state) if(part.loc != src) @@ -16,17 +17,17 @@ if(!pick) return var/part_reference = display_names[pick] - var/obj/item/part = locate(part_reference) in mod_parts + var/obj/item/part = locate(part_reference) in parts if(!istype(part) || user.incapacitated()) return if(active || activating) balloon_alert(user, "deactivate the suit first!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return - var/parts_to_check = mod_parts - part + var/parts_to_check = parts - part if(part.loc == src) deploy(user, part) - on_mod_deployed(user) + SEND_SIGNAL(src, COMSIG_MOD_DEPLOYED, user) for(var/obj/item/checking_part as anything in parts_to_check) if(checking_part.loc != src) continue @@ -34,7 +35,7 @@ break else retract(user, part) - on_mod_retracted(user) + SEND_SIGNAL(src, COMSIG_MOD_RETRACTED, user) for(var/obj/item/checking_part as anything in parts_to_check) if(checking_part.loc == src) continue @@ -48,28 +49,29 @@ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE var/deploy = TRUE - 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 deploy = FALSE break - for(var/obj/item/part as anything in mod_parts) + for(var/obj/item/part as anything in get_parts()) if(deploy && part.loc == src) deploy(null, part) else if(!deploy && part.loc != src) retract(null, part) - wearer.visible_message(span_notice("[wearer]'s [src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss."), - span_notice("[src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss."), + wearer.visible_message(span_notice("[wearer]'s [src] [deploy ? "deploys" : "retracts"] its parts with a mechanical hiss."), + span_notice("[src] [deploy ? "deploys" : "retracts"] its parts with a mechanical hiss."), span_hear("You hear a mechanical hiss.")) playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) if(deploy) - on_mod_deployed(user) + SEND_SIGNAL(src, COMSIG_MOD_DEPLOYED, user) else - on_mod_retracted(user) + SEND_SIGNAL(src, COMSIG_MOD_RETRACTED, user) return TRUE /// Deploys a part of the suit onto the user. /obj/item/mod/control/proc/deploy(mob/user, obj/item/part) + var/datum/mod_part/part_datum = get_part_datum(part) if(!wearer) playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE // pAI is trying to deploy it from your hands @@ -78,10 +80,10 @@ return FALSE balloon_alert(user, "[part.name] already deployed!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) - if(part in overslotting_parts) + if(part_datum.can_overslot) var/obj/item/overslot = wearer.get_item_by_slot(part.slot_flags) if(overslot) - overslotting_parts[part] = overslot + part_datum.overslotting = overslot wearer.transferItemToLoc(overslot, part, force = TRUE) RegisterSignal(part, COMSIG_ATOM_EXITED, PROC_REF(on_overslot_exit)) if(wearer.equip_to_slot_if_possible(part, part.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) @@ -92,6 +94,7 @@ span_notice("[part] deploy[part.p_s()] with a mechanical hiss."), span_hear("You hear a mechanical hiss.")) playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + SEND_SIGNAL(src, COMSIG_MOD_PART_DEPLOYED, user, part) return TRUE else if(!user) @@ -102,6 +105,7 @@ /// Retract a part of the suit from the user. /obj/item/mod/control/proc/retract(mob/user, obj/item/part) + var/datum/mod_part/part_datum = get_part_datum(part) if(part.loc == src) if(!user) return FALSE @@ -109,12 +113,13 @@ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) REMOVE_TRAIT(part, TRAIT_NODROP, MOD_TRAIT) wearer.transferItemToLoc(part, src, force = TRUE) - if(overslotting_parts[part]) + if(part_datum.overslotting) UnregisterSignal(part, COMSIG_ATOM_EXITED) - var/obj/item/overslot = overslotting_parts[part] - if(!wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) + var/obj/item/overslot = part_datum.overslotting + if(!QDELING(wearer) && !wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) wearer.dropItemToGround(overslot, force = TRUE, silent = TRUE) - overslotting_parts[part] = null + part_datum.overslotting = null + SEND_SIGNAL(src, COMSIG_MOD_PART_RETRACTED, user, part) if(!user) return wearer.visible_message(span_notice("[wearer]'s [part.name] retract[part.p_s()] back into [src] with a mechanical hiss."), @@ -122,7 +127,7 @@ span_hear("You hear a mechanical hiss.")) playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) -/// Starts the activation sequence, where parts of the suit activate one by one until the whole suit is on +/// Starts the activation sequence, where parts of the suit activate one by one until the whole suit is on. /obj/item/mod/control/proc/toggle_activate(mob/user, force_deactivate = FALSE) if(!wearer) if(!force_deactivate) @@ -132,7 +137,7 @@ if(!force_deactivate && (SEND_SIGNAL(src, COMSIG_MOD_ACTIVATE, user) & MOD_CANCEL_ACTIVATE)) playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE - for(var/obj/item/part as anything in mod_parts) + for(var/obj/item/part as anything in get_parts()) if(!force_deactivate && part.loc == src) balloon_alert(user, "deploy all parts first!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) @@ -157,33 +162,21 @@ for(var/obj/item/mod/module/module as anything in modules) if(!module.active || (module.allow_flags & MODULE_ALLOW_INACTIVE)) continue - module.on_deactivation(display_message = FALSE) - mod_link.end_call() + module.deactivate(display_message = FALSE) activating = TRUE + mod_link.end_call() to_chat(wearer, span_notice("MODsuit [active ? "shutting down" : "starting up"].")) - if (ai_assistant) - to_chat(ai_assistant, span_notice("MODsuit [active ? "shutting down" : "starting up"].")) - if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) - to_chat(wearer, span_notice("[boots] [active ? "relax their grip on your legs" : "seal around your feet"].")) - playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - seal_part(boots, seal = !active) - if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) - to_chat(wearer, span_notice("[gauntlets] [active ? "become loose around your fingers" : "tighten around your fingers and wrists"].")) - playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - seal_part(gauntlets, seal = !active) - if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) - to_chat(wearer, span_notice("[chestplate] [active ? "releases your chest" : "cinches tightly against your chest"].")) - playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - seal_part(chestplate, seal = !active) - if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) - to_chat(wearer, span_notice("[helmet] hisses [active ? "open" : "closed"].")) - playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - seal_part(helmet, seal = !active) - if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(has_wearer)), hidden = TRUE)) + for(var/obj/item/part as anything in get_parts()) + var/datum/mod_part/part_datum = get_part_datum(part) + if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(get_wearer)), hidden = TRUE)) + to_chat(wearer, span_notice("[part] [active ? part_datum.unsealed_message : part_datum.sealed_message].")) + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + seal_part(part, is_sealed = !active) + if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(get_wearer)), hidden = TRUE)) to_chat(wearer, span_notice("Systems [active ? "shut down. Parts unsealed. Goodbye" : "started up. Parts sealed. Welcome"], [wearer].")) if(ai_assistant) to_chat(ai_assistant, span_notice("SYSTEMS [active ? "DEACTIVATED. GOODBYE" : "ACTIVATED. WELCOME"]: \"[ai_assistant]\"")) - finish_activation(on = !active) + finish_activation(is_on = !active) if(active) playsound(src, 'sound/machines/synth_yes.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000) if(!malfunctioning) @@ -194,16 +187,18 @@ SEND_SIGNAL(src, COMSIG_MOD_TOGGLED, user) return TRUE -///Seals or unseals the given part -/obj/item/mod/control/proc/seal_part(obj/item/clothing/part, seal) - if(seal) +///Seals or unseals the given part. +/obj/item/mod/control/proc/seal_part(obj/item/clothing/part, is_sealed) + var/datum/mod_part/part_datum = get_part_datum(part) + part_datum.sealed = is_sealed + if(part_datum.sealed) part.icon_state = "[skin]-[part.base_icon_state]-sealed" part.clothing_flags |= part.visor_flags part.flags_inv |= part.visor_flags_inv part.flags_cover |= part.visor_flags_cover part.heat_protection = initial(part.heat_protection) part.cold_protection = initial(part.cold_protection) - part.alternate_worn_layer = null + part.alternate_worn_layer = part_datum.sealed_layer else part.icon_state = "[skin]-[part.base_icon_state]" part.flags_cover &= ~part.visor_flags_cover @@ -211,15 +206,17 @@ part.clothing_flags &= ~part.visor_flags part.heat_protection = NONE part.cold_protection = NONE - part.alternate_worn_layer = mod_parts[part] + part.alternate_worn_layer = part_datum.unsealed_layer wearer.update_clothing(part.slot_flags) wearer.update_obscured_slots(part.visor_flags_inv) if((part.clothing_flags & (MASKINTERNALS|HEADINTERNALS)) && wearer.invalid_internals()) wearer.cutoff_internals() /// Finishes the suit's activation -/obj/item/mod/control/proc/finish_activation(on) - active = on +/obj/item/mod/control/proc/finish_activation(is_on) + var/datum/mod_part/part_datum = get_part_datum(src) + part_datum.sealed = is_on + active = is_on if(active) for(var/obj/item/mod/module/module as anything in modules) module.on_suit_activation() @@ -234,22 +231,13 @@ /// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits. /obj/item/mod/control/proc/quick_activation() var/seal = TRUE - for(var/obj/item/part as anything in mod_parts) + for(var/obj/item/part as anything in get_parts()) if(!deploy(null, part)) seal = FALSE if(!seal) return - for(var/obj/item/part as anything in mod_parts) - seal_part(part, seal = TRUE) - finish_activation(on = TRUE) - -/obj/item/mod/control/proc/has_wearer() - return wearer - -/obj/item/mod/control/proc/on_mod_deployed(mob/user) - SEND_SIGNAL(src, COMSIG_MOD_DEPLOYED, user) - -/obj/item/mod/control/proc/on_mod_retracted(mob/user) - SEND_SIGNAL(src, COMSIG_MOD_RETRACTED, user) + for(var/obj/item/part as anything in get_parts()) + seal_part(part, is_sealed = TRUE) + finish_activation(is_on = TRUE) #undef MOD_ACTIVATION_STEP_FLAGS diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index 1dd7fceae3dd2..4fd168cee044a 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -64,20 +64,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. @@ -102,43 +92,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)) @@ -151,44 +112,21 @@ 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) 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 @@ -245,11 +183,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) @@ -281,7 +218,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) @@ -290,7 +227,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) @@ -433,16 +370,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() @@ -450,7 +383,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) @@ -460,14 +393,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 @@ -487,17 +450,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() @@ -506,8 +472,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()) @@ -565,6 +530,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 @@ -587,7 +557,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) @@ -643,9 +613,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() @@ -653,52 +622,11 @@ 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 - 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 @@ -712,7 +640,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 @@ -723,27 +653,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 diff --git a/code/modules/mod/mod_paint.dm b/code/modules/mod/mod_paint.dm index 240c0897b33a1..1402a4aebc6bc 100644 --- a/code/modules/mod/mod_paint.dm +++ b/code/modules/mod/mod_paint.dm @@ -140,18 +140,18 @@ SStgui.close_uis(src) /obj/item/mod/paint/proc/paint_skin(obj/item/mod/control/mod, mob/user) - if(length(mod.theme.skins) <= 1) + if(length(mod.theme.variants) <= 1) balloon_alert(user, "no alternate skins!") return var/list/skins = list() - for(var/mod_skin_name in mod.theme.skins) - var/list/mod_skin = mod.theme.skins[mod_skin_name] + for(var/mod_skin_name in mod.theme.variants) + var/list/mod_skin = mod.theme.variants[mod_skin_name] skins[mod_skin_name] = image(icon = mod_skin[MOD_ICON_OVERRIDE] || mod.icon, icon_state = "[mod_skin_name]-control") var/pick = show_radial_menu(user, mod, skins, custom_check = CALLBACK(src, PROC_REF(check_menu), mod, user), require_near = TRUE) if(!pick) balloon_alert(user, "no skin picked!") return - mod.set_mod_skin(pick) + mod.theme.set_skin(pick) /obj/item/mod/paint/proc/check_menu(obj/item/mod/control/mod, mob/user) if(user.incapacitated() || !user.is_holding(src) || !mod || mod.active || mod.activating) @@ -171,7 +171,6 @@ icon = 'icons/obj/clothing/modsuit/mod_construction.dmi' icon_state = "skinapplier" var/skin = "civilian" - var/compatible_theme = /datum/mod_theme /obj/item/mod/skin_applier/Initialize(mapload) . = ..() @@ -184,14 +183,13 @@ if(mod.active || mod.activating) balloon_alert(user, "suit is active!") return TRUE - if(!istype(mod.theme, compatible_theme)) + if(!(skin in mod.theme.variants)) balloon_alert(user, "incompatible theme!") return TRUE - mod.set_mod_skin(skin) + mod.theme.set_skin(skin) balloon_alert(user, "skin applied") qdel(src) return TRUE /obj/item/mod/skin_applier/honkerative skin = "honkerative" - compatible_theme = /datum/mod_theme/syndicate diff --git a/code/modules/mod/mod_part.dm b/code/modules/mod/mod_part.dm new file mode 100644 index 0000000000000..88f8024628dc5 --- /dev/null +++ b/code/modules/mod/mod_part.dm @@ -0,0 +1,22 @@ +/// Datum to handle interactions between a MODsuit and its parts. +/datum/mod_part + /// The actual item we handle. + var/obj/item/part_item = null + /// Are we sealed? + var/sealed = FALSE + /// Message to user when unsealed. + var/unsealed_message + /// Message to user when sealed. + var/sealed_message + /// The layer the item will render on when unsealed. + var/unsealed_layer + /// The layer the item will render on when sealed. + var/sealed_layer + /// Can our part overslot over others? + var/can_overslot = FALSE + /// What are we overslotting over? + var/obj/item/overslotting = null + +/datum/mod_part/Destroy() + part_item = null + return ..() diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm index c4c8839bd82a6..ecfa570dde160 100644 --- a/code/modules/mod/mod_theme.dm +++ b/code/modules/mod/mod_theme.dm @@ -49,58 +49,155 @@ var/list/inbuilt_modules = list() /// Allowed items in the chestplate's suit storage. var/list/allowed_suit_storage = list() - /// List of skins with their appropriate clothing flags. - var/list/skins = list( + /// List of variants and items created by them, with the flags we set. + var/list/variants = list( "standard" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|HEADINTERNALS, - SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), "civilian" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) +#ifdef UNIT_TESTS +/datum/mod_theme/New() + var/list/skin_parts = list() + for(var/variant in variants) + skin_parts += list(assoc_to_keys(variants[variant])) + for(var/skin in skin_parts) + for(var/compared_skin in skin_parts) + if(skin ~! compared_skin) + stack_trace("[type] variants [skin] and [compared_skin] aren't made of the same parts.") + skin_parts -= skin +#endif + +/// Create parts of the suit and modify them using the theme's variables. +/datum/mod_theme/proc/set_up_parts(obj/item/mod/control/mod, skin) + var/list/parts = list(mod) + mod.slot_flags = slot_flags + mod.extended_desc = extended_desc + mod.slowdown_inactive = slowdown_inactive + mod.slowdown_active = slowdown_active + mod.activation_step_time = activation_step_time + mod.complexity_max = complexity_max + mod.ui_theme = ui_theme + mod.charge_drain = charge_drain + var/datum/mod_part/control_part_datum = new() + control_part_datum.part_item = mod + mod.mod_parts["[mod.slot_flags]"] = control_part_datum + for(var/path in variants[default_skin]) + var/obj/item/mod_part = new path(mod) + if(mod_part.slot_flags == ITEM_SLOT_OCLOTHING && isclothing(mod_part)) + var/obj/item/clothing/chestplate = mod_part + chestplate.allowed |= allowed_suit_storage + var/datum/mod_part/part_datum = new() + 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]" + part.set_armor(armor_type) + part.resistance_flags = resistance_flags + part.flags_1 |= 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 = 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) + mod.skin = skin + var/list/used_skin = variants[skin] + var/list/parts = mod.get_parts() + for(var/obj/item/clothing/part as anything in parts) + var/list/category = used_skin[part.type] + var/datum/mod_part/part_datum = mod.get_part_datum(part) + part_datum.unsealed_layer = category[UNSEALED_LAYER] + part_datum.sealed_layer = category[SEALED_LAYER] + part_datum.unsealed_message = category[UNSEALED_MESSAGE] || "No unseal message set! Tell a coder!" + part_datum.sealed_message = category[SEALED_MESSAGE] || "No seal message set! Tell a coder!" + part_datum.can_overslot = category[CAN_OVERSLOT] || FALSE + 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 + if(mod.get_part_datum(part).sealed) + part.clothing_flags |= part.visor_flags + part.flags_inv |= part.visor_flags_inv + part.flags_cover |= part.visor_flags_cover + part.alternate_worn_layer = part_datum.sealed_layer + else + part.alternate_worn_layer = part_datum.unsealed_layer + if(!part_datum.can_overslot && part_datum.overslotting) + var/obj/item/overslot = part_datum.overslotting + overslot.forceMove(mod.drop_location()) + for(var/obj/item/part as anything in parts + mod) + 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][mod.get_part_datum(part).sealed ? "-sealed" : ""]" + mod.wearer?.update_clothing(part.slot_flags) + /datum/armor/mod_theme melee = 10 bullet = 5 @@ -108,7 +205,7 @@ energy = 5 bio = 100 fire = 25 - acid =25 + acid = 25 wound = 5 /datum/mod_theme/engineering @@ -131,30 +228,38 @@ /obj/item/fireaxe/metal_h2_axe, /obj/item/storage/bag/construction, ) - skins = list( + variants = list( "engineering" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -190,9 +295,9 @@ /obj/item/pipe_dispenser, /obj/item/t_scanner, ) - skins = list( + variants = list( "atmospheric" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, @@ -200,21 +305,29 @@ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR, UNSEALED_COVER = HEADCOVERSMOUTH, SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -255,30 +368,38 @@ /obj/item/storage/bag/construction, /obj/item/t_scanner, ) - skins = list( + variants = list( "advanced" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -332,55 +453,69 @@ /obj/item/gun/energy/recharge/kinetic_accelerator, ) inbuilt_modules = list(/obj/item/mod/module/ash_accretion, /obj/item/mod/module/sphere_transform) - skins = list( + variants = list( "mining" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE|HIDEFACIALHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), "asteroid" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -423,23 +558,26 @@ /obj/item/storage/bag/mail, ) inbuilt_modules = list(/obj/item/mod/module/hydraulic, /obj/item/mod/module/clamp/loader, /obj/item/mod/module/magnet) - skins = list( + variants = list( "loader" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( SEALED_CLOTHING = THICKMATERIAL, CAN_OVERSLOT = TRUE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( SEALED_CLOTHING = THICKMATERIAL, CAN_OVERSLOT = TRUE, ), @@ -486,55 +624,71 @@ /obj/item/storage/bag/chemistry, /obj/item/storage/bag/bio, ) - skins = list( + variants = list( "medical" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), "corpsman" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -583,30 +737,38 @@ /obj/item/storage/bag/bio, /obj/item/melee/baton/telescopic, ) - skins = list( + variants = list( "rescue" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -649,29 +811,36 @@ /obj/item/storage/bag/bio, /obj/item/melee/baton/telescopic, ) - skins = list( + variants = list( "research" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -707,31 +876,38 @@ /obj/item/assembly/flash, /obj/item/melee/baton, ) - skins = list( + variants = list( "security" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE, UNSEALED_COVER = HEADCOVERSMOUTH, SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -769,29 +945,36 @@ /obj/item/assembly/flash, /obj/item/melee/baton, ) - skins = list( + variants = list( "safeguard" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -833,30 +1016,38 @@ /obj/item/assembly/flash, /obj/item/melee/baton, ) - skins = list( + variants = list( "magnate" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -893,30 +1084,38 @@ /obj/item/instrument, /obj/item/toy/balloon_animal, ) - skins = list( + variants = list( "cosmohonk" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -960,55 +1159,71 @@ /obj/item/melee/energy/sword, /obj/item/shield/energy, ) - skins = list( + variants = list( "syndicate" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), "honkerative" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1051,30 +1266,37 @@ /obj/item/melee/energy/sword, /obj/item/shield/energy, ) - skins = list( + variants = list( "elite" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1118,26 +1340,29 @@ /obj/item/melee/energy/sword, /obj/item/shield/energy, ) - skins = list( + variants = list( "infiltrator" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_INVISIBILITY = HIDEJUMPSUIT, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( SEALED_CLOTHING = THICKMATERIAL, CAN_OVERSLOT = TRUE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( SEALED_CLOTHING = THICKMATERIAL, CAN_OVERSLOT = TRUE, ), @@ -1173,7 +1398,7 @@ charge_drain = DEFAULT_CHARGE_DRAIN * 2 slowdown_inactive = 0.0 slowdown_active = -0.5 - inbuilt_modules = list(/obj/item/mod/module/quick_carry/advanced, /obj/item/mod/module/organ_thrower) + inbuilt_modules = list(/obj/item/mod/module/quick_carry/advanced) allowed_suit_storage = list( /obj/item/assembly/flash, /obj/item/healthanalyzer, @@ -1195,30 +1420,38 @@ /obj/item/storage/bag/chemistry, /obj/item/storage/pill_bottle, ) - skins = list( + variants = list( "interdyne" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1259,29 +1492,34 @@ /obj/item/highfrequencyblade/wizard, /obj/item/gun/magic, ) - skins = list( + variants = list( "enchanted" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL|CASTING_CLOTHES, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL|CASTING_CLOTHES, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1320,30 +1558,37 @@ /obj/item/melee/baton, /obj/item/restraints/handcuffs, ) - skins = list( + variants = list( "ninja" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1385,29 +1630,36 @@ /obj/item/pipe_dispenser, /obj/item/construction/rcd, ) - skins = list( + variants = list( "prototype" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1423,6 +1675,73 @@ acid = 75 wound = 5 +/datum/mod_theme/glitch + name = "glitch" + desc = "A modsuit outfitted for elite Cyber Authority units to track, capture, and eliminate organic intruders." + extended_desc = "The Cyber Authority function as a digital police force, patrolling the digital realm and enforcing the law. Cyber Tac units are \ + the elite of the elite, outfitted with lethal weaponry and fast mobility specially designed to quell organic uprisings." + default_skin = "glitch" + armor_type = /datum/armor/mod_theme_glitch + resistance_flags = FIRE_PROOF|ACID_PROOF + atom_flags = PREVENT_CONTENTS_EXPLOSION_1 + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + complexity_max = DEFAULT_MAX_COMPLEXITY + 3 + siemens_coefficient = 0 + slowdown_inactive = 1 + slowdown_active = 0.5 + ui_theme = "terminal" + inbuilt_modules = list(/obj/item/mod/module/armor_booster) + allowed_suit_storage = list( + /obj/item/ammo_box, + /obj/item/ammo_casing, + /obj/item/restraints/handcuffs, + /obj/item/assembly/flash, + ) + variants = list( + "glitch" = list( + /obj/item/clothing/head/mod = list( + UNSEALED_CLOTHING = SNUG_FIT, + SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, + UNSEALED_INVISIBILITY = HIDEFACIALHAIR, + SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, + ), + /obj/item/clothing/suit/mod = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, + ), + /obj/item/clothing/gloves/mod = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, + ), + /obj/item/clothing/shoes/mod = list( + UNSEALED_CLOTHING = THICKMATERIAL, + SEALED_CLOTHING = STOPSPRESSUREDAMAGE, + CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, + ), + ), + ) + +/datum/armor/mod_theme_glitch + melee = 15 + bullet = 20 + laser = 35 + bomb = 65 + bio = 100 + fire = 100 + acid = 100 + wound = 100 + /datum/mod_theme/responsory name = "responsory" desc = "A high-speed rescue suit by Nanotrasen, intended for its emergency response teams." @@ -1444,54 +1763,69 @@ /obj/item/assembly/flash, /obj/item/melee/baton, ) - skins = list( + variants = list( "responsory" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), "inquisitory" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1543,30 +1877,37 @@ /obj/item/melee/energy/sword, /obj/item/shield/energy, ) - skins = list( + variants = list( "apocryphal" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1605,30 +1946,37 @@ /obj/item/assembly/flash, /obj/item/melee/baton, ) - skins = list( + variants = list( "corporate" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1661,30 +2009,38 @@ allowed_suit_storage = list( /obj/item/restraints/handcuffs, ) - skins = list( + variants = list( "chrono" = list( - HELMET_FLAGS = list( + /obj/item/clothing/head/mod = list( UNSEALED_LAYER = NECK_LAYER, UNSEALED_CLOTHING = SNUG_FIT, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1719,31 +2075,38 @@ allowed_suit_storage = list( /obj/item/gun, ) - skins = list( + variants = list( "debug" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE, UNSEALED_COVER = HEADCOVERSMOUTH, SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, + UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, + SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE, + SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, + UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE, + SEALED_MESSAGE = BOOT_SEAL_MESSAGE, ), ), ) @@ -1779,24 +2142,25 @@ allowed_suit_storage = list( /obj/item/gun, ) - skins = list( + variants = list( "debug" = list( - HELMET_FLAGS = list( - UNSEALED_LAYER = null, + /obj/item/clothing/head/mod = list( UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, + SEALED_MESSAGE = HELMET_SEAL_MESSAGE, ), - CHESTPLATE_FLAGS = list( + /obj/item/clothing/suit/mod = list( UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, ), - GAUNTLETS_FLAGS = list( + /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, ), - BOOTS_FLAGS = list( + /obj/item/clothing/shoes/mod = list( UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, CAN_OVERSLOT = TRUE, ), diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm index 2789763e12cd2..8843a811756d7 100644 --- a/code/modules/mod/mod_types.dm +++ b/code/modules/mod/mod_types.dm @@ -212,7 +212,7 @@ /obj/item/mod/module/storage, /obj/item/mod/module/waddle, /obj/item/mod/module/bikehorn, - /obj/item/mod/module/balloon_advanced, + /obj/item/mod/module/balloon/advanced, ) /obj/item/mod/control/pre_equipped/traitor @@ -352,6 +352,7 @@ starting_frequency = MODLINK_FREQ_SYNDICATE applied_cell = /obj/item/stock_parts/cell/super applied_modules = list( + /obj/item/mod/module/organ_thrower, /obj/item/mod/module/defibrillator/combat, /obj/item/mod/module/flashlight, /obj/item/mod/module/health_analyzer, @@ -413,6 +414,23 @@ /obj/item/mod/module/anomaly_locked/kinesis/prototype, ) +/obj/item/mod/control/pre_equipped/glitch + theme = /datum/mod_theme/glitch + starting_frequency = null + applied_cell = /obj/item/stock_parts/cell/bluespace + applied_modules = list( + /obj/item/mod/module/storage, + /obj/item/mod/module/magnetic_harness, + /obj/item/mod/module/jetpack/advanced, + /obj/item/mod/module/jump_jet, + /obj/item/mod/module/flashlight, + ) + default_pins = list( + /obj/item/mod/module/armor_booster, + /obj/item/mod/module/jetpack/advanced, + /obj/item/mod/module/jump_jet, + ) + /obj/item/mod/control/pre_equipped/responsory theme = /datum/mod_theme/responsory starting_frequency = MODLINK_FREQ_CENTCOM diff --git a/code/modules/mod/mod_ui.dm b/code/modules/mod/mod_ui.dm index 2f1e6faa0f429..f994b91060fea 100644 --- a/code/modules/mod/mod_ui.dm +++ b/code/modules/mod/mod_ui.dm @@ -53,7 +53,7 @@ "cooldown" = round(COOLDOWN_TIMELEFT(module, cooldown_timer), 1 SECONDS), "id" = module.tgui_id, "ref" = REF(module), - "configuration_data" = module.get_configuration(user) + "configuration_data" = module.get_configuration(user), )) data["module_custom_status"] = module_custom_status data["module_info"] = module_info @@ -64,10 +64,13 @@ data["ui_theme"] = ui_theme data["control"] = name data["complexity_max"] = complexity_max - data["helmet"] = helmet?.name - data["chestplate"] = chestplate?.name - data["gauntlets"] = gauntlets?.name - data["boots"] = boots?.name + var/part_info = list() + for(var/obj/item/part as anything in get_parts()) + part_info += list(list( + "slot" = english_list(parse_slot_flags(part.slot_flags)), + "name" = part.name, + )) + data["parts"] = part_info return data /obj/item/mod/control/ui_state(mob/user) diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm index 8b3166f811b4a..4bd4ef0d2ab80 100644 --- a/code/modules/mod/modules/_module.dm +++ b/code/modules/mod/modules/_module.dm @@ -43,6 +43,8 @@ var/list/pinned_to = list() /// flags that let the module ability be used in odd circumstances var/allow_flags = NONE + /// A list of slots required in the suit to work. Formatted like list(x|y, z, ...) where either x or y are required and z is required. + var/list/required_slots = list() /// Timer for the cooldown COOLDOWN_DECLARE(cooldown_timer) @@ -65,23 +67,50 @@ /obj/item/mod/module/examine(mob/user) . = ..() + if(length(required_slots)) + var/list/slot_strings = list() + for(var/slot in required_slots) + var/list/slot_list = parse_slot_flags(slot) + slot_strings += (length(slot_list) == 1 ? "" : "one of ") + english_list(slot_list, and_text = " or ") + . += span_notice("Requires the MOD unit to have the following slots: [english_list(slot_strings)]") if(HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) . += span_notice("Complexity level: [complexity]") +/// Looks through the MODsuit's parts to see if it has the parts required to support this module +/obj/item/mod/module/proc/has_required_parts(list/parts, need_extended = FALSE) + if(!length(required_slots)) + return TRUE + var/total_slot_flags = NONE + for(var/part_slot in parts) + if(need_extended) + var/datum/mod_part/part_datum = parts[part_slot] + if(part_datum.part_item.loc == mod) + continue + total_slot_flags |= text2num(part_slot) + var/list/needed_slots = required_slots.Copy() + for(var/needed_slot in needed_slots) + if(!(needed_slot & total_slot_flags)) + break + needed_slots -= needed_slot + return !length(needed_slots) /// Called when the module is selected from the TGUI, radial or the action button /obj/item/mod/module/proc/on_select() + if(!mod.wearer) + if(ismob(mod.loc)) + balloon_alert(mod.loc, "not equipped!") + return if(((!mod.active || mod.activating) && !(allow_flags & MODULE_ALLOW_INACTIVE)) || module_type == MODULE_PASSIVE) if(mod.wearer) balloon_alert(mod.wearer, "not active!") return if(module_type != MODULE_USABLE) if(active) - on_deactivation() + deactivate() else - on_activation() + activate() else - on_use() + used() SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED, src) /// Apply a cooldown until this item can be used again @@ -92,7 +121,7 @@ SEND_SIGNAL(src, COMSIG_MODULE_COOLDOWN_STARTED, applied_cooldown) /// Called when the module is activated -/obj/item/mod/module/proc/on_activation() +/obj/item/mod/module/proc/activate() if(!COOLDOWN_FINISHED(src, cooldown_timer)) balloon_alert(mod.wearer, "on cooldown!") return FALSE @@ -106,7 +135,7 @@ if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED, mod.wearer) & MOD_ABORT_USE) return FALSE if(module_type == MODULE_ACTIVE) - if(mod.selected_module && !mod.selected_module.on_deactivation(display_message = FALSE)) + if(mod.selected_module && !mod.selected_module.deactivate(display_message = FALSE)) return FALSE mod.selected_module = src if(device) @@ -126,10 +155,11 @@ mod.wearer.update_clothing(mod.slot_flags) start_cooldown() SEND_SIGNAL(src, COMSIG_MODULE_ACTIVATED) + on_activation() return TRUE /// Called when the module is deactivated -/obj/item/mod/module/proc/on_deactivation(display_message = TRUE, deleting = FALSE) +/obj/item/mod/module/proc/deactivate(display_message = TRUE, deleting = FALSE) active = FALSE if(module_type == MODULE_ACTIVE) mod.selected_module = null @@ -144,10 +174,11 @@ used_signal = null mod.wearer.update_clothing(mod.slot_flags) SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED, mod.wearer) + on_deactivation(display_message = TRUE, deleting = FALSE) return TRUE /// Called when the module is used -/obj/item/mod/module/proc/on_use() +/obj/item/mod/module/proc/used() if(!COOLDOWN_FINISHED(src, cooldown_timer)) balloon_alert(mod.wearer, "on cooldown!") return FALSE @@ -164,6 +195,7 @@ addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/mob, update_clothing), mod.slot_flags), cooldown_time+1) //need to run it a bit after the cooldown starts to avoid conflicts mod.wearer.update_clothing(mod.slot_flags) SEND_SIGNAL(src, COMSIG_MODULE_USED) + on_use() return TRUE /// Called when an activated module without a device is used @@ -171,7 +203,7 @@ if(!(allow_flags & MODULE_ALLOW_INCAPACITATED) && mod.wearer.incapacitated(IGNORE_GRAB)) return FALSE mod.wearer.face_atom(target) - if(!on_use()) + if(!used()) return FALSE return TRUE @@ -185,22 +217,34 @@ /obj/item/mod/module/proc/on_process(seconds_per_tick) if(active) if(!drain_power(active_power_cost * seconds_per_tick)) - on_deactivation() + deactivate() return FALSE on_active_process(seconds_per_tick) else drain_power(idle_power_cost * seconds_per_tick) return TRUE +/// Called from the module's activate() +/obj/item/mod/module/proc/on_activation() + return + +/// Called from the module's deactivate() +/obj/item/mod/module/proc/on_deactivation(display_message = TRUE, deleting = FALSE) + return + +/// Called from the module's used() +/obj/item/mod/module/proc/on_use() + return + /// Called on the MODsuit's process if it is an active module /obj/item/mod/module/proc/on_active_process(seconds_per_tick) return -/// Called from MODsuit's install() proc, so when the module is installed. +/// Called from MODsuit's install() proc, so when the module is installed /obj/item/mod/module/proc/on_install() return -/// Called from MODsuit's uninstall() proc, so when the module is uninstalled. +/// Called from MODsuit's uninstall() proc, so when the module is uninstalled /obj/item/mod/module/proc/on_uninstall(deleting = FALSE) return @@ -258,7 +302,7 @@ if(part.loc == mod.wearer) return if(part == device) - on_deactivation(display_message = FALSE) + deactivate(display_message = FALSE) /// Called when the device gets deleted on active modules /obj/item/mod/module/proc/on_device_deletion(datum/source) @@ -321,7 +365,7 @@ if(user.get_active_held_item() != device) return - on_deactivation() + deactivate() return COMSIG_KB_ACTIVATED ///Anomaly Locked - Causes the module to not function without an anomaly. diff --git a/code/modules/mod/modules/module_kinesis.dm b/code/modules/mod/modules/module_kinesis.dm index 4f4fa44ff966c..81a266f8ff41a 100644 --- a/code/modules/mod/modules/module_kinesis.dm +++ b/code/modules/mod/modules/module_kinesis.dm @@ -15,6 +15,7 @@ overlay_state_inactive = "module_kinesis" overlay_state_active = "module_kinesis_on" accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav) + required_slots = list(ITEM_SLOT_GLOVES) /// Range of the knesis grab. var/grab_range = 5 /// Time between us hitting objects with kinesis. @@ -63,9 +64,6 @@ grab_atom(target) /obj/item/mod/module/anomaly_locked/kinesis/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return clear_grab(playsound = !deleting) /obj/item/mod/module/anomaly_locked/kinesis/process(seconds_per_tick) diff --git a/code/modules/mod/modules/module_pathfinder.dm b/code/modules/mod/modules/module_pathfinder.dm index 1681496036887..64790eacb3bec 100644 --- a/code/modules/mod/modules/module_pathfinder.dm +++ b/code/modules/mod/modules/module_pathfinder.dm @@ -13,6 +13,7 @@ complexity = 1 use_energy_cost = DEFAULT_CHARGE_DRAIN * 10 incompatible_modules = list(/obj/item/mod/module/pathfinder) + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// The pathfinding implant. var/obj/item/implant/mod/implant diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm index bd96c5aec5ff4..8d8d777592466 100644 --- a/code/modules/mod/modules/modules_antag.dm +++ b/code/modules/mod/modules/modules_antag.dm @@ -23,11 +23,9 @@ /// Speed that we actually added. var/actual_speed_added = 0 /// Armor values added to the suit parts. - var/list/armor_mod = /datum/armor/mod_module_armor_boost + 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() - /// List of traits added when the mod is activated - var/list/traits_to_add = list(TRAIT_HEAD_INJURY_BLOCKED) /obj/item/mod/module/armor_booster/no_speedbost speed_added = 0 @@ -39,25 +37,27 @@ energy = 15 /obj/item/mod/module/armor_booster/on_suit_activation() - mod.helmet.flash_protect = FLASH_PROTECTION_WELDER + 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 /obj/item/mod/module/armor_booster/on_suit_deactivation(deleting = FALSE) if(deleting) return - mod.helmet.flash_protect = initial(mod.helmet.flash_protect) + 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) /obj/item/mod/module/armor_booster/on_activation() - . = ..() - if(!.) - return playsound(src, 'sound/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_active, speed_added)) mod.slowdown -= actual_speed_added mod.wearer.update_equipment_speed_mods() - mod.wearer.add_traits(traits_to_add, MOD_TRAIT) - var/list/parts = mod.mod_parts + mod - for(var/obj/item/part as anything in parts) + 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)) + ADD_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, MOD_TRAIT) + for(var/obj/item/part as anything in mod.get_parts(all = TRUE)) part.set_armor(part.get_armor().add_other_armor(armor_mod)) if(!remove_pressure_protection || !isclothing(part)) continue @@ -67,17 +67,15 @@ spaceproofed[clothing_part] = TRUE /obj/item/mod/module/armor_booster/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return if(!deleting) playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) balloon_alert(mod.wearer, "armor retracts, EVA ready") mod.slowdown += actual_speed_added mod.wearer.update_equipment_speed_mods() - mod.wearer.remove_traits(traits_to_add, MOD_TRAIT) - var/list/parts = mod.mod_parts + mod - for(var/obj/item/part as anything in parts) + 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)) + REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, MOD_TRAIT) + 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(!remove_pressure_protection || !isclothing(part)) continue @@ -103,6 +101,7 @@ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 use_energy_cost = DEFAULT_CHARGE_DRAIN * 2 incompatible_modules = list(/obj/item/mod/module/energy_shield) + required_slots = list(ITEM_SLOT_BACK) /// Max charges of the shield. var/max_charges = 1 /// The time it takes for the first charge to recover. @@ -167,6 +166,7 @@ shield_icon_file = 'icons/effects/magic.dmi' shield_icon = "mageshield" recharge_path = /obj/item/wizard_armour_charge + required_slots = list() ///Magic Nullifier - Protects you from magic. /obj/item/mod/module/anti_magic @@ -179,6 +179,7 @@ icon_state = "magic_nullifier" removable = FALSE incompatible_modules = list(/obj/item/mod/module/anti_magic) + required_slots = list(ITEM_SLOT_BACK) /obj/item/mod/module/anti_magic/on_suit_activation() mod.wearer.add_traits(list(TRAIT_ANTIMAGIC, TRAIT_HOLY), MOD_TRAIT) @@ -193,6 +194,7 @@ The field will neutralize all magic that comes into contact with the user. \ It will not protect the caster from social ridicule." icon_state = "magic_neutralizer" + required_slots = list() /obj/item/mod/module/anti_magic/wizard/on_suit_activation() mod.wearer.add_traits(list(TRAIT_ANTIMAGIC, TRAIT_ANTIMAGIC_NO_SELFBLOCK), MOD_TRAIT) @@ -254,6 +256,7 @@ complexity = 1 idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.1 incompatible_modules = list(/obj/item/mod/module/noslip) + required_slots = list(ITEM_SLOT_FEET) /obj/item/mod/module/noslip/on_suit_activation() ADD_TRAIT(mod.wearer, TRAIT_NO_SLIP_WATER, MOD_TRAIT) @@ -298,6 +301,7 @@ cooldown_time = 2.5 SECONDS overlay_state_inactive = "module_flamethrower" overlay_state_active = "module_flamethrower_on" + required_slots = list(ITEM_SLOT_OCLOTHING|ITEM_SLOT_ICLOTHING) /obj/item/mod/module/flamethrower/on_select_use(atom/target) . = ..() @@ -320,6 +324,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 incompatible_modules = list(/obj/item/mod/module/power_kick) cooldown_time = 5 SECONDS + required_slots = list(ITEM_SLOT_FEET) /// Damage on kick. var/damage = 20 /// The wound bonus of the kick. @@ -399,13 +404,13 @@ return_look() possible_disguises = null -/obj/item/mod/module/chameleon/on_use() +/obj/item/mod/module/chameleon/used() if(mod.active || mod.activating) balloon_alert(mod.wearer, "suit active!") - return - . = ..() - if(!.) - return + return FALSE + return ..() + +/obj/item/mod/module/chameleon/on_use() if(current_disguise) return_look() return @@ -433,10 +438,9 @@ mod.name = "[mod.theme.name] [initial(mod.name)]" mod.desc = "[initial(mod.desc)] [mod.theme.desc]" mod.icon_state = "[mod.skin]-[initial(mod.icon_state)]" - var/list/mod_skin = mod.theme.skins[mod.skin] + var/list/mod_skin = mod.theme.variants[mod.skin] mod.icon = mod_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi' mod.worn_icon = mod_skin[MOD_WORN_ICON_OVERRIDE] || 'icons/mob/clothing/modsuit/mod_clothing.dmi' - mod.alternate_worn_layer = mod_skin[CONTROL_LAYER] mod.lefthand_file = initial(mod.lefthand_file) mod.righthand_file = initial(mod.righthand_file) mod.worn_icon_state = null @@ -481,6 +485,7 @@ complexity = 0 idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.1 removable = FALSE + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) var/datum/proximity_monitor/advanced/demoraliser/demoralizer /obj/item/mod/module/demoralizer/on_suit_activation() @@ -500,6 +505,7 @@ removable = FALSE idle_power_cost = DEFAULT_CHARGE_DRAIN * 0 incompatible_modules = list(/obj/item/mod/module/infiltrator, /obj/item/mod/module/armor_booster, /obj/item/mod/module/welding, /obj/item/mod/module/headprotector) + required_slots = list(ITEM_SLOT_FEET, ITEM_SLOT_HEAD, ITEM_SLOT_OCLOTHING) /// List of traits added when the suit is activated var/list/traits_to_add = list(TRAIT_SILENT_FOOTSTEPS, TRAIT_UNKNOWN, TRAIT_HEAD_INJURY_BLOCKED) @@ -510,18 +516,22 @@ mod.item_flags &= ~EXAMINE_SKIP /obj/item/mod/module/infiltrator/on_suit_activation() - mod.wearer.add_traits(traits_to_add, MOD_TRAIT) - mod.helmet.flash_protect = FLASH_PROTECTION_WELDER + mod.wearer.add_traits(list(TRAIT_SILENT_FOOTSTEPS, TRAIT_UNKNOWN), MOD_TRAIT) + var/obj/item/clothing/head_cover = mod.get_part_from_slot(ITEM_SLOT_HEAD) + if(istype(head_cover)) + head_cover.flash_protect = FLASH_PROTECTION_WELDER /obj/item/mod/module/infiltrator/on_suit_deactivation(deleting = FALSE) mod.wearer.remove_traits(traits_to_add, MOD_TRAIT) if(deleting) return - mod.helmet.flash_protect = initial(mod.helmet.flash_protect) + var/obj/item/clothing/head_cover = mod.get_part_from_slot(ITEM_SLOT_HEAD) + if(istype(head_cover)) + head_cover.flash_protect = initial(head_cover.flash_protect) ///Medbeam - Medbeam but built into a modsuit /obj/item/mod/module/medbeam - name = "MOD Medbeam Module" + name = "MOD medical beamgun module" desc = "A wrist mounted variant of the medbeam gun, allowing the user to heal their allies without the risk of dropping it." icon_state = "chronogun" module_type = MODULE_ACTIVE @@ -531,6 +541,7 @@ incompatible_modules = list(/obj/item/mod/module/medbeam) removable = TRUE cooldown_time = 0.5 + required_slots = list(ITEM_SLOT_BACK) /obj/item/gun/medbeam/mod name = "MOD medbeam" diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm index 1ddcab0818073..cb830b2128e7e 100644 --- a/code/modules/mod/modules/modules_engineering.dm +++ b/code/modules/mod/modules/modules_engineering.dm @@ -10,14 +10,19 @@ complexity = 1 incompatible_modules = list(/obj/item/mod/module/welding, /obj/item/mod/module/armor_booster) overlay_state_inactive = "module_welding" + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_EYES|ITEM_SLOT_MASK) /obj/item/mod/module/welding/on_suit_activation() - mod.helmet.flash_protect = FLASH_PROTECTION_WELDER + 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 /obj/item/mod/module/welding/on_suit_deactivation(deleting = FALSE) if(deleting) return - mod.helmet.flash_protect = initial(mod.helmet.flash_protect) + 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) ///T-Ray Scan - Scans the terrain for undertile objects. /obj/item/mod/module/t_ray @@ -31,6 +36,7 @@ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 incompatible_modules = list(/obj/item/mod/module/t_ray) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_EYES|ITEM_SLOT_MASK) /// T-ray scan range. var/range = 4 @@ -50,23 +56,18 @@ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 incompatible_modules = list(/obj/item/mod/module/magboot, /obj/item/mod/module/atrocinator) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_FEET) /// Slowdown added onto the suit. var/slowdown_active = 0.5 /// 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_activation() - . = ..() - if(!.) - return mod.wearer.add_traits(active_traits, MOD_TRAIT) mod.slowdown += slowdown_active mod.wearer.update_equipment_speed_mods() /obj/item/mod/module/magboot/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return mod.wearer.remove_traits(active_traits, MOD_TRAIT) mod.slowdown -= slowdown_active mod.wearer.update_equipment_speed_mods() @@ -89,8 +90,9 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN incompatible_modules = list(/obj/item/mod/module/tether) cooldown_time = 1.5 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) -/obj/item/mod/module/tether/on_use() +/obj/item/mod/module/tether/used() if(mod.wearer.has_gravity(get_turf(src))) balloon_alert(mod.wearer, "too much gravity!") playsound(src, 'sound/weapons/gun/general/dry_fire.ogg', 25, TRUE) @@ -153,14 +155,14 @@ AddComponent(/datum/component/geiger_sound) ADD_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT) RegisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION, PROC_REF(on_pre_potential_irradiation)) - for(var/obj/item/part in mod.mod_parts) + for(var/obj/item/part in mod.get_parts(all = TRUE)) ADD_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT) /obj/item/mod/module/rad_protection/on_suit_deactivation(deleting = FALSE) qdel(GetComponent(/datum/component/geiger_sound)) REMOVE_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT) UnregisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION) - for(var/obj/item/part in mod.mod_parts) + for(var/obj/item/part in mod.get_parts(all = TRUE)) REMOVE_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT) /obj/item/mod/module/rad_protection/add_ui_data() @@ -190,6 +192,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 2 incompatible_modules = list(/obj/item/mod/module/constructor, /obj/item/mod/module/quick_carry) cooldown_time = 11 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) /obj/item/mod/module/constructor/on_suit_activation() ADD_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) @@ -198,15 +201,12 @@ REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) /obj/item/mod/module/constructor/on_use() - . = ..() - if(!.) - return rcd_scan(src, fade_time = 10 SECONDS) drain_power(use_energy_cost) ///Safety-First Head Protection - Protects your brain matter from sudden impacts. /obj/item/mod/module/headprotector - name = "MOD Safety-First Head Protection module" + name = "MOD safety-first head protection module" desc = "A series of dampening plates are installed along the back and upper areas of \ the helmet. These plates absorb abrupt kinetic shocks delivered to the skull. \ The bulk of this module prevents it from being installed in any suit that is capable \ @@ -215,6 +215,7 @@ icon_state = "welding" complexity = 1 incompatible_modules = list(/obj/item/mod/module/armor_booster, /obj/item/mod/module/infiltrator) + required_slots = list(ITEM_SLOT_HEAD) /obj/item/mod/module/constructor/on_suit_activation() ADD_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, MOD_TRAIT) @@ -233,6 +234,7 @@ device = /obj/item/reagent_containers/spray/mister incompatible_modules = list(/obj/item/mod/module/mister) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_BACK) /// Volume of our reagent holder. var/volume = 500 diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 2aec3e361c4b4..87cab74b24f76 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -8,6 +8,7 @@ icon_state = "storage" complexity = 3 incompatible_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/plate_compression) + required_slots = list(ITEM_SLOT_BACK) /// Max weight class of items in the storage. var/max_w_class = WEIGHT_CLASS_NORMAL /// Max combined weight of all items in the storage. @@ -28,16 +29,20 @@ modstorage.set_real_location(src) modstorage.allow_big_nesting = big_nesting atom_storage.locked = STORAGE_NOT_LOCKED - RegisterSignal(mod.chestplate, COMSIG_ITEM_PRE_UNEQUIP, PROC_REF(on_chestplate_unequip)) + var/obj/item/clothing/suit = mod.get_part_from_slot(ITEM_SLOT_OCLOTHING) + if(istype(suit)) + RegisterSignal(suit, COMSIG_ITEM_PRE_UNEQUIP, PROC_REF(on_suit_unequip)) /obj/item/mod/module/storage/on_uninstall(deleting = FALSE) atom_storage.locked = STORAGE_FULLY_LOCKED QDEL_NULL(mod.atom_storage) if(!deleting) atom_storage.remove_all(mod.drop_location()) - UnregisterSignal(mod.chestplate, COMSIG_ITEM_PRE_UNEQUIP) + var/obj/item/clothing/suit = mod.get_part_from_slot(ITEM_SLOT_OCLOTHING) + if(istype(suit)) + UnregisterSignal(suit, COMSIG_ITEM_PRE_UNEQUIP) -/obj/item/mod/module/storage/proc/on_chestplate_unequip(obj/item/source, force, atom/newloc, no_move, invdrop, silent) +/obj/item/mod/module/storage/proc/on_suit_unequip(obj/item/source, force, atom/newloc, no_move, invdrop, silent) if(QDELETED(source) || !mod.wearer || newloc == mod.wearer || !mod.wearer.s_store) return if(!atom_storage?.attempt_insert(mod.wearer.s_store, mod.wearer, override = TRUE)) @@ -68,14 +73,14 @@ /obj/item/mod/module/storage/belt name = "MOD case storage module" desc = "Some concessions had to be made when creating a compressed modular suit core. \ - As a result, Roseus Galactic equipped their suit with a slimline storage case. \ - If you find this equipped to a standard modular suit, then someone has almost certainly shortchanged you on a proper storage module." + As a result, Roseus Galactic equipped their suit with a slimline storage case. \ + If you find this equipped to a standard modular suit, then someone has almost certainly shortchanged you on a proper storage module." icon_state = "storage_case" complexity = 0 max_w_class = WEIGHT_CLASS_SMALL - removable = FALSE max_combined_w_class = 21 max_items = 7 + required_slots = list(ITEM_SLOT_BELT) /obj/item/mod/module/storage/bluespace name = "MOD bluespace storage module" @@ -102,6 +107,7 @@ cooldown_time = 0.5 SECONDS overlay_state_inactive = "module_jetpack" overlay_state_active = "module_jetpack_on" + required_slots = list(ITEM_SLOT_BACK) /// Do we give the wearer a speed buff. var/full_speed = FALSE /// Do we have stabilizers? If yes the user won't move from inertia. @@ -138,14 +144,10 @@ ) /obj/item/mod/module/jetpack/on_activation() - . = ..() - if(!.) - return if(full_speed) mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) /obj/item/mod/module/jetpack/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() if(full_speed) mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) @@ -188,6 +190,7 @@ cooldown_time = 30 SECONDS use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 incompatible_modules = list(/obj/item/mod/module/jump_jet) + required_slots = list(ITEM_SLOT_BACK) /obj/item/mod/module/jump_jet/on_use() . = ..() @@ -228,6 +231,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 0.1 incompatible_modules = list(/obj/item/mod/module/status_readout) tgui_id = "status_readout" + required_slots = list(ITEM_SLOT_BACK) /// Does this show damage types, body temp, satiety var/display_detailed_vitals = TRUE /// Does this show DNA data @@ -305,22 +309,41 @@ complexity = 1 incompatible_modules = list(/obj/item/mod/module/mouthhole) overlay_state_inactive = "module_apparatus" + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_MASK) /// Former flags of the helmet. - var/former_flags = NONE + var/former_helmet_flags = NONE /// Former visor flags of the helmet. - var/former_visor_flags = NONE + var/former_visor_helmet_flags = NONE + /// Former flags of the mask. + var/former_mask_flags = NONE + /// Former visor flags of the mask. + var/former_visor_mask_flags = NONE /obj/item/mod/module/mouthhole/on_install() - former_flags = mod.helmet.flags_cover - former_visor_flags = mod.helmet.visor_flags_cover - mod.helmet.flags_cover &= ~(HEADCOVERSMOUTH|PEPPERPROOF) - mod.helmet.visor_flags_cover &= ~(HEADCOVERSMOUTH|PEPPERPROOF) + var/obj/item/clothing/helmet = mod.get_part_from_slot(ITEM_SLOT_HEAD) + if(istype(helmet)) + former_helmet_flags = helmet.flags_cover + former_visor_helmet_flags = helmet.visor_flags_cover + helmet.flags_cover &= ~(HEADCOVERSMOUTH|PEPPERPROOF) + helmet.visor_flags_cover &= ~(HEADCOVERSMOUTH|PEPPERPROOF) + var/obj/item/clothing/mask = mod.get_part_from_slot(ITEM_SLOT_MASK) + if(istype(mask)) + former_mask_flags = mask.flags_cover + former_visor_mask_flags = mask.visor_flags_cover + mask.flags_cover &= ~(MASKCOVERSMOUTH |PEPPERPROOF) + mask.visor_flags_cover &= ~(MASKCOVERSMOUTH |PEPPERPROOF) /obj/item/mod/module/mouthhole/on_uninstall(deleting = FALSE) if(deleting) return - mod.helmet.flags_cover |= former_flags - mod.helmet.visor_flags_cover |= former_visor_flags + var/obj/item/clothing/helmet = mod.get_part_from_slot(ITEM_SLOT_HEAD) + if(istype(helmet)) + helmet.flags_cover |= former_helmet_flags + helmet.visor_flags_cover |= former_visor_helmet_flags + var/obj/item/clothing/mask = mod.get_part_from_slot(ITEM_SLOT_MASK) + if(istype(mask)) + mask.flags_cover |= former_mask_flags + mask.visor_flags_cover |= former_visor_mask_flags ///EMP Shield - Protects the suit from EMPs. /obj/item/mod/module/emp_shield @@ -332,6 +355,7 @@ complexity = 1 idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 incompatible_modules = list(/obj/item/mod/module/emp_shield) + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /obj/item/mod/module/emp_shield/on_install() mod.AddElement(/datum/element/empprotection, EMP_PROTECT_ALL) @@ -349,7 +373,7 @@ /obj/item/mod/module/emp_shield/advanced/on_suit_activation() mod.wearer.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS) -/obj/item/mod/module/emp_shield/advanced/on_suit_deactivation(deleting) +/obj/item/mod/module/emp_shield/advanced/on_suit_deactivation(deleting = FALSE) mod.wearer.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS) ///Flashlight - Gives the suit a customizable flashlight. @@ -370,6 +394,7 @@ light_range = 4 light_power = 1 light_on = FALSE + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_MASK) /// Charge drain per range amount. var/base_power = DEFAULT_CHARGE_DRAIN * 0.1 /// Minimum range we can set. @@ -384,17 +409,11 @@ UnregisterSignal(mod.wearer, COMSIG_HIT_BY_SABOTEUR) /obj/item/mod/module/flashlight/on_activation() - . = ..() - if(!.) - return set_light_flags(light_flags | LIGHT_ATTACHED) set_light_on(active) active_power_cost = base_power * light_range /obj/item/mod/module/flashlight/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return set_light_flags(light_flags & ~LIGHT_ATTACHED) set_light_on(active) @@ -464,15 +483,13 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 2 incompatible_modules = list(/obj/item/mod/module/dispenser) cooldown_time = 5 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) /// Path we dispense. var/dispense_type = /obj/item/food/burger/plain /// Time it takes for us to dispense. var/dispense_time = 0 SECONDS /obj/item/mod/module/dispenser/on_use() - . = ..() - if(!.) - return if(dispense_time && !do_after(mod.wearer, dispense_time, target = mod)) balloon_alert(mod.wearer, "interrupted!") return FALSE @@ -494,6 +511,7 @@ complexity = 1 use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 incompatible_modules = list(/obj/item/mod/module/longfall) + required_slots = list(ITEM_SLOT_FEET) /obj/item/mod/module/longfall/on_suit_activation() RegisterSignal(mod.wearer, COMSIG_LIVING_Z_IMPACT, PROC_REF(z_impact_react)) @@ -532,6 +550,7 @@ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 incompatible_modules = list(/obj/item/mod/module/thermal_regulator) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// The temperature we are regulating to. var/temperature_setting = BODYTEMP_NORMAL /// Minimum temperature we can set. @@ -579,9 +598,6 @@ UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT) /obj/item/mod/module/dna_lock/on_use() - . = ..() - if(!.) - return dna = mod.wearer.dna.unique_enzymes balloon_alert(mod.wearer, "dna updated") drain_power(use_energy_cost) @@ -640,6 +656,7 @@ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer) overlay_state_inactive = "module_plasma" + required_slots = list(ITEM_SLOT_HEAD) /obj/item/mod/module/plasma_stabilizer/generate_worn_overlay() if(locate(/obj/item/mod/module/infiltrator) in mod.modules) @@ -663,6 +680,7 @@ This is a must-have for Nanotrasen Captains, enabling them to show off their authoritative hat even while in their MODsuit." icon_state = "hat_holder" incompatible_modules = list(/obj/item/mod/module/hat_stabilizer) + required_slots = list(ITEM_SLOT_HEAD) /*Intentionally left inheriting 0 complexity and removable = TRUE; even though it comes inbuilt into the Magnate/Corporate MODS and spawns in maints, I like the idea of stealing them*/ /// Currently "stored" hat. No armor or function will be inherited, only the icon and cover flags. @@ -672,18 +690,24 @@ var/former_visor_flags /obj/item/mod/module/hat_stabilizer/on_suit_activation() - RegisterSignal(mod.helmet, COMSIG_ATOM_EXAMINE, PROC_REF(add_examine)) - RegisterSignal(mod.helmet, COMSIG_ATOM_ATTACKBY, PROC_REF(place_hat)) - RegisterSignal(mod.helmet, COMSIG_ATOM_ATTACK_HAND_SECONDARY, PROC_REF(remove_hat)) + var/obj/item/clothing/helmet = mod.get_part_from_slot(ITEM_SLOT_HEAD) + if(!istype(helmet)) + return + RegisterSignal(helmet, COMSIG_ATOM_EXAMINE, PROC_REF(add_examine)) + RegisterSignal(helmet, COMSIG_ATOM_ATTACKBY, PROC_REF(place_hat)) + RegisterSignal(helmet, COMSIG_ATOM_ATTACK_HAND_SECONDARY, PROC_REF(remove_hat)) /obj/item/mod/module/hat_stabilizer/on_suit_deactivation(deleting = FALSE) if(deleting) return if(attached_hat) //knock off the helmet if its on their head. Or, technically, auto-rightclick it for them; that way it saves us code, AND gives them the bubble remove_hat(src, mod.wearer) - UnregisterSignal(mod.helmet, COMSIG_ATOM_EXAMINE) - UnregisterSignal(mod.helmet, COMSIG_ATOM_ATTACKBY) - UnregisterSignal(mod.helmet, COMSIG_ATOM_ATTACK_HAND_SECONDARY) + var/obj/item/clothing/helmet = mod.get_part_from_slot(ITEM_SLOT_HEAD) + if(!istype(helmet)) + return + UnregisterSignal(helmet, COMSIG_ATOM_EXAMINE) + UnregisterSignal(helmet, COMSIG_ATOM_ATTACKBY) + UnregisterSignal(helmet, COMSIG_ATOM_ATTACK_HAND_SECONDARY) /obj/item/mod/module/hat_stabilizer/proc/add_examine(datum/source, mob/user, list/base_examine) SIGNAL_HANDLER @@ -708,10 +732,12 @@ return if(mod.wearer.transferItemToLoc(hitting_item, src, force = FALSE, silent = TRUE)) attached_hat = hat - former_flags = mod.helmet.flags_cover - former_visor_flags = mod.helmet.visor_flags_cover - mod.helmet.flags_cover |= attached_hat.flags_cover - mod.helmet.visor_flags_cover |= attached_hat.visor_flags_cover + var/obj/item/clothing/helmet = mod.get_part_from_slot(ITEM_SLOT_HEAD) + if(istype(helmet)) + former_flags = helmet.flags_cover + former_visor_flags = helmet.visor_flags_cover + helmet.flags_cover |= attached_hat.flags_cover + helmet.visor_flags_cover |= attached_hat.visor_flags_cover balloon_alert(user, "hat attached, right-click to remove") mod.wearer.update_clothing(mod.slot_flags) @@ -731,10 +757,21 @@ else balloon_alert_to_viewers("the hat falls to the floor!") attached_hat = null - mod.helmet.flags_cover = former_flags - mod.helmet.visor_flags_cover = former_visor_flags + var/obj/item/clothing/helmet = mod.get_part_from_slot(ITEM_SLOT_HEAD) + if(istype(helmet)) + helmet.flags_cover = former_flags + helmet.visor_flags_cover = former_visor_flags mod.wearer.update_clothing(mod.slot_flags) +/obj/item/mod/module/hat_stabilizer/syndicate + name = "MOD elite hat stabilizer module" + desc = "A simple set of deployable stands, directly atop one's head; \ + these will deploy under a hat to keep it from falling off, allowing them to be worn atop the sealed helmet. \ + You still need to take the hat off your head while the helmet deploys, though. This is a must-have for \ + Syndicate Operatives and Agents alike, enabling them to continue to style on the opposition even while in their MODsuit." + complexity = 0 + removable = FALSE + ///Sign Language Translator - allows people to sign over comms using the modsuit's gloves. /obj/item/mod/module/signlang_radio name = "MOD glove translator module" @@ -745,6 +782,7 @@ complexity = 1 idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 incompatible_modules = list(/obj/item/mod/module/signlang_radio) + required_slots = list(ITEM_SLOT_GLOVES) /obj/item/mod/module/signlang_radio/on_suit_activation() ADD_TRAIT(mod.wearer, TRAIT_CAN_SIGN_ON_COMMS, MOD_TRAIT) @@ -759,6 +797,7 @@ icon_state = "joint_torsion" complexity = 1 incompatible_modules = list(/obj/item/mod/module/joint_torsion) + required_slots = list(ITEM_SLOT_FEET) var/power_per_step = DEFAULT_CHARGE_DRAIN * 0.3 /obj/item/mod/module/joint_torsion/on_suit_activation() @@ -788,15 +827,6 @@ return mod.core.add_charge(power_per_step) -/obj/item/mod/module/hat_stabilizer/syndicate - name = "MOD elite hat stabilizer module" - desc = "A simple set of deployable stands, directly atop one's head; \ - these will deploy under a hat to keep it from falling off, allowing them to be worn atop the sealed helmet. \ - You still need to take the hat off your head while the helmet deploys, though. This is a must-have for \ - Syndicate Operatives and Agents alike, enabling them to continue to style on the opposition even while in their MODsuit." - complexity = 0 - removable = FALSE - /// Module that shoves garbage inside its material container when the user crosses it, and eject the recycled material with MMB. /obj/item/mod/module/recycler name = "MOD recycler module" @@ -810,6 +840,7 @@ incompatible_modules = list(/obj/item/mod/module/recycler) overlay_state_inactive = "module_recycler" overlay_state_active = "module_recycler" + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// A multiplier of the amount of material extracted from the item var/efficiency = 1 /// Items that will be collected diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm index 1f7c322e031f7..0d45bc4a44db8 100644 --- a/code/modules/mod/modules/modules_maint.dm +++ b/code/modules/mod/modules/modules_maint.dm @@ -10,6 +10,7 @@ icon_state = "springlock" complexity = 3 // it is inside every part of your suit, so incompatible_modules = list(/obj/item/mod/module/springlock) + var/set_off = FALSE /obj/item/mod/module/springlock/on_install() mod.activation_step_time *= 0.5 @@ -27,12 +28,13 @@ /obj/item/mod/module/springlock/proc/on_wearer_exposed(atom/source, list/reagents, datum/reagents/source_reagents, methods, volume_modifier, show_message) SIGNAL_HANDLER - if(!(methods & (VAPOR|PATCH|TOUCH))) + if(!(methods & (VAPOR|PATCH|TOUCH)) || set_off || mod.wearer.stat == DEAD) return //remove non-touch reagent exposure to_chat(mod.wearer, span_danger("[src] makes an ominous click sound...")) playsound(src, 'sound/items/modsuit/springlock.ogg', 75, TRUE) addtimer(CALLBACK(src, PROC_REF(snap_shut)), rand(3 SECONDS, 5 SECONDS)) RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_activate_spring_block)) + set_off = TRUE ///Signal fired when wearer attempts to activate/deactivate suits /obj/item/mod/module/springlock/proc/on_activate_spring_block(datum/source, user) @@ -55,6 +57,7 @@ mod.wearer.investigate_log("has been killed by [src].", INVESTIGATE_DEATHS) mod.wearer.death() //just in case, for some reason, they're still alive flash_color(mod.wearer, flash_color = "#FF0000", flash_time = 10 SECONDS) + set_off = FALSE ///Rave Visor - Gives you a rainbow visor and plays jukebox music to you. /obj/item/mod/module/visor/rave @@ -63,6 +66,7 @@ icon_state = "rave_visor" complexity = 1 overlay_state_inactive = "module_rave" + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_MASK) /// The client colors applied to the wearer. var/datum/client_colour/rave_screen /// The current element in the rainbow_order list we are on. @@ -90,17 +94,11 @@ return ..() /obj/item/mod/module/visor/rave/on_activation() - . = ..() - if(!.) - return rave_screen = mod.wearer.add_client_colour(/datum/client_colour/rave) rave_screen.update_colour(rainbow_order[rave_number]) music_player.start_music(mod.wearer) /obj/item/mod/module/visor/rave/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return QDEL_NULL(rave_screen) if(isnull(music_player.active_song_sound)) return @@ -151,11 +149,9 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 incompatible_modules = list(/obj/item/mod/module/tanner) cooldown_time = 30 SECONDS + required_slots = list(ITEM_SLOT_OCLOTHING|ITEM_SLOT_ICLOTHING) /obj/item/mod/module/tanner/on_use() - . = ..() - if(!.) - return playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, TRUE) var/datum/reagents/holder = new() holder.add_reagent(/datum/reagent/spraytan, 10) @@ -174,16 +170,17 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 0.5 incompatible_modules = list(/obj/item/mod/module/balloon) cooldown_time = 15 SECONDS + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_MASK) + var/balloon_path = /obj/item/toy/balloon + var/blowing_time = 10 SECONDS + var/oxygen_damage = 20 /obj/item/mod/module/balloon/on_use() - . = ..() - if(!.) - return - if(!do_after(mod.wearer, 10 SECONDS, target = mod)) + if(!do_after(mod.wearer, blowing_time, target = mod)) return FALSE - mod.wearer.adjustOxyLoss(20) + mod.wearer.adjustOxyLoss(oxygen_damage) playsound(src, 'sound/items/modsuit/inflate_bloon.ogg', 50, TRUE) - var/obj/item/toy/balloon/balloon = new(get_turf(src)) + var/obj/item/balloon = new balloon_path(get_turf(src)) mod.wearer.put_in_hands(balloon) drain_power(use_energy_cost) @@ -198,13 +195,11 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 0.5 incompatible_modules = list(/obj/item/mod/module/paper_dispenser) cooldown_time = 5 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) /// The total number of sheets created by this MOD. The more sheets, them more likely they set on fire. var/num_sheets_dispensed = 0 /obj/item/mod/module/paper_dispenser/on_use() - . = ..() - if(!.) - return if(!do_after(mod.wearer, 1 SECONDS, target = mod)) return FALSE @@ -243,6 +238,7 @@ device = /obj/item/stamp/mod incompatible_modules = list(/obj/item/mod/module/stamp) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) /obj/item/stamp/mod name = "MOD electronic stamp" @@ -267,15 +263,13 @@ incompatible_modules = list(/obj/item/mod/module/atrocinator, /obj/item/mod/module/magboot, /obj/item/mod/module/anomaly_locked/antigrav) cooldown_time = 0.5 SECONDS overlay_state_inactive = "module_atrocinator" + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// How many steps the user has taken since turning the suit on, used for footsteps. var/step_count = 0 /// If you use the module on a planetary turf, you fly up. To the sky. var/you_fucked_up = FALSE /obj/item/mod/module/atrocinator/on_activation() - . = ..() - if(!.) - return playsound(src, 'sound/effects/curseattack.ogg', 50) mod.wearer.AddElement(/datum/element/forced_gravity, NEGATIVE_GRAVITY) RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(check_upstairs)) @@ -284,14 +278,14 @@ passtable_on(mod.wearer, MOD_TRAIT) check_upstairs() //todo at some point flip your screen around -/obj/item/mod/module/atrocinator/on_deactivation(display_message = TRUE, deleting = FALSE) +/obj/item/mod/module/atrocinator/deactivate(display_message = TRUE, deleting = FALSE) if(you_fucked_up && !deleting) to_chat(mod.wearer, span_danger("It's too late.")) return FALSE - . = ..() - if(!.) - return - if(deleting) + return ..() + +/obj/item/mod/module/atrocinator/on_deactivation(display_message = TRUE, deleting = FALSE) + if(!deleting) playsound(src, 'sound/effects/curseattack.ogg', 50) qdel(mod.wearer.RemoveElement(/datum/element/forced_gravity, NEGATIVE_GRAVITY)) UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED) diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm index 0e04de51c86cf..3cf1d34a63a83 100644 --- a/code/modules/mod/modules/modules_medical.dm +++ b/code/modules/mod/modules/modules_medical.dm @@ -9,8 +9,7 @@ name = "MOD health analyzer module" desc = "A module installed into the glove of the suit. This is a high-tech biological scanning suite, \ allowing the user indepth information on the vitals and injuries of others even at a distance, \ - all with the flick of the wrist. Data is displayed in a convenient package on HUD in the helmet, \ - but it's up to you to do something with it." + all with the flick of the wrist. Data is displayed in a convenient package, but it's up to you to do something with it." icon_state = "health" module_type = MODULE_ACTIVE complexity = 1 @@ -18,6 +17,7 @@ incompatible_modules = list(/obj/item/mod/module/health_analyzer) cooldown_time = 0.5 SECONDS tgui_id = "health_analyzer" + required_slots = list(ITEM_SLOT_GLOVES) /// Scanning mode, changes how we scan something. var/mode = HEALTH_SCAN @@ -74,6 +74,7 @@ complexity = 1 idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 incompatible_modules = list(/obj/item/mod/module/quick_carry, /obj/item/mod/module/constructor) + required_slots = list(ITEM_SLOT_GLOVES) var/quick_carry_trait = TRAIT_QUICK_CARRY /obj/item/mod/module/quick_carry/on_suit_activation() @@ -105,6 +106,7 @@ device = /obj/item/reagent_containers/syringe/mod incompatible_modules = list(/obj/item/mod/module/injector) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) /obj/item/reagent_containers/syringe/mod name = "MOD injector syringe" @@ -131,6 +133,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN incompatible_modules = list(/obj/item/mod/module/organ_thrower, /obj/item/mod/module/microwave_beam) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) /// How many organs the module can hold. var/max_organs = 5 /// A list of all our organs. @@ -247,6 +250,7 @@ overlay_state_active = "module_defibrillator_active" incompatible_modules = list(/obj/item/mod/module/defibrillator) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) var/defib_cooldown = 5 SECONDS /obj/item/mod/module/defibrillator/Initialize(mapload) @@ -308,6 +312,7 @@ incompatible_modules = list(/obj/item/mod/module/thread_ripper) cooldown_time = 1.5 SECONDS overlay_state_inactive = "module_threadripper" + required_slots = list(ITEM_SLOT_GLOVES) /// An associated list of ripped clothing and the body part covering slots they covered before var/list/ripped_clothing = list() @@ -357,7 +362,7 @@ playsound(src, 'sound/items/zip.ogg', 25, TRUE) balloon_alert(mod.wearer, "clothing mended") -/obj/item/mod/module/thread_ripper/on_suit_deactivation(deleting) +/obj/item/mod/module/thread_ripper/on_suit_deactivation(deleting = FALSE) if(!length(ripped_clothing)) return for(var/obj/item/clothing as anything in ripped_clothing) diff --git a/code/modules/mod/modules/modules_ninja.dm b/code/modules/mod/modules/modules_ninja.dm index d52a5e1fb4c43..a868eb6205659 100644 --- a/code/modules/mod/modules/modules_ninja.dm +++ b/code/modules/mod/modules/modules_ninja.dm @@ -13,15 +13,13 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 10 incompatible_modules = list(/obj/item/mod/module/stealth) cooldown_time = 5 SECONDS + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// Whether or not the cloak turns off on bumping. var/bumpoff = TRUE /// The alpha applied when the cloak is on. var/stealth_alpha = 50 /obj/item/mod/module/stealth/on_activation() - . = ..() - if(!.) - return if(bumpoff) RegisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP, PROC_REF(unstealth)) RegisterSignal(mod.wearer, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(on_unarmed_attack)) @@ -31,9 +29,6 @@ drain_power(use_energy_cost) /obj/item/mod/module/stealth/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return if(bumpoff) UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP) UnregisterSignal(mod.wearer, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_MOB_ITEM_ATTACK, COMSIG_ATOM_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED)) @@ -45,7 +40,7 @@ to_chat(mod.wearer, span_warning("[src] gets discharged from contact!")) do_sparks(2, TRUE, src) drain_power(use_energy_cost) - on_deactivation(display_message = TRUE, deleting = FALSE) + deactivate() /obj/item/mod/module/stealth/proc/on_unarmed_attack(datum/source, atom/target) SIGNAL_HANDLER @@ -99,6 +94,7 @@ removable = FALSE complexity = 0 overlay_state_inactive = null + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_EYES|ITEM_SLOT_MASK) /obj/item/mod/module/welding/camera_vision/on_suit_activation() . = ..() @@ -133,6 +129,7 @@ icon_state = "hacker" removable = FALSE incompatible_modules = list(/obj/item/mod/module/hacker) + required_slots = list(ITEM_SLOT_GLOVES) /// Whether or not the communication console hack was used to summon another antagonist. var/communication_console_hack_success = FALSE /// How many times the module has been used to force open doors. @@ -173,6 +170,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 2 incompatible_modules = list(/obj/item/mod/module/weapon_recall) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_GLOVES, ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// The item linked to the module that will get recalled. var/obj/item/linked_weapon /// The accepted typepath we can link to. @@ -185,9 +183,6 @@ REMOVE_TRAIT(mod.wearer, TRAIT_NOGUNS, MOD_TRAIT) /obj/item/mod/module/weapon_recall/on_use() - . = ..() - if(!.) - return if(!linked_weapon) var/obj/item/weapon_to_link = mod.wearer.is_holding_item_of_type(accepted_type) if(!weapon_to_link) @@ -288,9 +283,6 @@ cooldown_time = 8 SECONDS /obj/item/mod/module/emp_shield/pulse/on_use() - . = ..() - if(!.) - return playsound(src, 'sound/effects/empulse.ogg', 60, TRUE) empulse(src, heavy_range = 4, light_range = 6) drain_power(use_energy_cost) @@ -320,6 +312,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 6 incompatible_modules = list(/obj/item/mod/module/energy_net) cooldown_time = 5 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) /// List of all energy nets this module made. var/list/energy_nets = list() @@ -404,6 +397,7 @@ allow_flags = MODULE_ALLOW_INCAPACITATED incompatible_modules = list(/obj/item/mod/module/adrenaline_boost) cooldown_time = 12 SECONDS + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// What reagent we need to refill? var/reagent_required = /datum/reagent/uranium/radium /// How much of a reagent we need to refill the boost. @@ -414,13 +408,13 @@ create_reagents(reagent_required_amount) reagents.add_reagent(reagent_required, reagent_required_amount) -/obj/item/mod/module/adrenaline_boost/on_use() +/obj/item/mod/module/adrenaline_boost/used() if(!reagents.has_reagent(reagent_required, reagent_required_amount)) balloon_alert(mod.wearer, "no charge!") - return - . = ..() - if(!.) - return + return FALSE + return ..() + +/obj/item/mod/module/adrenaline_boost/on_use() if(IS_SPACE_NINJA(mod.wearer)) mod.wearer.say(pick_list_replacements(NINJA_FILE, "lines"), forced = type) to_chat(mod.wearer, span_notice("You have used the adrenaline boost.")) @@ -438,7 +432,7 @@ /obj/item/mod/module/adrenaline_boost/on_install() RegisterSignal(mod, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby)) -/obj/item/mod/module/adrenaline_boost/on_uninstall(deleting) +/obj/item/mod/module/adrenaline_boost/on_uninstall(deleting = FALSE) UnregisterSignal(mod, COMSIG_ATOM_ATTACKBY) /obj/item/mod/module/adrenaline_boost/attackby(obj/item/attacking_item, mob/user, params) diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm index a5a56975f6c53..9f2c54b8effc3 100644 --- a/code/modules/mod/modules/modules_science.dm +++ b/code/modules/mod/modules/modules_science.dm @@ -12,17 +12,12 @@ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 incompatible_modules = list(/obj/item/mod/module/reagent_scanner) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_EYES|ITEM_SLOT_MASK) /obj/item/mod/module/reagent_scanner/on_activation() - . = ..() - if(!.) - return ADD_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT) /obj/item/mod/module/reagent_scanner/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return REMOVE_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT) /obj/item/mod/module/reagent_scanner/advanced @@ -32,16 +27,10 @@ var/explosion_detection_dist = 21 /obj/item/mod/module/reagent_scanner/advanced/on_activation() - . = ..() - if(!.) - return ADD_TRAIT(mod.wearer, TRAIT_RESEARCH_SCANNER, MOD_TRAIT) RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, PROC_REF(sense_explosion)) /obj/item/mod/module/reagent_scanner/advanced/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return REMOVE_TRAIT(mod.wearer, TRAIT_RESEARCH_SCANNER, MOD_TRAIT) UnregisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION) @@ -66,20 +55,15 @@ incompatible_modules = list(/obj/item/mod/module/anomaly_locked, /obj/item/mod/module/atrocinator) cooldown_time = 0.5 SECONDS accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav) + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /obj/item/mod/module/anomaly_locked/antigrav/on_activation() - . = ..() - if(!.) - return if(mod.wearer.has_gravity()) new /obj/effect/temp_visual/mook_dust(get_turf(src)) mod.wearer.AddElement(/datum/element/forced_gravity, 0) playsound(src, 'sound/effects/gravhit.ogg', 50) /obj/item/mod/module/anomaly_locked/antigrav/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return mod.wearer.RemoveElement(/datum/element/forced_gravity, 0) if(deleting) return @@ -103,6 +87,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 cooldown_time = 5 SECONDS accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/bluespace) + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// Time it takes to teleport var/teleport_time = 3 SECONDS diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm index 6d91b24469ba0..2a317becf18e6 100644 --- a/code/modules/mod/modules/modules_security.dm +++ b/code/modules/mod/modules/modules_security.dm @@ -8,6 +8,7 @@ complexity = 2 use_energy_cost = DEFAULT_CHARGE_DRAIN incompatible_modules = list(/obj/item/mod/module/magnetic_harness) + required_slots = list(ITEM_SLOT_OCLOTHING) /// Time before we activate the magnet. var/magnet_delay = 0.8 SECONDS /// The typecache of all guns we allow. @@ -21,13 +22,19 @@ guns_typecache = typecacheof(list(/obj/item/gun/ballistic, /obj/item/gun/energy, /obj/item/gun/grenadelauncher, /obj/item/gun/chem, /obj/item/gun/syringe)) /obj/item/mod/module/magnetic_harness/on_install() - already_allowed_guns = guns_typecache & mod.chestplate.allowed - mod.chestplate.allowed |= guns_typecache + var/obj/item/clothing/suit = mod.get_part_from_slot(ITEM_SLOT_OCLOTHING) + if(!istype(suit)) + return + already_allowed_guns = guns_typecache & suit.allowed + suit.allowed |= guns_typecache /obj/item/mod/module/magnetic_harness/on_uninstall(deleting = FALSE) if(deleting) return - mod.chestplate.allowed -= (guns_typecache - already_allowed_guns) + var/obj/item/clothing/suit = mod.get_part_from_slot(ITEM_SLOT_OCLOTHING) + if(!istype(suit)) + return + suit.allowed -= (guns_typecache - already_allowed_guns) /obj/item/mod/module/magnetic_harness/on_suit_activation() RegisterSignal(mod.wearer, COMSIG_MOB_UNEQUIPPED_ITEM, PROC_REF(check_dropped_item)) @@ -65,6 +72,7 @@ cooldown_time = 5 SECONDS overlay_state_inactive = "module_pepper" overlay_state_use = "module_pepper_used" + required_slots = list(ITEM_SLOT_OCLOTHING) /obj/item/mod/module/pepper_shoulders/on_suit_activation() RegisterSignal(mod.wearer, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(on_check_block)) @@ -73,9 +81,6 @@ UnregisterSignal(mod.wearer, COMSIG_LIVING_CHECK_BLOCK) /obj/item/mod/module/pepper_shoulders/on_use() - . = ..() - if(!.) - return playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6) var/datum/reagents/capsaicin_holder = new(10) capsaicin_holder.add_reagent(/datum/reagent/consumable/condensedcapsaicin, 10) @@ -92,7 +97,7 @@ if(!check_power(use_energy_cost)) return mod.wearer.visible_message(span_warning("[src] reacts to the attack with a smoke of pepper spray!"), span_notice("Your [src] releases a cloud of pepper spray!")) - on_use() + used() ///Holster - Instantly holsters any not huge gun. /obj/item/mod/module/holster @@ -107,13 +112,11 @@ incompatible_modules = list(/obj/item/mod/module/holster) cooldown_time = 0.5 SECONDS allow_flags = MODULE_ALLOW_INACTIVE + required_slots = list(ITEM_SLOT_OCLOTHING|ITEM_SLOT_GLOVES|ITEM_SLOT_FEET) /// Gun we have holstered. var/obj/item/gun/holstered /obj/item/mod/module/holster/on_use() - . = ..() - if(!.) - return if(!holstered) var/obj/item/gun/holding = mod.wearer.get_active_held_item() if(!holding) @@ -155,19 +158,14 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 0.5 incompatible_modules = list(/obj/item/mod/module/megaphone) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_EYES|ITEM_SLOT_MASK) /// List of spans we add to the speaker. var/list/voicespan = list(SPAN_COMMAND) /obj/item/mod/module/megaphone/on_activation() - . = ..() - if(!.) - return RegisterSignal(mod.wearer, COMSIG_MOB_SAY, PROC_REF(handle_speech)) /obj/item/mod/module/megaphone/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return UnregisterSignal(mod.wearer, COMSIG_MOB_SAY) /obj/item/mod/module/megaphone/proc/handle_speech(datum/source, list/speech_args) @@ -190,6 +188,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 0.5 incompatible_modules = list(/obj/item/mod/module/criminalcapture) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// Time to capture a prisoner. var/capture_time = 2.5 SECONDS /// Time to dematerialize a bodybag. @@ -203,10 +202,7 @@ idle_power_cost = linked_bodybag ? (DEFAULT_CHARGE_DRAIN * 3) : 0 return ..() -/obj/item/mod/module/criminalcapture/on_deactivation(display_message, deleting) - . = ..() - if(!.) - return +/obj/item/mod/module/criminalcapture/on_deactivation(display_message = TRUE, deleting = FALSE) if(!linked_bodybag) return packup() @@ -273,9 +269,6 @@ dispense_type = /obj/item/grenade/mirage /obj/item/mod/module/dispenser/mirage/on_use() - . = ..() - if(!.) - return var/obj/item/grenade/mirage/grenade = . grenade.arm_grenade(mod.wearer) @@ -310,6 +303,7 @@ active_power_cost = DEFAULT_CHARGE_DRAIN incompatible_modules = list(/obj/item/mod/module/projectile_dampener) cooldown_time = 1.5 SECONDS + required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /// Radius of the dampening field. var/field_radius = 2 /// Damage multiplier on projectiles. @@ -328,9 +322,6 @@ projectile_effect = image('icons/effects/fields.dmi', "projectile_dampen_effect") /obj/item/mod/module/projectile_dampener/on_activation() - . = ..() - if(!.) - return if(istype(dampening_field)) QDEL_NULL(dampening_field) dampening_field = new(mod.wearer, field_radius, TRUE, src) @@ -370,6 +361,7 @@ complexity = 2 incompatible_modules = list(/obj/item/mod/module/active_sonar) cooldown_time = 15 SECONDS + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_EYES|ITEM_SLOT_MASK) /// Time between us displaying radial scans var/scan_cooldown_time = 0.5 SECONDS /// The current slice we're going to scan @@ -454,9 +446,6 @@ COOLDOWN_START(src, scan_cooldown, scan_cooldown_time) /obj/item/mod/module/active_sonar/on_use() - . = ..() - if(!.) - return balloon_alert(mod.wearer, "readying sonar...") playsound(mod.wearer, 'sound/mecha/skyfall_power_up.ogg', vol = 20, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) if(!do_after(mod.wearer, 1.1 SECONDS, target = mod)) @@ -486,6 +475,7 @@ module_type = MODULE_PASSIVE complexity = 3 incompatible_modules = list(/obj/item/mod/module/shooting_assistant) + required_slots = list(ITEM_SLOT_GLOVES) var/selected_mode = SHOOTING_ASSISTANT_OFF ///Association list, the assoc values are the balloon alerts shown to the user when the mode is set. var/static/list/available_modes = list( @@ -585,6 +575,7 @@ icon_state = "bulwark" complexity = 3 incompatible_modules = list(/obj/item/mod/module/shove_blocker) + required_slots = list(ITEM_SLOT_OCLOTHING) /obj/item/mod/module/shove_blocker/on_suit_activation() mod.wearer.add_traits(list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), MOD_TRAIT) @@ -603,6 +594,7 @@ desc = "Enhanced gauntlent grip pads that help with placing individuals in restraints more quickly. Doesn't look like they'll come off." removable = FALSE complexity = 0 + required_slots = list(ITEM_SLOT_GLOVES) /obj/item/mod/module/quick_cuff/on_suit_activation() . = ..() diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm index be71c62180298..044137f0f2d07 100644 --- a/code/modules/mod/modules/modules_service.dm +++ b/code/modules/mod/modules/modules_service.dm @@ -13,36 +13,17 @@ cooldown_time = 1 SECONDS /obj/item/mod/module/bikehorn/on_use() - . = ..() - if(!.) - return playsound(src, 'sound/items/bikehorn.ogg', 100, FALSE) drain_power(use_energy_cost) ///Advanced Balloon Blower - Blows a long balloon. -/obj/item/mod/module/balloon_advanced +/obj/item/mod/module/balloon/advanced name = "MOD advanced balloon blower module" desc = "A relatively new piece of technology developed by finest clown engineers to make long balloons and balloon animals \ - at party-appropriate rate." - icon_state = "bloon" - module_type = MODULE_USABLE - complexity = 1 - use_energy_cost = DEFAULT_CHARGE_DRAIN * 0.5 - incompatible_modules = list(/obj/item/mod/module/balloon_advanced) - cooldown_time = 15 SECONDS - -/obj/item/mod/module/balloon_advanced/on_use() - . = ..() - if(!.) - return - if(!do_after(mod.wearer, 15 SECONDS, target = mod)) - return FALSE - mod.wearer.adjustOxyLoss(20) - playsound(src, 'sound/items/modsuit/inflate_bloon.ogg', 50, TRUE) - var/obj/item/toy/balloon/long/l_balloon = new(get_turf(src)) - mod.wearer.put_in_hands(l_balloon) - drain_power(use_energy_cost) - + at party-appropriate rate." + cooldown_time = 20 SECONDS + balloon_path = /obj/item/toy/balloon/long + blowing_time = 15 SECONDS ///Microwave Beam - Microwaves items instantly. /obj/item/mod/module/microwave_beam @@ -56,6 +37,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 incompatible_modules = list(/obj/item/mod/module/microwave_beam, /obj/item/mod/module/organ_thrower) cooldown_time = 10 SECONDS + required_slots = list(ITEM_SLOT_GLOVES) /obj/item/mod/module/microwave_beam/on_select_use(atom/target) . = ..() @@ -84,7 +66,7 @@ /obj/item/mod/module/waddle name = "MOD waddle module" desc = "Some of the most primitive technology in use by Honk Co. This module works off an automatic intention system, \ - utilizing its' sensitivity to the pilot's often-limited brainwaves to directly read their next step, \ + utilizing its sensitivity to the pilot's often-limited brainwaves to directly read their next step, \ affecting the boots they're installed in. Employing a twin-linked gravitonic drive to create \ miniaturized etheric blasts of space-time beneath the user's feet, this enables them to... \ to waddle around, bouncing to and fro with a pep in their step." @@ -92,16 +74,20 @@ complexity = 1 idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 incompatible_modules = list(/obj/item/mod/module/waddle) + required_slots = list(ITEM_SLOT_FEET) /obj/item/mod/module/waddle/on_suit_activation() - mod.boots.AddComponent(/datum/component/squeak, list('sound/effects/footstep/clownstep1.ogg'=1,'sound/effects/footstep/clownstep2.ogg'=1), 50, falloff_exponent = 20) //die off quick please + var/obj/item/shoes = mod.get_part_from_slot(ITEM_SLOT_FEET) + if(shoes) + shoes.AddComponent(/datum/component/squeak, list('sound/effects/footstep/clownstep1.ogg'=1,'sound/effects/footstep/clownstep2.ogg'=1), 50, falloff_exponent = 20) //die off quick please mod.wearer.AddElementTrait(TRAIT_WADDLING, MOD_TRAIT, /datum/element/waddling) if(is_clown_job(mod.wearer.mind?.assigned_role)) mod.wearer.add_mood_event("clownshoes", /datum/mood_event/clownshoes) /obj/item/mod/module/waddle/on_suit_deactivation(deleting = FALSE) - if(!deleting) - qdel(mod.boots.GetComponent(/datum/component/squeak)) + var/obj/item/shoes = mod.get_part_from_slot(ITEM_SLOT_FEET) + if(shoes && !deleting) + qdel(shoes.GetComponent(/datum/component/squeak)) REMOVE_TRAIT(mod.wearer, TRAIT_WADDLING, MOD_TRAIT) if(is_clown_job(mod.wearer.mind?.assigned_role)) mod.wearer.clear_mood_event("clownshoes") diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm index de1efdcc4e2fd..c86cb182c6b84 100644 --- a/code/modules/mod/modules/modules_supply.dm +++ b/code/modules/mod/modules/modules_supply.dm @@ -19,9 +19,6 @@ AddComponent(/datum/component/gps/item, "MOD0", state = GLOB.deep_inventory_state, overlay_state = FALSE) /obj/item/mod/module/gps/on_use() - . = ..() - if(!.) - return attack_self(mod.wearer) ///Hydraulic Clamp - Lets you pick up and drop crates. @@ -38,6 +35,7 @@ cooldown_time = 0.5 SECONDS overlay_state_inactive = "module_clamp" overlay_state_active = "module_clamp_on" + required_slots = list(ITEM_SLOT_GLOVES, ITEM_SLOT_BACK) /// Time it takes to load a crate. var/load_time = 3 SECONDS /// The max amount of crates you can carry. @@ -110,6 +108,7 @@ load_time = 1 SECONDS max_crates = 5 use_mod_colors = TRUE + required_slots = list(ITEM_SLOT_BACK) ///Drill - Lets you dig through rock and basalt. /obj/item/mod/module/drill @@ -123,17 +122,12 @@ incompatible_modules = list(/obj/item/mod/module/drill) cooldown_time = 0.5 SECONDS overlay_state_active = "module_drill" + required_slots = list(ITEM_SLOT_GLOVES) /obj/item/mod/module/drill/on_activation() - . = ..() - if(!.) - return RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, PROC_REF(bump_mine)) /obj/item/mod/module/drill/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP) /obj/item/mod/module/drill/on_select_use(atom/target) @@ -180,6 +174,7 @@ incompatible_modules = list(/obj/item/mod/module/orebag) cooldown_time = 0.5 SECONDS allow_flags = MODULE_ALLOW_INACTIVE + required_slots = list(ITEM_SLOT_BACK) /// The ores stored in the bag. var/list/ores = list() @@ -208,9 +203,6 @@ ores += ore /obj/item/mod/module/orebag/on_use() - . = ..() - if(!.) - return for(var/obj/item/ore as anything in ores) ore.forceMove(drop_location()) ores -= ore @@ -228,6 +220,7 @@ overlay_state_inactive = "module_hydraulic" overlay_state_active = "module_hydraulic_active" use_mod_colors = TRUE + required_slots = list(ITEM_SLOT_BACK) /// Time it takes to launch var/launch_time = 2 SECONDS /// User overlay @@ -316,6 +309,7 @@ cooldown_time = 1.5 SECONDS overlay_state_active = "module_magnet" use_mod_colors = TRUE + required_slots = list(ITEM_SLOT_BACK) /obj/item/mod/module/magnet/on_select_use(atom/target) . = ..() @@ -340,9 +334,6 @@ callback = CALLBACK(src, PROC_REF(check_locker), locker)) /obj/item/mod/module/magnet/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return if(istype(mod.wearer.pulling, /obj/structure/closet)) mod.wearer.stop_pulling() @@ -370,6 +361,7 @@ incompatible_modules = list(/obj/item/mod/module/ash_accretion) overlay_state_inactive = "module_ash" use_mod_colors = TRUE + required_slots = list(ITEM_SLOT_OCLOTHING|ITEM_SLOT_ICLOTHING) /// How many tiles we can travel to max out the armor. var/max_traveled_tiles = 10 /// How many tiles we traveled through. @@ -423,9 +415,8 @@ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED) if(!traveled_tiles) return - var/list/parts = mod.mod_parts + mod var/datum/armor/to_remove = get_armor_by_type(armor_mod) - for(var/obj/item/part as anything in parts) + 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 @@ -445,8 +436,7 @@ if(traveled_tiles >= max_traveled_tiles) return traveled_tiles++ - var/list/parts = mod.mod_parts + mod - for(var/obj/item/part as anything in parts) + for(var/obj/item/part as anything in mod.get_parts(all = TRUE)) part.set_armor(part.get_armor().add_other_armor(armor_mod)) if(traveled_tiles >= max_traveled_tiles) balloon_alert(mod.wearer, "fully ash covered") @@ -465,8 +455,7 @@ mod.slowdown += actual_speed_added mod.wearer.update_equipment_speed_mods() traveled_tiles-- - var/list/parts = mod.mod_parts + mod - for(var/obj/item/part as anything in parts) + 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) balloon_alert(mod.wearer, "ran out of ash!") @@ -482,6 +471,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 3 incompatible_modules = list(/obj/item/mod/module/sphere_transform) cooldown_time = 1.25 SECONDS + required_slots = list(ITEM_SLOT_OCLOTHING|ITEM_SLOT_ICLOTHING, ITEM_SLOT_HEAD|ITEM_SLOT_MASK) /// Time it takes us to complete the animation. var/animate_time = 0.25 SECONDS /// List of traits to add/remove from our subject as needed. @@ -492,13 +482,13 @@ TRAIT_NO_SLIP_ALL, ) -/obj/item/mod/module/sphere_transform/on_activation() +/obj/item/mod/module/sphere_transform/activate() if(!mod.wearer.has_gravity()) balloon_alert(mod.wearer, "no gravity!") return FALSE - . = ..() - if(!.) - return + return ..() + +/obj/item/mod/module/sphere_transform/on_activation() playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE) mod.wearer.add_filter("mod_ball", 1, alpha_mask_filter(icon = icon('icons/mob/clothing/modsuit/mod_modules.dmi', "ball_mask"), flags = MASK_INVERSE)) mod.wearer.add_filter("mod_blur", 2, angular_blur_filter(size = 15)) @@ -513,9 +503,6 @@ RegisterSignal(mod.wearer, COMSIG_MOB_STATCHANGE, PROC_REF(on_statchange)) /obj/item/mod/module/sphere_transform/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return if(!deleting) playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE, frequency = -1) mod.wearer.base_pixel_y += 4 @@ -528,7 +515,7 @@ mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/sphere) UnregisterSignal(mod.wearer, COMSIG_MOB_STATCHANGE) -/obj/item/mod/module/sphere_transform/on_use() +/obj/item/mod/module/sphere_transform/used() if(!lavaland_equipment_pressure_check(get_turf(src))) balloon_alert(mod.wearer, "too much pressure!") playsound(src, 'sound/weapons/gun/general/dry_fire.ogg', 25, TRUE) @@ -550,14 +537,14 @@ animate(mod.wearer) //stop the animation mod.wearer.SpinAnimation(1.5) //start it back again if(!mod.wearer.has_gravity()) - on_deactivation() //deactivate in no grav + deactivate() //deactivate in no grav /obj/item/mod/module/sphere_transform/proc/on_statchange(datum/source) SIGNAL_HANDLER if(!mod.wearer.stat) return - on_deactivation() + deactivate() /obj/projectile/bullet/mining_bomb name = "mining bomb" diff --git a/code/modules/mod/modules/modules_timeline.dm b/code/modules/mod/modules/modules_timeline.dm index 4e4d751065c71..522ddf57501d1 100644 --- a/code/modules/mod/modules/modules_timeline.dm +++ b/code/modules/mod/modules/modules_timeline.dm @@ -28,9 +28,6 @@ UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL) /obj/item/mod/module/eradication_lock/on_use() - . = ..() - if(!.) - return true_owner_ckey = mod.wearer.ckey balloon_alert(mod.wearer, "user remembered") drain_power(use_energy_cost) @@ -65,11 +62,9 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 incompatible_modules = list(/obj/item/mod/module/rewinder) cooldown_time = 20 SECONDS + required_slots = list(ITEM_SLOT_BACK) /obj/item/mod/module/rewinder/on_use() - . = ..() - if(!.) - return balloon_alert(mod.wearer, "anchor point set") playsound(src, 'sound/items/modsuit/time_anchor_set.ogg', 50, TRUE) //stops all mods from triggering during rewinding @@ -109,16 +104,17 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 incompatible_modules = list(/obj/item/mod/module/timestopper) cooldown_time = 60 SECONDS + required_slots = list(ITEM_SLOT_BACK) ///The current timestop in progress. var/obj/effect/timestop/channelled/timestop -/obj/item/mod/module/timestopper/on_use() - . = ..() - if(!.) - return +/obj/item/mod/module/timestopper/used() if(timestop) mod.balloon_alert(mod.wearer, "already freezing time!") - return + return FALSE + return ..() + +/obj/item/mod/module/timestopper/on_use() //stops all mods from triggering during timestop- including timestop itself for(var/obj/item/mod/module/module as anything in mod.modules) RegisterSignal(module, COMSIG_MODULE_TRIGGERED, PROC_REF(on_module_triggered)) @@ -157,18 +153,18 @@ incompatible_modules = list(/obj/item/mod/module/timeline_jumper) cooldown_time = 5 SECONDS allow_flags = MODULE_ALLOW_PHASEOUT + required_slots = list(ITEM_SLOT_BACK) ///The dummy for phasing from this module, the wearer is phased out while this exists. var/obj/effect/dummy/phased_mob/chrono/phased_mob -/obj/item/mod/module/timeline_jumper/on_use() - . = ..() - if(!.) - return +/obj/item/mod/module/timeline_jumper/used() var/area/noteleport_check = get_area(mod.wearer) if(noteleport_check && noteleport_check.area_flags & NOTELEPORT) to_chat(mod.wearer, span_danger("Some dull, universal force is between you and the [phased_mob ? "current timeline" : "stream between timelines"].")) return FALSE + return ..() +/obj/item/mod/module/timeline_jumper/on_use() if(!phased_mob) //phasing out mod.visible_message(span_warning("[mod.wearer] leaps out of the timeline!")) @@ -210,6 +206,7 @@ use_energy_cost = DEFAULT_CHARGE_DRAIN * 5 incompatible_modules = list(/obj/item/mod/module/tem) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_BACK) ///Reference to the chrono field being controlled by this module var/obj/structure/chrono_field/field = null ///Where the chronofield maker was when the field went up diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm index e8656fe92331a..4527fa631a65c 100644 --- a/code/modules/mod/modules/modules_visor.dm +++ b/code/modules/mod/modules/modules_visor.dm @@ -9,15 +9,13 @@ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 incompatible_modules = list(/obj/item/mod/module/visor) cooldown_time = 0.5 SECONDS + required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_EYES|ITEM_SLOT_MASK) /// The HUD type given by the visor. var/hud_type /// The traits given by the visor. var/list/visor_traits = list() /obj/item/mod/module/visor/on_activation() - . = ..() - if(!.) - return if(hud_type) var/datum/atom_hud/hud = GLOB.huds[hud_type] hud.show_to(mod.wearer) @@ -26,9 +24,6 @@ mod.wearer.update_sight() /obj/item/mod/module/visor/on_deactivation(display_message = TRUE, deleting = FALSE) - . = ..() - if(!.) - return if(hud_type) var/datum/atom_hud/hud = GLOB.huds[hud_type] hud.hide_from(mod.wearer) diff --git a/code/modules/power/apc/apc_attack.dm b/code/modules/power/apc/apc_attack.dm index acaaac1bd3eec..8c9715f1dcb1c 100644 --- a/code/modules/power/apc/apc_attack.dm +++ b/code/modules/power/apc/apc_attack.dm @@ -111,13 +111,11 @@ if(!HAS_SILICON_ACCESS(user)) return TRUE . = TRUE - var/mob/living/silicon/ai/AI = user - var/mob/living/silicon/robot/robot = user - if(istype(AI) || istype(robot)) + if(isAI(user) || iscyborg(user)) if(aidisabled) . = FALSE - else if(istype(malfai) && (malfai != AI || !(robot in malfai.connected_robots))) - . = FALSE + else if(istype(malfai) && !(malfai == user || (user in malfai.connected_robots))) + . = FALSE if (!. && !loud) balloon_alert(user, "it's disabled!") return . diff --git a/code/modules/power/apc/apc_tool_act.dm b/code/modules/power/apc/apc_tool_act.dm index 0116205fdd4ca..a82ce2f8f0d50 100644 --- a/code/modules/power/apc/apc_tool_act.dm +++ b/code/modules/power/apc/apc_tool_act.dm @@ -484,7 +484,7 @@ else if(machine_stat & (BROKEN|MAINT)) balloon_alert(user, "nothing happens!") else - if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && !malfhack && !remote_control_user) + if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && ((!malfhack && !remote_control_user) || (malfhack && (malfai == user || (user in malfai.connected_robots))))) locked = !locked balloon_alert(user, locked ? "locked" : "unlocked") update_appearance() diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index 2d552dbbe9edb..43713cc49ae0c 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -286,11 +286,17 @@ var/delay = rand(BROKEN_SPARKS_MIN, BROKEN_SPARKS_MAX) addtimer(CALLBACK(src, PROC_REF(broken_sparks)), delay, TIMER_UNIQUE | TIMER_NO_HASH_WAIT) +/obj/machinery/light/proc/is_full_charge() + if(cell) + return cell.charge == cell.maxcharge + return TRUE + /obj/machinery/light/process(seconds_per_tick) - if(has_power()) //If the light is being powered by the station. + if(has_power()) + // If the cell is done mooching station power, and reagents don't need processing, stop processing + if(is_full_charge() && !reagents) + return PROCESS_KILL if(cell) - if(cell.charge == cell.maxcharge && !reagents) //If the cell is done mooching station power, and reagents don't need processing, stop processing - return PROCESS_KILL charge_cell(LIGHT_EMERGENCY_POWER_USE * seconds_per_tick, cell = cell) //Recharge emergency power automatically while not using it if(reagents) //with most reagents coming out at 300, and with most meaningful reactions coming at 370+, this rate gives a few seconds of time to place it in and get out of dodge regardless of input. reagents.adjust_thermal_energy(8 * reagents.total_volume * SPECIFIC_HEAT_DEFAULT * seconds_per_tick) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 8bc358b43874c..877326fd98546 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -1,6 +1,7 @@ #define MOVES_HITSCAN -1 //Not actually hitscan but close as we get without actual hitscan. #define MUZZLE_EFFECT_PIXEL_INCREMENT 17 //How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers. +#define MAX_RANGE_HIT_PRONE_TARGETS 10 //How far do the projectile hits the prone mob /obj/projectile name = "projectile" @@ -191,8 +192,10 @@ var/shrapnel_type ///If we have a shrapnel_type defined, these embedding stats will be passed to the spawned shrapnel type, which will roll for embedding on the target var/list/embedding - ///If TRUE, hit mobs even if they're on the floor and not our target + ///If TRUE, hit mobs, even if they are lying on the floor and are not our target within MAX_RANGE_HIT_PRONE_TARGETS tiles var/hit_prone_targets = FALSE + ///if TRUE, ignores the range of MAX_RANGE_HIT_PRONE_TARGETS tiles of hit_prone_targets + var/ignore_range_hit_prone_targets = FALSE ///For what kind of brute wounds we're rolling for, if we're doing such a thing. Lasers obviously don't care since they do burn instead. var/sharpness = NONE ///How much we want to drop damage per tile as it travels through the air @@ -621,13 +624,15 @@ return FALSE if(HAS_TRAIT(living_target, TRAIT_IMMOBILIZED) && HAS_TRAIT(living_target, TRAIT_FLOORED) && HAS_TRAIT(living_target, TRAIT_HANDS_BLOCKED)) return FALSE - if(!hit_prone_targets) + if(hit_prone_targets) var/mob/living/buckled_to = living_target.lowest_buckled_mob() - if(!buckled_to.density) // Will just be us if we're not buckled to another mob - return FALSE - if(living_target.body_position != LYING_DOWN) + if((decayedRange - range) <= MAX_RANGE_HIT_PRONE_TARGETS) // after MAX_RANGE_HIT_PRONE_TARGETS tiles, auto-aim hit for mobs on the floor turns off return TRUE - return TRUE + if(ignore_range_hit_prone_targets) // doesn't apply to projectiles that must hit the target in combat mode or something else, no matter what + return TRUE + if(buckled_to.density) // Will just be us if we're not buckled to another mob + return TRUE + return FALSE /** * Scan if we should hit something and hit it if we need to @@ -1177,6 +1182,7 @@ #undef MOVES_HITSCAN #undef MUZZLE_EFFECT_PIXEL_INCREMENT +#undef MAX_RANGE_HIT_PRONE_TARGETS /// Fire a projectile from this atom at another atom /atom/proc/fire_projectile(projectile_type, atom/target, sound, firer, list/ignore_targets = list()) diff --git a/code/modules/projectiles/projectile/bullets/sniper.dm b/code/modules/projectiles/projectile/bullets/sniper.dm index 4425b20eeedc4..6118d90644d9e 100644 --- a/code/modules/projectiles/projectile/bullets/sniper.dm +++ b/code/modules/projectiles/projectile/bullets/sniper.dm @@ -9,6 +9,7 @@ dismemberment = 50 catastropic_dismemberment = TRUE armour_penetration = 50 + ignore_range_hit_prone_targets = TRUE ///Determines object damage. var/object_damage = 80 ///Determines how much additional damage the round does to mechs. diff --git a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm index 369efd99dcb35..7e47586e20c3b 100644 --- a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm @@ -386,6 +386,8 @@ if(the_reagent2 == src) continue var/amount2purge = 3 + if(holder.has_reagent(/datum/reagent/toxin/anacea)) + amount2purge = 0 if(medibonus >= 3 && istype(the_reagent2, /datum/reagent/medicine)) //3 unique meds (2+multiver) | (1 + pure multiver) will make it not purge medicines continue affected_mob.reagents.remove_reagent(the_reagent2.type, amount2purge * REM * seconds_per_tick) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index d4f26367a5a54..cf5f41ef82c66 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -1108,7 +1108,7 @@ . = ..() for(var/effect in status_effects_to_clear) affected_mob.remove_status_effect(effect) - affected_mob.reagents.remove_reagent(/datum/reagent/consumable/ethanol, 3 * REM * seconds_per_tick * normalise_creation_purity(), include_subtypes = TRUE) + affected_mob.reagents.remove_reagent(/datum/reagent/consumable/ethanol, 8 * REM * seconds_per_tick * normalise_creation_purity(), include_subtypes = TRUE) if(affected_mob.adjustToxLoss(-0.2 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype)) . = UPDATE_MOB_HEALTH affected_mob.adjust_drunk_effect(-10 * REM * seconds_per_tick * normalise_creation_purity()) diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index dee575cf06363..cf9c7ae38c64e 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -577,6 +577,7 @@ /datum/chemical_reaction/monkey required_reagents = list(/datum/reagent/monkey_powder = 50, /datum/reagent/water = 1) + reaction_flags = REACTION_INSTANT mix_message = "Expands into a brown mass before shaping itself into a monkey!." /datum/chemical_reaction/monkey/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index a3cda722a358d..e4bb7dfe769fe 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -318,18 +318,23 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) replacement.set_organ_damage(damage) /// Called by medical scanners to get a simple summary of how healthy the organ is. Returns an empty string if things are fine. -/obj/item/organ/proc/get_status_text() - var/status = "" +/obj/item/organ/proc/get_status_text(advanced) + if(advanced && (organ_flags & ORGAN_PROMINENT)) + return "Harmful Foreign Body" + if(owner.has_reagent(/datum/reagent/inverse/technetium)) - status = "[round((damage/maxHealth)*100, 1)]% damaged." - else if(organ_flags & ORGAN_FAILING) - status = "Non-Functional" - else if(damage > high_threshold) - status = "Severely Damaged" - else if (damage > low_threshold) - status = "Mildly Damaged" - - return status + return "[round((damage/maxHealth)*100, 1)]% damaged." + + if(organ_flags & ORGAN_FAILING) + return "Non-Functional" + + if(damage > high_threshold) + return "Severely Damaged" + + if (damage > low_threshold) + return "Mildly Damaged" + + return "" /// Tries to replace the existing organ on the passed mob with this one, with special handling for replacing a brain without ghosting target /obj/item/organ/proc/replace_into(mob/living/carbon/new_owner) diff --git a/code/modules/surgery/organs/internal/appendix/_appendix.dm b/code/modules/surgery/organs/internal/appendix/_appendix.dm index e5190f1282ec7..83ed8da84aca0 100644 --- a/code/modules/surgery/organs/internal/appendix/_appendix.dm +++ b/code/modules/surgery/organs/internal/appendix/_appendix.dm @@ -87,7 +87,7 @@ ADD_TRAIT(organ_owner, TRAIT_DISEASELIKE_SEVERITY_MEDIUM, type) organ_owner.med_hud_set_status() -/obj/item/organ/internal/appendix/get_status_text() +/obj/item/organ/internal/appendix/get_status_text(advanced) if((!(organ_flags & ORGAN_FAILING)) && inflamation_stage) return "Inflamed" else diff --git a/code/modules/unit_tests/modsuit.dm b/code/modules/unit_tests/modsuit.dm index 0dfc9815117d4..33aedb9ce49be 100644 --- a/code/modules/unit_tests/modsuit.dm +++ b/code/modules/unit_tests/modsuit.dm @@ -7,10 +7,6 @@ for(var/modpath in paths) var/obj/item/mod/control/pre_equipped/mod = new modpath() TEST_ASSERT(mod.theme, "[modpath] spawned without a theme.") - TEST_ASSERT(mod.helmet, "[modpath] spawned without a helmet.") - TEST_ASSERT(mod.chestplate, "[modpath] spawned without a chestplate.") - TEST_ASSERT(mod.gauntlets, "[modpath] spawned without gauntlets.") - TEST_ASSERT(mod.boots, "[modpath] spawned without boots.") var/list/modules = list() var/complexity_max = mod.complexity_max var/complexity = 0 @@ -18,6 +14,7 @@ module = new module() complexity += module.complexity TEST_ASSERT(complexity <= complexity_max, "[modpath] starting modules reach above max complexity.") + TEST_ASSERT(module.has_required_parts(mod.mod_parts), "[modpath] initial module [module.type] is not supported by its parts.") for(var/obj/item/mod/module/module_to_check as anything in modules) TEST_ASSERT(!is_type_in_list(module, module_to_check.incompatible_modules), "[modpath] initial module [module.type] is incompatible with initial module [module_to_check.type]") TEST_ASSERT(!is_type_in_list(module_to_check, module.incompatible_modules), "[modpath] initial module [module.type] is incompatible with initial module [module_to_check.type]") diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_moth.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_moth.png index ed782c8ec9f6e..284205a73ec13 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_moth.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_moth.png differ diff --git a/code/modules/unit_tests/suit_storage_icons.dm b/code/modules/unit_tests/suit_storage_icons.dm index 12305e7abfc0b..7cc987bb46801 100644 --- a/code/modules/unit_tests/suit_storage_icons.dm +++ b/code/modules/unit_tests/suit_storage_icons.dm @@ -14,9 +14,9 @@ for(var/path in clothing_path::allowed) //find all usable suit storage stuff. wearable_item_paths |= path - for(var/obj/item/mod/control/mod_path in subtypesof(/obj/item/mod/control)) - for(var/path in mod_path::chestplate::allowed) - wearable_item_paths |= path + for(var/datum/mod_theme/mod_theme as anything in GLOB.mod_themes) + mod_theme = GLOB.mod_themes[mod_theme] + wearable_item_paths |= mod_theme.allowed_suit_storage var/list/already_warned_icons = list() var/count = 1 //to be removed once the test goes live / into CI failure mode. diff --git a/code/modules/uplink/uplink_items/explosive.dm b/code/modules/uplink/uplink_items/explosive.dm index 72f40b00dfca3..ef9f3b4f074c5 100644 --- a/code/modules/uplink/uplink_items/explosive.dm +++ b/code/modules/uplink/uplink_items/explosive.dm @@ -57,6 +57,12 @@ if(HAS_TRAIT(SSstation, STATION_TRAIT_CYBERNETIC_REVOLUTION)) cost *= 3 +/datum/uplink_item/explosives/smoke + name = "Smoke Grenades" + desc = "A box that contains five smoke grenades. Useful for vanishing and ninja fans with katana." + item = /obj/item/storage/box/syndie_kit/smoke + cost = 2 + /datum/uplink_item/explosives/pizza_bomb name = "Pizza Bomb" desc = "A pizza box with a bomb cunningly attached to the lid. The timer needs to be set by opening the box; afterwards, \ diff --git a/code/modules/uplink/uplink_items/suits.dm b/code/modules/uplink/uplink_items/suits.dm index 57982a8cf980a..8e7212744822a 100644 --- a/code/modules/uplink/uplink_items/suits.dm +++ b/code/modules/uplink/uplink_items/suits.dm @@ -70,6 +70,6 @@ provides the user with superior armor and mobility compared to the standard Syndicate MODsuit." item = /obj/item/mod/control/pre_equipped/traitor_elite // This one costs more than the nuke op counterpart - purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS) + purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS | UPLINK_SPY) progression_minimum = 90 MINUTES cost = 16 diff --git a/code/modules/vehicles/mecha/combat/reticence.dm b/code/modules/vehicles/mecha/combat/reticence.dm index 5e400b12d9788..5dcc5f34e912b 100644 --- a/code/modules/vehicles/mecha/combat/reticence.dm +++ b/code/modules/vehicles/mecha/combat/reticence.dm @@ -37,7 +37,7 @@ MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/silenced, MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/rcd, MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/radio, /obj/item/mecha_parts/mecha_equipment/air_tank/full, /obj/item/mecha_parts/mecha_equipment/thrusters/ion), - MECHA_POWER = /obj/item/mecha_parts/mecha_equipment/generator, + MECHA_POWER = list(/obj/item/mecha_parts/mecha_equipment/generator), MECHA_ARMOR = list(), ) diff --git a/code/modules/vehicles/mecha/working/ripley.dm b/code/modules/vehicles/mecha/working/ripley.dm index a4872289e962c..1bed2350f08c8 100644 --- a/code/modules/vehicles/mecha/working/ripley.dm +++ b/code/modules/vehicles/mecha/working/ripley.dm @@ -265,8 +265,6 @@ GLOBAL_DATUM(cargo_ripley, /obj/vehicle/sealed/mecha/ripley/cargo) /obj/vehicle/sealed/mecha/ripley/cargo/Initialize(mapload) . = ..() - if(cell) - cell.charge = FLOOR(cell.charge * 0.25, 1) //Starts at very low charge //Attach hydraulic clamp ONLY var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/HC = new diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 096cd735bd483..8e69eb10ce03f 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -1417,7 +1417,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) if(isliving(usr)) living_user = usr card_used = living_user.get_idcard(TRUE) - else if(age_restrictions && item_record.age_restricted && (!card_used.registered_age || card_used.registered_age < AGE_MINOR)) + if(age_restrictions && item_record.age_restricted && (!card_used.registered_age || card_used.registered_age < AGE_MINOR)) speak("You are not of legal age to purchase [item_record.name].") if(!(usr in GLOB.narcd_underages)) if (isnull(sec_radio)) diff --git a/code/world.dm b/code/world.dm index 8b1cdbf1fe496..7cc35e5529ad8 100644 --- a/code/world.dm +++ b/code/world.dm @@ -17,6 +17,7 @@ hub_password = "kMZy3U5jJHSiBQjr" name = "/tg/ Station 13" fps = 20 + cache_lifespan = 0 map_format = SIDE_MAP #ifdef FIND_REF_NO_CHECK_TICK loop_checks = FALSE diff --git a/html/changelogs/AutoChangeLog-pr-83270.yml b/html/changelogs/AutoChangeLog-pr-83270.yml new file mode 100644 index 0000000000000..5a6873dd01cff --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-83270.yml @@ -0,0 +1,4 @@ +author: "paganiy" +delete-after: True +changes: + - balance: "Auto-aim in combat mode at mobs on the floor is disabled after the projectile passes 10 tiles." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-83334.yml b/html/changelogs/AutoChangeLog-pr-83334.yml new file mode 100644 index 0000000000000..626cafcdff571 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-83334.yml @@ -0,0 +1,4 @@ +author: "Muffindrake" +delete-after: True +changes: + - balance: "reduced claymore/weak force from 30 to 24 and armor penetration from 15 to 10" \ No newline at end of file diff --git a/html/changelogs/archive/2024-05.yml b/html/changelogs/archive/2024-05.yml index 05dafdfef0c7f..08f3a52b9ce33 100644 --- a/html/changelogs/archive/2024-05.yml +++ b/html/changelogs/archive/2024-05.yml @@ -505,3 +505,116 @@ - balance: Nukies ordnance now include more gasses and stock parts sheets, spacemenart, ben10omintrix, goofball, infrared baron, aofie: - rscadd: adds lavaland raptors and the raptor ranch +2024-05-21: + ArcaneMusic: + - balance: Ore vents, if blocked off from all four sides while being defended, now + cause a mild gas explosion, resulting in a mild dissuasive explosion. + - bugfix: NODE drones spawned from ore vent defense have lower maximum health. + Ben10Omintrix: + - bugfix: ' fixes being able to ride all mobs through space' + - bugfix: playful raptors now correctly play with owners + EnterTheJake: + - spellcheck: Pulse of entropy description now displays the correct reagents for + the ritual + Fikou: + - bugfix: carps now properly stop floating when you kill them + - refactor: modsuits have been refactored if you see bugs report them + - bugfix: admin cargo tech modsuit outfit now works correctly + Jacquerel: + - balance: Certain unhealthy organs can be detected via the advanced health scanner + - balance: If you can't absorb a species' DNA as a changeling, you also can't use + transform sting to turn someone into that species. + - admin: Various bad things that can happen as a result of Legion organs are now + logged + Melbert: + - qol: Custom emotes now default to both visible and audible rather than just audible + - qol: Invoking the custom emote verb now explains how to set your custom emote + to visible or audible + Pickle-Coding: + - balance: Cargo ripley's cell starts fully charged. + Profakos: + - bugfix: Slimes will no longer consider feeding upon slime people + Sadboysuss: + - bugfix: HoS on birdshot now has a pet like on all other maps + - bugfix: jumpsuit sensors quick maxing now works + ShizCalev: + - bugfix: Icecream vats will no longer eat cyborg beakers. + - bugfix: Balloons are no longer invisible when held. + - bugfix: Malf AI can now properly interact with APCs under their control + - bugfix: Malf AI & their slaved cyborgs won't be told that access is denied when + trying to right-click lock/unlock APCs. + Striders13: + - admin: sentience balloon can now assign antag to affected mobs + Watermelon914: + - bugfix: Fixed spies having a slightly increased chance of getting a different + cost model for the elite syndicate hardsuit + Xander3359: + - bugfix: The RD remote works on the AI upload door now + carlarctg: + - balance: Halved Psyker echolocation cooldown. This will hopefully make it actually + usable. + dopamiin0: + - rscadd: Nanotrasen has made strides in their frontier sector food networks, increasing + the selection of produce available to chefs. + grungussuss and Virgilcore: + - sound: added sounds for the gateway + iwishforducks: + - image: Medical doctors and CMOs now start with their jumpsuits rather than surgery + scrubs and caps. + kawoppi: + - bugfix: 'underage drinkers beware: the Booze-O-Mat and ShadyCigs Deluxe check + your age again' + necromanceranne: + - bugfix: Miners can actually access and fix their engineering issues on the lavaland + base via the engineering section of the base. AGAIN. + - bugfix: The gulag SMES unit is no longer needlessly draining the entire power + grid of the main mining base. AGAIN. + nikothedude: + - spellcheck: Improved the grammar/spelling of a few raptor descriptions + starrm4nn: + - balance: Buffs Antihol's purge rate to 8u/tick when pure (6u/tick minimum). + - balance: Multiver stops purging when Anacea is present in the bloodstream. + tmyqlfpir: + - bugfix: '[Metastation] Service hallway door being bypassed by lying down' +2024-05-22: + Bisar: + - balance: A wound being inflicted doesn't get broadcasted to everyone in view until + a higher severity now. + - sound: Wound sounds drop off more quickly, and no longer travel through walls. + This has no effect on attack sounds. + Fikou: + - spellcheck: the nuke op reinforcement beacon no longer talks about clones + Horatio22: + - spellcheck: Corrects spelling of "amulette" to "amulet" + - spellcheck: Correctly spells "received." + Iamgoofball: + - spellcheck: Removes a shit clown name and adds a better clown name instead + SyncIt21: + - bugfix: Crafting menu loads long lists faster + - bugfix: Monkey power reactions are instant and won't occur when exact reagent + requirements (50u power & 1u water) or above aren't met. Stops players from + cheesing monkeys from plumbing factories + Watermelon914: + - qol: New players will no longer start with ghost ears, ghost sight and ghost whispers + on by default. + Wayland-Smithy: + - qol: Crew Monitor UI now scrolls properly even after clicking the inner list. + Xander3359: + - bugfix: fixed a weird door in icebox bridge + hack-wrench: + - rscadd: Added smoke kit (5 grenades) with four grenades to uplink by 2 TC + improvedname: + - rscdel: Removes night vision quirk + necromanceranne: + - rscadd: The Research Director is now actually strong. + - rscadd: Fitness bros will determine the Research Director to be stronger than + they actually are, without even working out. How does he do it? + - balance: Suplexing a rod grants a large burst of athletics experience. + - rscadd: The more sentient casualties of the rod, the more experience suplexing + that rod grants. Absorb the souls of the weak and feeble. + nikothedude: + - rscadd: Adds a new deathmatch modifier that enables quirks + - bugfix: The deathmatch modifier menu works now + norsvenska: + - spellcheck: The message displayed when adjusting a mask no longer incorrectly + states the way in which the mask has moved. diff --git a/sound/effects/gateway_calibrated.ogg b/sound/effects/gateway_calibrated.ogg new file mode 100644 index 0000000000000..c88d0862c988d Binary files /dev/null and b/sound/effects/gateway_calibrated.ogg differ diff --git a/sound/effects/gateway_calibrating.ogg b/sound/effects/gateway_calibrating.ogg new file mode 100644 index 0000000000000..09ca63fbc10ff Binary files /dev/null and b/sound/effects/gateway_calibrating.ogg differ diff --git a/sound/effects/gateway_close.ogg b/sound/effects/gateway_close.ogg new file mode 100644 index 0000000000000..98e964aca130b Binary files /dev/null and b/sound/effects/gateway_close.ogg differ diff --git a/sound/effects/gateway_open.ogg b/sound/effects/gateway_open.ogg new file mode 100644 index 0000000000000..f6d11f7eedee2 Binary files /dev/null and b/sound/effects/gateway_open.ogg differ diff --git a/sound/effects/gateway_travel.ogg b/sound/effects/gateway_travel.ogg new file mode 100644 index 0000000000000..bbaed502790f0 Binary files /dev/null and b/sound/effects/gateway_travel.ogg differ diff --git a/strings/names/clown.txt b/strings/names/clown.txt index 028494580babe..1a5f8740ae7fe 100644 --- a/strings/names/clown.txt +++ b/strings/names/clown.txt @@ -28,6 +28,7 @@ Delicious Dan Dinkster Dinky Doodle Doctor Greenthumb +Doctor Rockso Doink Early Worm Eggy @@ -118,7 +119,6 @@ Unimaginable Nut Valid Vincent Weather Report Widderwise -Yanye Kest Yesterdays Beef Yobbo Ziggy Yoyo diff --git a/tgstation.dme b/tgstation.dme index c966d0de5f3d1..12cd8bdb54ee2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -909,6 +909,7 @@ #include "code\datums\ai\basic_mobs\basic_subtrees\mine_walls.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\move_to_cardinal.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\opportunistic_ventcrawler.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\play_with_owners.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\prepare_travel_to_destination.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\ranged_skirmish.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\run_emote.dm" @@ -1737,7 +1738,6 @@ #include "code\datums\quirks\positive_quirks\light_step.dm" #include "code\datums\quirks\positive_quirks\mime_fan.dm" #include "code\datums\quirks\positive_quirks\musician.dm" -#include "code\datums\quirks\positive_quirks\night_vision.dm" #include "code\datums\quirks\positive_quirks\poster_boy.dm" #include "code\datums\quirks\positive_quirks\self_aware.dm" #include "code\datums\quirks\positive_quirks\settler.dm" @@ -5141,6 +5141,7 @@ #include "code\modules\mod\mod_core.dm" #include "code\modules\mod\mod_link.dm" #include "code\modules\mod\mod_paint.dm" +#include "code\modules\mod\mod_part.dm" #include "code\modules\mod\mod_theme.dm" #include "code\modules\mod\mod_types.dm" #include "code\modules\mod\mod_ui.dm" diff --git a/tgui/packages/tgui/interfaces/CrewConsole.tsx b/tgui/packages/tgui/interfaces/CrewConsole.tsx index aa4f272f24869..52af3eaff4320 100644 --- a/tgui/packages/tgui/interfaces/CrewConsole.tsx +++ b/tgui/packages/tgui/interfaces/CrewConsole.tsx @@ -175,7 +175,6 @@ const CrewTable = () => { return (
diff --git a/tgui/packages/tgui/interfaces/MODsuit.tsx b/tgui/packages/tgui/interfaces/MODsuit.tsx index 8752fc3c8741a..2b7070e3ba50b 100644 --- a/tgui/packages/tgui/interfaces/MODsuit.tsx +++ b/tgui/packages/tgui/interfaces/MODsuit.tsx @@ -27,10 +27,7 @@ type MODsuitData = { ui_theme: string; control: string; complexity_max: number; - helmet: string; - chestplate: string; - gauntlets: string; - boots: string; + parts: PartData[]; // Dynamic suit_status: SuitStatus; user_status: UserStatus; @@ -38,6 +35,11 @@ type MODsuitData = { module_info: Module[]; }; +type PartData = { + slot: string; + name: string; +}; + type SuitStatus = { core_name: string; cell_charge_current: number; @@ -477,7 +479,7 @@ const SuitStatusSection = (props) => { const HardwareSection = (props) => { const { act, data } = useBackend(); - const { control, helmet, chestplate, gauntlets, boots } = data; + const { control } = data; const { ai_name, core_name } = data.suit_status; return (
@@ -489,19 +491,28 @@ const HardwareSection = (props) => { {core_name || 'No Core Detected'} {control} - {helmet || 'None'} - - {chestplate || 'None'} - - - {gauntlets || 'None'} - - {boots || 'None'} +
); }; +const ModParts = (props) => { + const { act, data } = useBackend(); + const { parts } = data; + return ( + <> + {parts.map((part) => { + return ( + + {part.name} + + ); + })} + + ); +}; + const UserStatusSection = (props) => { const { act, data } = useBackend(); const { active } = data.suit_status; diff --git a/tgui/packages/tgui/interfaces/PersonalCrafting.tsx b/tgui/packages/tgui/interfaces/PersonalCrafting.tsx index 166ab59b44ece..9acb77f1c4d7a 100644 --- a/tgui/packages/tgui/interfaces/PersonalCrafting.tsx +++ b/tgui/packages/tgui/interfaces/PersonalCrafting.tsx @@ -15,6 +15,7 @@ import { Stack, Tabs, Tooltip, + VirtualList, } from '../components'; import { Window } from '../layouts'; import { Food } from './PreferencesMenu/data'; @@ -496,32 +497,36 @@ export const PersonalCrafting = (props) => { style={{ overflowY: 'auto' }} > {recipes.length > 0 ? ( - recipes - .slice(0, displayLimit) - .map((item) => - display_compact ? ( - - ) : ( - - ), - ) + + {recipes + .slice(0, displayLimit) + .map((item) => + display_compact ? ( + + ) : ( + + ), + )} + ) : ( No recipes found. diff --git a/tgui/packages/tgui/interfaces/SentienceFunBalloon.jsx b/tgui/packages/tgui/interfaces/SentienceFunBalloon.jsx index 62330cc6a05c4..ab4353aab2846 100644 --- a/tgui/packages/tgui/interfaces/SentienceFunBalloon.jsx +++ b/tgui/packages/tgui/interfaces/SentienceFunBalloon.jsx @@ -11,9 +11,9 @@ import { Window } from '../layouts'; export const SentienceFunBalloon = (props) => { const { act, data } = useBackend(); - const { group_name, range } = data; + const { group_name, range, antag } = data; return ( - +
@@ -44,6 +44,14 @@ export const SentienceFunBalloon = (props) => { } /> + + act('select_antag')} + /> +