diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm index 895f37a7a9..0bdf362e20 100644 --- a/code/__DEFINES/_flags/_flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -214,5 +214,17 @@ GLOBAL_LIST_INIT(bitflags, list( ///Turns the dir by 180 degrees #define DIRFLIP(d) turn(d, 180) +// timed_action_flags parameter for `/proc/do_after_mob`, `/proc/do_mob` and `/proc/do_after` +/// Can do the action even if mob moves location +#define IGNORE_USER_LOC_CHANGE (1<<0) +/// Can do the action even if the target moves location +#define IGNORE_TARGET_LOC_CHANGE (1<<1) +/// Can do the action even if the item is no longer being held +#define IGNORE_HELD_ITEM (1<<2) +/// Can do the action even if the mob is incapacitated (ex. handcuffed) +#define IGNORE_INCAPACITATED (1<<3) +/// Used to prevent important slowdowns from being abused by drugs like kronkaine +#define IGNORE_SLOWDOWNS (1<<4) + /// 33554431 (2^24 - 1) is the maximum value our bitflags can reach. #define MAX_BITFLAG_DIGITS 8 diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 2b9fa902e9..cd2c735e09 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -474,6 +474,8 @@ #define COMSIG_ITEM_AFTERATTACK "item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, params) #define COMSIG_ITEM_ALT_AFTERATTACK "item_alt_afterattack" //from base of obj/item/altafterattack(): (atom/target, mob/user, proximity, params) #define COMSIG_ITEM_EQUIPPED "item_equip" //from base of obj/item/equipped(): (/mob/equipper, slot) +/// A mob has just unequipped an item. +#define COMSIG_MOB_UNEQUIPPED_ITEM "mob_unequipped_item" // Do not grant actions on equip. #define COMPONENT_NO_GRANT_ACTIONS 1 #define COMSIG_ITEM_DROPPED "item_drop" //from base of obj/item/dropped(): (mob/user) diff --git a/code/__DEFINES/dcs/signals/signals_medical.dm b/code/__DEFINES/dcs/signals/signals_medical.dm new file mode 100644 index 0000000000..801eb6b41b --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_medical.dm @@ -0,0 +1,14 @@ +/// From /datum/surgery/New(): (datum/surgery/surgery, surgery_location (body zone), obj/item/bodypart/targeted_limb) +#define COMSIG_MOB_SURGERY_STARTED "mob_surgery_started" + +/// From /datum/surgery_step/success(): (datum/surgery_step/step, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results) +#define COMSIG_MOB_SURGERY_STEP_SUCCESS "mob_surgery_step_success" + +/// From /obj/item/shockpaddles/proc/do_success(): (obj/item/shockpaddles/source) +#define COMSIG_DEFIBRILLATOR_SUCCESS "defib_success" + #define COMPONENT_DEFIB_STOP (1<<0) + +/// From /datum/surgery/can_start(): (mob/source, datum/surgery/surgery, mob/living/patient) +#define COMSIG_SURGERY_STARTING "surgery_starting" + #define COMPONENT_CANCEL_SURGERY (1<<0) + #define COMPONENT_FORCE_SURGERY (1<<1) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm deleted file mode 100644 index be1e9b31c8..0000000000 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ /dev/null @@ -1,3 +0,0 @@ -/// from base of atom/movable/Process_Spacemove(): (movement_dir) -#define COMSIG_MOVABLE_SPACEMOVE "spacemove" - #define COMSIG_MOVABLE_STOP_SPACEMOVE (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm index 4c8ec4d6de..2533b69852 100644 --- a/code/__DEFINES/dcs/signals/signals_mod.dm +++ b/code/__DEFINES/dcs/signals/signals_mod.dm @@ -1,7 +1,35 @@ //MODsuit signals -/// Called when a module is selected to be the active one from on_select() +/// 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. +#define COMSIG_MOD_DEPLOYED "mod_deployed" +/// Called when a MOD retracts one or more of its parts. +#define COMSIG_MOD_RETRACTED "mod_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) #define COMSIG_MOD_ACTIVATE "mod_activate" /// Cancels the suit's activation #define MOD_CANCEL_ACTIVATE (1 << 0) +/// Called when a MOD finishes having a module removed from it. +#define COMSIG_MOD_MODULE_REMOVED "mod_module_removed" +/// Called when a MOD finishes having a module added to it. +#define COMSIG_MOD_MODULE_ADDED "mod_module_added" +/// Called when a MOD is having modules removed from crowbar_act(mob/user, obj/crowbar) +#define COMSIG_MOD_MODULE_REMOVAL "mod_module_removal" + /// Cancels the removal of modules + #define MOD_CANCEL_REMOVAL (1 << 0) +/// Called when a module attempts to activate, however it does. At the end of checks so you can add some yourself, or work on trigger behavior (mob/user) +#define COMSIG_MODULE_TRIGGERED "mod_module_triggered" + /// Cancels activation, with no message. Include feedback on your cancel. + #define MOD_ABORT_USE (1<<0) +/// Called when a module activates, after all checks have passed and cooldown started. +#define COMSIG_MODULE_ACTIVATED "mod_module_activated" +/// Called when a module deactivates, after all checks have passed. +#define COMSIG_MODULE_DEACTIVATED "mod_module_deactivated" +/// Called when a module is used, after all checks have passed and cooldown started. +#define COMSIG_MODULE_USED "mod_module_used" +/// Called when the MODsuit wearer is set. +#define COMSIG_MOD_WEARER_SET "mod_wearer_set" +/// Called when the MODsuit wearer is unset. +#define COMSIG_MOD_WEARER_UNSET "mod_wearer_unset" diff --git a/code/__DEFINES/mod.dm b/code/__DEFINES/mod.dm index 03163497d4..0e380f9e95 100644 --- a/code/__DEFINES/mod.dm +++ b/code/__DEFINES/mod.dm @@ -2,7 +2,10 @@ #define DEFAULT_MAX_COMPLEXITY 15 /// Default cell drain per process on MODsuits -#define DEFAULT_CELL_DRAIN 5 +#define DEFAULT_CHARGE_DRAIN 5 + +/// Default time for a part to seal +#define MOD_ACTIVATION_STEP_TIME (2 SECONDS) /// Passive module, just acts when put in naturally. #define MODULE_PASSIVE 0 @@ -14,18 +17,24 @@ #define MODULE_ACTIVE 3 //Defines used by the theme for clothing flags and similar -#define HELMET_LAYER "helmet_layer" +#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 UNSEALED_CLOTHING "unsealed_clothing" #define SEALED_CLOTHING "sealed_clothing" #define UNSEALED_INVISIBILITY "unsealed_invisibility" #define SEALED_INVISIBILITY "sealed_invisibility" #define UNSEALED_COVER "unsealed_cover" #define SEALED_COVER "sealed_cover" +#define CAN_OVERSLOT "can_overslot" + +//Defines used to override MOD clothing's icon and worn icon files in the skin. +#define MOD_ICON_OVERRIDE "mod_icon_override" +#define MOD_WORN_ICON_OVERRIDE "mod_worn_icon_override" /// Global list of all /datum/mod_theme GLOBAL_LIST_INIT(mod_themes, setup_mod_themes()) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index af5dccb3fd..96b0bc4d33 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -160,6 +160,7 @@ #define TRAIT_CAPTAIN_METABOLISM "captain-metabolism" /// Prevents plasmamen from self-igniting #define TRAIT_NOSELFIGNITION "no_selfignition" +#define TRAIT_NOSELFIGNITION_HEAD_ONLY "no_selfignition_head_only" /// Like antimagic, but doesn't block the user from casting #define TRAIT_ANTIMAGIC_NO_SELFBLOCK "anti_magic_no_selfblock" /// Gives us turf, mob and object vision through walls @@ -229,8 +230,6 @@ #define TRAIT_AUTO_CATCH_ITEM "auto_catch_item" #define TRAIT_CLOWN_MENTALITY "clown_mentality" // The future is now, clownman. #define TRAIT_FREESPRINT "free_sprinting" -#define TRAIT_XRAY_VISION "xray_vision" -#define TRAIT_THERMAL_VISION "thermal_vision" #define TRAIT_NO_TELEPORT "no-teleport" //you just can't #define TRAIT_NO_INTERNALS "no-internals" #define TRAIT_TOXIC_ALCOHOL "alcohol_intolerance" diff --git a/code/datums/components/crafting/recipes/recipes_misc.dm b/code/datums/components/crafting/recipes/recipes_misc.dm index 7f6408f583..fd72964505 100644 --- a/code/datums/components/crafting/recipes/recipes_misc.dm +++ b/code/datums/components/crafting/recipes/recipes_misc.dm @@ -557,7 +557,7 @@ /datum/crafting_recipe/mod_core name = "MOD core" result = /obj/item/mod/construction/core - tool_behaviors = list(TOOL_SCREWDRIVER) + tools = list(TOOL_SCREWDRIVER) time = 10 SECONDS reqs = list(/obj/item/stack/cable_coil = 5, /obj/item/stack/rods = 2, diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 1218e8b9af..b3e6d63cb8 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -369,7 +369,7 @@ //Tries to dump content /datum/component/storage/proc/dump_content_at(atom/dest_object, mob/M) var/atom/A = parent - var/atom/dump_destination = get_dumping_location(dest_object) + var/atom/dump_destination = dest_object.get_dumping_location(dest_object) if(M.CanReach(A) && dump_destination && M.CanReach(dump_destination)) if(check_locked(null, M, TRUE)) to_chat(M, "[parent] seems to be locked!") diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm index a4d5591653..a795bb562e 100644 --- a/code/datums/holocall.dm +++ b/code/datums/holocall.dm @@ -348,21 +348,12 @@ /datum/preset_holoimage/engineer/rig outfit_type = /datum/outfit/job/engineer/gloved/rig -/datum/preset_holoimage/engineer/mod - outfit_type = /datum/outfit/job/engineer/mod - /datum/preset_holoimage/engineer/ce outfit_type = /datum/outfit/job/ce -/datum/preset_holoimage/engineer/ce/mod - outfit_type = /datum/outfit/job/ce/mod - /datum/preset_holoimage/engineer/ce/rig outfit_type = /datum/outfit/job/engineer/gloved/rig -/datum/preset_holoimage/engineer/atmos/mod - outfit_type = /datum/outfit/job/atmos/mod - /datum/preset_holoimage/engineer/atmos outfit_type = /datum/outfit/job/atmos diff --git a/code/datums/outfit.dm b/code/datums/outfit.dm index c5221bdaf8..0718e54cab 100755 --- a/code/datums/outfit.dm +++ b/code/datums/outfit.dm @@ -105,8 +105,18 @@ */ var/list/implants = null - ///ID of the slot containing a gas tank - var/internals_slot = null + //skyrat edit + ///Slot for underwear like boxers and panties + var/underwear = null + ///Slot for socks, yes, the thing that usually goes before your shoes + var/socks = null + ///Slot for the undershirt (which is quite a foreign concept to me) or bras + var/shirt = null + ///Slot for the opposite ear. + var/ears_extra = null + ///Slot for the part of your arms that isn't quite hands yet. + var/wrists = null + // /// Should the toggle helmet proc be called on the helmet during equip var/toggle_helmet = TRUE @@ -144,15 +154,6 @@ //to be overridden for toggling internals, id binding, access etc return -#define EQUIP_OUTFIT_ITEM(item_path, slot_name) if(##item_path) { \ - H.equip_to_slot_or_del(SSwardrobe.provide_type(##item_path), ##slot_name, TRUE); \ - var/obj/item/outfit_item = H.get_item_by_slot(##slot_name); \ - if (outfit_item && outfit_item.type == ##item_path) { \ - outfit_item.on_outfit_equip(H, visualsOnly, ##slot_name); \ - } \ -} - - /** * Equips all defined types and paths to the mob passed in * @@ -166,34 +167,31 @@ //Start with uniform,suit,backpack for additional slots if(uniform) - EQUIP_OUTFIT_ITEM(uniform, ITEM_SLOT_ICLOTHING) + H.equip_to_slot_or_del(new uniform(H), ITEM_SLOT_ICLOTHING, TRUE) if(suit) - EQUIP_OUTFIT_ITEM(suit, ITEM_SLOT_OCLOTHING) + H.equip_to_slot_or_del(new suit(H), ITEM_SLOT_OCLOTHING, TRUE) if(belt) - EQUIP_OUTFIT_ITEM(belt, ITEM_SLOT_BELT) + H.equip_to_slot_or_del(new belt(H), ITEM_SLOT_BELT, TRUE) if(gloves) - EQUIP_OUTFIT_ITEM(gloves, ITEM_SLOT_GLOVES) + H.equip_to_slot_or_del(new gloves(H), ITEM_SLOT_GLOVES, TRUE) if(shoes) - EQUIP_OUTFIT_ITEM(shoes, ITEM_SLOT_FEET) + H.equip_to_slot_or_del(new shoes(H), ITEM_SLOT_FEET, TRUE) if(head) - EQUIP_OUTFIT_ITEM(head, ITEM_SLOT_HEAD) + H.equip_to_slot_or_del(new head(H), ITEM_SLOT_HEAD, TRUE) if(mask) - EQUIP_OUTFIT_ITEM(mask, ITEM_SLOT_MASK) + H.equip_to_slot_or_del(new mask(H), ITEM_SLOT_MASK, TRUE) if(neck) - EQUIP_OUTFIT_ITEM(neck, ITEM_SLOT_NECK) + H.equip_to_slot_or_del(new neck(H), ITEM_SLOT_NECK, TRUE) if(ears) - EQUIP_OUTFIT_ITEM(ears, ITEM_SLOT_EARS) + H.equip_to_slot_or_del(new ears(H), ITEM_SLOT_EARS, TRUE) if(glasses) - EQUIP_OUTFIT_ITEM(glasses, ITEM_SLOT_EYES) + H.equip_to_slot_or_del(new glasses(H), ITEM_SLOT_EYES, TRUE) if(back) - EQUIP_OUTFIT_ITEM(back, ITEM_SLOT_BACK) + H.equip_to_slot_or_del(new back(H), ITEM_SLOT_BACK, TRUE) if(id) - EQUIP_OUTFIT_ITEM(id, ITEM_SLOT_ID) + H.equip_to_slot_or_del(new id(H), ITEM_SLOT_ID, TRUE) if(suit_store) - EQUIP_OUTFIT_ITEM(suit_store, ITEM_SLOT_SUITSTORE) - - if(undershirt) - H.undershirt = initial(undershirt.name) + H.equip_to_slot_or_del(new suit_store(H), ITEM_SLOT_SUITSTORE, TRUE) if(accessory) var/obj/item/clothing/under/U = H.w_uniform @@ -209,9 +207,9 @@ if(!visualsOnly) // Items in pockets or backpack don't show up on mob's icon. if(l_pocket) - EQUIP_OUTFIT_ITEM(l_pocket, ITEM_SLOT_LPOCKET) + H.equip_to_slot_or_del(l_pocket, ITEM_SLOT_LPOCKET, TRUE) if(r_pocket) - EQUIP_OUTFIT_ITEM(r_pocket, ITEM_SLOT_RPOCKET) + H.equip_to_slot_or_del(r_pocket, ITEM_SLOT_RPOCKET, TRUE) if(box) if(!backpack_contents) @@ -225,7 +223,7 @@ if(!isnum(number))//Default to 1 number = 1 for(var/i in 1 to number) - EQUIP_OUTFIT_ITEM(path, ITEM_SLOT_BACKPACK) + H.equip_to_slot_or_del(path, ITEM_SLOT_BACKPACK, TRUE) post_equip(H, visualsOnly, preference_source) @@ -242,9 +240,6 @@ H.update_body() return TRUE -#undef EQUIP_OUTFIT_ITEM - - /** * Apply a fingerprint from the passed in human to all items in the outfit * diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 5e509a637b..aa351e6c90 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -31,6 +31,8 @@ var/uv_cycles = 6 var/message_cooldown var/breakout_time = 300 + /// How fast it charges cells in a suit + var/charge_rate = 250 /obj/machinery/suit_storage_unit/standard_unit suit_type = /obj/item/clothing/suit/space/eva @@ -43,7 +45,7 @@ storage_type = /obj/item/tank/jetpack/oxygen/captain /obj/machinery/suit_storage_unit/captainmod - mask_type = /obj/item/clothing/mask/gas/atmos/captain + mask_type = /obj/item/clothing/mask/gas/sechailer storage_type = /obj/item/tank/jetpack/oxygen/captain mod_type = /obj/item/mod/control/pre_equipped/magnate @@ -58,11 +60,11 @@ /obj/machinery/suit_storage_unit/atmos suit_type = /obj/item/clothing/suit/space/hardsuit/engine/atmos - mask_type = /obj/item/clothing/mask/gas/atmos + mask_type = /obj/item/clothing/mask/breath storage_type = /obj/item/watertank/atmos /obj/machinery/suit_storage_unit/atmosmod - mask_type = /obj/item/clothing/mask/gas/atmos + mask_type = /obj/item/clothing/mask/breath storage_type = /obj/item/watertank/atmos mod_type = /obj/item/mod/control/pre_equipped/atmospheric @@ -332,7 +334,7 @@ things_to_clear += mask.GetAllContents() if(mod) things_to_clear += mod - things_to_clear += mod.get_all_contents() + things_to_clear += mod.GetAllContents() if(storage) things_to_clear += storage things_to_clear += storage.GetAllContents() @@ -352,13 +354,7 @@ /obj/machinery/suit_storage_unit/process(delta_time) var/obj/item/stock_parts/cell/cell - if(suit) - if(!istype(suit)) - return - if(!suit.cell) - return - cell = suit.cell - else if(mod) + if(mod) if(!istype(mod)) return if(!mod.cell) @@ -370,6 +366,14 @@ use_power(charge_rate * delta_time) cell.give(charge_rate * delta_time) +/obj/machinery/suit_storage_unit/proc/shock(mob/user, prb) + if(!prob(prb)) + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, src) + s.start() + if(electrocute_mob(user, src, src, 1, TRUE)) + return 1 + /obj/machinery/suit_storage_unit/relaymove(mob/user) if(locked) if(message_cooldown <= world.time) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 5fd55eeb45..7a953089a5 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -399,7 +399,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb if(throwing) throwing.finalize(FALSE) if(loc == user) - if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src)) + if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(I = src)) return . = FALSE @@ -428,7 +428,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb if(throwing) throwing.finalize(FALSE) if(loc == user) - if(!user.temporarilyRemoveItemFromInventory(src)) + if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src)) return pickup(user) diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm index f680d0034b..aea961860d 100644 --- a/code/game/objects/items/defib.dm +++ b/code/game/objects/items/defib.dm @@ -461,6 +461,8 @@ cooldown = TRUE busy = FALSE update_icon() + if(SEND_SIGNAL(src, COMSIG_DEFIBRILLATOR_SUCCESS) & COMPONENT_DEFIB_STOP) + return if(req_defib) defib.cooldowncheck(user) else @@ -514,6 +516,8 @@ cooldown = TRUE busy = FALSE update_icon() + if(SEND_SIGNAL(src, COMSIG_DEFIBRILLATOR_SUCCESS) & COMPONENT_DEFIB_STOP) + return if(!req_defib) recharge(60) if(req_defib && (defib.cooldowncheck(user))) @@ -632,6 +636,8 @@ defib.deductcharge(revivecost) cooldown = 1 update_icon() + if(SEND_SIGNAL(src, COMSIG_DEFIBRILLATOR_SUCCESS) & COMPONENT_DEFIB_STOP) + return if(req_defib) defib.cooldowncheck(user) else diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index 52ec7f796f..d94f078f8a 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -28,17 +28,11 @@ /obj/item/aicard/pre_attack(atom/target, mob/living/user, params) if(AI) //AI is on the card, implies user wants to upload it. - var/our_ai = AI target.transfer_ai(AI_TRANS_FROM_CARD, user, AI, src) else //No AI on the card, therefore the user wants to download one. target.transfer_ai(AI_TRANS_TO_CARD, user, null, src) if(AI) - log_combat(user, our_ai, "uploaded", src, "to [target].") - return TRUE - else //No AI on the card, therefore the user wants to download one. - target.transfer_ai(AI_TRANS_TO_CARD, user, null, src) - if(AI) - log_combat(user, AI, "carded", src) + log_combat(user, AI, "uploaded", src, "to [target].") return TRUE update_appearance() //Whatever happened, update the card's state (icon, name) to match. return ..() diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm index 6ec6554693..96d9a97b5f 100644 --- a/code/modules/cargo/packs/science.dm +++ b/code/modules/cargo/packs/science.dm @@ -244,7 +244,6 @@ desc = "Three cores, perfect for any MODsuit construction! Naturally harvested™, of course." cost = CARGO_CRATE_VALUE * 3 access = ACCESS_ROBOTICS - access_view = ACCESS_ROBOTICS contains = list(/obj/item/mod/construction/core, /obj/item/mod/construction/core, /obj/item/mod/construction/core) diff --git a/code/modules/client/preferences/mod_select.dm b/code/modules/client/preferences/mod_select.dm deleted file mode 100644 index c80c8b4c69..0000000000 --- a/code/modules/client/preferences/mod_select.dm +++ /dev/null @@ -1,23 +0,0 @@ -/// Switches between mouse buttons for MODsuit active modules -/datum/preference/choiced/mod_select - category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "mod_select" - savefile_identifier = PREFERENCE_PLAYER - -/datum/preference/choiced/mod_select/init_possible_values() - return list(MIDDLE_CLICK, ALT_CLICK) - -/datum/preference/choiced/mod_select/create_default_value() - return MIDDLE_CLICK - -/datum/preference/choiced/mod_select/apply_to_client_updated(client/client, value) - if(!ishuman(client.mob)) - return - var/mob/living/carbon/human/client_owner = client.mob - if(!istype(client_owner.back, /obj/item/mod/control)) - return - var/obj/item/mod/control/mod = client_owner.back - if(!mod.selected_module) - return - UnregisterSignal(mod.wearer, mod.selected_module.used_signal) - mod.selected_module.update_signal(value) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 203c578c2c..51659a2904 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -337,6 +337,7 @@ on_item_dropped(I) if(I.dropped(src) == ITEM_RELOCATED_BY_DROPPED) return FALSE + SEND_SIGNAL(src, COMSIG_MOB_UNEQUIPPED_ITEM, I, force, newloc, no_move, invdrop, silent) return TRUE //This is a SAFE proc. Use this instead of equip_to_slot()! diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 2f3c1357da..85bef76d98 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -305,6 +305,12 @@ update_inv_hands() return var/datum/component/storage/storage = equipped_back.GetComponent(/datum/component/storage) + if(istype(equipped_back, /obj/item/mod/control)) + var/obj/item/mod/control/C = equipped_back + for(var/obj/item/mod/module/storage/S in C.modules) + if(S.stored) + equipped_back = S.stored + storage = S.stored.GetComponent(/datum/component/storage) if(!storage) if(!thing) equipped_back.attack_hand(src) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 68a9f8bd0f..75dfb0e14c 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1303,8 +1303,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(ITEM_SLOT_FEET) if(H.shoes) return FALSE - if((DIGITIGRADE in species_traits) && !(I.item_flags & IGNORE_DIGITIGRADE)) - return FALSE if(num_legs < 2) return FALSE if(DIGITIGRADE in species_traits) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 3cda68c015..5ba5f2431d 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -99,6 +99,13 @@ var/display_icon_override var/emote_display = "Neutral" //text string of the current emote we set for the status displays, to prevent logins resetting it. + var/datum/robot_control/robot_control + /// Station alert datum for showing alerts UI + var/datum/station_alert/alert_control + ///remember AI's last location + var/atom/lastloc + interaction_range = INFINITY + /mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai) . = ..() if(!target_ai) //If there is no player/brain inside. @@ -177,9 +184,10 @@ QDEL_NULL(spark_system) QDEL_NULL(malf_picker) QDEL_NULL(doomsday_device) - QDEL_NULL(robot_control) + // TODO: Why these no work? + // QDEL_NULL(robot_control) QDEL_NULL(aiMulti) - QDEL_NULL(alert_control) + // QDEL_NULL(alert_control) malfhack = null current = null Bot = null diff --git a/code/modules/mod/mod_actions.dm b/code/modules/mod/mod_actions.dm index 2c27879e79..b280ffdd70 100644 --- a/code/modules/mod/mod_actions.dm +++ b/code/modules/mod/mod_actions.dm @@ -3,28 +3,41 @@ icon_icon = 'icons/mob/actions/actions_mod.dmi' check_flags = AB_CHECK_CONSCIOUS var/obj/item/mod/control/mod + /// Whether this action is intended for the AI. Stuff breaks a lot if this is done differently. + var/ai_action = FALSE + /// Whether this action is intended for the inserted pAI. Stuff breaks a lot if this is done differently. + var/pai_action = FALSE /datum/action/item_action/mod/New(Target) ..() - mod = Target - -/datum/action/item_action/mod/Grant(mob/M) - if(owner) - Share(M) + if(!istype(Target, /obj/item/mod/control)) + qdel(src) return - ..() + if(ai_action) + background_icon_state = ACTION_BUTTON_DEFAULT_BACKGROUND -/datum/action/item_action/mod/Remove(mob/M) - var/mob_to_grant - for(var/datum/weakref/reference as anything in sharers) - var/mob/freeloader = reference.resolve() - if(!freeloader) - continue - mob_to_grant = freeloader - break - ..() - if(mob_to_grant) - Grant(mob_to_grant) +/datum/action/item_action/mod/Grant(mob/user) + mod = target + if(ai_action && user != mod.ai) + return + else if(!ai_action && user == mod.ai) + return + return ..() + +/datum/action/item_action/mod/Remove(mob/user) + if(ai_action && mod && user != mod.ai) + return + else if(!ai_action && mod && user == mod.ai) + return + return ..() + +/datum/action/item_action/mod/Trigger(trigger_flags) + if(!IsAvailable()) + return FALSE + if(mod.malfunctioning && prob(75)) + mod.balloon_alert(usr, "button malfunctions!") + return FALSE + return TRUE /datum/action/item_action/mod/deploy name = "Deploy MODsuit" @@ -37,6 +50,12 @@ mod.choose_deploy(usr) return TRUE +/datum/action/item_action/mod/deploy/ai + ai_action = TRUE + +/datum/action/item_action/mod/deploy/pai + pai_action = TRUE + /datum/action/item_action/mod/activate name = "Activate MODsuit" desc = "Activate/Deactivate the MODsuit." @@ -48,6 +67,12 @@ mod.toggle_activate(usr) return TRUE +/datum/action/item_action/mod/activate/ai + ai_action = TRUE + +/datum/action/item_action/mod/activate/pai + pai_action = TRUE + /datum/action/item_action/mod/module name = "Toggle Module" desc = "Toggle a MODsuit module." @@ -59,6 +84,12 @@ mod.quick_module(usr) return TRUE +/datum/action/item_action/mod/module/ai + ai_action = TRUE + +/datum/action/item_action/mod/module/pai + pai_action = TRUE + /datum/action/item_action/mod/panel name = "MODsuit Panel" desc = "Open the MODsuit's panel." @@ -69,3 +100,9 @@ return FALSE mod.ui_interact(usr) return TRUE + +/datum/action/item_action/mod/panel/ai + ai_action = TRUE + +/datum/action/item_action/mod/panel/pai + pai_action = TRUE diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm index f7869dc227..c0b1300dc4 100644 --- a/code/modules/mod/mod_activation.dm +++ b/code/modules/mod/mod_activation.dm @@ -1,4 +1,3 @@ -#define MOD_ACTIVATION_STEP_TIME 2 SECONDS #define MOD_ACTIVATION_STEP_FLAGS IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM|IGNORE_INCAPACITATED /// Creates a radial menu from which the user chooses parts of the suit to deploy/retract. Repeats until all parts are extended or retracted. @@ -72,7 +71,8 @@ /obj/item/mod/control/proc/conceal(mob/user, part) var/obj/item/piece = part REMOVE_TRAIT(piece, TRAIT_NODROP, MOD_TRAIT) - wearer.transferItemToLoc(piece, src, force = TRUE) + if(wearer) + wearer.transferItemToLoc(piece, src, force = TRUE) if(piece == gauntlets) gauntlets.show_overslot() if(piece == boots) @@ -117,28 +117,36 @@ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE for(var/obj/item/mod/module/module as anything in modules) - if(!module.active) + if(!module.active || module.allowed_inactive) continue module.on_deactivation() activating = TRUE to_chat(wearer, span_notice("MODsuit [active ? "shutting down" : "starting up"].")) - if(do_after(wearer, MOD_ACTIVATION_STEP_TIME, wearer, MOD_ACTIVATION_STEP_FLAGS)) + if(do_after(wearer, MOD_ACTIVATION_STEP_TIME, target = wearer, required_mobility_flags = NONE)) 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, MOD_ACTIVATION_STEP_TIME, wearer, MOD_ACTIVATION_STEP_FLAGS)) + else + return toggle_activate_fail() + if(do_after(wearer, MOD_ACTIVATION_STEP_TIME, target = wearer, required_mobility_flags = NONE)) 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, MOD_ACTIVATION_STEP_TIME, wearer, MOD_ACTIVATION_STEP_FLAGS)) + else + return toggle_activate_fail() + if(do_after(wearer, MOD_ACTIVATION_STEP_TIME, target = wearer, required_mobility_flags = NONE)) 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, MOD_ACTIVATION_STEP_TIME, wearer, MOD_ACTIVATION_STEP_FLAGS)) + else + return toggle_activate_fail() + if(do_after(wearer, MOD_ACTIVATION_STEP_TIME, target = wearer, required_mobility_flags = NONE)) 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, MOD_ACTIVATION_STEP_TIME, wearer, MOD_ACTIVATION_STEP_FLAGS)) + else + return toggle_activate_fail() + if(do_after(wearer, MOD_ACTIVATION_STEP_TIME, target = wearer, required_mobility_flags = NONE)) to_chat(wearer, span_notice("Systems [active ? "shut down. Parts unsealed. Goodbye" : "started up. Parts sealed. Welcome"], [wearer].")) if(ai) to_chat(ai, span_notice("SYSTEMS [active ? "DEACTIVATED. GOODBYE" : "ACTIVATED. WELCOME"]: \"[ai]\"")) @@ -148,9 +156,20 @@ SEND_SOUND(wearer, sound('sound/mecha/nominal.ogg',volume=50)) else playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000) + else + return toggle_activate_fail() activating = FALSE return TRUE +/obj/item/mod/control/proc/toggle_activate_fail() + seal_part(boots, seal = active) + seal_part(gauntlets, seal = active) + seal_part(chestplate,seal = active) + seal_part(helmet, seal = active) + to_chat(wearer, span_warning("[active ? "Shut down" : "Start up"] cancelled.")) + activating = FALSE + return FALSE + ///Seals or unseals the given part /obj/item/mod/control/proc/seal_part(obj/item/clothing/part, seal) if(seal) @@ -165,18 +184,16 @@ part.clothing_flags &= ~part.visor_flags part.heat_protection = NONE part.cold_protection = NONE + part.icon_state = "[skin]-[initial(part.icon_state)][seal ? "-sealed" : ""]" + part.item_state = "[skin]-[initial(part.item_state)][seal ? "-sealed" : ""]" if(part == boots) - boots.icon_state = "[skin]-boots[seal ? "-sealed" : ""]" wearer.update_inv_shoes() if(part == gauntlets) - gauntlets.icon_state = "[skin]-gauntlets[seal ? "-sealed" : ""]" wearer.update_inv_gloves() if(part == chestplate) - chestplate.icon_state = "[skin]-chestplate[seal ? "-sealed" : ""]" wearer.update_inv_wear_suit() wearer.update_inv_w_uniform() if(part == helmet) - helmet.icon_state = "[skin]-helmet[seal ? "-sealed" : ""]" if(seal) helmet.alternate_worn_layer = null else diff --git a/code/modules/mod/mod_ai.dm b/code/modules/mod/mod_ai.dm index f470fb8046..c7356e41f0 100644 --- a/code/modules/mod/mod_ai.dm +++ b/code/modules/mod/mod_ai.dm @@ -14,6 +14,8 @@ if(!do_after(user, 5 SECONDS, target = src)) balloon_alert(user, "interrupted!") return + if(!ai) + return intAI = ai intAI.ai_restore_power()//So the AI initially has power. intAI.control_disabled = TRUE @@ -22,10 +24,7 @@ intAI.forceMove(card) card.AI = intAI for(var/datum/action/action as anything in actions) - if(action.owner == intAI) - action.Remove(intAI) - else - action.Unshare(intAI) + action.Remove(intAI) intAI.controlled_equipment = null intAI.remote_control = null balloon_alert(intAI, "transferred to a card") @@ -37,6 +36,9 @@ if(!intAI) balloon_alert(user, "no AI in card!") return + if(ai) + balloon_alert(user, "already has AI!") + return if(intAI.deployed_shell) //Recall AI if shelled so it can be checked for a client intAI.disconnect_shell() if(intAI.stat || !intAI.client) @@ -46,6 +48,8 @@ if(!do_after(user, 5 SECONDS, target = src)) balloon_alert(user, "interrupted!") return + if(ai) + return balloon_alert(user, "AI transferred to suit") ai_enter_mod(intAI) card.AI = null @@ -63,14 +67,95 @@ for(var/datum/action/action as anything in actions) action.Grant(new_ai) +/** + * Simple proc to insert the pAI into the MODsuit. + * + * user - The person trying to put the pAI into the MODsuit. + * card - The pAI card we're slotting in the MODsuit. + */ + +/obj/item/mod/control/proc/insert_pai(mob/user, obj/item/paicard/card) + if(mod_pai) + balloon_alert(user, "pAI already installed!") + return + if(!card.pai || !card.pai.mind) + balloon_alert(user, "pAI unresponsive!") + return + balloon_alert(user, "transferring to suit...") + if(!do_after(user, 5 SECONDS, target = src)) + balloon_alert(user, "interrupted!") + return FALSE + if(!user.transferItemToLoc(card, src)) + return + + mod_pai = card.pai + balloon_alert(user, "pAI transferred to suit") + balloon_alert(mod_pai, "transferred to a suit") + mod_pai.canholo = FALSE + mod_pai.remote_control = src + for(var/datum/action/action as anything in actions) + action.Grant(mod_pai) + return TRUE + +/** + * Simple proc to extract the pAI from the MODsuit. It's the proc to call if you want to take it out, + * remove_pai() is there so atom_destruction() doesn't have any risk of sleeping. + * + * user - The person trying to take out the pAI from the MODsuit. + * forced - Whether or not we skip the checks and just eject the pAI. Defaults to FALSE. + * feedback - Whether to give feedback via balloon alerts or not. Defaults to TRUE. + */ +/obj/item/mod/control/proc/extract_pai(mob/user, forced = FALSE, feedback = TRUE) + if(!mod_pai) + if(user && feedback) + balloon_alert(user, "no pAI to remove!") + return + if(!forced) + if(!open) + if(user && feedback) + balloon_alert(user, "open the suit panel!") + return FALSE + if(!do_after(user, 5 SECONDS, target = src)) + if(user && feedback) + balloon_alert(user, "interrupted!") + return FALSE + + remove_pai(feedback) + + if(feedback && user) + balloon_alert(user, "pAI removed from the suit") + +/** + * Simple proc that handles the safe removal of the pAI from a MOD control unit. + * + * Arguments: + * * feedback - Whether or not we want to give balloon alert feedback to the mod_pai. Defaults to FALSE. + */ +/obj/item/mod/control/proc/remove_pai(feedback = FALSE) + var/turf/drop_off = get_turf(src) + if(drop_off) // In case there's no drop_off, the pAI will simply get deleted. + mod_pai.card.forceMove(drop_off) + + for(var/datum/action/action as anything in actions) + if(action.owner == mod_pai) + action.Remove(mod_pai) + + if(feedback) + balloon_alert(mod_pai, "removed from a suit") + mod_pai.remote_control = null + mod_pai.canholo = TRUE + mod_pai = null + #define MOVE_DELAY 2 #define WEARER_DELAY 1 #define LONE_DELAY 5 -#define CELL_PER_STEP DEFAULT_CELL_DRAIN * 2.5 +#define CELL_PER_STEP (DEFAULT_CHARGE_DRAIN * 2.5) /obj/item/mod/control/relaymove(mob/user, direction) if((!active && wearer) || !cell || cell.charge < CELL_PER_STEP || user != ai || !COOLDOWN_FINISHED(src, cooldown_mod_move) || (wearer?.pulledby?.grab_state > GRAB_PASSIVE)) return FALSE + if(wearer && (wearer.pulledby?.grab_state || wearer.incapacitated() || wearer.stat)) + return FALSE var/timemodifier = MOVE_DELAY * (ISDIAGONALDIR(direction) ? SQRT_2 : 1) * (wearer ? WEARER_DELAY : LONE_DELAY) COOLDOWN_START(src, cooldown_mod_move, movedelay * timemodifier + slowdown) playsound(src, 'sound/mecha/mechmove01.ogg', 25, TRUE) @@ -86,3 +171,8 @@ #undef WEARER_DELAY #undef LONE_DELAY #undef CELL_PER_STEP + +/obj/item/mod/control/ui_state(mob/user) + if(user == mod_pai) + return GLOB.contained_state + return ..() diff --git a/code/modules/mod/mod_clothes.dm b/code/modules/mod/mod_clothes.dm index 37045015d6..548f6064e5 100644 --- a/code/modules/mod/mod_clothes.dm +++ b/code/modules/mod/mod_clothes.dm @@ -1,9 +1,12 @@ /obj/item/clothing/head/helmet/space/mod name = "MOD helmet" desc = "A helmet for a MODsuit." - icon = 'icons/obj/mod.dmi' + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + anthro_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + taur_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' icon_state = "helmet" - worn_icon = 'icons/mob/mod.dmi' + item_state = "helmet" armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10) body_parts_covered = HEAD heat_protection = HEAD @@ -12,15 +15,16 @@ min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT clothing_flags = THICKMATERIAL resistance_flags = NONE - flash_protect = FLASH_PROTECTION_NONE - clothing_flags = SNUG_FIT + flash_protect = 0 + clothing_flags = NONE flags_inv = HIDEFACIALHAIR flags_cover = NONE visor_flags = THICKMATERIAL|STOPSPRESSUREDAMAGE visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT - visor_flags_cover = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF + visor_flags_cover = HEADCOVERSMOUTH|HEADCOVERSEYES var/alternate_layer = NECK_LAYER var/obj/item/mod/control/mod + mutantrace_variation = STYLE_MUZZLE /obj/item/clothing/head/helmet/space/mod/Destroy() if(!QDELETED(mod)) @@ -32,9 +36,12 @@ /obj/item/clothing/suit/armor/mod name = "MOD chestplate" desc = "A chestplate for a MODsuit." - icon = 'icons/obj/mod.dmi' + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + anthro_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + taur_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' icon_state = "chestplate" - worn_icon = 'icons/mob/mod.dmi' + item_state = "chestplate" blood_overlay_type = "armor" armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10) body_parts_covered = CHEST|GROIN @@ -48,6 +55,7 @@ allowed = list(/obj/item/flashlight, /obj/item/tank/internals) resistance_flags = NONE var/obj/item/mod/control/mod + mutantrace_variation = STYLE_DIGITIGRADE|STYLE_SNEK_TAURIC|STYLE_PAW_TAURIC /obj/item/clothing/suit/armor/mod/Destroy() if(!QDELETED(mod)) @@ -59,9 +67,12 @@ /obj/item/clothing/gloves/mod name = "MOD gauntlets" desc = "A pair of gauntlets for a MODsuit." - icon = 'icons/obj/mod.dmi' + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + anthro_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + taur_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' icon_state = "gauntlets" - worn_icon = 'icons/mob/mod.dmi' + item_state = "gauntlets" armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10) body_parts_covered = HANDS|ARMS heat_protection = HANDS|ARMS @@ -86,15 +97,18 @@ if(!overslot) return if(!mod.wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) - mod.wearer.dropItemToGround(overslot, force = TRUE, silent = TRUE) + mod.wearer.dropItemToGround(overslot, force = TRUE) overslot = null /obj/item/clothing/shoes/mod name = "MOD boots" desc = "A pair of boots for a MODsuit." - icon = 'icons/obj/mod.dmi' + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + anthro_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + taur_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' icon_state = "boots" - worn_icon = 'icons/mob/mod.dmi' + item_state = "boots" armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10) body_parts_covered = FEET|LEGS heat_protection = FEET|LEGS @@ -103,7 +117,7 @@ min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT clothing_flags = THICKMATERIAL resistance_flags = NONE - item_flags = IGNORE_DIGITIGRADE + item_flags = NONE var/obj/item/mod/control/mod var/obj/item/clothing/overslot @@ -119,5 +133,5 @@ if(!overslot) return if(!mod.wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) - mod.wearer.dropItemToGround(overslot, force = TRUE, silent = TRUE) + mod.wearer.dropItemToGround(overslot, force = TRUE) overslot = null diff --git a/code/modules/mod/mod_construction.dm b/code/modules/mod/mod_construction.dm index 4916f8b6bc..19c188dcf6 100644 --- a/code/modules/mod/mod_construction.dm +++ b/code/modules/mod/mod_construction.dm @@ -1,6 +1,7 @@ /obj/item/mod/construction desc = "A part used in MOD construction." - inhand_icon_state = "rack_parts" + icon = 'icons/obj/clothing/modsuit/mod_construction.dmi' + item_state = "rack_parts" /obj/item/mod/construction/helmet name = "MOD helmet" @@ -20,7 +21,7 @@ /obj/item/mod/construction/core name = "MOD core" - icon_state = "mod-core" + icon_state = "mod-core-standard" desc = "Growing in the most lush, fertile areas of the planet Sprout, there is a crystal known as the Heartbloom. \ These rare, organic piezoelectric crystals are of incredible cultural significance to the artist castes of the Ethereals, \ owing to their appearance; which is exactly similar to that of an Ethereal's heart. \n\ @@ -29,7 +30,7 @@ /obj/item/mod/construction/broken_core name = "broken MOD core" - icon_state = "mod-core-broken" + icon_state = "mod-core" desc = "An internal power source for a Modular Outerwear Device. You don't seem to be able to source any power from this one, though." /obj/item/mod/construction/broken_core/examine(mob/user) @@ -46,7 +47,7 @@ /obj/item/mod/construction/armor name = "MOD armor plates" desc = "Armor plates used to finish a MOD." - icon_state = "standard-armor" + icon_state = "standard-plating" var/datum/mod_theme/theme = /datum/mod_theme /obj/item/mod/construction/armor/Initialize(mapload) @@ -54,7 +55,7 @@ var/datum/mod_theme/used_theme = GLOB.mod_themes[theme] name = "MOD [used_theme.name] armor plates" desc = "[desc] [used_theme.desc]" - icon_state = "[used_theme.default_skin]-armor" + icon_state = "[used_theme.default_skin]-plating" /obj/item/mod/construction/armor/engineering theme = /datum/mod_theme/engineering @@ -77,7 +78,7 @@ /obj/item/mod/paint name = "MOD paint kit" desc = "This kit will repaint your MODsuit to something unique." - icon = 'icons/obj/mod.dmi' + icon = 'icons/obj/clothing/modsuit/mod_construction.dmi' icon_state = "paintkit" #define START_STEP "start" diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index 8ca7dc8573..ca4ec2a468 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -2,15 +2,18 @@ /obj/item/mod name = "Base MOD" desc = "You should not see this, yell at a coder!" - icon = 'icons/obj/mod.dmi' + icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi' + mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + anthro_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' + taur_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing.dmi' icon_state = "standard-control" - worn_icon = 'icons/mob/mod.dmi' + item_state = "standard-control" /obj/item/mod/control name = "MOD control unit" desc = "The control unit of a Modular Outerwear Device, a powered, back-mounted suit that protects against various environments." icon_state = "control" - inhand_icon_state = "mod_control" + item_state = "control" w_class = WEIGHT_CLASS_BULKY slot_flags = ITEM_SLOT_BACK strip_delay = 10 SECONDS @@ -48,7 +51,7 @@ /// How much module complexity this MOD is carrying. var/complexity = 0 /// Power usage of the MOD. - var/cell_drain = DEFAULT_CELL_DRAIN + var/cell_drain = DEFAULT_CHARGE_DRAIN /// Slowdown of the MOD when not active. var/slowdown_inactive = 2 /// Slowdown of the MOD when active. @@ -73,6 +76,8 @@ var/obj/item/mod/module/selected_module /// AI mob inhabiting the MOD. var/mob/living/silicon/ai/ai + /// pAI mob inhabiting the MOD. + var/mob/living/silicon/pai/mod_pai /// Delay between moves as AI. var/movedelay = 0 /// Cooldown for AI moves. @@ -123,6 +128,7 @@ piece.permeability_coefficient = theme.permeability_coefficient piece.siemens_coefficient = theme.siemens_coefficient piece.icon_state = "[skin]-[initial(piece.icon_state)]" + piece.item_state = "[skin]-[initial(piece.item_state)]" update_flags() for(var/obj/item/mod/module/module as anything in initial_modules) module = new module(src) @@ -206,6 +212,7 @@ balloon_alert(carbon_user, "retract parts first!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE) return FALSE + return ..() /obj/item/mod/control/MouseDrop(atom/over_object) if(src != wearer?.back || !istype(over_object, /atom/movable/screen/inventory/hand)) @@ -241,10 +248,22 @@ return return ..() +/obj/item/mod/control/AltClick(mob/user) + if(seconds_electrified && cell?.charge) + if(shock(user)) + return + if(!open) + for(var/obj/item/mod/module/storage/S in modules) + if(S.stored) + playsound(user, "rustle", 50, 1, -5) + SEND_SIGNAL(S.stored, COMSIG_TRY_STORAGE_SHOW, wearer, TRUE) + return + . = ..() + /obj/item/mod/control/screwdriver_act(mob/living/user, obj/item/screwdriver) if(..()) return TRUE - if(active || activating || ai_controller) + if(active || activating) balloon_alert(user, "deactivate suit first!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE @@ -330,12 +349,6 @@ if(open) return cell -/obj/item/mod/control/GetAccess() - if(ai_controller) - return req_access.Copy() - else - return ..() - /obj/item/mod/control/emag_act(mob/user) locked = !locked balloon_alert(user, "[locked ? "locked" : "unlocked"]") @@ -363,12 +376,12 @@ conceal(null, part) return ..() -/obj/item/mod/control/worn_overlays(mutable_appearance/standing, isinhands = FALSE, icon_file) +/obj/item/mod/control/worn_overlays(isinhands = FALSE, icon_file) . = ..() if(!active) return for(var/obj/item/mod/module/module as anything in modules) - var/list/module_icons = module.generate_worn_overlay(standing) + var/list/module_icons = module.generate_worn_overlay() if(!length(module_icons)) continue . += module_icons @@ -394,8 +407,8 @@ var/used_category if(part == helmet) used_category = HELMET_FLAGS - helmet.alternate_worn_layer = used_skin[HELMET_LAYER] - helmet.alternate_layer = used_skin[HELMET_LAYER] + helmet.alternate_worn_layer = used_skin["HELMET_LAYER"] + helmet.alternate_layer = used_skin["HELMET_LAYER"] if(part == chestplate) used_category = CHESTPLATE_FLAGS if(part == gauntlets) diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm index 174cc1bab3..ed183557b9 100644 --- a/code/modules/mod/mod_theme.dm +++ b/code/modules/mod/mod_theme.dm @@ -28,7 +28,7 @@ /// How much modules can the MOD carry without malfunctioning. var/complexity_max = DEFAULT_MAX_COMPLEXITY /// How much battery power the MOD uses by just being on - var/cell_drain = DEFAULT_CELL_DRAIN + var/cell_drain = DEFAULT_CHARGE_DRAIN /// Slowdown of the MOD when not active. var/slowdown_inactive = 1.25 /// Slowdown of the MOD when active. @@ -44,11 +44,11 @@ "standard" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -67,10 +67,10 @@ "civilian" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -102,11 +102,11 @@ "engineering" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -137,12 +137,12 @@ "atmospheric" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR, UNSEALED_COVER = HEADCOVERSMOUTH, - SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -175,11 +175,11 @@ "advanced" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -204,17 +204,17 @@ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 50, BIO = 100, FIRE = 100, ACID = 75, WOUND = 15) resistance_flags = FIRE_PROOF max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - cell_drain = DEFAULT_CELL_DRAIN * 2 + cell_drain = DEFAULT_CHARGE_DRAIN * 2 complexity_max = DEFAULT_MAX_COMPLEXITY + 5 skins = list( "mining" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -237,18 +237,18 @@ desc = "A lightweight suit by DeForest Medical Corporation, allows for easier movement." default_skin = "medical" armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 10, BIO = 100, FIRE = 60, ACID = 75, WOUND = 10) - cell_drain = DEFAULT_CELL_DRAIN * 1.5 + cell_drain = DEFAULT_CHARGE_DRAIN * 1.5 slowdown_inactive = 1 slowdown_active = 0.5 skins = list( "medical" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -267,11 +267,11 @@ "corpsman" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -296,7 +296,7 @@ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 10, BIO = 100, FIRE = 100, ACID = 100, WOUND = 10) resistance_flags = FIRE_PROOF|ACID_PROOF max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - cell_drain = DEFAULT_CELL_DRAIN * 1.5 + cell_drain = DEFAULT_CHARGE_DRAIN * 1.5 slowdown_inactive = 0.75 slowdown_active = 0.25 inbuilt_modules = list(/obj/item/mod/module/quick_carry/advanced) @@ -304,11 +304,11 @@ "rescue" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -341,10 +341,10 @@ "research" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -375,12 +375,12 @@ "security" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE, UNSEALED_COVER = HEADCOVERSMOUTH, - SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -413,10 +413,10 @@ "safeguard" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -449,11 +449,11 @@ "magnate" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -476,18 +476,18 @@ desc = "A suit by Honk Ltd. Protects against low humor environments. Most of the tech went to lower the power cost." default_skin = "cosmohonk" armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 10, BIO = 100, FIRE = 60, ACID = 30, WOUND = 5) - cell_drain = DEFAULT_CELL_DRAIN * 0.25 + cell_drain = DEFAULT_CHARGE_DRAIN * 0.25 slowdown_inactive = 1.75 slowdown_active = 1.25 skins = list( "cosmohonk" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -515,16 +515,16 @@ slowdown_inactive = 1 slowdown_active = 0.5 ui_theme = "syndicate" - inbuilt_modules = list(/obj/item/mod/module/armor_booster) + inbuilt_modules = list() skins = list( "syndicate" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -553,16 +553,16 @@ slowdown_inactive = 0.75 slowdown_active = 0.25 ui_theme = "syndicate" - inbuilt_modules = list(/obj/item/mod/module/armor_booster/elite) + inbuilt_modules = list() skins = list( "elite" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -597,10 +597,10 @@ "enchanted" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -633,10 +633,10 @@ "prototype" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -668,11 +668,11 @@ "responsory" = list( HELMET_LAYER = NECK_LAYER, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT, + UNSEALED_CLOTHING = NONE, SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -691,10 +691,10 @@ "inquisitory" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, - UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -725,11 +725,11 @@ "apocryphal" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR, SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -761,11 +761,11 @@ "corporate" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE, - SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -797,12 +797,12 @@ "debug" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE, UNSEALED_COVER = HEADCOVERSMOUTH, - SEALED_COVER = HEADCOVERSEYES|PEPPERPROOF, + SEALED_COVER = HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( UNSEALED_CLOTHING = THICKMATERIAL, @@ -828,20 +828,20 @@ resistance_flags = INDESTRUCTIBLE|LAVA_PROOF|FIRE_PROOF|UNACIDABLE|ACID_PROOF max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT complexity_max = 1000 - cell_drain = DEFAULT_CELL_DRAIN * 0 + cell_drain = DEFAULT_CHARGE_DRAIN * 0 slowdown_inactive = 0 slowdown_active = 0 skins = list( "debug" = list( HELMET_LAYER = null, HELMET_FLAGS = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL|STOPSPRESSUREDAMAGE, + UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT, SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE, - UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, + UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES, ), CHESTPLATE_FLAGS = list( - UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCKS_SHOVE_KNOCKDOWN, + UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, ), GAUNTLETS_FLAGS = list( diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm index af482e50f4..3296cf7ba3 100644 --- a/code/modules/mod/mod_types.dm +++ b/code/modules/mod/mod_types.dm @@ -20,12 +20,12 @@ /obj/item/mod/control/pre_equipped/advanced theme = /datum/mod_theme/advanced cell = /obj/item/stock_parts/cell/super - initial_modules = list(/obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/welding, /obj/item/mod/module/rad_protection, /obj/item/mod/module/jetpack, /obj/item/mod/module/flashlight) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/rad_protection, /obj/item/mod/module/flashlight) /obj/item/mod/control/pre_equipped/mining theme = /datum/mod_theme/mining cell = /obj/item/stock_parts/cell/high/plus - initial_modules = list(/obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/welding, /obj/item/mod/module/orebag, /obj/item/mod/module/flashlight, /obj/item/mod/module/magboot, /obj/item/mod/module/drill) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/orebag, /obj/item/mod/module/flashlight, /obj/item/mod/module/magboot, /obj/item/mod/module/drill) /obj/item/mod/control/pre_equipped/medical theme = /datum/mod_theme/medical @@ -34,12 +34,12 @@ /obj/item/mod/control/pre_equipped/rescue theme = /datum/mod_theme/rescue cell = /obj/item/stock_parts/cell/super - initial_modules = list(/obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/flashlight, /obj/item/mod/module/health_analyzer, /obj/item/mod/module/injector) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/flashlight, /obj/item/mod/module/health_analyzer, /obj/item/mod/module/injector) /obj/item/mod/control/pre_equipped/research theme = /datum/mod_theme/research cell = /obj/item/stock_parts/cell/super - initial_modules = list(/obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/welding, /obj/item/mod/module/flashlight, /obj/item/mod/module/circuit, /obj/item/mod/module/t_ray) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/flashlight, /obj/item/mod/module/t_ray) /obj/item/mod/control/pre_equipped/security theme = /datum/mod_theme/security @@ -48,42 +48,37 @@ /obj/item/mod/control/pre_equipped/safeguard theme = /datum/mod_theme/safeguard cell = /obj/item/stock_parts/cell/super - initial_modules = list(/obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/welding, /obj/item/mod/module/flashlight, /obj/item/mod/module/jetpack, /obj/item/mod/module/holster) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/flashlight, /obj/item/mod/module/holster) /obj/item/mod/control/pre_equipped/magnate theme = /datum/mod_theme/magnate cell = /obj/item/stock_parts/cell/hyper - initial_modules = list(/obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/welding, /obj/item/mod/module/holster, /obj/item/mod/module/pathfinder) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/holster) /obj/item/mod/control/pre_equipped/traitor theme = /datum/mod_theme/syndicate cell = /obj/item/stock_parts/cell/super - initial_modules = list(/obj/item/mod/module/storage/syndicate, /obj/item/mod/module/welding, /obj/item/mod/module/tether, /obj/item/mod/module/pathfinder, /obj/item/mod/module/flashlight, /obj/item/mod/module/dna_lock) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/flashlight, /obj/item/mod/module/dna_lock) /obj/item/mod/control/pre_equipped/nuclear theme = /datum/mod_theme/syndicate cell = /obj/item/stock_parts/cell/hyper - initial_modules = list(/obj/item/mod/module/storage/syndicate, /obj/item/mod/module/welding, /obj/item/mod/module/jetpack, /obj/item/mod/module/visor/thermal, /obj/item/mod/module/flashlight, /obj/item/mod/module/holster) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/visor/thermal, /obj/item/mod/module/flashlight, /obj/item/mod/module/holster) /obj/item/mod/control/pre_equipped/elite theme = /datum/mod_theme/elite cell = /obj/item/stock_parts/cell/bluespace - initial_modules = list(/obj/item/mod/module/storage/syndicate, /obj/item/mod/module/welding, /obj/item/mod/module/emp_shield, /obj/item/mod/module/jetpack, /obj/item/mod/module/visor/thermal, /obj/item/mod/module/flashlight, /obj/item/mod/module/holster) - -/obj/item/mod/control/pre_equipped/enchanted - theme = /datum/mod_theme/enchanted - cell = /obj/item/stock_parts/cell/crystal_cell/wizard - initial_modules = list(/obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/energy_shield/wizard, /obj/item/mod/module/emp_shield) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/emp_shield, /obj/item/mod/module/visor/thermal, /obj/item/mod/module/flashlight, /obj/item/mod/module/holster) /obj/item/mod/control/pre_equipped/prototype theme = /datum/mod_theme/prototype cell = /obj/item/stock_parts/cell/high/plus - initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/rad_protection, /obj/item/mod/module/flashlight, /obj/item/mod/module/tether) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/rad_protection, /obj/item/mod/module/flashlight) /obj/item/mod/control/pre_equipped/responsory theme = /datum/mod_theme/responsory cell = /obj/item/stock_parts/cell/hyper - initial_modules = list(/obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/welding, /obj/item/mod/module/emp_shield, /obj/item/mod/module/flashlight, /obj/item/mod/module/holster) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/emp_shield, /obj/item/mod/module/flashlight, /obj/item/mod/module/holster) var/insignia_type = /obj/item/mod/module/insignia /obj/item/mod/control/pre_equipped/responsory/Initialize(mapload, new_theme, new_skin) @@ -112,7 +107,7 @@ insignia_type = /obj/item/mod/module/insignia/chaplain /obj/item/mod/control/pre_equipped/responsory/inquisitory - initial_modules = list(/obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/anti_magic, /obj/item/mod/module/welding, /obj/item/mod/module/emp_shield, /obj/item/mod/module/flashlight, /obj/item/mod/module/holster) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/anti_magic, /obj/item/mod/module/welding, /obj/item/mod/module/emp_shield, /obj/item/mod/module/flashlight, /obj/item/mod/module/holster) applied_skin = "inquisitory" /obj/item/mod/control/pre_equipped/responsory/inquisitory/commander @@ -130,22 +125,22 @@ /obj/item/mod/control/pre_equipped/apocryphal theme = /datum/mod_theme/apocryphal cell = /obj/item/stock_parts/cell/bluespace - initial_modules = list(/obj/item/mod/module/storage/bluespace, /obj/item/mod/module/welding, /obj/item/mod/module/emp_shield, /obj/item/mod/module/jetpack, /obj/item/mod/module/holster) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/emp_shield, /obj/item/mod/module/holster) /obj/item/mod/control/pre_equipped/corporate theme = /datum/mod_theme/corporate cell = /obj/item/stock_parts/cell/bluespace - initial_modules = list(/obj/item/mod/module/storage/bluespace, /obj/item/mod/module/holster) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/holster) /obj/item/mod/control/pre_equipped/debug theme = /datum/mod_theme/debug cell = /obj/item/stock_parts/cell/bluespace - initial_modules = list(/obj/item/mod/module/storage/bluespace, /obj/item/mod/module/welding, /obj/item/mod/module/flashlight, /obj/item/mod/module/bikehorn, /obj/item/mod/module/rad_protection, /obj/item/mod/module/tether, /obj/item/mod/module/injector) //one of every type of module, for testing if they all work correctly + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/flashlight, /obj/item/mod/module/bikehorn, /obj/item/mod/module/rad_protection, /obj/item/mod/module/injector) //one of every type of module, for testing if they all work correctly /obj/item/mod/control/pre_equipped/administrative theme = /datum/mod_theme/administrative cell = /obj/item/stock_parts/cell/infinite/abductor - initial_modules = list(/obj/item/mod/module/storage/bluespace, /obj/item/mod/module/welding, /obj/item/mod/module/stealth/ninja, /obj/item/mod/module/quick_carry/advanced, /obj/item/mod/module/magboot/advanced, /obj/item/mod/module/jetpack) + initial_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/welding, /obj/item/mod/module/quick_carry/advanced, /obj/item/mod/module/magboot/advanced) //these exist for the prefs menu /obj/item/mod/control/pre_equipped/syndicate_empty diff --git a/code/modules/mod/mod_ui.dm b/code/modules/mod/mod_ui.dm index 1e719e93eb..065f3f6748 100644 --- a/code/modules/mod/mod_ui.dm +++ b/code/modules/mod/mod_ui.dm @@ -54,7 +54,7 @@ . = ..() if(.) return - if(!allowed(usr) && locked) + if(locked && !allowed(usr)) balloon_alert(usr, "insufficient access!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm index a310f95537..5bbd96bd68 100644 --- a/code/modules/mod/modules/_module.dm +++ b/code/modules/mod/modules/_module.dm @@ -1,5 +1,6 @@ /obj/item/mod/module name = "MOD module" + icon = 'icons/obj/clothing/modsuit/mod_modules.dmi' icon_state = "module" /// If it can be removed var/removable = TRUE @@ -10,11 +11,11 @@ /// How much space it takes up in the MOD var/complexity = 0 /// Power use when idle - var/idle_power_cost = DEFAULT_CELL_DRAIN * 0 + var/idle_power_cost = DEFAULT_CHARGE_DRAIN * 0 /// Power use when active - var/active_power_cost = DEFAULT_CELL_DRAIN * 0 + var/active_power_cost = DEFAULT_CHARGE_DRAIN * 0 /// Power use when used, we call it manually - var/use_power_cost = DEFAULT_CELL_DRAIN * 0 + var/use_power_cost = DEFAULT_CHARGE_DRAIN * 0 /// ID used by their TGUI var/tgui_id /// Linked MODsuit @@ -27,12 +28,20 @@ var/overlay_state_active /// Overlay given to the user when the module is used, lasts until cooldown finishes var/overlay_state_use + /// Icon file for the overlay. + var/overlay_icon_file = 'icons/mob/clothing/modsuit/mod_modules.dmi' + /// Does the overlay use the control unit's colors? + var/use_mod_colors = FALSE /// What modules are we incompatible with? var/list/incompatible_modules = list() /// Cooldown after use var/cooldown_time = 0 /// The mouse button needed to use this module var/used_signal + /// If we're allowed to use this module while phased out. + var/allowed_in_phaseout = FALSE + /// If we're allowed to use this module while the suit is disabled. + var/allowed_inactive = FALSE /// Timer for the cooldown COOLDOWN_DECLARE(cooldown_timer) @@ -55,7 +64,7 @@ /obj/item/mod/module/examine(mob/user) . = ..() - if(HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + if(user.hud_list[DIAG_HUD] && user.client.images & user.hud_list[DIAG_HUD]) . += span_notice("Complexity level: [complexity]") /// Called from MODsuit's install() proc, so when the module is installed. @@ -84,7 +93,9 @@ /// Called when the module is selected from the TGUI /obj/item/mod/module/proc/on_select() - if(!mod.active || mod.activating || module_type == MODULE_PASSIVE) + if(((!mod.active || mod.activating) && !allowed_inactive) || module_type == MODULE_PASSIVE) + if(mod.wearer) + balloon_alert(mod.wearer, "not active!") return if(module_type != MODULE_USABLE) if(active) @@ -93,7 +104,7 @@ on_activation() else on_use() - SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED) + SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED, src) /// Called when the module is activated /obj/item/mod/module/proc/on_activation() @@ -103,6 +114,10 @@ if(!mod.active || mod.activating || !mod.cell?.charge) balloon_alert(mod.wearer, "unpowered!") return FALSE + if(!allowed_in_phaseout && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob)) + //specifically a to_chat because the user is phased out. + to_chat(mod.wearer, span_warning("You cannot activate this right now.")) + return FALSE if(module_type == MODULE_ACTIVE) if(mod.selected_module && !mod.selected_module.on_deactivation()) return @@ -115,9 +130,8 @@ balloon_alert(mod.wearer, "can't extend [device]!") return else - var/used_button = mod.wearer.client?.prefs.read_preference(/datum/preference/choiced/mod_select) || MIDDLE_CLICK - update_signal(used_button) - balloon_alert(mod.wearer, "[src] activated, [used_button]-click to use") + update_signal() + balloon_alert(mod.wearer, "[src] activated, alt-click to use") active = TRUE COOLDOWN_START(src, cooldown_timer, cooldown_time) mod.wearer.update_inv_back() @@ -145,6 +159,10 @@ return FALSE if(!check_power(use_power_cost)) return FALSE + if(!allowed_in_phaseout && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob)) + //specifically a to_chat because the user is phased out. + to_chat(mod.wearer, span_warning("You cannot activate this right now.")) + return FALSE COOLDOWN_START(src, cooldown_timer, cooldown_time) addtimer(CALLBACK(mod.wearer, /mob.proc/update_inv_back), cooldown_time) mod.wearer.update_inv_back() @@ -228,8 +246,10 @@ qdel(src) /// Generates an icon to be used for the suit's worn overlays -/obj/item/mod/module/proc/generate_worn_overlay(mutable_appearance/standing) +/obj/item/mod/module/proc/generate_worn_overlay() . = list() + if(!mod.active) + return var/used_overlay if(overlay_state_use && !COOLDOWN_FINISHED(src, cooldown_timer)) used_overlay = overlay_state_use @@ -239,14 +259,98 @@ used_overlay = overlay_state_inactive else return - var/mutable_appearance/module_icon = mutable_appearance('icons/mob/mod.dmi', used_overlay, layer = standing.layer + 0.1) + var/mutable_appearance/module_icon = mutable_appearance(overlay_icon_file, used_overlay) + if(!use_mod_colors) + module_icon.appearance_flags |= RESET_COLOR . += module_icon /// Updates the signal used by active modules to be activated -/obj/item/mod/module/proc/update_signal(value) - switch(value) - if(MIDDLE_CLICK) - mod.selected_module.used_signal = COMSIG_MOB_MIDDLECLICKON - if(ALT_CLICK) - mod.selected_module.used_signal = COMSIG_MOB_ALTCLICKON +/obj/item/mod/module/proc/update_signal() + mod.selected_module.used_signal = COMSIG_MOB_ALTCLICKON RegisterSignal(mod.wearer, mod.selected_module.used_signal, /obj/item/mod/module.proc/on_special_click) + +/obj/item/mod/module/anomaly_locked + name = "MOD anomaly locked module" + desc = "A form of a module, locked behind an anomalous core to function." + incompatible_modules = list(/obj/item/mod/module/anomaly_locked) + /// The core item the module runs off. + var/obj/item/assembly/signaler/anomaly/core + /// Accepted types of anomaly cores. + var/list/accepted_anomalies = list(/obj/item/assembly/signaler/anomaly) + /// If this one starts with a core in. + var/prebuilt = FALSE + +/obj/item/mod/module/anomaly_locked/Initialize(mapload) + . = ..() + if(!prebuilt || !length(accepted_anomalies)) + return + var/core_path = pick(accepted_anomalies) + core = new core_path(src) + update_icon_state() + +/obj/item/mod/module/anomaly_locked/Destroy() + QDEL_NULL(core) + return ..() + +/obj/item/mod/module/anomaly_locked/examine(mob/user) + . = ..() + if(!length(accepted_anomalies)) + return + if(core) + . += span_notice("There is a [core.name] installed in it. You could remove it with a screwdriver...") + else + var/list/core_list = list() + for(var/path in accepted_anomalies) + var/atom/core_path = path + core_list += initial(core_path.name) + . += span_notice("You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function.") + +/obj/item/mod/module/anomaly_locked/on_select() + if(!core) + balloon_alert(mod.wearer, "no core!") + return + return ..() + +/obj/item/mod/module/anomaly_locked/on_process(delta_time) + . = ..() + if(!core) + return FALSE + +/obj/item/mod/module/anomaly_locked/on_active_process(delta_time) + if(!core) + return FALSE + return TRUE + +/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, params) + if(item.type in accepted_anomalies) + if(core) + balloon_alert(user, "core already in!") + return + if(!user.transferItemToLoc(item, src)) + return + core = item + balloon_alert(user, "core installed") + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + update_icon_state() + else + return ..() + +/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool) + . = ..() + if(!core) + balloon_alert(user, "no core!") + return + balloon_alert(user, "removing core...") + if(!do_after(user, 3 SECONDS, target = src)) + balloon_alert(user, "interrupted!") + return + balloon_alert(user, "core removed") + core.forceMove(drop_location()) + if(Adjacent(user) && !issilicon(user)) + user.put_in_hands(core) + core = null + update_icon_state() + +/obj/item/mod/module/anomaly_locked/update_icon_state() + icon_state = initial(icon_state) + (core ? "-core" : "") + return ..() diff --git a/code/modules/mod/modules/modules.dm b/code/modules/mod/modules/modules.dm index b5fc9a41e9..0e0da3572b 100644 --- a/code/modules/mod/modules/modules.dm +++ b/code/modules/mod/modules/modules.dm @@ -1,259 +1,3 @@ -/obj/item/mod/module - name = "MOD module" - icon_state = "module" - /// If it can be removed - var/removable = TRUE - /// If it's passive, togglable, usable or active - var/module_type = MODULE_PASSIVE - /// Is the module active - var/active = FALSE - /// How much space it takes up in the MOD - var/complexity = 0 - /// Power use when idle - var/idle_power_cost = DEFAULT_CELL_DRAIN * 0 - /// Power use when active - var/active_power_cost = DEFAULT_CELL_DRAIN * 0 - /// Power use when used, we call it manually - var/use_power_cost = DEFAULT_CELL_DRAIN * 0 - /// ID used by their TGUI - var/tgui_id - /// Linked MODsuit - var/obj/item/mod/control/mod - /// If we're an active module, what item are we? - var/obj/item/device - /// Overlay given to the user when the module is inactive - var/overlay_state_inactive - /// Overlay given to the user when the module is active - var/overlay_state_active - /// Overlay given to the user when the module is used, lasts until cooldown finishes - var/overlay_state_use - /// What modules are we incompatible with? - var/list/incompatible_modules = list() - /// Cooldown after use - var/cooldown_time = 0 - /// The mouse button needed to use this module - var/used_signal - /// Timer for the cooldown - COOLDOWN_DECLARE(cooldown_timer) - -/obj/item/mod/module/Initialize(mapload) - . = ..() - if(module_type != MODULE_ACTIVE) - return - if(ispath(device)) - device = new device(src) - ADD_TRAIT(device, TRAIT_NODROP, MOD_TRAIT) - RegisterSignal(device, COMSIG_PARENT_PREQDELETED, .proc/on_device_deletion) - RegisterSignal(src, COMSIG_ATOM_EXITED, .proc/on_exit) - -/obj/item/mod/module/Destroy() - mod?.uninstall(src) - if(device) - UnregisterSignal(device, COMSIG_PARENT_PREQDELETED) - QDEL_NULL(device) - return ..() - -/obj/item/mod/module/examine(mob/user) - . = ..() - if(HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) - . += span_notice("Complexity level: [complexity]") - -/// 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. -/obj/item/mod/module/proc/on_uninstall() - return - -/// Called when the MODsuit is activated -/obj/item/mod/module/proc/on_suit_activation() - return - -/// Called when the MODsuit is deactivated -/obj/item/mod/module/proc/on_suit_deactivation() - return - -/// Called when the MODsuit is equipped -/obj/item/mod/module/proc/on_equip() - return - -/// Called when the MODsuit is unequipped -/obj/item/mod/module/proc/on_unequip() - return - -/// Called when the module is selected from the TGUI -/obj/item/mod/module/proc/on_select() - if(!mod.active || mod.activating || module_type == MODULE_PASSIVE) - return - if(module_type != MODULE_USABLE) - if(active) - on_deactivation() - else - on_activation() - else - on_use() - SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED) - -/// Called when the module is activated -/obj/item/mod/module/proc/on_activation() - if(!COOLDOWN_FINISHED(src, cooldown_timer)) - balloon_alert(mod.wearer, "on cooldown!") - return FALSE - if(!mod.active || mod.activating || !mod.cell?.charge) - balloon_alert(mod.wearer, "unpowered!") - return FALSE - if(module_type == MODULE_ACTIVE) - if(mod.selected_module && !mod.selected_module.on_deactivation()) - return - mod.selected_module = src - if(device) - if(mod.wearer.put_in_hands(device)) - balloon_alert(mod.wearer, "[device] extended") - RegisterSignal(mod.wearer, COMSIG_ATOM_EXITED, .proc/on_exit) - else - balloon_alert(mod.wearer, "can't extend [device]!") - return - else - var/used_button = mod.wearer.client?.prefs.read_preference(/datum/preference/choiced/mod_select) || MIDDLE_CLICK - update_signal(used_button) - balloon_alert(mod.wearer, "[src] activated, [used_button]-click to use") - active = TRUE - COOLDOWN_START(src, cooldown_timer, cooldown_time) - mod.wearer.update_inv_back() - return TRUE - -/// Called when the module is deactivated -/obj/item/mod/module/proc/on_deactivation() - active = FALSE - if(module_type == MODULE_ACTIVE) - mod.selected_module = null - if(device) - mod.wearer.transferItemToLoc(device, src, TRUE) - balloon_alert(mod.wearer, "[device] retracted") - UnregisterSignal(mod.wearer, COMSIG_ATOM_EXITED) - else - balloon_alert(mod.wearer, "[src] deactivated") - UnregisterSignal(mod.wearer, used_signal) - used_signal = null - mod.wearer.update_inv_back() - return TRUE - -/// Called when the module is used -/obj/item/mod/module/proc/on_use() - if(!COOLDOWN_FINISHED(src, cooldown_timer)) - return FALSE - if(!check_power(use_power_cost)) - return FALSE - COOLDOWN_START(src, cooldown_timer, cooldown_time) - addtimer(CALLBACK(mod.wearer, /mob.proc/update_inv_back), cooldown_time) - mod.wearer.update_inv_back() - return TRUE - -/// Called when an activated module without a device is used -/obj/item/mod/module/proc/on_select_use(atom/target) - mod.wearer.face_atom(target) - if(!on_use()) - return FALSE - return TRUE - -/// Called when an activated module without a device is active and the user alt/middle-clicks -/obj/item/mod/module/proc/on_special_click(mob/source, atom/target) - SIGNAL_HANDLER - on_select_use(target) - return COMSIG_MOB_CANCEL_CLICKON - -/// Called on the MODsuit's process -/obj/item/mod/module/proc/on_process(delta_time) - if(active) - if(!drain_power(active_power_cost * delta_time)) - on_deactivation() - return FALSE - on_active_process(delta_time) - else - drain_power(idle_power_cost * delta_time) - return TRUE - -/// Called on the MODsuit's process if it is an active module -/obj/item/mod/module/proc/on_active_process(delta_time) - return - -/// Drains power from the suit cell -/obj/item/mod/module/proc/drain_power(amount) - if(!check_power(amount)) - return FALSE - mod.cell.charge = max(0, mod.cell.charge - amount) - return TRUE - -/obj/item/mod/module/proc/check_power(amount) - if(!mod.cell || (mod.cell.charge < amount)) - return FALSE - return TRUE - -/// Adds additional things to the MODsuit ui_data() -/obj/item/mod/module/proc/add_ui_data() - return list() - -/// Creates a list of configuring options for this module -/obj/item/mod/module/proc/get_configuration() - return list() - -/// Generates an element of the get_configuration list with a display name, type and value -/obj/item/mod/module/proc/add_ui_configuration(display_name, type, value, list/values) - return list("display_name" = display_name, "type" = type, "value" = value, "values" = values) - -/// Receives configure edits from the TGUI and edits the vars -/obj/item/mod/module/proc/configure_edit(key, value) - return - -/// Called when the device moves to a different place on active modules -/obj/item/mod/module/proc/on_exit(datum/source, atom/movable/part, direction) - SIGNAL_HANDLER - - if(!active) - return - if(part.loc == src) - return - if(part.loc == mod.wearer) - return - if(part == device) - on_deactivation() - -/// Called when the device gets deleted on active modules -/obj/item/mod/module/proc/on_device_deletion(datum/source) - SIGNAL_HANDLER - - if(source == device) - device = null - qdel(src) - -/// Generates an icon to be used for the suit's worn overlays -/obj/item/mod/module/proc/generate_worn_overlay(mutable_appearance/standing) - . = list() - var/used_overlay - if(overlay_state_use && !COOLDOWN_FINISHED(src, cooldown_timer)) - used_overlay = overlay_state_use - else if(overlay_state_active && active) - used_overlay = overlay_state_active - else if(overlay_state_inactive) - used_overlay = overlay_state_inactive - else - return - var/mutable_appearance/module_icon = mutable_appearance('icons/mob/mod.dmi', used_overlay, layer = standing.layer + 0.1) - . += module_icon - -/// Updates the signal used by active modules to be activated -/obj/item/mod/module/proc/update_signal(value) - switch(value) - if(MIDDLE_CLICK) - mod.selected_module.used_signal = COMSIG_MOB_MIDDLECLICKON - if(ALT_CLICK) - mod.selected_module.used_signal = COMSIG_MOB_ALTCLICKON - RegisterSignal(mod.wearer, mod.selected_module.used_signal, /obj/item/mod/module.proc/on_special_click) - icon_state = "magic_nullifier" - removable = FALSE - incompatible_modules = list(/obj/item/mod/module/anti_magic) - /obj/item/mod/module/anti_magic/on_suit_activation() ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, MOD_TRAIT) ADD_TRAIT(mod.wearer, TRAIT_HOLY, MOD_TRAIT) @@ -288,8 +32,8 @@ module_type = MODULE_TOGGLE // complexity = 3 complexity = 0 - active_power_cost = DEFAULT_CELL_DRAIN*0.75 -// use_power_cost = DEFAULT_CELL_DRAIN*3 + active_power_cost = DEFAULT_CHARGE_DRAIN*0.75 +// use_power_cost = DEFAULT_CHARGE_DRAIN*3 removable = FALSE incompatible_modules = list(/obj/item/mod/module/kinesis) cooldown_time = 0.5 SECONDS @@ -324,7 +68,7 @@ incompatible_modules = list(/obj/item/mod/module/insignia) overlay_state_inactive = "insignia" -/obj/item/mod/module/insignia/generate_worn_overlay(mutable_appearance/standing) +/obj/item/mod/module/insignia/generate_worn_overlay() overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" . = ..() for(var/mutable_appearance/appearance as anything in .) diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm new file mode 100644 index 0000000000..9bd391bd22 --- /dev/null +++ b/code/modules/mod/modules/modules_engineering.dm @@ -0,0 +1,154 @@ +//Engineering modules for MODsuits + +///Welding Protection - Makes the helmet protect from flashes and welding. +/obj/item/mod/module/welding + name = "MOD welding protection module" + desc = "A module installed into the visor of the suit, this projects a \ + polarized, holographic overlay in front of the user's eyes. It's rated high enough for \ + immunity against extremities such as spot and arc welding, solar eclipses, and handheld flashlights." + icon_state = "welding" + complexity = 1 + incompatible_modules = list(/obj/item/mod/module/welding) + overlay_state_inactive = "module_welding" + +/obj/item/mod/module/welding/on_suit_activation() + mod.helmet.flash_protect = 2 + +/obj/item/mod/module/welding/on_suit_deactivation(deleting = FALSE) + if(deleting) + return + mod.helmet.flash_protect = initial(mod.helmet.flash_protect) + +///T-Ray Scan - Scans the terrain for undertile objects. +/obj/item/mod/module/t_ray + name = "MOD t-ray scan module" + desc = "A module installed into the visor of the suit, allowing the user to use a pulse of terahertz radiation \ + to essentially echolocate things beneath the floor, mostly cables and pipes. \ + A staple of atmospherics work, and counter-smuggling work." + icon_state = "tray" + module_type = MODULE_TOGGLE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/t_ray) + cooldown_time = 0.5 SECONDS + /// T-ray scan range. + var/range = 4 + +/obj/item/mod/module/t_ray/on_active_process(delta_time) + t_ray_scan(mod.wearer, 0.8 SECONDS, range) + +///Magnetic Stability - Gives the user a slowdown but makes them negate gravity and be immune to slips. +/obj/item/mod/module/magboot + name = "MOD magnetic stability module" + desc = "These are powerful electromagnets fitted into the suit's boots, allowing users both \ + excellent traction no matter the condition indoors, and to essentially hitch a ride on the exterior of a hull. \ + However, these basic models do not feature computerized systems to automatically toggle them on and off, \ + so numerous users report a certain stickiness to their steps." + icon_state = "magnet" + module_type = MODULE_TOGGLE + complexity = 2 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/magboot) + cooldown_time = 0.5 SECONDS + /// Slowdown added onto the suit. + var/slowdown_active = 0.5 + +/obj/item/mod/module/magboot/on_activation() + . = ..() + if(!.) + return + ADD_TRAIT(mod.wearer, TRAIT_NEGATES_GRAVITY, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT) + mod.slowdown += slowdown_active + mod.wearer.update_gravity(mod.wearer.has_gravity()) + mod.wearer.update_equipment_speed_mods() + +/obj/item/mod/module/magboot/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + REMOVE_TRAIT(mod.wearer, TRAIT_NEGATES_GRAVITY, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT) + mod.slowdown -= slowdown_active + mod.wearer.update_gravity(mod.wearer.has_gravity()) + mod.wearer.update_equipment_speed_mods() + +/obj/item/mod/module/magboot/advanced + name = "MOD advanced magnetic stability module" + removable = FALSE + complexity = 0 + slowdown_active = 0 + +// No tether module, sorry! + +///Radiation Protection - Protects the user from radiation, gives them a geiger counter and rad info in the panel. +/obj/item/mod/module/rad_protection + name = "MOD radiation protection module" + desc = "A module utilizing polymers and reflective shielding to protect the user against ionizing radiation." + icon_state = "radshield" + complexity = 2 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/rad_protection) + +/obj/item/mod/module/rad_protection/on_suit_activation() + for(var/obj/item/part in mod.mod_parts) + armor[RAD] = 65 + rad_flags = RAD_PROTECT_CONTENTS|RAD_NO_CONTAMINATE + +/obj/item/mod/module/rad_protection/on_suit_deactivation(deleting = FALSE) + for(var/obj/item/part in mod.mod_parts) + armor[RAD] = 0 + rad_flags = NONE + +///Constructor - Lets you build quicker and create RCD holograms. +/obj/item/mod/module/constructor + name = "MOD constructor module" + desc = "This module entirely occupies the wearer's forearm, notably causing conflict with \ + advanced arm servos meant to carry crewmembers. However, it contains the \ + latest engineering schematics combined with inbuilt memory to help the user build walls." + icon_state = "constructor" + module_type = MODULE_USABLE + complexity = 2 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 + use_power_cost = DEFAULT_CHARGE_DRAIN * 2 + incompatible_modules = list(/obj/item/mod/module/constructor, /obj/item/mod/module/quick_carry) + cooldown_time = 11 SECONDS + +/obj/item/mod/module/constructor/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) + +/obj/item/mod/module/constructor/on_suit_deactivation(deleting = FALSE) + REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) + +///Mister - Sprays water over an area. +/obj/item/mod/module/mister + name = "MOD water mister module" + desc = "A module containing a mister, able to spray it over areas." + icon_state = "mister" + module_type = MODULE_ACTIVE + complexity = 2 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + device = /obj/item/reagent_containers/spray/mister + incompatible_modules = list(/obj/item/mod/module/mister) + cooldown_time = 0.5 SECONDS + /// Volume of our reagent holder. + var/volume = 500 + +/obj/item/mod/module/mister/Initialize(mapload) + create_reagents(volume, OPENCONTAINER) + return ..() + +///Resin Mister - Sprays resin over an area. +/obj/item/mod/module/mister/atmos + name = "MOD resin mister module" + desc = "An atmospheric resin mister, able to fix up areas quickly." + device = /obj/item/extinguisher/mini/nozzle/mod + volume = 250 + +/obj/item/mod/module/mister/atmos/Initialize(mapload) + . = ..() + reagents.add_reagent(/datum/reagent/water, volume) + +/obj/item/extinguisher/mini/nozzle/mod + name = "MOD atmospheric mister" + desc = "An atmospheric resin mister with three modes, mounted as a module." diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm new file mode 100644 index 0000000000..f7f7dfbfb0 --- /dev/null +++ b/code/modules/mod/modules/modules_general.dm @@ -0,0 +1,339 @@ +//General modules for MODsuits + +///Storage - Adds a storage component to the suit. +/obj/item/mod/module/storage + name = "MOD storage containment module" + desc = "What amounts to a series of integrated storage compartments and specialized pockets installed across \ + the surface of the suit, useful for storing various bits, and or bobs." + icon_state = "storage" + complexity = 3 + incompatible_modules = list(/obj/item/mod/module/storage) + module_type = MODULE_USABLE + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + /// Bag we have stored. + var/obj/item/storage/backpack/stored + +/obj/item/mod/module/storage/attackby(obj/item/I, mob/user, params) + if(!istype(I, /obj/item/storage/backpack)) + return ..() + var/obj/item/storage/backpack/B = I + if(stored) + balloon_alert(user, "backpack already installed!") + return + if(!user.transferItemToLoc(B, src)) + return + stored = B + balloon_alert(user, "backpack installed") + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + +/obj/item/mod/module/storage/screwdriver_act(mob/living/user, obj/item/tool) + . = ..() + if(!stored) + balloon_alert(user, "no backpack!") + return + balloon_alert(user, "removing backpack...") + if(!do_after(user, 3 SECONDS, target = src)) + balloon_alert(user, "interrupted!") + return + balloon_alert(user, "backpack removed") + stored.forceMove(drop_location()) + if(Adjacent(user) && !issilicon(user)) + user.put_in_hands(stored) + stored = null + +/obj/item/mod/module/storage/on_use() + . = ..() + if(!.) + return + if(!stored) + var/obj/item/storage/backpack/holding = mod.wearer.get_active_held_item() + if(!holding) + balloon_alert(mod.wearer, "no backpack installed!") + return + if(mod.wearer.transferItemToLoc(holding, src, force = FALSE, silent = TRUE)) + stored = holding + balloon_alert(mod.wearer, "backpack stored") + playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE) + else if(mod.wearer.put_in_active_hand(stored, forced = FALSE, ignore_animation = TRUE)) + balloon_alert(mod.wearer, "backpack retrieved") + playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE) + else + balloon_alert(mod.wearer, "backpack storage full!") + +/obj/item/mod/module/storage/Exited(atom/movable/gone, direction) + . = ..() + if(gone == stored) + stored = null + +/obj/item/mod/module/storage/Destroy() + QDEL_NULL(stored) + return ..() + +/// No Ion Jetpack + +///Eating Apparatus - Lets the user eat/drink with the suit on. +/obj/item/mod/module/mouthhole + name = "MOD eating apparatus module" + desc = "A favorite by Miners, this modification to the helmet utilizes a nanotechnology barrier infront of the mouth \ + to allow eating and drinking while retaining protection and atmosphere. However, it won't free you from masks, \ + lets pepper spray pass through and it will do nothing to improve the taste of a goliath steak." + icon_state = "apparatus" + complexity = 1 + incompatible_modules = list(/obj/item/mod/module/mouthhole) + overlay_state_inactive = "module_apparatus" + /// Former flags of the helmet. + var/former_flags = NONE + /// Former visor flags of the helmet. + var/former_visor_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 + mod.helmet.visor_flags_cover &= ~HEADCOVERSMOUTH + +/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 + +///EMP Shield - Protects the suit from EMPs. +/obj/item/mod/module/emp_shield + name = "MOD EMP shield module" + desc = "A field inhibitor installed into the suit, protecting it against feedback such as \ + electromagnetic pulses that would otherwise damage the electronic systems of the suit or it's modules. \ + However, it will take from the suit's power to do so." + icon_state = "empshield" + complexity = 1 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/emp_shield) + +/obj/item/mod/module/emp_shield/on_install() + mod.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + +/obj/item/mod/module/emp_shield/on_uninstall(deleting = FALSE) + mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + +/obj/item/mod/module/emp_shield/advanced + name = "MOD advanced EMP shield module" + desc = "An advanced field inhibitor installed into the suit, protecting it against feedback such as \ + electromagnetic pulses that would otherwise damage the electronic systems of the suit or electronic devices on the wearer, \ + including augmentations. However, it will take from the suit's power to do so." + complexity = 2 + +/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) + mod.wearer.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS) + +///Flashlight - Gives the suit a customizable flashlight. +/obj/item/mod/module/flashlight + name = "MOD flashlight module" + desc = "A simple pair of configurable flashlights installed on the left and right sides of the helmet, \ + useful for providing light in a variety of ranges and colors. \ + Some survivalists prefer the color green for their illumination, for reasons unknown." + icon_state = "flashlight" + module_type = MODULE_TOGGLE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/flashlight) + cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_light" + light_color = COLOR_WHITE + light_range = 4 + light_power = 1 + /// Charge drain per range amount. + var/base_power = DEFAULT_CHARGE_DRAIN * 0.1 + /// Minimum range we can set. + var/min_range = 2 + /// Maximum range we can set. + var/max_range = 5 + +/obj/item/mod/module/flashlight/on_activation() + . = ..() + if(!.) + return + mod.set_light(light_range, light_power, light_color) + active_power_cost = base_power * light_range + +/obj/item/mod/module/flashlight/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + mod.set_light(0, 0) + +/obj/item/mod/module/flashlight/on_process(delta_time) + active_power_cost = base_power * light_range + return ..() + +/obj/item/mod/module/flashlight/generate_worn_overlay() + . = ..() + if(!active) + return + var/mutable_appearance/light_icon = mutable_appearance(overlay_icon_file, "module_light_on") + light_icon.appearance_flags = RESET_COLOR + light_icon.color = light_color + . += light_icon + +/obj/item/mod/module/flashlight/get_configuration() + . = ..() + .["light_color"] = add_ui_configuration("Light Color", "color", light_color) + .["light_range"] = add_ui_configuration("Light Range", "number", light_range) + +/obj/item/mod/module/flashlight/configure_edit(key, value) + switch(key) + if("light_color") + value = input(usr, "Pick new light color", "Flashlight Color") as color|null + if(!value) + return + var/list/hsl = rgb2hsl(hex2num(copytext(value,2,4)),hex2num(copytext(value,4,6)),hex2num(copytext(value,6,8))) + if(hsl[3] < 0.5) + balloon_alert(mod.wearer, "too dark!") + return + mod.set_light_color(value) + mod.wearer.regenerate_icons() + light_color = value + if("light_range") + mod.set_light_range(clamp(value, min_range, max_range)) + light_range = clamp(value, min_range, max_range) + +///Dispenser - Dispenses an item after a time passes. +/obj/item/mod/module/dispenser + name = "MOD burger dispenser module" + desc = "A rare piece of technology reverse-engineered from a prototype found in a Donk Corporation vessel. \ + This can draw incredible amounts of power from the suit's charge to create edible organic matter in the \ + palm of the wearer's glove; however, research seemed to have entirely stopped at burgers. \ + Notably, all attempts to get it to dispense Earl Grey tea have failed." + icon_state = "dispenser" + module_type = MODULE_USABLE + complexity = 3 + use_power_cost = DEFAULT_CHARGE_DRAIN * 2 + incompatible_modules = list(/obj/item/mod/module/dispenser) + cooldown_time = 5 SECONDS + /// Path we dispense. + var/dispense_type = /obj/item/reagent_containers/food/snacks/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 + var/obj/item/dispensed = new dispense_type(mod.wearer.loc) + mod.wearer.put_in_hands(dispensed) + balloon_alert(mod.wearer, "[dispensed] dispensed") + playsound(src, 'sound/machines/click.ogg', 100, TRUE) + drain_power(use_power_cost) + return dispensed + +///Longfall - Nullifies fall damage, removing charge instead. +/obj/item/mod/module/longfall + name = "MOD longfall module" + desc = "Useful for protecting both the suit and the wearer, \ + utilizing commonplace systems to convert the possible damage from a fall into kinetic charge, \ + as well as internal gyroscopes to ensure the user's safe falling. \ + Useful for mining, monorail tracks, or even skydiving!" + icon_state = "longfall" + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 5 + incompatible_modules = list(/obj/item/mod/module/longfall) + +/obj/item/mod/module/longfall/on_suit_activation() + RegisterSignal(mod.wearer, COMSIG_LIVING_Z_IMPACT, .proc/z_impact_react) + +/obj/item/mod/module/longfall/on_suit_deactivation(deleting = FALSE) + UnregisterSignal(mod.wearer, COMSIG_LIVING_Z_IMPACT) + +/obj/item/mod/module/longfall/proc/z_impact_react(datum/source, levels, turf/fell_on) + if(!drain_power(use_power_cost*levels)) + return + new /obj/effect/temp_visual/mook_dust(fell_on) + mod.wearer.Stun(levels * 1 SECONDS) + to_chat(mod.wearer, span_notice("[src] protects you from the damage!")) + return NO_Z_IMPACT_DAMAGE + +///Thermal Regulator - Naw. + +///DNA Lock - Prevents people without the set DNA from activating the suit. +/obj/item/mod/module/dna_lock + name = "MOD DNA lock module" + desc = "A module which engages with the various locks and seals tied to the suit's systems, \ + enabling it to only be worn by someone corresponding with the user's exact DNA profile; \ + however, this incredibly sensitive module is shorted out by EMPs. Luckily, cloning has been outlawed." + icon_state = "dnalock" + module_type = MODULE_USABLE + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN * 3 + incompatible_modules = list(/obj/item/mod/module/dna_lock) + cooldown_time = 0.5 SECONDS + /// The DNA we lock with. + var/dna = null + +/obj/item/mod/module/dna_lock/on_install() + RegisterSignal(mod, COMSIG_MOD_ACTIVATE, .proc/on_mod_activation) + RegisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL, .proc/on_mod_removal) + RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, .proc/on_emp) + RegisterSignal(mod, COMSIG_ATOM_EMAG_ACT, .proc/on_emag) + +/obj/item/mod/module/dna_lock/on_uninstall(deleting = FALSE) + UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) + UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL) + UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT) + 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_power_cost) + +/obj/item/mod/module/dna_lock/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + on_emp(src, severity) + +/obj/item/mod/module/dna_lock/emag_act(mob/user, obj/item/card/emag/emag_card) + . = ..() + on_emag(src, user, emag_card) + +/obj/item/mod/module/dna_lock/proc/dna_check(mob/user) + if(!iscarbon(user)) + return FALSE + var/mob/living/carbon/carbon_user = user + if(!dna || (carbon_user.has_dna() && carbon_user.dna.unique_enzymes == dna)) + return TRUE + balloon_alert(user, "dna locked!") + return FALSE + +/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity) + SIGNAL_HANDLER + + dna = null + +/obj/item/mod/module/dna_lock/proc/on_emag(datum/source, mob/user, obj/item/card/emag/emag_card) + SIGNAL_HANDLER + + dna = null + +/obj/item/mod/module/dna_lock/proc/on_mod_activation(datum/source, mob/user) + SIGNAL_HANDLER + + if(!dna_check(user)) + return MOD_CANCEL_ACTIVATE + +/obj/item/mod/module/dna_lock/proc/on_mod_removal(datum/source, mob/user) + SIGNAL_HANDLER + + if(!dna_check(user)) + return MOD_CANCEL_REMOVAL + +///Sign Language Translator - I want, but no diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm new file mode 100644 index 0000000000..9252ed7ce8 --- /dev/null +++ b/code/modules/mod/modules/modules_maint.dm @@ -0,0 +1,101 @@ +//Maint modules for MODsuits + +///Springlock Mechanism - Nope + +///Rave Visor - Pointless + +///Tanner - Maybe another time + +///Balloon Blower - Blows a balloon. +/obj/item/mod/module/balloon + name = "MOD balloon blower module" + desc = "A strange module invented years ago by some ingenious mimes. It blows balloons." + icon_state = "bloon" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/balloon) + cooldown_time = 15 SECONDS + +/obj/item/mod/module/balloon/on_use() + . = ..() + if(!.) + return + if(!do_after(mod.wearer, 10 SECONDS, target = mod)) + return FALSE + mod.wearer.adjustOxyLoss(20) + playsound(src, 'sound/items/modsuit/inflate_bloon.ogg', 50, TRUE) + var/obj/item/toy/balloon/balloon = new(get_turf(src)) + mod.wearer.put_in_hands(balloon) + drain_power(use_power_cost) + +///Paper Dispenser - Dispenses (sometimes burning) paper sheets. +/obj/item/mod/module/paper_dispenser + name = "MOD paper dispenser module" + desc = "A simple module designed by the bureaucrats of Torch Bay. \ + It dispenses 'warm, clean, and crisp sheets of paper' onto a nearby table. Usually." + icon_state = "paper_maker" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/paper_dispenser) + cooldown_time = 5 SECONDS + /// 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 + + var/obj/item/paper/crisp_paper = new(get_turf(src)) + crisp_paper.desc = "It's crisp and warm to the touch. Must be fresh." + + var/obj/structure/table/nearby_table = locate() in range(1, mod.wearer) + playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) + balloon_alert(mod.wearer, "dispensed paper[nearby_table ? " onto table":""]") + + mod.wearer.put_in_hands(crisp_paper) + if(nearby_table) + mod.wearer.transferItemToLoc(crisp_paper, nearby_table.drop_location(), silent = FALSE) + + // Up to a 30% chance to set the sheet on fire, +2% per sheet made + if(prob(min(num_sheets_dispensed * 2, 30))) + if(crisp_paper in mod.wearer.held_items) + mod.wearer.dropItemToGround(crisp_paper, force = TRUE) + crisp_paper.balloon_alert(mod.wearer, "PC LOAD LETTER!") + crisp_paper.visible_message(span_warning("[crisp_paper] bursts into flames, it's too crisp!")) + crisp_paper.fire_act(1000, 100) + + drain_power(use_power_cost) + num_sheets_dispensed++ + + +///Stamper - Extends a stamp that can switch between accept/deny modes. +/obj/item/mod/module/stamp + name = "MOD stamper module" + desc = "A module installed into the wrist of the suit, this functions as a high-power stamp, \ + able to switch between accept and deny modes." + icon_state = "stamp" + module_type = MODULE_ACTIVE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + device = /obj/item/stamp/mod + incompatible_modules = list(/obj/item/mod/module/stamp) + cooldown_time = 0.5 SECONDS + +/obj/item/stamp/mod + name = "MOD electronic stamp" + desc = "A high-power stamp, able to switch between accept and deny mode when used." + +/obj/item/stamp/mod/attack_self(mob/user, modifiers) + . = ..() + if(icon_state == "stamp-ok") + icon_state = "stamp-deny" + else + icon_state = "stamp-ok" + balloon_alert(user, "switched mode") + +///Atrocinator - Perhaps another time diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm new file mode 100644 index 0000000000..974e7e31ad --- /dev/null +++ b/code/modules/mod/modules/modules_medical.dm @@ -0,0 +1,208 @@ +//Medical modules for MODsuits + +#define HEALTH_SCAN "Health" +#define WOUND_SCAN "Wound" +#define CHEM_SCAN "Chemical" + +///Health Analyzer - Gives the user a ranged health analyzer and their health status in the panel. +/obj/item/mod/module/health_analyzer + 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." + icon_state = "health" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/health_analyzer) + cooldown_time = 0.5 SECONDS + tgui_id = "health_analyzer" + /// Scanning mode, changes how we scan something. + var/mode = HEALTH_SCAN + /// List of all scanning modes. + var/static/list/modes = list(HEALTH_SCAN, WOUND_SCAN, CHEM_SCAN) + +/obj/item/mod/module/health_analyzer/add_ui_data() + . = ..() + .["userhealth"] = mod.wearer?.health || 0 + .["usermaxhealth"] = mod.wearer?.getMaxHealth() || 0 + .["userbrute"] = mod.wearer?.getBruteLoss() || 0 + .["userburn"] = mod.wearer?.getFireLoss() || 0 + .["usertoxin"] = mod.wearer?.getToxLoss() || 0 + .["useroxy"] = mod.wearer?.getOxyLoss() || 0 + +/obj/item/mod/module/health_analyzer/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!isliving(target) || !mod.wearer.can_read(src)) + return + switch(mode) + if(HEALTH_SCAN) + healthscan(mod.wearer, target) + if(WOUND_SCAN) + woundscan(mod.wearer, target) + if(CHEM_SCAN) + chemscan(mod.wearer, target) + drain_power(use_power_cost) + +/obj/item/mod/module/health_analyzer/get_configuration() + . = ..() + .["mode"] = add_ui_configuration("Scan Mode", "list", mode, modes) + +/obj/item/mod/module/health_analyzer/configure_edit(key, value) + switch(key) + if("mode") + mode = value + +#undef HEALTH_SCAN +#undef WOUND_SCAN +#undef CHEM_SCAN + +///Quick Carry - Lets the user carry bodies quicker. +/obj/item/mod/module/quick_carry + name = "MOD quick carry module" + desc = "A suite of advanced servos, redirecting power from the suit's arms to help carry the wounded; \ + or simply for fun. However, Nanotrasen has locked the module's ability to assist in hand-to-hand combat." + icon_state = "carry" + complexity = 1 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/quick_carry, /obj/item/mod/module/constructor) + +/obj/item/mod/module/quick_carry/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT) + +/obj/item/mod/module/quick_carry/on_suit_deactivation(deleting = FALSE) + REMOVE_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT) + +/obj/item/mod/module/quick_carry/advanced + name = "MOD advanced quick carry module" + removable = FALSE + complexity = 0 + +///Injector - Gives the suit an extendable large-capacity piercing syringe. +/obj/item/mod/module/injector + name = "MOD injector module" + desc = "A module installed into the wrist of the suit, this functions as a high-capacity syringe, \ + with a tip fine enough to locate the emergency injection ports on any suit of armor, \ + penetrating it with ease. Even yours." + icon_state = "injector" + module_type = MODULE_ACTIVE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + device = /obj/item/reagent_containers/syringe/mod + incompatible_modules = list(/obj/item/mod/module/injector) + cooldown_time = 0.5 SECONDS + +/obj/item/reagent_containers/syringe/mod + name = "MOD injector syringe" + desc = "A high-capacity syringe, with a tip fine enough to locate \ + the emergency injection ports on any suit of armor, penetrating it with ease. Even yours." + icon_state = "mod_0" + base_icon_state = "mod" + amount_per_transfer_from_this = 30 + possible_transfer_amounts = list(5, 10, 15, 20, 30) + volume = 30 + proj_piercing = 1 + +///Organ Thrower + +///Patrient Transport + +///Defibrillator - Gives the suit an extendable pair of shock paddles. +/obj/item/mod/module/defibrillator + name = "MOD defibrillator module" + desc = "A module built into the gauntlets of the suit; commonly known as the 'Healing Hands' by medical professionals. \ + The user places their palms above the patient. Onboard computers in the suit calculate the necessary voltage, \ + and a modded targeting computer determines the best position for the user to push. \ + Twenty five pounds of force are applied to the patient's skin. Shocks travel from the suit's gloves \ + and counter-shock the heart, and the wearer returns to Medical a hero. Don't you even think about using it as a weapon; \ + regulations on manufacture and software locks expressly forbid it." + icon_state = "defibrillator" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN * 25 + device = /obj/item/shockpaddles/mod + overlay_state_inactive = "module_defibrillator" + overlay_state_active = "module_defibrillator_active" + incompatible_modules = list(/obj/item/mod/module/defibrillator) + cooldown_time = 0.5 SECONDS + var/defib_cooldown = 5 SECONDS + +/obj/item/mod/module/defibrillator/Initialize(mapload) + . = ..() + RegisterSignal(device, COMSIG_DEFIBRILLATOR_SUCCESS, .proc/on_defib_success) + +/obj/item/mod/module/defibrillator/proc/on_defib_success(obj/item/shockpaddles/source) + drain_power(use_power_cost) + source.recharge(defib_cooldown) + return COMPONENT_DEFIB_STOP + +/obj/item/shockpaddles/mod + name = "MOD defibrillator gauntlets" + req_defib = FALSE + icon_state = "defibgauntlets0" + item_state = "defibgauntlets0" + base_icon_state = "defibgauntlets" + +/obj/item/mod/module/defibrillator/combat + name = "MOD combat defibrillator module" + desc = "A module built into the gauntlets of the suit; commonly known as the 'Healing Hands' by medical professionals. \ + The user places their palms above the patient. Onboard computers in the suit calculate the necessary voltage, \ + and a modded targeting computer determines the best position for the user to push. \ + Twenty five pounds of force are applied to the patient's skin. Shocks travel from the suit's gloves \ + and counter-shock the heart, and the wearer returns to Medical a hero. \ + Interdyne Pharmaceutics marketed the domestic version of the Healing Hands as foolproof and unusable as a weapon. \ + But when it came time to provide their operatives with usable medical equipment, they didn't hesitate to remove \ + those in-built safeties. Operatives in the field can benefit from what they dub as 'Stun Gloves', able to apply shocks \ + straight to a victims heart to disable them, or maybe even outright stop their heart with enough power." + complexity = 1 + module_type = MODULE_ACTIVE + overlay_state_inactive = "module_defibrillator_combat" + overlay_state_active = "module_defibrillator_combat_active" + device = /obj/item/shockpaddles/syndicate/mod + defib_cooldown = 2.5 SECONDS + +/obj/item/shockpaddles/syndicate/mod + name = "MOD combat defibrillator gauntlets" + req_defib = FALSE + icon_state = "syndiegauntlets0" + item_state = "syndiegauntlets0" + base_icon_state = "syndiegauntlets" + +///Thread Ripper + +///Surgical Processor - Lets you do advanced surgeries portably. +/obj/item/mod/module/surgical_processor + name = "MOD surgical processor module" + desc = "A module using an onboard surgical computer which can be connected to other computers to download and \ + perform advanced surgeries on the go." + icon_state = "surgical_processor" + module_type = MODULE_ACTIVE + complexity = 2 + active_power_cost = DEFAULT_CHARGE_DRAIN + device = /obj/item/surgical_processor/mod + incompatible_modules = list(/obj/item/mod/module/surgical_processor) + cooldown_time = 0.5 SECONDS + +/obj/item/surgical_processor/mod + name = "MOD surgical processor" + +/obj/item/mod/module/surgical_processor/preloaded + desc = "A module using an onboard surgical computer which can be connected to other computers to download and \ + perform advanced surgeries on the go. This one came pre-loaded with some advanced surgeries." + device = /obj/item/surgical_processor/mod/preloaded + +/obj/item/surgical_processor/mod/preloaded + advanced_surgeries = list( + /datum/surgery/advanced/pacify, + /datum/surgery/healing/combo/upgraded/femto, + /datum/surgery/advanced/brainwashing, + /datum/surgery/advanced/bioware/nerve_splicing, + /datum/surgery/advanced/bioware/nerve_grounding, + /datum/surgery/advanced/bioware/vein_threading, + /datum/surgery/advanced/bioware/muscled_veins, + /datum/surgery/advanced/bioware/ligament_hook, + /datum/surgery/advanced/bioware/ligament_reinforcement + ) diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm new file mode 100644 index 0000000000..7208210b76 --- /dev/null +++ b/code/modules/mod/modules/modules_science.dm @@ -0,0 +1,135 @@ +//Science modules for MODsuits + +///Reagent Scanner - Lets the user scan reagents. +/obj/item/mod/module/reagent_scanner + name = "MOD reagent scanner module" + desc = "A module based off research-oriented Nanotrasen HUDs, this is capable of scanning the contents of \ + containers and projecting the information in an easy-to-read format on the wearer's display. \ + It cannot detect flavors, so that's up to you." + icon_state = "scanner" + module_type = MODULE_TOGGLE + complexity = 1 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 + incompatible_modules = list(/obj/item/mod/module/reagent_scanner) + cooldown_time = 0.5 SECONDS + +/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 + name = "MOD advanced reagent scanner module" + complexity = 0 + removable = FALSE + var/explosion_detection_dist = 21 + var/had_research_scanner = FALSE + +/obj/item/mod/module/reagent_scanner/advanced/on_activation() + . = ..() + if(!.) + return + had_research_scanner = mod.wearer.research_scanner + mod.wearer.research_scanner = TRUE + RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, .proc/sense_explosion) + +/obj/item/mod/module/reagent_scanner/advanced/on_deactivation(display_message = TRUE, deleting = FALSE) + . = ..() + if(!.) + return + mod.wearer.research_scanner = had_research_scanner + had_research_scanner = FALSE + UnregisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION) + +/obj/item/mod/module/reagent_scanner/advanced/proc/sense_explosion(datum/source, turf/epicenter, + devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) + SIGNAL_HANDLER + var/turf/wearer_turf = get_turf(mod.wearer) + if(wearer_turf.z == epicenter.z) + return + if(get_dist(epicenter, wearer_turf) > explosion_detection_dist) + return + to_chat(mod.wearer, span_notice("Explosion detected! Epicenter: [devastation_range], Outer: [heavy_impact_range], Shock: [light_impact_range]")) + +///Anti-Gravity - Makes the user weightless. +/obj/item/mod/module/anomaly_locked/antigrav + name = "MOD anti-gravity module" + desc = "A module that uses a gravitational core to make the user completely weightless." + icon_state = "antigrav" + module_type = MODULE_TOGGLE + complexity = 3 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.7 + incompatible_modules = list(/obj/item/mod/module/anomaly_locked) + cooldown_time = 0.5 SECONDS + accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav) + +/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) + mod.wearer.update_gravity(mod.wearer.has_gravity()) + 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) + mod.wearer.update_gravity(mod.wearer.has_gravity()) + if(deleting) + return + if(mod.wearer.has_gravity()) + new /obj/effect/temp_visual/mook_dust(get_turf(src)) + playsound(src, 'sound/effects/gravhit.ogg', 50) + +/obj/item/mod/module/anomaly_locked/antigrav/prebuilt + prebuilt = TRUE + +///Teleporter - Lets the user teleport to a nearby location. +/obj/item/mod/module/anomaly_locked/teleporter + name = "MOD teleporter module" + desc = "A module that uses a bluespace core to let the user transport their particles elsewhere." + icon_state = "teleporter" + module_type = MODULE_ACTIVE + complexity = 3 + use_power_cost = DEFAULT_CHARGE_DRAIN * 5 + cooldown_time = 5 SECONDS + accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/bluespace) + /// Time it takes to teleport + var/teleport_time = 3 SECONDS + +/obj/item/mod/module/anomaly_locked/teleporter/on_select_use(atom/target) + . = ..() + if(!.) + return + var/turf/open/target_turf = get_turf(target) + if(!istype(target_turf) || is_blocked_turf(target_turf) || !(target_turf in view(mod.wearer))) + balloon_alert(mod.wearer, "invalid target!") + return + balloon_alert(mod.wearer, "teleporting...") + var/matrix/pre_matrix = matrix() + pre_matrix.Scale(4, 0.25) + var/matrix/post_matrix = matrix() + post_matrix.Scale(0.25, 4) + animate(mod.wearer, teleport_time, color = COLOR_CYAN, transform = pre_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT) + if(!do_after(mod.wearer, teleport_time, target = mod)) + balloon_alert(mod.wearer, "interrupted!") + animate(mod.wearer, teleport_time*0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_IN) + return + animate(mod.wearer, teleport_time*0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_IN) + if(!do_teleport(mod.wearer, target_turf, asoundin = 'sound/effects/phasein.ogg')) + return + drain_power(use_power_cost) + +/obj/item/mod/module/anomaly_locked/teleporter/prebuilt + prebuilt = TRUE diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm new file mode 100644 index 0000000000..098abf8015 --- /dev/null +++ b/code/modules/mod/modules/modules_security.dm @@ -0,0 +1,207 @@ +//Security modules for MODsuits + +///Cloaking - Lowers the user's visibility, can be interrupted by being touched or attacked. +/obj/item/mod/module/stealth + name = "MOD prototype cloaking module" + desc = "A complete retrofitting of the suit, this is a form of visual concealment tech employing esoteric technology \ + to bend light around the user, as well as mimetic materials to make the surface of the suit match the \ + surroundings based off sensor data. For some reason, this tech is rarely seen." + icon_state = "cloak" + module_type = MODULE_TOGGLE + complexity = 4 + active_power_cost = DEFAULT_CHARGE_DRAIN * 2 + use_power_cost = DEFAULT_CHARGE_DRAIN * 10 + incompatible_modules = list(/obj/item/mod/module/stealth) + cooldown_time = 5 SECONDS + /// 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/unstealth) + RegisterSignal(mod.wearer, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_unarmed_attack) + RegisterSignal(mod.wearer, COMSIG_ATOM_BULLET_ACT, .proc/on_bullet_act) + RegisterSignal(mod.wearer, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW), .proc/unstealth) + animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS) + drain_power(use_power_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_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW)) + animate(mod.wearer, alpha = 255, time = 1.5 SECONDS) + +/obj/item/mod/module/stealth/proc/unstealth(datum/source) + SIGNAL_HANDLER + + to_chat(mod.wearer, span_warning("[src] gets discharged from contact!")) + do_sparks(2, TRUE, src) + drain_power(use_power_cost) + on_deactivation(display_message = TRUE, deleting = FALSE) + +/obj/item/mod/module/stealth/proc/on_unarmed_attack(datum/source, atom/target) + SIGNAL_HANDLER + + if(!isliving(target)) + return + unstealth(source) + +/obj/item/mod/module/stealth/proc/on_bullet_act(datum/source, obj/item/projectile/projectile) + SIGNAL_HANDLER + + if(projectile.nodamage) + return + unstealth(source) + +///Magnetic Harness - Automatically puts guns in your suit storage when you drop them. +/obj/item/mod/module/magnetic_harness + name = "MOD magnetic harness module" + desc = "Based off old TerraGov harness kits, this magnetic harness automatically attaches dropped guns back to the wearer." + icon_state = "mag_harness" + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/magnetic_harness) + /// Time before we activate the magnet. + var/magnet_delay = 0.8 SECONDS + /// The typecache of all guns we allow. + var/static/list/guns_typecache + /// The guns already allowed by the modsuit chestplate. + var/list/already_allowed_guns = list() + +/obj/item/mod/module/magnetic_harness/Initialize(mapload) + . = ..() + if(!guns_typecache) + 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 + +/obj/item/mod/module/magnetic_harness/on_uninstall(deleting = FALSE) + if(deleting) + return + mod.chestplate.allowed -= (guns_typecache - already_allowed_guns) + +/obj/item/mod/module/magnetic_harness/on_suit_activation() + RegisterSignal(mod.wearer, COMSIG_MOB_UNEQUIPPED_ITEM, .proc/check_dropped_item) + +/obj/item/mod/module/magnetic_harness/on_suit_deactivation(deleting = FALSE) + UnregisterSignal(mod.wearer, COMSIG_MOB_UNEQUIPPED_ITEM) + +/obj/item/mod/module/magnetic_harness/proc/check_dropped_item(datum/source, obj/item/dropped_item, force, new_location) + SIGNAL_HANDLER + + if(!is_type_in_typecache(dropped_item, guns_typecache)) + return + if(new_location != get_turf(src)) + return + addtimer(CALLBACK(src, .proc/pick_up_item, dropped_item), magnet_delay) + +/obj/item/mod/module/magnetic_harness/proc/pick_up_item(obj/item/item) + if(!isturf(item.loc) || !item.Adjacent(mod.wearer)) + return + if(!mod.wearer.equip_to_slot_if_possible(item, ITEM_SLOT_SUITSTORE, qdel_on_fail = FALSE, disable_warning = TRUE)) + return + playsound(src, 'sound/items/modsuit/magnetic_harness.ogg', 50, TRUE) + balloon_alert(mod.wearer, "[item] reattached") + drain_power(use_power_cost) + +///Pepper Shoulders + +///Holster - Instantly holsters any not huge gun. +/obj/item/mod/module/holster + name = "MOD holster module" + desc = "Based off typical storage compartments, this system allows the suit to holster a \ + standard firearm across its surface and allow for extremely quick retrieval. \ + While some users prefer the chest, others the forearm for quick deployment, \ + some law enforcement prefer the holster to extend from the thigh." + icon_state = "holster" + module_type = MODULE_USABLE + complexity = 2 + incompatible_modules = list(/obj/item/mod/module/holster) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + /// 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) + balloon_alert(mod.wearer, "nothing to holster!") + return + if(!istype(holding) || holding.w_class > WEIGHT_CLASS_BULKY) + balloon_alert(mod.wearer, "it doesn't fit!") + return + if(mod.wearer.transferItemToLoc(holding, src, force = FALSE, silent = TRUE)) + holstered = holding + balloon_alert(mod.wearer, "weapon holstered") + playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE) + else if(mod.wearer.put_in_active_hand(holstered, forced = FALSE, ignore_animation = TRUE)) + balloon_alert(mod.wearer, "weapon drawn") + playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE) + else + balloon_alert(mod.wearer, "holster full!") + +/obj/item/mod/module/holster/on_uninstall(deleting = FALSE) + if(holstered) + holstered.forceMove(drop_location()) + +/obj/item/mod/module/holster/Exited(atom/movable/gone, direction) + . = ..() + if(gone == holstered) + holstered = null + +/obj/item/mod/module/holster/Destroy() + QDEL_NULL(holstered) + return ..() + +///Megaphone - Lets you speak loud. +/obj/item/mod/module/megaphone + name = "MOD megaphone module" + desc = "A microchip megaphone linked to a MODsuit, for very important purposes, like: loudness." + icon_state = "megaphone" + module_type = MODULE_TOGGLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/megaphone) + cooldown_time = 0.5 SECONDS + /// 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/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) + SIGNAL_HANDLER + + speech_args[SPEECH_SPANS] |= voicespan + drain_power(use_power_cost) + +///Criminal Capture + +///Mirage grenade dispenser + +///Projectile Dampener + +///Active Sonar diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm new file mode 100644 index 0000000000..cb3c45946f --- /dev/null +++ b/code/modules/mod/modules/modules_service.dm @@ -0,0 +1,65 @@ +//Service modules for MODsuits + +///Bike Horn - Plays a bike horn sound. +/obj/item/mod/module/bikehorn + name = "MOD bike horn module" + desc = "A shoulder-mounted piece of heavy sonic artillery, this module uses the finest femto-manipulator technology to \ + precisely deliver an almost lethal squeeze to... a bike horn, producing a significantly memorable sound." + icon_state = "bikehorn" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/bikehorn) + cooldown_time = 1 SECONDS + +/obj/item/mod/module/bikehorn/on_use() + . = ..() + if(!.) + return + playsound(src, 'sound/items/bikehorn.ogg', 100, FALSE) + drain_power(use_power_cost) + +///Microwave Beam - Microwaves items instantly. +/obj/item/mod/module/microwave_beam + name = "MOD microwave beam module" + desc = "An oddly domestic device, this module is installed into the user's palm, \ + hooking up with culinary scanners located in the helmet to blast food with precise microwave radiation, \ + allowing them to cook food from a distance, with the greatest of ease. Not recommended for use against grapes." + icon_state = "microwave_beam" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CHARGE_DRAIN * 5 + incompatible_modules = list(/obj/item/mod/module/microwave_beam) + cooldown_time = 10 SECONDS + var/obj/machinery/microwave/microwave + +/obj/item/mod/module/microwave_beam/Initialize(mapload) + microwave = new() + . = ..() + +/obj/item/mod/module/microwave_beam/Destroy() + QDEL_NULL(microwave) + . = ..() + +/obj/item/mod/module/microwave_beam/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!isitem(target)) + return + if(!isturf(target.loc)) + balloon_alert(mod.wearer, "must be on the floor!") + return + var/obj/item/microwave_target = target + var/datum/effect_system/spark_spread/spark_effect = new() + spark_effect.set_up(2, 1, mod.wearer) + spark_effect.start() + mod.wearer.Beam(target,icon_state="lightning[rand(1,12)]", time = 5) + if(microwave_target.microwave_act(microwave)) + playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, FALSE) + else + balloon_alert(mod.wearer, "can't be microwaved!") + var/datum/effect_system/spark_spread/spark_effect_two = new() + spark_effect_two.set_up(2, 1, microwave_target) + spark_effect_two.start() + drain_power(use_power_cost) diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm new file mode 100644 index 0000000000..7f0db2709c --- /dev/null +++ b/code/modules/mod/modules/modules_supply.dm @@ -0,0 +1,174 @@ +//Supply modules for MODsuits + +///Internal GPS - Extends a GPS you can use. +/obj/item/mod/module/gps + name = "MOD internal GPS module" + desc = "This module uses common Nanotrasen technology to calculate the user's position anywhere in space, \ + down to the exact coordinates. This information is fed to a central database viewable from the device itself, \ + though using it to help people is up to you." + icon_state = "gps" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 + incompatible_modules = list(/obj/item/mod/module/gps) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + +/obj/item/mod/module/gps/Initialize(mapload) + . = ..() + 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. +/obj/item/mod/module/clamp + name = "MOD hydraulic clamp module" + desc = "A series of actuators installed into both arms of the suit, boasting a lifting capacity of almost a ton. \ + However, this design has been locked by Nanotrasen to be primarily utilized for lifting various crates. \ + A lot of people would say that loading cargo is a dull job, but you could not disagree more." + icon_state = "clamp" + module_type = MODULE_ACTIVE + complexity = 3 + use_power_cost = DEFAULT_CHARGE_DRAIN + incompatible_modules = list(/obj/item/mod/module/clamp) + cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_clamp" + overlay_state_active = "module_clamp_on" + /// Time it takes to load a crate. + var/load_time = 3 SECONDS + /// The max amount of crates you can carry. + var/max_crates = 3 + /// The crates stored in the module. + var/list/stored_crates = list() + +/obj/item/mod/module/clamp/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!mod.wearer.Adjacent(target)) + return + if(istype(target, /obj/structure/closet/crate) || istype(target, /obj/structure/bigDelivery)) + var/atom/movable/picked_crate = target + if(!check_crate_pickup(picked_crate)) + return + playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) + if(!do_after(mod.wearer, load_time, target = target)) + balloon_alert(mod.wearer, "interrupted!") + return + if(!check_crate_pickup(picked_crate)) + return + stored_crates += picked_crate + picked_crate.forceMove(src) + balloon_alert(mod.wearer, "picked up [picked_crate]") + drain_power(use_power_cost) + else if(length(stored_crates)) + var/turf/target_turf = get_turf(target) + if(is_blocked_turf(target_turf)) + return + playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) + if(!do_after(mod.wearer, load_time, target = target)) + balloon_alert(mod.wearer, "interrupted!") + return + if(is_blocked_turf(target_turf)) + return + var/atom/movable/dropped_crate = pop(stored_crates) + dropped_crate.forceMove(target_turf) + balloon_alert(mod.wearer, "dropped [dropped_crate]") + drain_power(use_power_cost) + else + balloon_alert(mod.wearer, "invalid target!") + +/obj/item/mod/module/clamp/on_suit_deactivation(deleting = FALSE) + if(deleting) + return + for(var/atom/movable/crate as anything in stored_crates) + crate.forceMove(drop_location()) + stored_crates -= crate + +/obj/item/mod/module/clamp/proc/check_crate_pickup(atom/movable/target) + if(length(stored_crates) >= max_crates) + balloon_alert(mod.wearer, "too many crates!") + return FALSE + for(var/mob/living/mob in target.GetAllContents()) + if(mob.mob_size < MOB_SIZE_HUMAN) + continue + balloon_alert(mod.wearer, "crate too heavy!") + return FALSE + return TRUE + +/obj/item/mod/module/clamp/loader + name = "MOD loader hydraulic clamp module" + icon_state = "clamp_loader" + complexity = 0 + removable = FALSE + overlay_state_inactive = null + overlay_state_active = "module_clamp_loader" + load_time = 1 SECONDS + max_crates = 5 + use_mod_colors = TRUE + +///Drill - Lets you dig through rock and basalt. +/obj/item/mod/module/drill // TODO: Would be cooler with a built-in drill, but meh + name = "MOD pickaxe/drill storage module" + desc = "Provides a convenient storage compartment for pickaxes and drills." + icon_state = "drill" + complexity = 2 + incompatible_modules = list(/obj/item/mod/module/drill) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + module_type = MODULE_USABLE + /// Pickaxe we have stored. + var/obj/item/pickaxe/stored + +/obj/item/mod/module/drill/on_use() + . = ..() + if(!.) + return + if(!stored) + var/obj/item/pickaxe/holding = mod.wearer.get_active_held_item() + if(!holding) + balloon_alert(mod.wearer, "nothing to store!") + return + if(!istype(holding)) + balloon_alert(mod.wearer, "it doesn't fit!") + return + if(mod.wearer.transferItemToLoc(holding, src, force = FALSE, silent = TRUE)) + stored = holding + balloon_alert(mod.wearer, "mining instrument stored") + playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE) + else if(mod.wearer.put_in_active_hand(stored, forced = FALSE, ignore_animation = TRUE)) + balloon_alert(mod.wearer, "mining instrument retrieved") + playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE) + else + balloon_alert(mod.wearer, "mining instrument storage full!") + +/obj/item/mod/module/drill/on_uninstall(deleting = FALSE) + if(stored) + stored.forceMove(drop_location()) + +/obj/item/mod/module/drill/Exited(atom/movable/gone, direction) + . = ..() + if(gone == stored) + stored = null + +/obj/item/mod/module/drill/Destroy() + QDEL_NULL(stored) + return ..() + +/obj/item/mod/module/orebag // TODO + name = "MOD mining satchel storage module" + desc = "Provides a convenient storage department for a mining satchel." + icon_state = "ore" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 + incompatible_modules = list(/obj/item/mod/module/orebag) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + +// Ash accretion looks cool, but can't be arsed to implement +// Same with sphere transformation diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm new file mode 100644 index 0000000000..0e44900a46 --- /dev/null +++ b/code/modules/mod/modules/modules_visor.dm @@ -0,0 +1,91 @@ +//Visor modules for MODsuits + +///Base Visor - Adds a specific HUD and traits to you. +/obj/item/mod/module/visor + name = "MOD visor module" + desc = "A heads-up display installed into the visor of the suit. They say these also let you see behind you." + module_type = MODULE_TOGGLE + complexity = 2 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/visor) + cooldown_time = 0.5 SECONDS + /// 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.add_hud_to(mod.wearer) + for(var/trait in visor_traits) + ADD_TRAIT(mod.wearer, trait, MOD_TRAIT) + 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.remove_hud_from(mod.wearer) + for(var/trait in visor_traits) + REMOVE_TRAIT(mod.wearer, trait, MOD_TRAIT) + mod.wearer.update_sight() + +//Medical Visor - Gives you a medical HUD. +/obj/item/mod/module/visor/medhud + name = "MOD medical visor module" + desc = "A heads-up display installed into the visor of the suit. This cross-references suit sensor data with a modern \ + biological scanning suite, allowing the user to visualize the current health of organic lifeforms, as well as \ + access data such as patient files in a convenient readout. They say these also let you see behind you." + icon_state = "medhud_visor" + hud_type = DATA_HUD_MEDICAL_ADVANCED + +//Diagnostic Visor - Gives you a diagnostic HUD. +/obj/item/mod/module/visor/diaghud + name = "MOD diagnostic visor module" + desc = "A heads-up display installed into the visor of the suit. This uses a series of advanced sensors to access data \ + from advanced machinery, exosuits, and other devices, allowing the user to visualize current power levels \ + and integrity of such. They say these also let you see behind you." + icon_state = "diaghud_visor" + hud_type = DATA_HUD_DIAGNOSTIC_ADVANCED + +//Security Visor - Gives you a security HUD. +/obj/item/mod/module/visor/sechud + name = "MOD security visor module" + desc = "A heads-up display installed into the visor of the suit. This module is a heavily-retrofitted targeting system, \ + plugged into various criminal databases to be able to view arrest records, command simple security-oriented robots, \ + and generally know who to shoot. They say these also let you see behind you." + icon_state = "sechud_visor" + hud_type = DATA_HUD_SECURITY_ADVANCED + +//Meson Visor - Gives you meson vision. +/obj/item/mod/module/visor/meson + name = "MOD meson visor module" + desc = "A heads-up display installed into the visor of the suit. This module is based off well-loved meson scanner \ + technology, used by construction workers and miners across the galaxy to see basic structural and terrain layouts \ + through walls, regardless of lighting conditions. They say these also let you see behind you." + icon_state = "meson_visor" + visor_traits = list(TRAIT_MESON_VISION) + +//Thermal Visor - Gives you thermal vision. +/obj/item/mod/module/visor/thermal + name = "MOD thermal visor module" + desc = "A heads-up display installed into the visor of the suit. This uses a small IR scanner to detect and identify \ + the thermal radiation output of objects near the user. While it can detect the heat output of even something as \ + small as a rodent, it still produces irritating red overlay. They say these also let you see behind you." + icon_state = "thermal_visor" + visor_traits = list(TRAIT_THERMAL_VISION) + +//Night Visor - Gives you night vision. +/obj/item/mod/module/visor/night + name = "MOD night visor module" + desc = "A heads-up display installed into the visor of the suit. Typical for both civilian and military applications, \ + this allows the user to perceive their surroundings while in complete darkness, enhancing the view by tenfold; \ + yet brightening everything into a spooky green glow. They say these also let you see behind you." + icon_state = "night_visor" + visor_traits = list(TRAIT_TRUE_NIGHT_VISION) diff --git a/code/modules/research/designs/mod_designs.dm b/code/modules/research/designs/mod_designs.dm new file mode 100644 index 0000000000..97f5577aa0 --- /dev/null +++ b/code/modules/research/designs/mod_designs.dm @@ -0,0 +1,392 @@ +//MODsuit construction + +/datum/design/mod_shell + name = "MOD Shell" + desc = "A 'Nakamura Engineering' designed shell for a Modular Suit." + id = "mod_shell" + build_type = MECHFAB + materials = list(/datum/material/iron = 10000, /datum/material/plasma = 5000) + construction_time = 25 SECONDS + build_path = /obj/item/mod/construction/shell + category = list("MODsuit Chassis") + +/datum/design/mod_helmet + name = "MOD Helmet" + desc = "A 'Nakamura Engineering' designed helmet for a Modular Suit." + id = "mod_helmet" + build_type = MECHFAB + materials = list(/datum/material/iron = 5000) + construction_time = 10 SECONDS + build_path = /obj/item/mod/construction/helmet + category = list("MODsuit Chassis") + +/datum/design/mod_chestplate + name = "MOD Chestplate" + desc = "A 'Nakamura Engineering' designed chestplate for a Modular Suit." + id = "mod_chestplate" + build_type = MECHFAB + materials = list(/datum/material/iron = 5000) + construction_time = 10 SECONDS + build_path = /obj/item/mod/construction/chestplate + category = list("MODsuit Chassis") + +/datum/design/mod_gauntlets + name = "MOD Gauntlets" + desc = "'Nakamura Engineering' designed gauntlets for a Modular Suit." + id = "mod_gauntlets" + build_type = MECHFAB + materials = list(/datum/material/iron = 5000) + construction_time = 10 SECONDS + build_path = /obj/item/mod/construction/gauntlets + category = list("MODsuit Chassis") + +/datum/design/mod_boots + name = "MOD Boots" + desc = "'Nakamura Engineering' designed boots for a Modular Suit." + id = "mod_boots" + build_type = MECHFAB + materials = list(/datum/material/iron = 5000) + construction_time = 10 SECONDS + build_path = /obj/item/mod/construction/boots + category = list("MODsuit Chassis") + +/datum/design/mod_plating + name = "MOD External Plating" + desc = "External plating for a MODsuit." + id = "mod_plating_standard" + build_type = PROTOLATHE | MECHFAB + materials = list(/datum/material/iron = 6000, /datum/material/glass = 3000, /datum/material/plasma = 1000) + construction_time = 15 SECONDS + build_path = /obj/item/mod/construction/armor + category = list("MODsuit Chassis", "MODsuit Designs") + departmental_flags = DEPARTMENTAL_FLAG_ALL + research_icon = 'icons/obj/clothing/modsuit/mod_construction.dmi' + research_icon_state = "standard-plating" + +/datum/design/mod_plating/New() + . = ..() + var/obj/item/mod/construction/armor/armor_type = build_path + var/datum/mod_theme/theme = GLOB.mod_themes[initial(armor_type.theme)] + desc = "External plating for a MODsuit. [theme.desc]" + +/datum/design/mod_plating/engineering + name = "MOD Engineering Plating" + id = "mod_plating_engineering" + build_path = /obj/item/mod/construction/armor/engineering + materials = list(/datum/material/iron = 6000, /datum/material/gold = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000) + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + research_icon_state = "engineering-plating" + +/datum/design/mod_plating/atmospheric + name = "MOD Atmospheric Plating" + id = "mod_plating_atmospheric" + build_path = /obj/item/mod/construction/armor/atmospheric + materials = list(/datum/material/iron = 6000, /datum/material/titanium = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000) + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + research_icon_state = "atmospheric-plating" + +/datum/design/mod_plating/medical + name = "MOD Medical Plating" + id = "mod_plating_medical" + build_path = /obj/item/mod/construction/armor/medical + materials = list(/datum/material/iron = 6000, /datum/material/silver = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000) + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + research_icon_state = "medical-plating" + +/datum/design/mod_plating/security + name = "MOD Security Plating" + id = "mod_plating_security" + build_path = /obj/item/mod/construction/armor/security + materials = list(/datum/material/iron = 6000, /datum/material/uranium = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000) + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + research_icon_state = "security-plating" + +/datum/design/mod_plating/cosmohonk + name = "MOD Cosmohonk Plating" + id = "mod_plating_cosmohonk" + build_path = /obj/item/mod/construction/armor/cosmohonk + materials = list(/datum/material/iron = 6000, /datum/material/bananium = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000) + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + research_icon_state = "cosmohonk-plating" + +/datum/design/mod_paint_kit + name = "MOD Paint Kit" + desc = "A paint kit for Modular Suits." + id = "mod_paint_kit" + build_type = PROTOLATHE | MECHFAB + materials = list(/datum/material/iron = 1000, /datum/material/plastic = 500) + construction_time = 5 SECONDS + build_path = /obj/item/mod/paint + category = list("MODsuit Modules", "MODsuit Designs") + +//MODsuit modules + +/datum/design/module + name = "MOD Module" + build_type = PROTOLATHE | MECHFAB + construction_time = 1 SECONDS + materials = list(/datum/material/iron = 1000, /datum/material/glass = 1000) + build_path = /obj/item/mod/module + category = list("MODsuit Modules", "MODsuit Designs") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/module/New() + . = ..() + var/obj/item/mod/module/module = build_path + desc = "[initial(module.desc)] It uses [initial(module.complexity)] complexity." + +/datum/design/module/mod_storage + name = "Storage Module" + id = "mod_storage" + materials = list(/datum/material/iron = 2500, /datum/material/glass = 500) + build_path = /obj/item/mod/module/storage + +/datum/design/module/mod_visor_medhud + name = "Medical Visor Module" + id = "mod_visor_medhud" + materials = list(/datum/material/silver = 500, /datum/material/glass = 1000) + build_path = /obj/item/mod/module/visor/medhud + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/module/mod_visor_diaghud + name = "Diagnostic Visor Module" + id = "mod_visor_diaghud" + materials = list(/datum/material/gold = 500, /datum/material/glass = 1000) + build_path = /obj/item/mod/module/visor/diaghud + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/module/mod_visor_sechud + name = "Security Visor Module" + id = "mod_visor_sechud" + materials = list(/datum/material/titanium = 500, /datum/material/glass = 1000) + build_path = /obj/item/mod/module/visor/sechud + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + +/datum/design/module/mod_visor_meson + name = "Meson Visor Module" + id = "mod_visor_meson" + materials = list(/datum/material/uranium = 500, /datum/material/glass = 1000) + build_path = /obj/item/mod/module/visor/meson + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/module/mod_visor_welding + name = "Welding Protection Module" + id = "mod_welding" + materials = list(/datum/material/iron = 500, /datum/material/glass = 1000) + build_path = /obj/item/mod/module/welding + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/module/mod_t_ray + name = "T-Ray Scanner Module" + id = "mod_t_ray" + materials = list(/datum/material/iron = 500, /datum/material/glass = 1000) + build_path = /obj/item/mod/module/t_ray + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/module/mod_health_analyzer + name = "Health Analyzer Module" + id = "mod_health_analyzer" + materials = list(/datum/material/iron = 500, /datum/material/glass = 1000) + build_path = /obj/item/mod/module/health_analyzer + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/module/mod_stealth + name = "Cloak Module" + id = "mod_stealth" + materials = list(/datum/material/iron = 1000, /datum/material/bluespace = 500) + build_path = /obj/item/mod/module/stealth + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + +/datum/design/module/mod_magboot + name = "Magnetic Stabilizator Module" + id = "mod_magboot" + materials = list(/datum/material/iron = 1000, /datum/material/gold = 500) + build_path = /obj/item/mod/module/magboot + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/module/mod_mag_harness + name = "Magnetic Harness Module" + id = "mod_mag_harness" + materials = list(/datum/material/iron = 1500, /datum/material/silver = 500) + build_path = /obj/item/mod/module/magnetic_harness + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + +/datum/design/module/mod_mouthhole + name = "Eating Apparatus Module" + id = "mod_mouthhole" + materials = list(/datum/material/iron = 1500) + build_path = /obj/item/mod/module/mouthhole + +/datum/design/module/mod_rad_protection + name = "Radiation Protection Module" + id = "mod_rad_protection" + materials = list(/datum/material/iron = 1000, /datum/material/uranium = 1000) + build_path = /obj/item/mod/module/rad_protection + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/module/mod_emp_shield + name = "EMP Shield Module" + id = "mod_emp_shield" + materials = list(/datum/material/iron = 1000, /datum/material/plasma = 1000) + build_path = /obj/item/mod/module/emp_shield + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/module/mod_flashlight + name = "Flashlight Module" + id = "mod_flashlight" + materials = list(/datum/material/iron = 500, /datum/material/glass = 1000) + build_path = /obj/item/mod/module/flashlight + +/datum/design/module/mod_reagent_scanner + name = "Reagent Scanner Module" + id = "mod_reagent_scanner" + materials = list(/datum/material/glass = 1000) + build_path = /obj/item/mod/module/reagent_scanner + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/module/mod_gps + name = "Internal GPS Module" + id = "mod_gps" + materials = list(/datum/material/iron = 500, /datum/material/glass = 500) + build_path = /obj/item/mod/module/gps + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO + +/datum/design/module/mod_constructor + name = "Constructor Module" + id = "mod_constructor" + materials = list(/datum/material/iron = 1000, /datum/material/titanium = 500) + build_path = /obj/item/mod/module/constructor + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/module/mod_quick_carry + name = "Quick Carry Module" + id = "mod_quick_carry" + materials = list(/datum/material/iron = 1000, /datum/material/titanium = 500) + build_path = /obj/item/mod/module/quick_carry + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/module/mod_longfall + name = "Longfall Module" + id = "mod_longfall" + materials = list(/datum/material/iron = 1000) + build_path = /obj/item/mod/module/longfall + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/module/mod_injector + name = "Injector Module" + id = "mod_injector" + materials = list(/datum/material/iron = 1000, /datum/material/diamond = 500) + build_path = /obj/item/mod/module/injector + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/module/mod_bikehorn + name = "Bike Horn Module" + id = "mod_bikehorn" + materials = list(/datum/material/plastic = 500, /datum/material/iron = 500) + build_path = /obj/item/mod/module/bikehorn + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/module/mod_microwave_beam + name = "Microwave Beam Module" + id = "mod_microwave_beam" + materials = list(/datum/material/iron = 1000, /datum/material/uranium = 500) + build_path = /obj/item/mod/module/microwave_beam + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/module/mod_clamp + name = "Crate Clamp Module" + id = "mod_clamp" + materials = list(/datum/material/iron = 2000) + build_path = /obj/item/mod/module/clamp + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/module/mod_drill + name = "Drill Module" + id = "mod_drill" + materials = list(/datum/material/silver = 1000, /datum/material/iron = 2000) + build_path = /obj/item/mod/module/drill + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/module/mod_orebag + name = "Ore Bag Module" + id = "mod_orebag" + materials = list(/datum/material/iron = 1500) + build_path = /obj/item/mod/module/orebag + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/module/mod_dna_lock + name = "DNA Lock Module" + id = "mod_dna_lock" + materials = list(/datum/material/diamond = 500, /datum/material/glass = 1000) + build_path = /obj/item/mod/module/dna_lock + +/datum/design/module/mister_atmos + name = "Resin Mister Module" + id = "mod_mister_atmos" + materials = list(/datum/material/glass = 1000, /datum/material/titanium = 1500) + build_path = /obj/item/mod/module/mister/atmos + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/module/mod_holster + name = "Holster Module" + id = "mod_holster" + materials = list(/datum/material/iron = 1500, /datum/material/glass = 500) + build_path = /obj/item/mod/module/holster + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + +/datum/design/module/surgicalprocessor + name = "Surgical Processor Module" + id = "mod_surgicalprocessor" + materials = list(/datum/material/titanium = 250, /datum/material/glass = 1000, /datum/material/silver = 1500) + build_path = /obj/item/mod/module/surgical_processor + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/module/defibrillator + name = "Defibrillator Module" + id = "mod_defib" + materials = list(/datum/material/titanium = 250, /datum/material/diamond = 1000, /datum/material/silver = 1500) + build_path = /obj/item/mod/module/defibrillator + build_type = PROTOLATHE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +//MODsuit anomalock modules +/datum/design/module/mod_antigrav + name = "Anti-Gravity Module" + id = "mod_antigrav" + materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/uranium = 2000) + build_path = /obj/item/mod/module/anomaly_locked/antigrav + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/module/mod_teleporter + name = "Teleporter Module" + id = "mod_teleporter" + materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/bluespace = 2000) + build_path = /obj/item/mod/module/anomaly_locked/teleporter + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE diff --git a/code/modules/research/machinery/protolathe.dm b/code/modules/research/machinery/protolathe.dm index 684f27ccad..4057503e09 100644 --- a/code/modules/research/machinery/protolathe.dm +++ b/code/modules/research/machinery/protolathe.dm @@ -15,7 +15,8 @@ "Weapons", "Ammo", "Firing Pins", - "Computer Parts" + "Computer Parts", + "MODsuit Designs" ) production_animation = "protolathe_n" allowed_buildtypes = PROTOLATHE diff --git a/code/modules/research/machinery/techfab.dm b/code/modules/research/machinery/techfab.dm index f93560ed10..110b6c037c 100644 --- a/code/modules/research/machinery/techfab.dm +++ b/code/modules/research/machinery/techfab.dm @@ -26,7 +26,8 @@ "Subspace Telecomms", "Research Machinery", "Misc. Machinery", - "Computer Parts" + "Computer Parts", + "MODsuit Designs" ) console_link = FALSE production_animation = "protolathe_n" diff --git a/code/modules/research/techweb/nodes/mod_nodes.dm b/code/modules/research/techweb/nodes/mod_nodes.dm new file mode 100644 index 0000000000..7b3f79c640 --- /dev/null +++ b/code/modules/research/techweb/nodes/mod_nodes.dm @@ -0,0 +1,123 @@ +/datum/techweb_node/mod_basic + id = "mod" + starting_node = TRUE + display_name = "Basic Modular Suits" + description = "Specialized back mounted power suits with various different modules." + design_ids = list( + "mod_shell", + "mod_helmet", + "mod_chestplate", + "mod_gauntlets", + "mod_boots", + "mod_plating_standard", + "mod_storage", + "mod_welding", + "mod_mouthhole", + "mod_flashlight", + "mod_longfall", + ) + +/datum/techweb_node/mod_advanced + id = "mod_advanced" + display_name = "Advanced Modular Suits" + description = "More advanced modules, to improve modular suits." + prereq_ids = list("mod", "robotics") + design_ids = list( + "mod_visor_diaghud", + "mod_gps", + "mod_reagent_scanner", + "mod_clamp", + "mod_drill", + "mod_orebag", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + +/datum/techweb_node/mod_engineering + id = "mod_engineering" + display_name = "Engineering Modular Suits" + description = "Engineering suits, for powered engineers." + prereq_ids = list("mod_advanced", "engineering") + design_ids = list( + "mod_plating_engineering", + "mod_visor_meson", + "mod_t_ray", + "mod_magboot", + "mod_constructor", + "mod_mister_atmos", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + +/datum/techweb_node/mod_advanced_engineering + id = "mod_advanced_engineering" + display_name = "Advanced Engineering Modular Suits" + description = "Advanced Engineering suits, for advanced powered engineers." + prereq_ids = list("mod_engineering", "adv_engi") + design_ids = list( + "mod_plating_atmospheric", + "mod_rad_protection", + "mod_emp_shield", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500) + +/datum/techweb_node/mod_medical + id = "mod_medical" + display_name = "Medical Modular Suits" + description = "Medical suits for quick rescue purposes." + prereq_ids = list("mod_advanced", "biotech") + design_ids = list( + "mod_plating_medical", + "mod_visor_medhud", + "mod_health_analyzer", + "mod_quick_carry", + "mod_injector", + "mod_dna_lock", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + +/datum/techweb_node/mod_advanced_medical + id = "mod_advanced_medical" + display_name = "Advanced Medical Modular Suits" + description = "Advanced medical suits for quicker rescue purposes." + prereq_ids = list("mod_medical", "adv_biotech") + design_ids = list( + "mod_defib", + "mod_surgicalprocessor", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500) + +/datum/techweb_node/mod_security + id = "mod_security" + display_name = "Security Modular Suits" + description = "Security suits for space crime handling." + prereq_ids = list("mod_advanced", "sec_basic") + design_ids = list( + "mod_plating_security", + "mod_visor_sechud", + "mod_stealth", + "mod_mag_harness", + "mod_holster", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + +/datum/techweb_node/mod_entertainment + id = "mod_entertainment" + display_name = "Entertainment Modular Suits" + description = "Powered suits for protection against low-humor environments." + prereq_ids = list("mod_advanced", "clown") + design_ids = list( + "mod_plating_cosmohonk", + "mod_bikehorn", + "mod_microwave_beam", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + +/datum/techweb_node/mod_anomaly + id = "mod_anomaly" + display_name = "Anomalock Modular Suits" + description = "Modules for modular suits that require anomaly cores to function." + prereq_ids = list("mod_advanced", "anomaly_research") + design_ids = list( + "mod_antigrav", + "mod_teleporter", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index a4eafc4b01..9e39ec58e8 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -68,6 +68,15 @@ var/obj/item/surgical_processor/SP = locate() in R.module.modules if(SP) advanced_surgeries |= SP.advanced_surgeries + else + var/obj/item/surgical_processor/SP + for(var/obj/item/surgical_processor/processor in user.held_items) + SP = processor + break + if(!SP) + SP = locate(/obj/item/surgical_processor) in get_turf(user) + if(SP) + advanced_surgeries |= SP.advanced_surgeries var/turf/T = get_turf(patient) var/obj/structure/table/optable/table = locate(/obj/structure/table/optable, T) diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm index f1e4d4468b..3823bfa1dd 100644 --- a/code/modules/vehicles/mecha/_mecha.dm +++ b/code/modules/vehicles/mecha/_mecha.dm @@ -817,7 +817,7 @@ mecha_flags &= ~SILICON_PILOT AI.forceMove(card) card.AI = AI - AI.controlled_mech = null + AI.controlled_equipment = null AI.remote_control = null to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") @@ -856,7 +856,7 @@ mecha_flags |= SILICON_PILOT moved_inside(AI) AI.cancel_camera() - AI.controlled_mech = src + AI.controlled_equipment = src AI.remote_control = src AI.mobility_flags = ALL //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. if(interaction == AI_MECH_HACK) @@ -1083,7 +1083,7 @@ AI.linked_core = null return to_chat(AI, "Returning to core...") - AI.controlled_mech = null + AI.controlled_equipment = null AI.remote_control = null mob_container = AI newloc = get_turf(AI.linked_core) diff --git a/code/modules/vehicles/mecha/mech_fabricator.dm b/code/modules/vehicles/mecha/mech_fabricator.dm index bbbfa7f1bd..29cc88a563 100644 --- a/code/modules/vehicles/mecha/mech_fabricator.dm +++ b/code/modules/vehicles/mecha/mech_fabricator.dm @@ -57,6 +57,8 @@ "Exosuit Equipment", "Exosuit Ammunition", "Cyborg Upgrade Modules", + "MODsuit Chassis", + "MODsuit Modules", "Cybernetics", "Implants", "Control Interfaces", diff --git a/code/modules/vehicles/mecha/mecha_wreckage.dm b/code/modules/vehicles/mecha/mecha_wreckage.dm index b9f299731a..70c543fbde 100644 --- a/code/modules/vehicles/mecha/mecha_wreckage.dm +++ b/code/modules/vehicles/mecha/mecha_wreckage.dm @@ -35,7 +35,7 @@ AI.death() //The damage is not enough to kill the AI, but to be 'corrupted files' in need of repair. AI.forceMove(src) //Put the dead AI inside the wreckage for recovery add_overlay(mutable_appearance('icons/obj/projectiles.dmi', "green_laser")) //Overlay for the recovery beacon - AI.controlled_mech = null + AI.controlled_equipment = null AI.remote_control = null /obj/structure/mecha_wreckage/Destroy() diff --git a/icons/mob/actions/actions_mod.dmi b/icons/mob/actions/actions_mod.dmi new file mode 100644 index 0000000000..84fcbc00db Binary files /dev/null and b/icons/mob/actions/actions_mod.dmi differ diff --git a/icons/mob/clothing/modsuit/mod_clothing.dmi b/icons/mob/clothing/modsuit/mod_clothing.dmi new file mode 100644 index 0000000000..f8dba0a006 Binary files /dev/null and b/icons/mob/clothing/modsuit/mod_clothing.dmi differ diff --git a/icons/mob/clothing/modsuit/mod_modules.dmi b/icons/mob/clothing/modsuit/mod_modules.dmi new file mode 100644 index 0000000000..1001ae77d2 Binary files /dev/null and b/icons/mob/clothing/modsuit/mod_modules.dmi differ diff --git a/icons/obj/clothing/modsuit/mod_clothing.dmi b/icons/obj/clothing/modsuit/mod_clothing.dmi new file mode 100644 index 0000000000..41ebc1a2ba Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_clothing.dmi differ diff --git a/icons/obj/clothing/modsuit/mod_construction.dmi b/icons/obj/clothing/modsuit/mod_construction.dmi new file mode 100644 index 0000000000..a6be94284a Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_construction.dmi differ diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi new file mode 100644 index 0000000000..037e7fe696 Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_modules.dmi differ diff --git a/icons/obj/defibrillators.dmi b/icons/obj/defibrillators.dmi index a4dc9e65e3..8871d5b049 100644 Binary files a/icons/obj/defibrillators.dmi and b/icons/obj/defibrillators.dmi differ diff --git a/sound/items/modsuit/atrocinator_step.ogg b/sound/items/modsuit/atrocinator_step.ogg new file mode 100644 index 0000000000..deda85ac35 Binary files /dev/null and b/sound/items/modsuit/atrocinator_step.ogg differ diff --git a/sound/items/modsuit/ballin.ogg b/sound/items/modsuit/ballin.ogg new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sound/items/modsuit/ballout.ogg b/sound/items/modsuit/ballout.ogg new file mode 100644 index 0000000000..f911f1a6a6 Binary files /dev/null and b/sound/items/modsuit/ballout.ogg differ diff --git a/sound/items/modsuit/flamethrower.ogg b/sound/items/modsuit/flamethrower.ogg new file mode 100644 index 0000000000..447245d50b Binary files /dev/null and b/sound/items/modsuit/flamethrower.ogg differ diff --git a/sound/items/modsuit/inflate_bloon.ogg b/sound/items/modsuit/inflate_bloon.ogg new file mode 100644 index 0000000000..9b030d66ce Binary files /dev/null and b/sound/items/modsuit/inflate_bloon.ogg differ diff --git a/sound/items/modsuit/loader_charge.ogg b/sound/items/modsuit/loader_charge.ogg new file mode 100644 index 0000000000..61d5531f72 Binary files /dev/null and b/sound/items/modsuit/loader_charge.ogg differ diff --git a/sound/items/modsuit/loader_launch.ogg b/sound/items/modsuit/loader_launch.ogg new file mode 100644 index 0000000000..513118f3c6 Binary files /dev/null and b/sound/items/modsuit/loader_launch.ogg differ diff --git a/sound/items/modsuit/magnetic_harness.ogg b/sound/items/modsuit/magnetic_harness.ogg new file mode 100644 index 0000000000..3d19fccc56 Binary files /dev/null and b/sound/items/modsuit/magnetic_harness.ogg differ diff --git a/sound/items/modsuit/rewinder.ogg b/sound/items/modsuit/rewinder.ogg new file mode 100644 index 0000000000..2587562dc1 Binary files /dev/null and b/sound/items/modsuit/rewinder.ogg differ diff --git a/sound/items/modsuit/springlock.ogg b/sound/items/modsuit/springlock.ogg new file mode 100644 index 0000000000..8d0013d263 Binary files /dev/null and b/sound/items/modsuit/springlock.ogg differ diff --git a/sound/items/modsuit/tem_shot.ogg b/sound/items/modsuit/tem_shot.ogg new file mode 100644 index 0000000000..50905b95f1 Binary files /dev/null and b/sound/items/modsuit/tem_shot.ogg differ diff --git a/sound/items/modsuit/time_anchor_set.ogg b/sound/items/modsuit/time_anchor_set.ogg new file mode 100644 index 0000000000..457f8e6dba Binary files /dev/null and b/sound/items/modsuit/time_anchor_set.ogg differ diff --git a/sound/mecha/hydraulic.ogg b/sound/mecha/hydraulic.ogg new file mode 100644 index 0000000000..3281ed2dc0 Binary files /dev/null and b/sound/mecha/hydraulic.ogg differ diff --git a/sound/weapons/revolverempty.ogg b/sound/weapons/revolverempty.ogg new file mode 100644 index 0000000000..81ddb1e2e0 Binary files /dev/null and b/sound/weapons/revolverempty.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 5efc223582..31ab41768c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -159,11 +159,11 @@ #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\dcs\signals\signals_medical.dm" #include "code\__DEFINES\dcs\signals\signals_mod.dm" #include "code\__DEFINES\dcs\signals\signals_subsystem.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movable.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm" -#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_carbon.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_living.dm" #include "code\__DEFINES\mapping\maploader.dm" #include "code\__DEFINES\material\worth.dm" @@ -2022,7 +2022,6 @@ #include "code\modules\client\preferences_savefile.dm" #include "code\modules\client\preferences_toggles.dm" #include "code\modules\client\preferences_vr.dm" -#include "code\modules\client\preferences\mod_select.dm" #include "code\modules\client\verbs\aooc.dm" #include "code\modules\client\verbs\autobunker.dm" #include "code\modules\client\verbs\etips.dm" @@ -2963,13 +2962,21 @@ #include "code\modules\mod\mod_ai.dm" #include "code\modules\mod\mod_clothes.dm" #include "code\modules\mod\mod_construction.dm" -#include "code\modules\mod\mod_construction.dm" #include "code\modules\mod\mod_control.dm" #include "code\modules\mod\mod_theme.dm" #include "code\modules\mod\mod_types.dm" #include "code\modules\mod\mod_ui.dm" #include "code\modules\mod\modules\_module.dm" #include "code\modules\mod\modules\modules.dm" +#include "code\modules\mod\modules\modules_engineering.dm" +#include "code\modules\mod\modules\modules_general.dm" +#include "code\modules\mod\modules\modules_maint.dm" +#include "code\modules\mod\modules\modules_medical.dm" +#include "code\modules\mod\modules\modules_science.dm" +#include "code\modules\mod\modules\modules_security.dm" +#include "code\modules\mod\modules\modules_service.dm" +#include "code\modules\mod\modules\modules_supply.dm" +#include "code\modules\mod\modules\modules_visor.dm" #include "code\modules\modular_computers\laptop_vendor.dm" #include "code\modules\modular_computers\computers\_modular_computer_shared.dm" #include "code\modules\modular_computers\computers\item\computer.dm" @@ -3382,6 +3389,7 @@ #include "code\modules\research\designs\medical_designs.dm" #include "code\modules\research\designs\mining_designs.dm" #include "code\modules\research\designs\misc_designs.dm" +#include "code\modules\research\designs\mod_designs.dm" #include "code\modules\research\designs\nanite_designs.dm" #include "code\modules\research\designs\power_designs.dm" #include "code\modules\research\designs\smelting_designs.dm" @@ -3451,6 +3459,7 @@ #include "code\modules\research\techweb\nodes\mecha_nodes.dm" #include "code\modules\research\techweb\nodes\medical_nodes.dm" #include "code\modules\research\techweb\nodes\misc_nodes.dm" +#include "code\modules\research\techweb\nodes\mod_nodes.dm" #include "code\modules\research\techweb\nodes\nanites_nodes.dm" #include "code\modules\research\techweb\nodes\robotics_nodes.dm" #include "code\modules\research\techweb\nodes\syndicate_nodes.dm" diff --git a/tgui/packages/tgui/interfaces/MODpaint.js b/tgui/packages/tgui/interfaces/MODpaint.js new file mode 100644 index 0000000000..9a329688eb --- /dev/null +++ b/tgui/packages/tgui/interfaces/MODpaint.js @@ -0,0 +1,151 @@ +import { useBackend } from '../backend'; +import { Box, Stack, Section, ByondUi, Slider, Flex, Button } from '../components'; +import { Window } from '../layouts'; +import { capitalize } from 'common/string'; + +const colorToMatrix = (param) => { + switch (param) { + case 'red': + return [ + 1, 0, 0, 0, 0.25, 0.5, 0, 0, 0.25, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, + ]; + case 'yellow': + return [ + 0.5, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0.25, 0.25, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, + 0, + ]; + case 'green': + return [ + 0.5, 0.25, 0, 0, 0, 1, 0, 0, 0, 0.25, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, + ]; + case 'teal': + return [ + 0.25, 0.25, 0.25, 0, 0, 0.5, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0, 0, 1, 0, 0, + 0, 0, + ]; + case 'blue': + return [ + 0.25, 0, 0.25, 0, 0, 0.5, 0.25, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, + ]; + case 'purple': + return [ + 0.5, 0, 0.5, 0, 0.25, 0.5, 0.25, 0, 0.5, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, + 0, + ]; + } +}; + +const displayText = (param) => { + switch (param) { + case 'r': + return 'Red'; + case 'g': + return 'Green'; + case 'b': + return 'Blue'; + } +}; + +export const MODpaint = (props, context) => { + const { act, data } = useBackend(context); + const { mapRef, currentColor } = data; + const [ + [rr, rg, rb, ra], + [gr, gg, gb, ga], + [br, bg, bb, ba], + [ar, ag, ab, aa], + [cr, cg, cb, ca], + ] = currentColor; + const presets = ['red', 'yellow', 'green', 'teal', 'blue', 'purple']; + const prefixes = ['r', 'g', 'b']; + return ( + + + + + {[0, 1, 2].map((row) => ( +
+ {[0, 1, 2].map((col) => ( + + + + {`${displayText(prefixes[col])}:`} + + + + `${value}%`} + onDrag={(e, value) => { + let retColor = currentColor; + retColor[row * 4 + col] = value / 100; + act('transition_color', { color: retColor }); + }} + /> + + + ))} +
+ ))} +
+ +
+ + {presets.map((preset) => ( +
+
+
+
+ +
+ +
+
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/MODsuit.js b/tgui/packages/tgui/interfaces/MODsuit.js new file mode 100644 index 0000000000..1ddef04399 --- /dev/null +++ b/tgui/packages/tgui/interfaces/MODsuit.js @@ -0,0 +1,752 @@ +import { useBackend, useLocalState } from '../backend'; +import { Button, ColorBox, LabeledList, ProgressBar, Section, Collapsible, Box, Icon, Stack, Table, Dimmer, NumberInput, Flex, AnimatedNumber, Dropdown } from '../components'; +import { Window } from '../layouts'; + +const ConfigureNumberEntry = (props, context) => { + const { name, value, module_ref } = props; + const { act } = useBackend(context); + return ( + + act('configure', { + 'key': name, + 'value': value, + 'ref': module_ref, + })} + /> + ); +}; + +const ConfigureBoolEntry = (props, context) => { + const { name, value, module_ref } = props; + const { act } = useBackend(context); + return ( + + act('configure', { + 'key': name, + 'value': !value, + 'ref': module_ref, + })} + /> + ); +}; + +const ConfigureColorEntry = (props, context) => { + const { name, value, module_ref } = props; + const { act } = useBackend(context); + return ( + <> + + + + + + ); +}; + +const displayText = (param) => { + switch (param) { + case 1: + return 'Use'; + case 2: + return 'Toggle'; + case 3: + return 'Select'; + } +}; + +const ParametersSection = (props, context) => { + const { act, data } = useBackend(context); + const { + active, + malfunctioning, + locked, + open, + selected_module, + complexity, + complexity_max, + wearer_name, + wearer_job, + AI, + } = data; + const status = malfunctioning + ? 'Malfunctioning' + : active + ? 'Active' + : 'Inactive'; + return ( +
+ + act('activate')} + /> + }> + {status} + + act('lock')} + /> + }> + {locked ? 'Locked' : 'Unlocked'} + + + {open ? 'Open' : 'Closed'} + + + {selected_module || 'None'} + + + {complexity} ({complexity_max}) + + + {wearer_name}, {wearer_job} + + {AI || 'None'} + +
+ ); +}; + +const HardwareSection = (props, context) => { + const { act, data } = useBackend(context); + const { + active, + control, + helmet, + chestplate, + gauntlets, + boots, + cell, + charge, + } = data; + return ( +
+ + + {control} + {helmet || 'None'} + + {chestplate || 'None'} + + + {gauntlets || 'None'} + + {boots || 'None'} + + + + {(cell && ( + + {cell} + + + + + )) || ( + + No Cell Detected + + )} + +
+ ); +}; + +const InfoSection = (props, context) => { + const { act, data } = useBackend(context); + const { active, modules } = data; + const info_modules = modules.filter((module) => !!module.id); + + return ( +
+ + {(info_modules.length !== 0 + && info_modules.map((module) => { + const Module = ID2MODULE[module.id]; + return ( + + {!active && } + + + ); + })) || No Info Modules Detected} + +
+ ); +}; + +const ModuleSection = (props, context) => { + const { act, data } = useBackend(context); + const { complexity_max, modules } = data; + const [configureState, setConfigureState] = useLocalState( + context, + 'module_configuration', + null + ); + return ( +
+ + {(modules.length !== 0 + && modules.map((module) => { + return ( + + +
+ {configureState === module.ref && ( + setConfigureState(null)} + /> + )} + + + +
+ {module.description} +
+
+
+ ); + })) || ( + + No Modules Detected + + )} +
+
+ ); +}; + +export const MODsuit = (props, context) => { + const { act, data } = useBackend(context); + const { ui_theme, interface_break } = data; + return ( + + + {(!!interface_break && ) || ( + + + + + + + + + + + + + + + )} + + + ); +};