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 (
+ <>
+