From 18e2cc85d1e181a7b7ba5d12dcbe69523db6b470 Mon Sep 17 00:00:00 2001
From: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Date: Wed, 29 Dec 2021 23:20:10 +0100
Subject: [PATCH] [MIRROR] modsuit works with speed potions + ai movement fix +
file sortage [MDB IGNORE] (#10341)
* modsuit works with speed potions + ai movement fix + file sortage (#63670)
modsuit examining now tells you about wire panels
fixes being able to remove stuff with the dna lock
fixes 63650
fixes 63633
sorts module files
* modsuit works with speed potions + ai movement fix + file sortage
Co-authored-by: Fikou <23585223+Fikou@users.noreply.github.com>
---
code/__DEFINES/dcs/signals/signals_mod.dm | 4 +
code/__DEFINES/dcs/signals/signals_object.dm | 4 +
code/datums/components/storage/storage.dm | 6 +-
code/datums/elements/ridable.dm | 13 +-
code/game/objects/items.dm | 4 +
code/modules/mod/mod_activation.dm | 6 +-
code/modules/mod/mod_ai.dm | 18 +-
code/modules/mod/mod_control.dm | 35 +-
code/modules/mod/modules/modules.dm | 1780 -----------------
code/modules/mod/modules/modules_antag.dm | 208 ++
.../mod/modules/modules_engineering.dm | 207 ++
code/modules/mod/modules/modules_general.dm | 657 ++++++
code/modules/mod/modules/modules_medical.dm | 203 ++
code/modules/mod/modules/modules_science.dm | 162 ++
code/modules/mod/modules/modules_security.dm | 125 ++
code/modules/mod/modules/modules_service.dm | 58 +
code/modules/mod/modules/modules_supply.dm | 169 ++
code/modules/mod/modules/modules_visor.dm | 99 +
code/modules/paperwork/handlabeler.dm | 3 +
.../designs/mechfabricator_designs.dm | 3 +-
.../research/xenobiology/xenobiology.dm | 12 +-
tgstation.dme | 10 +-
22 files changed, 1974 insertions(+), 1812 deletions(-)
delete mode 100644 code/modules/mod/modules/modules.dm
create mode 100644 code/modules/mod/modules/modules_antag.dm
create mode 100644 code/modules/mod/modules/modules_engineering.dm
create mode 100644 code/modules/mod/modules/modules_general.dm
create mode 100644 code/modules/mod/modules/modules_medical.dm
create mode 100644 code/modules/mod/modules/modules_science.dm
create mode 100644 code/modules/mod/modules/modules_security.dm
create mode 100644 code/modules/mod/modules/modules_service.dm
create mode 100644 code/modules/mod/modules/modules_supply.dm
create mode 100644 code/modules/mod/modules/modules_visor.dm
diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm
index 4c8ec4d6ded..73031b3035f 100644
--- a/code/__DEFINES/dcs/signals/signals_mod.dm
+++ b/code/__DEFINES/dcs/signals/signals_mod.dm
@@ -5,3 +5,7 @@
#define COMSIG_MOD_ACTIVATE "mod_activate"
/// Cancels the suit's activation
#define MOD_CANCEL_ACTIVATE (1 << 0)
+/// 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)
diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm
index b5fac043bef..9a9c3db887f 100644
--- a/code/__DEFINES/dcs/signals/signals_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_object.dm
@@ -365,3 +365,7 @@
///from base of /obj/item/mmi/set_brainmob(): (mob/living/brain/new_brainmob)
#define COMSIG_MMI_SET_BRAINMOB "mmi_set_brainmob"
+
+/// from base of /obj/item/slimepotion/speed/afterattack(): (obj/target, /obj/src, mob/user)
+#define COMSIG_SPEED_POTION_APPLIED "speed_potion"
+ #define SPEED_POTION_SUCCESSFUL (1<<0)
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
index 9e55caa5fea..34a297bb506 100644
--- a/code/datums/components/storage/storage.dm
+++ b/code/datums/components/storage/storage.dm
@@ -545,10 +545,8 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
/datum/component/storage/proc/attackby(datum/source, obj/item/I, mob/M, params)
SIGNAL_HANDLER
- if(istype(I, /obj/item/hand_labeler))
- var/obj/item/hand_labeler/labeler = I
- if(labeler.mode)
- return FALSE
+ if(!I.attackby_storage_insert(src, parent, M))
+ return FALSE
. = TRUE //no afterattack
if(iscyborg(M))
return
diff --git a/code/datums/elements/ridable.dm b/code/datums/elements/ridable.dm
index 3bc31b14494..5bafbd3a0b9 100644
--- a/code/datums/elements/ridable.dm
+++ b/code/datums/elements/ridable.dm
@@ -29,10 +29,10 @@
RegisterSignal(target, COMSIG_MOVABLE_PREBUCKLE, .proc/check_mounting)
if(isvehicle(target))
- RegisterSignal(target, COMSIG_PARENT_ATTACKBY, .proc/check_potion)
+ RegisterSignal(target, COMSIG_SPEED_POTION_APPLIED, .proc/check_potion)
/datum/element/ridable/Detach(datum/target)
- UnregisterSignal(target, list(COMSIG_MOVABLE_PREBUCKLE, COMSIG_PARENT_ATTACKBY))
+ UnregisterSignal(target, list(COMSIG_MOVABLE_PREBUCKLE, COMSIG_SPEED_POTION_APPLIED))
return ..()
/// Someone is buckling to this movable, which is literally the only thing we care about (other than speed potions)
@@ -108,33 +108,28 @@
return FALSE
/// Checks to see if we've been hit with a red xenobio potion to make us faster. This is only registered if we're a vehicle
-/datum/element/ridable/proc/check_potion(atom/movable/ridable_atom, obj/item/slimepotion/speed/speed_potion, mob/living/user, params)
+/datum/element/ridable/proc/check_potion(atom/movable/ridable_atom, obj/item/slimepotion/speed/speed_potion, mob/living/user)
SIGNAL_HANDLER
- if(!istype(speed_potion))
- return
if(potion_boosted)
to_chat(user, span_warning("[ridable_atom] has already been coated with red, that's as fast as it'll go!"))
return
if(ridable_atom.has_buckled_mobs()) // effect won't take place til the next time someone mounts it, so just prevent that situation
to_chat(user, span_warning("It's too dangerous to smear [speed_potion] on [ridable_atom] while it's being ridden!"))
return
-
var/speed_limit = round(CONFIG_GET(number/movedelay/run_delay) * 0.85, 0.01)
var/datum/component/riding/theoretical_riding_component = riding_component_type
var/theoretical_speed = initial(theoretical_riding_component.vehicle_move_delay)
-
if(theoretical_speed <= speed_limit) // i say speed but this is actually move delay, so you have to be ABOVE the speed limit to pass
to_chat(user, span_warning("[ridable_atom] can't be made any faster!"))
return
-
Detach(ridable_atom)
ridable_atom.AddElement(/datum/element/ridable, component_type = riding_component_type, potion_boost = TRUE)
to_chat(user, span_notice("You slather the red gunk over [ridable_atom], making it faster."))
ridable_atom.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
ridable_atom.add_atom_colour("#FF0000", FIXED_COLOUR_PRIORITY)
qdel(speed_potion)
- return COMPONENT_NO_AFTERATTACK
+ return SPEED_POTION_SUCCESSFUL
/// Remove all of the relevant [riding offhand items][/obj/item/riding_offhand] from the target
/datum/element/ridable/proc/unequip_buckle_inhands(mob/living/carbon/user, atom/movable/target_movable)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 5e66f5e707a..82309f66db4 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1315,3 +1315,7 @@ attack_basic_mob
/// Special stuff you want to do when an outfit equips this item.
/obj/item/proc/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot)
return
+
+/// Whether or not this item can be put into a storage item through attackby
+/obj/item/proc/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
+ return TRUE
diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm
index f7869dc2278..447f1068550 100644
--- a/code/modules/mod/mod_activation.dm
+++ b/code/modules/mod/mod_activation.dm
@@ -51,7 +51,7 @@
ADD_TRAIT(piece, TRAIT_NODROP, MOD_TRAIT)
if(!user)
return TRUE
- wearer.visible_message(span_notice("[wearer]'s [piece] deploy[piece.p_s()] with a mechanical hiss."),
+ wearer.visible_message(span_notice("[wearer]'s [piece.name] deploy[piece.p_s()] with a mechanical hiss."),
span_notice("[piece] deploy[piece.p_s()] with a mechanical hiss."),
span_hear("You hear a mechanical hiss."))
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
@@ -59,7 +59,7 @@
else if(piece.loc != src)
if(!user)
return FALSE
- balloon_alert(user, "[piece] already deployed!")
+ balloon_alert(user, "[piece.name] already deployed!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
else
if(!user)
@@ -79,7 +79,7 @@
boots.show_overslot()
if(!user)
return
- wearer.visible_message(span_notice("[wearer]'s [piece] retract[piece.p_s()] back into [src] with a mechanical hiss."),
+ wearer.visible_message(span_notice("[wearer]'s [piece.name] retract[piece.p_s()] back into [src] with a mechanical hiss."),
span_notice("[piece] retract[piece.p_s()] back into [src] with a mechanical hiss."),
span_hear("You hear a mechanical hiss."))
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
diff --git a/code/modules/mod/mod_ai.dm b/code/modules/mod/mod_ai.dm
index 26bf7607b34..700fce07333 100644
--- a/code/modules/mod/mod_ai.dm
+++ b/code/modules/mod/mod_ai.dm
@@ -70,16 +70,18 @@
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
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)
- cell.charge = max(0, cell.charge - CELL_PER_STEP)
- if(wearer)
- ADD_TRAIT(wearer, TRAIT_FORCED_STANDING, MOD_TRAIT)
- addtimer(CALLBACK(src, .proc/ai_fall), AI_FALL_TIME, TIMER_UNIQUE | TIMER_OVERRIDE)
- if(ismovable(wearer?.loc))
- return wearer.loc.relaymove(wearer, direction)
if(wearer && !wearer.Process_Spacemove(direction))
return FALSE
+ else if(!wearer && (!has_gravity() || !isturf(loc)))
+ return FALSE
+ COOLDOWN_START(src, cooldown_mod_move, movedelay * timemodifier + slowdown_active)
+ cell.charge = max(0, cell.charge - CELL_PER_STEP)
+ playsound(src, 'sound/mecha/mechmove01.ogg', 25, TRUE)
+ if(ismovable(wearer?.loc))
+ return wearer.loc.relaymove(wearer, direction)
+ else if(wearer)
+ ADD_TRAIT(wearer, TRAIT_FORCED_STANDING, MOD_TRAIT)
+ addtimer(CALLBACK(src, .proc/ai_fall), AI_FALL_TIME, TIMER_UNIQUE | TIMER_OVERRIDE)
var/atom/movable/mover = wearer || src
return step(mover, direction)
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index ef3da163ae6..015fa5e44ae 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -14,7 +14,7 @@
w_class = WEIGHT_CLASS_BULKY
slot_flags = ITEM_SLOT_BACK
strip_delay = 10 SECONDS
- slowdown = 2
+ slowdown = 1.25
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10)
actions_types = list(
/datum/action/item_action/mod/deploy,
@@ -59,9 +59,9 @@
/// Power usage of the MOD.
var/cell_drain = DEFAULT_CELL_DRAIN
/// Slowdown of the MOD when not active.
- var/slowdown_inactive = 2
+ var/slowdown_inactive = 1.25
/// Slowdown of the MOD when active.
- var/slowdown_active = 1
+ var/slowdown_active = 0.75
/// MOD cell.
var/obj/item/stock_parts/cell/cell
/// MOD helmet.
@@ -137,6 +137,7 @@
module = new module(src)
install(module)
RegisterSignal(src, COMSIG_ATOM_EXITED, .proc/on_exit)
+ RegisterSignal(src, COMSIG_SPEED_POTION_APPLIED, .proc/on_potion)
movedelay = CONFIG_GET(number/movedelay/run_delay)
/obj/item/mod/control/Destroy()
@@ -200,12 +201,13 @@
. += span_notice("You could use modules on it to install them.")
. += span_notice("You could remove modules with a crowbar.")
. += span_notice("You could update the access with an ID.")
+ . += span_notice("You could access the wire panel with a wire configuring tool.")
if(cell)
. += span_notice("You could remove the cell with an empty hand.")
else
. += span_notice("You could use a cell on it to install one.")
if(ai)
- . += span_notice("You could remove [ai] with an intellicard")
+ . += span_notice("You could remove [ai] with an intellicard.")
else
. += span_notice("You could install an AI with an intellicard.")
@@ -314,6 +316,9 @@
balloon_alert(user, "insufficient access!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
+ if(SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVAL, user) & MOD_CANCEL_REMOVAL)
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
if(length(modules))
var/list/removable_modules = list()
for(var/obj/item/mod/module/module as anything in modules)
@@ -406,6 +411,8 @@
if(active && !toggle_activate(stripper, force_deactivate = TRUE))
return
for(var/obj/item/part in mod_parts)
+ if(part.loc == src)
+ continue
conceal(null, part)
return ..()
@@ -617,3 +624,23 @@
return
cell.give(amount)
update_cell_alert()
+
+/obj/item/mod/control/proc/on_potion(atom/movable/source, obj/item/slimepotion/speed/speed_potion, mob/living/user)
+ SIGNAL_HANDLER
+
+ if(slowdown_inactive <= 0)
+ to_chat(user, span_warning("[src] has already been coated with red, that's as fast as it'll go!"))
+ return
+ if(wearer)
+ to_chat(user, span_warning("It's too dangerous to smear [speed_potion] on [src] while it's on someone!"))
+ return
+ to_chat(user, span_notice("You slather the red gunk over [src], making it faster."))
+ var/list/all_parts = mod_parts.Copy() + src
+ for(var/obj/item/part as anything in all_parts)
+ part.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
+ part.add_atom_colour("#FF0000", FIXED_COLOUR_PRIORITY)
+ slowdown_inactive = 0
+ slowdown_active = 0
+ slowdown = 0
+ qdel(speed_potion)
+ return SPEED_POTION_SUCCESSFUL
diff --git a/code/modules/mod/modules/modules.dm b/code/modules/mod/modules/modules.dm
deleted file mode 100644
index 0ff9b5f48c0..00000000000
--- a/code/modules/mod/modules/modules.dm
+++ /dev/null
@@ -1,1780 +0,0 @@
-/obj/item/mod/module/storage
- name = "MOD storage 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)
- var/datum/component/storage/concrete/storage
- var/max_w_class = WEIGHT_CLASS_NORMAL
- var/max_combined_w_class = 15
- var/max_items = 7
-
-/obj/item/mod/module/storage/Initialize(mapload)
- . = ..()
- storage = AddComponent(/datum/component/storage/concrete)
- storage.max_w_class = max_w_class
- storage.max_combined_w_class = max_combined_w_class
- storage.max_items = max_items
- storage.allow_big_nesting = TRUE
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
-
-/obj/item/mod/module/storage/on_install()
- var/datum/component/storage/modstorage = mod.AddComponent(/datum/component/storage, storage)
- modstorage.max_w_class = max_w_class
- modstorage.max_combined_w_class = max_combined_w_class
- modstorage.max_items = max_items
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE)
-
-/obj/item/mod/module/storage/on_uninstall()
- var/datum/component/storage/modstorage = mod.GetComponent(/datum/component/storage)
- storage.slaves -= modstorage
- qdel(modstorage)
- SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
-
-/obj/item/mod/module/storage/large_capacity
- name = "MOD expanded storage module"
- desc = "Reverse engineered by Nakamura Engineering from Donk Corporation designs, this system of hidden compartments \
- is entirely within the suit, distributing items and weight evenly to ensure a comfortable experience for the user; \
- whether smuggling, or simply hauling."
- icon_state = "storage_large"
- max_combined_w_class = 21
- max_items = 14
-
-/obj/item/mod/module/storage/syndicate
- name = "MOD syndicate storage module"
- desc = "A storage system using nanotechnology developed by Cybersun Industries, these compartments use \
- esoteric technology to compress the physical matter of items put inside of them, \
- essentially shrinking items for much easier and more portable storage."
- icon_state = "storage_syndi"
- max_combined_w_class = 30
- max_items = 21
-
-/obj/item/mod/module/storage/bluespace
- name = "MOD bluespace storage module"
- desc = "A storage system developed by Nanotrasen, these compartments employ \
- miniaturized bluespace pockets for the ultimate in storage technology; regardless of the weight of objects put inside."
- icon_state = "storage_large"
- max_w_class = WEIGHT_CLASS_GIGANTIC
- max_combined_w_class = 60
- max_items = 21
-
-/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_CELL_DRAIN*0.3
- incompatible_modules = list(/obj/item/mod/module/visor)
- cooldown_time = 0.5 SECONDS
- var/hud_type
- 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()
- . = ..()
- 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()
-
-/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
- visor_traits = list(TRAIT_MEDICAL_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
- visor_traits = list(TRAIT_DIAGNOSTIC_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
- visor_traits = list(TRAIT_SECURITY_HUD)
-
-/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, TRAIT_SUPERMATTER_MADNESS_IMMUNE)
-
-/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)
-
-/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)
-
-/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 = FLASH_PROTECTION_WELDER
-
-/obj/item/mod/module/welding/on_suit_deactivation()
- mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
-
-/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 = 2
- active_power_cost = DEFAULT_CELL_DRAIN * 0.2
- incompatible_modules = list(/obj/item/mod/module/t_ray)
- cooldown_time = 0.5 SECONDS
- var/range = 2
-
-/obj/item/mod/module/t_ray/on_active_process(delta_time)
- t_ray_scan(mod.wearer, 8, range)
-
-#define HEALTH_SCAN "Health"
-#define WOUND_SCAN "Wound"
-#define CHEM_SCAN "Chemical"
-
-/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_CELL_DRAIN
- incompatible_modules = list(/obj/item/mod/module/health_analyzer)
- cooldown_time = 0.5 SECONDS
- var/mode = HEALTH_SCAN
- var/static/list/modes = list(HEALTH_SCAN, WOUND_SCAN, CHEM_SCAN)
-
-/obj/item/mod/module/health_analyzer/on_select_use(atom/target)
- . = ..()
- if(!.)
- return
- if(!isliving(target))
- 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
-
-/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_CELL_DRAIN * 2
- use_power_cost = DEFAULT_CELL_DRAIN * 10
- incompatible_modules = list(/obj/item/mod/module/stealth)
- cooldown_time = 5 SECONDS
- var/bumpoff = TRUE
- 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_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED), .proc/unstealth)
- animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS)
- drain_power(use_power_cost)
-
-/obj/item/mod/module/stealth/on_deactivation()
- . = ..()
- if(!.)
- return
- if(bumpoff)
- UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP)
- UnregisterSignal(mod.wearer, list(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED))
- 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()
-
-/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/projectile/projectile)
- SIGNAL_HANDLER
-
- if(projectile.nodamage)
- return
- unstealth(source)
-
-/obj/item/mod/module/stealth/ninja
- name = "MOD advanced cloaking module"
- desc = "The latest in stealth technology, this module is a definite upgrade over previous versions. \
- The field has been tuned to be even more responsive and fast-acting, with enough stability to \
- continue operation of the field even if the user bumps into others. \
- The draw on the power cell has been reduced drastically, \
- making this perfect for activities like standing near sentry turrets for extended periods of time."
- icon_state = "cloak_ninja"
- bumpoff = FALSE
- stealth_alpha = 20
- active_power_cost = DEFAULT_CELL_DRAIN
- use_power_cost = DEFAULT_CELL_DRAIN * 5
- cooldown_time = 3 SECONDS
-
-/obj/item/mod/module/jetpack
- name = "MOD ion jetpack module"
- desc = "A series of electric thrusters installed across the suit, this is a module highly anticipated by trainee Engineers. \
- Rather than using gasses for combustion thrust, these jets are capable of accelerating ions using \
- charge from the suit's cell. Some say this isn't Nakamura Engineering's first foray into jet-enabled suits."
- icon_state = "jetpack"
- module_type = MODULE_TOGGLE
- complexity = 3
- active_power_cost = DEFAULT_CELL_DRAIN * 0.5
- use_power_cost = DEFAULT_CELL_DRAIN
- incompatible_modules = list(/obj/item/mod/module/jetpack)
- cooldown_time = 0.5 SECONDS
- overlay_state_inactive = "module_jetpack"
- overlay_state_active = "module_jetpack_on"
- var/stabilizers = FALSE
- var/full_speed = FALSE
- var/datum/effect_system/trail_follow/ion/ion_trail
-
-/obj/item/mod/module/jetpack/Initialize(mapload)
- . = ..()
- ion_trail = new
- ion_trail.auto_process = FALSE
- ion_trail.set_up(src)
-
-/obj/item/mod/module/jetpack/Destroy()
- QDEL_NULL(ion_trail)
- return ..()
-
-/obj/item/mod/module/jetpack/on_activation()
- . = ..()
- if(!.)
- return
- ion_trail.start()
- RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/move_react)
- RegisterSignal(mod.wearer, COMSIG_MOVABLE_PRE_MOVE, .proc/pre_move_react)
- RegisterSignal(mod.wearer, COMSIG_MOVABLE_SPACEMOVE, .proc/spacemove_react)
- if(full_speed)
- mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
-
-/obj/item/mod/module/jetpack/on_deactivation(mob/user)
- . = ..()
- if(!.)
- return
- ion_trail.stop()
- UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
- UnregisterSignal(mod.wearer, COMSIG_MOVABLE_PRE_MOVE)
- UnregisterSignal(mod.wearer, COMSIG_MOVABLE_SPACEMOVE)
- if(full_speed)
- mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
-
-/obj/item/mod/module/jetpack/get_configuration()
- . = ..()
- .["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers)
-
-/obj/item/mod/module/jetpack/configure_edit(key, value)
- switch(key)
- if("stabilizers")
- stabilizers = text2num(value)
-
-/obj/item/mod/module/jetpack/proc/move_react(mob/user)
- SIGNAL_HANDLER
-
- if(!active)//If jet dont work, it dont work
- return
- if(!isturf(mod.wearer.loc))//You can't use jet in nowhere or from mecha/closet
- return
- if(!(mod.wearer.movement_type & FLOATING) || mod.wearer.buckled)//You don't want use jet in gravity or while buckled.
- return
- if(mod.wearer.pulledby)//You don't must use jet if someone pull you
- return
- if(mod.wearer.throwing)//You don't must use jet if you thrown
- return
- if(user.client && length(user.client.keys_held & user.client.movement_keys))//You use jet when press keys. yes.
- allow_thrust()
-
-/obj/item/mod/module/jetpack/proc/pre_move_react(mob/user)
- SIGNAL_HANDLER
-
- ion_trail.oldposition = get_turf(src)
-
-/obj/item/mod/module/jetpack/proc/spacemove_react(mob/user, movement_dir)
- SIGNAL_HANDLER
-
- if(active && (stabilizers || movement_dir))
- return COMSIG_MOVABLE_STOP_SPACEMOVE
-
-/obj/item/mod/module/jetpack/proc/allow_thrust()
- if(!drain_power(use_power_cost))
- return
- ion_trail.generate_effect()
- return TRUE
-
-/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_CELL_DRAIN * 0.5
- incompatible_modules = list(/obj/item/mod/module/magboot)
- cooldown_time = 0.5 SECONDS
- 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()
- . = ..()
- 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
-
-/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
- use_power_cost = DEFAULT_CELL_DRAIN * 0.5
- incompatible_modules = list(/obj/item/mod/module/holster)
- cooldown_time = 0.5 SECONDS
- 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, FALSE, FALSE))
- holstered = holding
- balloon_alert(mod.wearer, "weapon holstered")
- playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE)
- drain_power(use_power_cost)
- else if(mod.wearer.put_in_active_hand(holstered, FALSE, TRUE))
- balloon_alert(mod.wearer, "weapon drawn")
- holstered = null
- playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE)
- drain_power(use_power_cost)
- else
- balloon_alert(mod.wearer, "holster full!")
-
-/obj/item/mod/module/holster/on_uninstall()
- if(holstered)
- holstered.forceMove(drop_location())
- holstered = null
-
-/obj/item/mod/module/holster/Destroy()
- QDEL_NULL(holstered)
- return ..()
-
-/obj/item/mod/module/tether
- name = "MOD emergency tether module"
- desc = "A custom-built grappling-hook powered by a winch capable of hauling the user. \
- While some older models of cargo-oriented grapples have capacities of a few tons, \
- these are only capable of working in zero-gravity environments, a blessing to some Engineers."
- icon_state = "tether"
- module_type = MODULE_ACTIVE
- complexity = 3
- use_power_cost = DEFAULT_CELL_DRAIN
- incompatible_modules = list(/obj/item/mod/module/tether)
- cooldown_time = 1.5 SECONDS
-
-/obj/item/mod/module/tether/on_use()
- if(mod.wearer.has_gravity(get_turf(src)))
- balloon_alert(mod.wearer, "too much gravity!")
- playsound(src, 'sound/weapons/gun/general/dry_fire.ogg', 25, TRUE)
- return FALSE
- return ..()
-
-/obj/item/mod/module/tether/on_select_use(atom/target)
- . = ..()
- if(!.)
- return
- var/obj/projectile/tether = new /obj/projectile/tether(mod.wearer.loc)
- tether.preparePixelProjectile(target, mod.wearer)
- tether.firer = mod.wearer
- INVOKE_ASYNC(tether, /obj/projectile.proc/fire)
- drain_power(use_power_cost)
-
-/obj/projectile/tether
- name = "tether"
- icon_state = "tether_projectile"
- icon = 'icons/obj/mod.dmi'
- pass_flags = PASSTABLE
- damage = 0
- nodamage = TRUE
- range = 10
- hitsound = 'sound/weapons/batonextend.ogg'
- hitsound_wall = 'sound/weapons/batonextend.ogg'
- suppressed = SUPPRESSED_VERY
- hit_threshhold = LATTICE_LAYER
- var/line
-
-/obj/projectile/tether/fire(setAngle)
- if(firer)
- line = firer.Beam(src, "line", 'icons/obj/mod.dmi')
- ..()
-
-/obj/projectile/tether/on_hit(atom/target)
- . = ..()
- if(firer)
- firer.throw_at(target, 10, 1, firer, FALSE, FALSE, null, MOVE_FORCE_NORMAL, TRUE)
-
-/obj/projectile/tether/Destroy()
- QDEL_NULL(line)
- return ..()
-
-/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 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"
- var/former_flags = NONE
- 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()
- if(!(former_flags & HEADCOVERSMOUTH))
- mod.helmet.flags_cover |= HEADCOVERSMOUTH
- if(!(former_visor_flags & HEADCOVERSMOUTH))
- mod.helmet.visor_flags_cover |= HEADCOVERSMOUTH
-
-/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; \
- a common danger in space. This comes with software to notify the wearer that they're even in a radioactive area, \
- giving a voice to an otherwise silent killer."
- icon_state = "radshield"
- complexity = 2
- idle_power_cost = DEFAULT_CELL_DRAIN * 0.3
- incompatible_modules = list(/obj/item/mod/module/rad_protection)
- tgui_id = "rad_counter"
- var/perceived_threat_level
-
-/obj/item/mod/module/rad_protection/on_suit_activation()
- AddComponent(/datum/component/geiger_sound)
- ADD_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT)
- RegisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION, .proc/on_pre_potential_irradiation)
- for(var/obj/item/part in mod.mod_parts)
- ADD_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT)
-
-/obj/item/mod/module/rad_protection/on_suit_deactivation()
- qdel(GetComponent(/datum/component/geiger_sound))
- REMOVE_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT)
- UnregisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION)
- for(var/obj/item/part in mod.mod_parts)
- REMOVE_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT)
-
-/obj/item/mod/module/rad_protection/add_ui_data()
- . = ..()
- .["userradiated"] = mod.wearer ? HAS_TRAIT(mod.wearer, TRAIT_IRRADIATED) : 0
- .["usertoxins"] = mod.wearer ? mod.wearer.getToxLoss() : 0
- .["threatlevel"] = perceived_threat_level
-
-/obj/item/mod/module/rad_protection/proc/on_pre_potential_irradiation(datum/source, datum/radiation_pulse_information/pulse_information, insulation_to_target)
- SIGNAL_HANDLER
-
- perceived_threat_level = get_perceived_radiation_danger(pulse_information, insulation_to_target)
- addtimer(VARSET_CALLBACK(src, perceived_threat_level, null), TIME_WITHOUT_RADIATION_BEFORE_RESET, TIMER_UNIQUE | TIMER_OVERRIDE)
-
-/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 devices on the wearer. \
- However, it will take from the suit's power to do so. Luckily, your PDA already has one of these."
- icon_state = "empshield"
- complexity = 1
- idle_power_cost = DEFAULT_CELL_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()
- mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS)
-
-/obj/item/mod/module/flashlight
- name = "MOD flashlight module"
- desc = "A simple pair of 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_CELL_DRAIN * 0.3
- incompatible_modules = list(/obj/item/mod/module/flashlight)
- cooldown_time = 0.5 SECONDS
- overlay_state_inactive = "module_light"
- light_system = MOVABLE_LIGHT_DIRECTIONAL
- light_color = COLOR_WHITE
- light_range = 3
- light_power = 1
- light_on = FALSE
- var/base_power = DEFAULT_CELL_DRAIN * 0.1
- var/min_range = 2
- var/max_range = 5
-
-/obj/item/mod/module/flashlight/on_activation()
- . = ..()
- if(!.)
- return
- set_light_flags(light_flags | LIGHT_ATTACHED)
- set_light_on(active)
- active_power_cost = base_power * light_range
-
-/obj/item/mod/module/flashlight/on_deactivation()
- . = ..()
- if(!.)
- return
- set_light_flags(light_flags & ~LIGHT_ATTACHED)
- set_light_on(active)
-
-/obj/item/mod/module/flashlight/on_process(delta_time)
- . = ..()
- if(!.)
- return
- active_power_cost = base_power * light_range
-
-/obj/item/mod/module/flashlight/generate_worn_overlay(mutable_appearance/standing)
- . = ..()
- if(!active)
- return
- var/mutable_appearance/light_icon = mutable_appearance('icons/mob/mod.dmi', "module_light_on", layer = standing.layer + 0.2)
- 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
- if(is_color_dark(value, 50))
- balloon_alert(mod.wearer, "too dark!")
- return
- set_light_color(value)
- mod.wearer.update_inv_back()
- if("light_range")
- set_light_range(clamp(value, min_range, max_range))
-
-/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_CELL_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()
- . = ..()
- 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
-
-/obj/item/mod/module/reagent_scanner/advanced/on_activation()
- . = ..()
- if(!.)
- return
- mod.wearer.research_scanner++
- RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, .proc/sense_explosion)
-
-/obj/item/mod/module/reagent_scanner/advanced/on_deactivation()
- . = ..()
- if(!.)
- return
- mod.wearer.research_scanner--
- RegisterSignal(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]"))
-
-
-/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 cell 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_CELL_DRAIN * 2
- incompatible_modules = list(/obj/item/mod/module/dispenser)
- cooldown_time = 5 SECONDS
- var/dispense_type = /obj/item/food/burger/plain
- 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
- 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)
-
-/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_ACTIVE
- complexity = 1
- active_power_cost = DEFAULT_CELL_DRAIN * 0.3
- device = /obj/item/gps/mod
- incompatible_modules = list(/obj/item/mod/module/gps)
- cooldown_time = 0.5 SECONDS
-
-/obj/item/gps/mod
- name = "MOD internal GPS"
- desc = "Common Nanotrasen technology that calcaulates the user's position from anywhere in space, down to their coordinates."
- icon_state = "gps-b"
- gpstag = "MOD0"
-
-/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 functions as an \
- extremely advanced construction hologram scanner, as well as containing 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_CELL_DRAIN * 0.2
- use_power_cost = DEFAULT_CELL_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()
- REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT)
-
-/obj/item/mod/module/constructor/on_use()
- . = ..()
- if(!.)
- return
- rcd_scan(src, fade_time = 10 SECONDS)
- drain_power(use_power_cost)
-
-/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_CELL_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_QUICK_CARRY, MOD_TRAIT)
-
-/obj/item/mod/module/quick_carry/on_suit_deactivation()
- REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT)
-
-/obj/item/mod/module/quick_carry/advanced
- name = "MOD advanced quick carry module"
- removable = FALSE
- complexity = 0
-
-/obj/item/mod/module/quick_carry/on_suit_activation()
- ADD_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT)
- ADD_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT)
-
-/obj/item/mod/module/quick_carry/on_suit_deactivation()
- REMOVE_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT)
- REMOVE_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT)
-
-/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_CELL_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()
- 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
-
-/obj/item/mod/module/thermal_regulator
- name = "MOD thermal regulator module"
- desc = "Advanced climate control, using an inner body glove interwoven with thousands of tiny, \
- flexible cooling lines. This circulates coolant at various user-controlled temperatures, \
- ensuring they're comfortable; even if they're some that like it hot."
- icon_state = "regulator"
- module_type = MODULE_TOGGLE
- complexity = 2
- active_power_cost = DEFAULT_CELL_DRAIN * 0.3
- incompatible_modules = list(/obj/item/mod/module/thermal_regulator)
- cooldown_time = 0.5 SECONDS
- var/temperature_setting = BODYTEMP_NORMAL
- var/min_temp = 293.15
- var/max_temp = 318.15
-
-/obj/item/mod/module/thermal_regulator/get_configuration()
- . = ..()
- .["temperature_setting"] = add_ui_configuration("Temperature", "number", temperature_setting - T0C)
-
-/obj/item/mod/module/thermal_regulator/configure_edit(key, value)
- switch(key)
- if("temperature_setting")
- temperature_setting = clamp(value + T0C, min_temp, max_temp)
-
-/obj/item/mod/module/thermal_regulator/on_active_process(delta_time)
- mod.wearer.adjust_bodytemperature(get_temp_change_amount((temperature_setting - mod.wearer.bodytemperature), 0.08 * delta_time))
-
-/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_CELL_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
- inject_flags = INJECT_CHECK_PENETRATE_THICK
-
-/obj/item/mod/module/circuit
- name = "MOD circuit adapter module"
- desc = "A popular aftermarket module, seen in wide varieties with wide applications by those across the galaxy. \
- This is able to fit any sort of integrated circuit, hooking it into controls in the suit and displaying information \
- to the HUD. Useful for universal translation, or perhaps as a calculator."
- module_type = MODULE_USABLE
- complexity = 3
- idle_power_cost = DEFAULT_CELL_DRAIN * 0.5
- incompatible_modules = list(/obj/item/mod/module/circuit)
- cooldown_time = 0.5 SECONDS
- var/obj/item/integrated_circuit/circuit
-
-/obj/item/mod/module/circuit/Initialize(mapload)
- . = ..()
- circuit = new()
- AddComponent(/datum/component/shell, \
- list(new /obj/item/circuit_component/mod()), \
- capacity = SHELL_CAPACITY_LARGE, \
- shell_flags = SHELL_FLAG_CIRCUIT_UNREMOVABLE, \
- starting_circuit = circuit, \
- )
-
-/obj/item/mod/module/circuit/on_install()
- circuit.set_cell(mod.cell)
-
-/obj/item/mod/module/circuit/on_uninstall()
- circuit.set_cell(mod.cell)
-
-/obj/item/mod/module/circuit/on_suit_activation()
- circuit.set_on(TRUE)
-
-/obj/item/mod/module/circuit/on_suit_deactivation()
- circuit.set_on(FALSE)
-
-/obj/item/mod/module/circuit/on_use()
- . = ..()
- if(!.)
- return
- circuit.interact(mod.wearer)
-
-/obj/item/circuit_component/mod
- display_name = "MOD"
- desc = "Used to send and receive signals from a MODsuit."
-
- var/obj/item/mod/module/attached_module
-
- var/datum/port/input/module_to_select
- var/datum/port/input/toggle_suit
- var/datum/port/input/select_module
-
- var/datum/port/output/wearer
- var/datum/port/output/selected_module
-
-/obj/item/circuit_component/mod/populate_ports()
- // Input Signals
- module_to_select = add_input_port("Module to Select", PORT_TYPE_STRING)
- toggle_suit = add_input_port("Toggle Suit", PORT_TYPE_SIGNAL)
- select_module = add_input_port("Select Module", PORT_TYPE_SIGNAL)
- // States
- wearer = add_output_port("Wearer", PORT_TYPE_ATOM)
- selected_module = add_output_port("Selected Module", PORT_TYPE_ATOM)
-
-/obj/item/circuit_component/mod/register_shell(atom/movable/shell)
- if(istype(shell, /obj/item/mod/module))
- attached_module = shell
- RegisterSignal(attached_module, COMSIG_MOVABLE_MOVED, .proc/on_move)
-
-/obj/item/circuit_component/mod/unregister_shell(atom/movable/shell)
- UnregisterSignal(attached_module, COMSIG_MOVABLE_MOVED)
- attached_module = null
-
-/obj/item/circuit_component/mod/input_received(datum/port/input/port)
- var/obj/item/mod/module/module
- for(var/obj/item/mod/module/potential_module as anything in attached_module.mod.modules)
- if(potential_module.name == module_to_select.value)
- module = potential_module
- if(COMPONENT_TRIGGERED_BY(toggle_suit, port))
- INVOKE_ASYNC(attached_module.mod, /obj/item/mod/control.proc/toggle_activate, attached_module.mod.wearer)
- if(module && COMPONENT_TRIGGERED_BY(select_module, port))
- INVOKE_ASYNC(module, /obj/item/mod/module.proc/on_select)
-
-/obj/item/circuit_component/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced)
- SIGNAL_HANDLER
- if(istype(source.loc, /obj/item/mod/control))
- RegisterSignal(source.loc, COMSIG_MOD_MODULE_SELECTED, .proc/on_module_select)
- RegisterSignal(source.loc, COMSIG_ITEM_EQUIPPED, .proc/equip_check)
- equip_check()
- else if(istype(old_loc, /obj/item/mod/control))
- UnregisterSignal(old_loc, list(COMSIG_MOD_MODULE_SELECTED, COMSIG_ITEM_EQUIPPED))
- selected_module.set_output(null)
- wearer.set_output(null)
-
-/obj/item/circuit_component/mod/proc/on_module_select()
- SIGNAL_HANDLER
- selected_module.set_output(attached_module.mod.selected_module)
-
-/obj/item/circuit_component/mod/proc/equip_check()
- SIGNAL_HANDLER
-
- if(!attached_module.mod?.wearer)
- return
- wearer.set_output(attached_module.mod.wearer)
-
-/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_CELL_DRAIN
- incompatible_modules = list(/obj/item/mod/module/clamp)
- cooldown_time = 0.5 SECONDS
- var/max_crates = 5
- 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))
- var/atom/movable/picked_crate = target
- if(length(stored_crates) >= max_crates)
- balloon_alert(mod.wearer, "too many crates!")
- return
- if(!do_after(mod.wearer, 1 SECONDS, target = target))
- balloon_alert(mod.wearer, "interrupted!")
- return
- stored_crates += picked_crate
- picked_crate.forceMove(src)
- balloon_alert(mod.wearer, "picked up [picked_crate]")
- playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
- drain_power(use_power_cost)
- else if(length(stored_crates))
- var/turf/target_turf = get_turf(target)
- if(target_turf.is_blocked_turf())
- return
- if(!do_after(mod.wearer, 1 SECONDS, target = target))
- balloon_alert(mod.wearer, "interrupted!")
- return
- if(target_turf.is_blocked_turf())
- return
- var/atom/movable/dropped_crate = pop(stored_crates)
- dropped_crate.forceMove(target_turf)
- balloon_alert(mod.wearer, "dropped [dropped_crate]")
- playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
- drain_power(use_power_cost)
-
-/obj/item/mod/module/clamp/on_uninstall()
- for(var/atom/movable/crate as anything in stored_crates)
- crate.forceMove(drop_location())
- stored_crates -= crate
-
-/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_CELL_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)
-
-/obj/item/mod/module/drill
- name = "MOD drill module"
- desc = "An integrated drill, typically extending over the user's hand. While useful for drilling through rock, \
- your drill is surely the one that both pierces and creates the heavens."
- icon_state = "drill"
- module_type = MODULE_ACTIVE
- complexity = 2
- use_power_cost = DEFAULT_CELL_DRAIN
- incompatible_modules = list(/obj/item/mod/module/drill)
- cooldown_time = 0.5 SECONDS
-
-/obj/item/mod/module/drill/on_activation()
- . = ..()
- if(!.)
- return
- RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, .proc/bump_mine)
-
-/obj/item/mod/module/drill/on_deactivation()
- . = ..()
- if(!.)
- return
- UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP)
-
-/obj/item/mod/module/drill/on_select_use(atom/target)
- . = ..()
- if(!.)
- return
- if(!mod.wearer.Adjacent(target))
- return
- if(istype(target, /turf/closed/mineral))
- var/turf/closed/mineral/mineral_turf = target
- mineral_turf.gets_drilled(mod.wearer)
- drain_power(use_power_cost)
-
-/obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity)
- SIGNAL_HANDLER
- if(!istype(bumped_into, /turf/closed/mineral) || !drain_power(use_power_cost))
- return
- var/turf/closed/mineral/mineral_turf = bumped_into
- mineral_turf.gets_drilled(mod.wearer)
- return COMPONENT_CANCEL_ATTACK_CHAIN
-
-/obj/item/mod/module/orebag
- name = "MOD ore bag module"
- desc = "An integrated ore storage system installed into the suit, \
- this utilizes precise electromagnets and storage compartments to automatically collect and deposit ore. \
- It's recommended by Nakamura Engineering to actually deposit that ore at local refineries."
- icon_state = "ore"
- module_type = MODULE_USABLE
- complexity = 2
- use_power_cost = DEFAULT_CELL_DRAIN * 0.2
- incompatible_modules = list(/obj/item/mod/module/orebag)
- cooldown_time = 0.5 SECONDS
- var/list/ores = list()
-
-/obj/item/mod/module/orebag/on_equip()
- RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/ore_pickup)
-
-/obj/item/mod/module/orebag/on_unequip()
- UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
-
-/obj/item/mod/module/orebag/proc/ore_pickup(atom/movable/source, atom/old_loc, dir, forced)
- SIGNAL_HANDLER
-
- for(var/obj/item/stack/ore/ore in get_turf(mod.wearer))
- INVOKE_ASYNC(src, .proc/move_ore, ore)
- playsound(src, "rustle", 50, TRUE)
-
-/obj/item/mod/module/orebag/proc/move_ore(obj/item/stack/ore)
- for(var/obj/item/stack/stored_ore as anything in ores)
- if(!ore.can_merge(stored_ore))
- continue
- ore.merge(stored_ore)
- if(QDELETED(ore))
- return
- break
- ore.forceMove(src)
- ores += ore
-
-/obj/item/mod/module/orebag/on_use()
- . = ..()
- if(!.)
- return
- for(var/obj/item/ore as anything in ores)
- ore.forceMove(drop_location())
- ores -= ore
- drain_power(use_power_cost)
-
-/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_CELL_DRAIN * 5
- incompatible_modules = list(/obj/item/mod/module/microwave_beam)
- cooldown_time = 10 SECONDS
-
-/obj/item/mod/module/microwave_beam/on_select_use(atom/target)
- . = ..()
- if(!.)
- return
- if(!istype(target, /obj/item))
- 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())
- 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)
-
-/obj/item/mod/module/organ_thrower
- name = "MOD organ thrower module"
- desc = "A device recovered from a crashed Interdyne Pharmaceuticals vessel, \
- this module has been unearthed for better or for worse. \
- It's an arm-mounted device utilizing technology similar to modern-day part replacers, \
- capable of storing and inserting organs into open patients. \
- It's recommended by the DeForest Medical Corporation to not inform patients it has been used."
- icon_state = "organ_thrower"
- module_type = MODULE_ACTIVE
- complexity = 2
- use_power_cost = DEFAULT_CELL_DRAIN
- incompatible_modules = list(/obj/item/mod/module/organ_thrower)
- cooldown_time = 0.5 SECONDS
- var/max_organs = 5
- var/organ_list = list()
-
-/obj/item/mod/module/organ_thrower/on_select_use(atom/target)
- . = ..()
- if(!.)
- return
- var/mob/living/carbon/human/wearer_human = mod.wearer
- if(istype(target, /obj/item/organ))
- if(!wearer_human.Adjacent(target))
- return
- var/atom/movable/organ = target
- if(length(organ_list) >= max_organs)
- balloon_alert(mod.wearer, "too many organs!")
- return
- organ_list += organ
- organ.forceMove(src)
- balloon_alert(mod.wearer, "picked up [organ]")
- playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
- drain_power(use_power_cost)
- return
- if(!length(organ_list))
- return
- var/atom/movable/fired_organ = pop(organ_list)
- var/obj/projectile/organ/projectile = new /obj/projectile/organ(mod.wearer.loc, fired_organ)
- projectile.preparePixelProjectile(target, mod.wearer)
- projectile.firer = mod.wearer
- playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
- INVOKE_ASYNC(projectile, /obj/projectile.proc/fire)
- drain_power(use_power_cost)
-
-/obj/projectile/organ
- name = "organ"
- damage = 0
- nodamage = TRUE
- hitsound = 'sound/effects/attackblob.ogg'
- hitsound_wall = 'sound/effects/attackblob.ogg'
- var/obj/item/organ/organ
-
-/obj/projectile/organ/Initialize(mapload, obj/item/stored_organ)
- . = ..()
- if(!stored_organ)
- return INITIALIZE_HINT_QDEL
- appearance = stored_organ.appearance
- stored_organ.forceMove(src)
- organ = stored_organ
-
-/obj/projectile/organ/Destroy()
- organ = null
- return ..()
-
-/obj/projectile/organ/on_hit(atom/target)
- . = ..()
- if(!ishuman(target))
- organ.forceMove(drop_location())
- organ = null
- return
- var/mob/living/carbon/human/organ_receiver = target
- var/succeed = FALSE
- if(organ_receiver.surgeries.len)
- for(var/datum/surgery/procedure as anything in organ_receiver.surgeries)
- if(procedure.location != organ.zone)
- continue
- if(!istype(procedure, /datum/surgery/organ_manipulation))
- continue
- var/datum/surgery_step/surgery_step = procedure.get_surgery_step()
- if(!istype(surgery_step, /datum/surgery_step/manipulate_organs))
- continue
- succeed = TRUE
- break
- if(succeed)
- var/list/organs_to_boot_out = organ_receiver.getorganslot(organ.slot)
- for(var/obj/item/organ/organ_evacced as anything in organs_to_boot_out)
- if(organ_evacced.organ_flags & ORGAN_UNREMOVABLE)
- continue
- organ_evacced.Remove(target)
- organ_evacced.forceMove(get_turf(target))
- organ.Insert(target)
- else
- organ.forceMove(drop_location())
- organ = null
-
-/obj/item/mod/module/pathfinder
- name = "MOD pathfinder module"
- desc = "This module, brought to you by Nakamura Engineering, has two components. \
- The first component is a series of thrusters and a computerized location subroutine installed into the \
- very control unit of the suit, allowing it flight at highway speeds, \
- and to be able to locate the second part of the system; \
- a pathfinding implant installed into the base of the user's spine, \
- broadcasting their location to the suit and allowing them to recall it to their back at any time. \
- Nakamura Engineering swears up and down there's airbrakes."
- icon_state = "pathfinder"
- complexity = 2
- use_power_cost = DEFAULT_CELL_DRAIN * 10
- incompatible_modules = list(/obj/item/mod/module/pathfinder)
- var/obj/item/implant/mod/implant
-
-/obj/item/mod/module/pathfinder/Initialize(mapload)
- . = ..()
- implant = new(src)
-
-/obj/item/mod/module/pathfinder/Destroy()
- implant = null
- return ..()
-
-/obj/item/mod/module/pathfinder/examine(mob/user)
- . = ..()
- if(implant)
- . += span_notice("Use it on a human to implant them.")
- else
- . += span_warning("The implant is missing.")
-
-/obj/item/mod/module/pathfinder/attack(mob/living/target, mob/living/user, params)
- if(!ishuman(target) || !implant)
- return
- if(!do_after(user, 1.5 SECONDS, target = target))
- balloon_alert(user, "interrupted!")
- return
- if(!implant.implant(target, user))
- balloon_alert(user, "can't implant!")
- return
- if(target == user)
- to_chat(user, span_notice("You implant yourself with [implant]."))
- else
- target.visible_message(span_notice("[user] implants [target]."), span_notice("[user] implants you with [implant]."))
- playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6)
- icon_state = "pathfinder_empty"
- implant = null
-
-/obj/item/mod/module/pathfinder/proc/attach(mob/living/user)
- if(!ishuman(user))
- return
- var/mob/living/carbon/human/human_user = user
- if(human_user.back && !human_user.dropItemToGround(human_user.back))
- return
- if(!human_user.equip_to_slot_if_possible(mod, mod.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
- return
- human_user.update_action_buttons(TRUE)
- balloon_alert(human_user, "[mod] attached")
- playsound(mod, 'sound/machines/ping.ogg', 50, TRUE)
- drain_power(use_power_cost)
-
-/obj/item/implant/mod
- name = "MOD pathfinder implant"
- desc = "Lets you recall a MODsuit to you at any time."
- actions_types = list(/datum/action/item_action/mod_recall)
- var/obj/item/mod/module/pathfinder/module
- var/image/jet_icon
-
-/obj/item/implant/mod/Initialize(mapload)
- . = ..()
- if(!istype(loc, /obj/item/mod/module/pathfinder))
- return INITIALIZE_HINT_QDEL
- module = loc
- jet_icon = image(icon = 'icons/obj/mod.dmi', icon_state = "mod_jet", layer = LOW_ITEM_LAYER)
-
-/obj/item/implant/mod/Destroy()
- if(module?.mod?.ai_controller)
- end_recall(successful = FALSE)
- module = null
- jet_icon = null
- return ..()
-
-/obj/item/implant/mod/get_data()
- var/dat = {"Implant Specifications:
- Name: Nakamura Engineering Pathfinder Implant
- Implant Details: Allows for the recall of a Modular Outerwear Device by the implant owner at any time.
"}
- return dat
-
-/obj/item/implant/mod/proc/recall()
- if(!module?.mod)
- balloon_alert(imp_in, "no connected suit!")
- return FALSE
- if(module.mod.open)
- balloon_alert(imp_in, "suit is open!")
- return FALSE
- if(module.mod.ai_controller)
- balloon_alert(imp_in, "already in transit!")
- return FALSE
- if(ismob(get_atom_on_turf(module.mod)))
- balloon_alert(imp_in, "already on someone!")
- return FALSE
- if(module.z != z || get_dist(imp_in, module.mod) > MOD_AI_RANGE)
- balloon_alert(imp_in, "too far away!")
- return FALSE
- var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod)
- module.mod.ai_controller = mod_ai
- mod_ai.current_movement_target = imp_in
- mod_ai.blackboard[BB_MOD_TARGET] = imp_in
- mod_ai.blackboard[BB_MOD_IMPLANT] = src
- module.mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
- module.mod.AddElement(/datum/element/movetype_handler)
- ADD_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
- animate(module.mod, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y)
- module.mod.add_overlay(jet_icon)
- RegisterSignal(module.mod, COMSIG_MOVABLE_MOVED, .proc/on_move)
- balloon_alert(imp_in, "suit recalled")
- return TRUE
-
-/obj/item/implant/mod/proc/end_recall(successful = TRUE)
- if(!module?.mod)
- return
- QDEL_NULL(module.mod.ai_controller)
- module.mod.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
- REMOVE_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
- module.mod.RemoveElement(/datum/element/movetype_handler)
- module.mod.cut_overlay(jet_icon)
- module.mod.transform = matrix()
- UnregisterSignal(module.mod, COMSIG_MOVABLE_MOVED)
- if(!successful)
- balloon_alert(imp_in, "suit lost connection!")
-
-/obj/item/implant/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced)
- SIGNAL_HANDLER
-
- var/matrix/mod_matrix = matrix()
- mod_matrix.Turn(get_angle(source, imp_in))
- source.transform = mod_matrix
-
-/datum/action/item_action/mod_recall
- name = "Recall MOD"
- desc = "Recall a MODsuit anyplace, anytime."
- check_flags = AB_CHECK_CONSCIOUS
- background_icon_state = "bg_tech_blue"
- icon_icon = 'icons/mob/actions/actions_mod.dmi'
- button_icon_state = "recall"
- COOLDOWN_DECLARE(recall_cooldown)
- var/obj/item/implant/mod/implant
-
-/datum/action/item_action/mod_recall/New(Target)
- ..()
- implant = Target
-
-/datum/action/item_action/mod_recall/Trigger()
- . = ..()
- if(!.)
- return
- if(!COOLDOWN_FINISHED(src, recall_cooldown))
- implant.balloon_alert(implant.imp_in, "on cooldown!")
- return
- if(implant.recall())
- COOLDOWN_START(src, recall_cooldown, 15 SECONDS)
-
-/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_CELL_DRAIN * 3
- incompatible_modules = list(/obj/item/mod/module/dna_lock)
- cooldown_time = 0.5 SECONDS
- var/dna = null
-
-/obj/item/mod/module/dna_lock/on_install()
- RegisterSignal(mod, COMSIG_MOD_ACTIVATE, .proc/on_mod_activation)
- 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()
- UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
- 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/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)
- SIGNAL_HANDLER
-
- if(!dna || (mod.wearer.has_dna() && mod.wearer.dna.unique_enzymes == dna))
- return
- balloon_alert(mod.wearer, "dna locked!")
- return MOD_CANCEL_ACTIVATE
-
-/obj/item/mod/module/armor_booster
- name = "MOD armor booster module"
- desc = "A retrofitted series of retractable armor plates, allowing the suit to function as essentially power armor, \
- giving the user incredible protection against conventional firearms, or everyday attacks in close-quarters. \
- However, the additional plating cannot deploy alongside parts of the suit used for vacuum sealing, \
- so this extra armor provides zero ability for extravehicular activity while deployed."
- icon_state = "armor_booster"
- module_type = MODULE_TOGGLE
- active_power_cost = DEFAULT_CELL_DRAIN * 0.3
- removable = FALSE
- incompatible_modules = list(/obj/item/mod/module/armor_booster)
- cooldown_time = 0.5 SECONDS
- overlay_state_inactive = "module_armorbooster_off"
- overlay_state_active = "module_armorbooster_on"
- var/remove_pressure_protection = TRUE
- var/added_slowdown = -0.5
- var/list/armor_values = list(MELEE = 40, BULLET = 50, LASER = 30, ENERGY = 40)
- var/list/spaceproofed = list()
-
-/obj/item/mod/module/armor_booster/generate_worn_overlay(mutable_appearance/standing)
- overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
- overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]"
- return ..()
-
-/obj/item/mod/module/armor_booster/on_activation()
- . = ..()
- if(!.)
- return
- playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- mod.slowdown += added_slowdown
- mod.wearer.update_equipment_speed_mods()
- var/list/parts = mod.mod_parts + mod
- for(var/obj/item/part as anything in parts)
- part.armor = part.armor.modifyRating(arglist(armor_values))
- if(!remove_pressure_protection || !isclothing(part))
- continue
- var/obj/item/clothing/clothing_part = part
- if(clothing_part.clothing_flags & STOPSPRESSUREDAMAGE)
- clothing_part.clothing_flags &= ~STOPSPRESSUREDAMAGE
- clothing_part.heat_protection = NONE
- clothing_part.cold_protection = NONE
- spaceproofed[clothing_part] = TRUE
-
-/obj/item/mod/module/armor_booster/on_deactivation()
- . = ..()
- if(!.)
- return
- playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- mod.slowdown -= added_slowdown
- mod.wearer.update_equipment_speed_mods()
- var/list/parts = mod.mod_parts + mod
- var/list/removed_armor = armor_values.Copy()
- for(var/armor_type in removed_armor)
- removed_armor[armor_type] = -removed_armor[armor_type]
- for(var/obj/item/part as anything in parts)
- part.armor = part.armor.modifyRating(arglist(removed_armor))
- if(!remove_pressure_protection || !isclothing(part))
- continue
- var/obj/item/clothing/clothing_part = part
- if(spaceproofed[clothing_part])
- clothing_part.clothing_flags |= STOPSPRESSUREDAMAGE
- clothing_part.heat_protection = initial(clothing_part.heat_protection)
- clothing_part.cold_protection = initial(clothing_part.cold_protection)
- spaceproofed = list()
-
-/obj/item/mod/module/armor_booster/elite
- name = "MOD elite armor booster module"
- armor_values = list(MELEE = 60, BULLET = 60, LASER = 50, ENERGY = 60)
- added_slowdown = -0.25
-
-/obj/item/mod/module/energy_shield
- name = "MOD energy shield module"
- desc = "A personal, protective forcefield typically seen in military applications. \
- This advanced deflector shield is essentially a scaled down version of those seen on starships, \
- and the power cost can be an easy indicator of this. However, it is capable of blocking nearly any incoming attack, \
- though with its' low amount of separate charges, the user remains mortal."
- icon_state = "energy_shield"
- complexity = 3
- idle_power_cost = DEFAULT_CELL_DRAIN * 0.5
- use_power_cost = DEFAULT_CELL_DRAIN * 2
- incompatible_modules = list(/obj/item/mod/module/energy_shield)
- var/max_charges = 3
- var/recharge_start_delay = 20 SECONDS
- var/charge_increment_delay = 1 SECONDS
- var/charge_recovery = 1
- var/lose_multiple_charges = FALSE
- var/recharge_path = null
- var/shield_icon_file = 'icons/effects/effects.dmi'
- var/shield_icon = "shield-red"
- var/charges
-
-/obj/item/mod/module/energy_shield/Initialize(mapload)
- . = ..()
- charges = max_charges
-
-/obj/item/mod/module/energy_shield/on_suit_activation()
- mod.AddComponent(/datum/component/shielded, max_charges = max_charges, recharge_start_delay = recharge_start_delay, charge_increment_delay = charge_increment_delay, \
- charge_recovery = charge_recovery, lose_multiple_charges = lose_multiple_charges, recharge_path = recharge_path, starting_charges = charges, shield_icon_file = shield_icon_file, shield_icon = shield_icon)
- RegisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS, .proc/shield_reaction)
-
-/obj/item/mod/module/energy_shield/on_suit_deactivation()
- var/datum/component/shielded/shield = mod.GetComponent(/datum/component/shielded)
- charges = shield.current_charges
- qdel(shield)
- UnregisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS)
-
-/obj/item/mod/module/energy_shield/proc/shield_reaction(mob/living/carbon/human/owner, atom/movable/hitby, damage = 0, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0)
- if(SEND_SIGNAL(mod, COMSIG_ITEM_HIT_REACT, owner, hitby, attack_text, 0, damage, attack_type) & COMPONENT_HIT_REACTION_BLOCK)
- drain_power(use_power_cost)
- return SHIELD_BLOCK
- return NONE
-
-/obj/item/mod/module/energy_shield/wizard
- name = "MOD battlemage shield module"
- desc = "The caster wielding this spell gains a visible barrier around them, channeling arcane power through \
- specialized runes engraved onto the surface of the suit to generate a wall of force. \
- This shield can perfectly nullify attacks ranging from high-caliber rifles to magic missiles, \
- though can also be drained by more mundane attacks. It will not protect the caster from social ridicule."
- icon_state = "battlemage_shield"
- idle_power_cost = DEFAULT_CELL_DRAIN * 0 //magic
- use_power_cost = DEFAULT_CELL_DRAIN * 0 //magic too
- max_charges = 15
- recharge_start_delay = 0 SECONDS
- charge_recovery = 8
- shield_icon_file = 'icons/effects/magic.dmi'
- shield_icon = "mageshield"
- recharge_path = /obj/item/wizard_armour_charge
-
-/obj/item/mod/module/plasma_stabilizer
- name = "MOD plasma stabilizer module"
- desc = "This system essentially forms an atmosphere of its' own inside the suit, \
- safely ejecting oxygen from the inside and allowing the wearer, a plasmaman, \
- to have their internal plasma circulate around them somewhat like a sauna. \
- This prevents them from self-igniting, and leads to greater comfort overall. \
- The purple glass of the visor seems to be constructed for nostalgic purposes."
- icon_state = "plasma_stabilizer"
- complexity = 1
- idle_power_cost = DEFAULT_CELL_DRAIN * 0.3
- incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer)
- overlay_state_inactive = "module_plasma"
-
-/obj/item/mod/module/plasma_stabilizer/on_equip()
- ADD_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION, MOD_TRAIT)
-
-/obj/item/mod/module/plasma_stabilizer/on_unequip()
- REMOVE_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION, MOD_TRAIT)
-
-/obj/item/mod/module/anti_magic
- name = "MOD magic nullifier module"
- desc = "A series of obsidian rods installed into critical points around the suit, \
- vibrated at a certain low frequency to enable them to resonate. \
- This creates a low-range, yet strong, magic nullification field around the user, \
- aided by a full replacement of the suit's normal coolant with holy water. \
- Spells will spall right off this field, though it'll do nothing to help others believe you about all this."
- 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)
-
-/obj/item/mod/module/anti_magic/on_suit_deactivation()
- REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, MOD_TRAIT)
- REMOVE_TRAIT(mod.wearer, TRAIT_HOLY, MOD_TRAIT)
-
-/obj/item/mod/module/anti_magic/wizard
- name = "MOD magic neutralizer module"
- desc = "The caster wielding this spell gains an invisible barrier around them, channeling arcane power through \
- specialized runes engraved onto the surface of the suit to generate anti-magic field. \
- The field will neutralize all magic that comes into contact with the user. \
- It will not protect the caster from social ridicule."
- icon_state = "magic_neutralizer"
-
-/obj/item/mod/module/anti_magic/wizard/on_suit_activation()
- ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT)
-
-/obj/item/mod/module/anti_magic/wizard/on_suit_deactivation()
- REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT)
-
-/obj/item/mod/module/kinesis //TODO POST-MERGE MAKE NOT SUCK ASS, MAKE BALLER AS FUCK
- name = "MOD kinesis module"
- desc = "A modular plug-in to the forearm, this module was presumed lost for many years, \
- despite the suits it used to be mounted on still seeing some circulation. \
- This piece of technology allows the user to generate precise anti-gravity fields, \
- letting them move objects as small as a titanium rod to as large as industrial machinery. \
- Oddly enough, it doesn't seem to work on living creatures."
- icon_state = "kinesis"
-// module_type = MODULE_ACTIVE
- module_type = MODULE_TOGGLE
-// complexity = 3
- complexity = 0
- active_power_cost = DEFAULT_CELL_DRAIN*0.75
-// use_power_cost = DEFAULT_CELL_DRAIN*3
- removable = FALSE
- incompatible_modules = list(/obj/item/mod/module/kinesis)
- cooldown_time = 0.5 SECONDS
- var/has_tk = FALSE
-
-/obj/item/mod/module/kinesis/on_activation()
- . = ..()
- if(!.)
- return
- if(mod.wearer.dna.check_mutation(TK))
- has_tk = TRUE
- else
- mod.wearer.dna.add_mutation(TK)
-
-/obj/item/mod/module/kinesis/on_deactivation()
- . = ..()
- if(!.)
- return
- if(has_tk)
- has_tk = FALSE
- return
- mod.wearer.dna.remove_mutation(TK)
-
-/obj/item/mod/module/insignia
- name = "MOD insignia module"
- desc = "Despite the existence of IFF systems, radio communique, and modern methods of deductive reasoning involving \
- the wearer's own eyes, colorful paint jobs remain a popular way for different factions in the galaxy to display who \
- they are. This system utilizes a series of tiny moving paint sprayers to both apply and remove different \
- color patterns to and from the suit."
- icon_state = "insignia"
- removable = FALSE
- incompatible_modules = list(/obj/item/mod/module/insignia)
- overlay_state_inactive = "insignia"
-
-/obj/item/mod/module/insignia/generate_worn_overlay(mutable_appearance/standing)
- overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
- . = ..()
- for(var/mutable_appearance/appearance as anything in .)
- appearance.color = color
-
-/obj/item/mod/module/insignia/commander
- color = "#4980a5"
-
-/obj/item/mod/module/insignia/security
- color = "#b30d1e"
-
-/obj/item/mod/module/insignia/engineer
- color = "#e9c80e"
-
-/obj/item/mod/module/insignia/medic
- color = "#ebebf5"
-
-/obj/item/mod/module/insignia/janitor
- color = "#7925c7"
-
-/obj/item/mod/module/insignia/clown
- color = "#ff1fc7"
-
-/obj/item/mod/module/insignia/chaplain
- color = "#f0a00c"
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
new file mode 100644
index 00000000000..4412b1dea05
--- /dev/null
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -0,0 +1,208 @@
+//Antag modules for MODsuits
+
+//Armor Booster
+
+/obj/item/mod/module/armor_booster
+ name = "MOD armor booster module"
+ desc = "A retrofitted series of retractable armor plates, allowing the suit to function as essentially power armor, \
+ giving the user incredible protection against conventional firearms, or everyday attacks in close-quarters. \
+ However, the additional plating cannot deploy alongside parts of the suit used for vacuum sealing, \
+ so this extra armor provides zero ability for extravehicular activity while deployed."
+ icon_state = "armor_booster"
+ module_type = MODULE_TOGGLE
+ active_power_cost = DEFAULT_CELL_DRAIN * 0.3
+ removable = FALSE
+ incompatible_modules = list(/obj/item/mod/module/armor_booster)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_armorbooster_off"
+ overlay_state_active = "module_armorbooster_on"
+ var/remove_pressure_protection = TRUE
+ var/added_slowdown = -0.5
+ var/list/armor_values = list(MELEE = 40, BULLET = 50, LASER = 30, ENERGY = 40)
+ var/list/spaceproofed = list()
+
+/obj/item/mod/module/armor_booster/generate_worn_overlay(mutable_appearance/standing)
+ overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
+ overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]"
+ return ..()
+
+/obj/item/mod/module/armor_booster/on_activation()
+ . = ..()
+ if(!.)
+ return
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ mod.slowdown += added_slowdown
+ mod.wearer.update_equipment_speed_mods()
+ var/list/parts = mod.mod_parts + mod
+ for(var/obj/item/part as anything in parts)
+ part.armor = part.armor.modifyRating(arglist(armor_values))
+ if(!remove_pressure_protection || !isclothing(part))
+ continue
+ var/obj/item/clothing/clothing_part = part
+ if(clothing_part.clothing_flags & STOPSPRESSUREDAMAGE)
+ clothing_part.clothing_flags &= ~STOPSPRESSUREDAMAGE
+ clothing_part.heat_protection = NONE
+ clothing_part.cold_protection = NONE
+ spaceproofed[clothing_part] = TRUE
+
+/obj/item/mod/module/armor_booster/on_deactivation()
+ . = ..()
+ if(!.)
+ return
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ mod.slowdown -= added_slowdown
+ mod.wearer.update_equipment_speed_mods()
+ var/list/parts = mod.mod_parts + mod
+ var/list/removed_armor = armor_values.Copy()
+ for(var/armor_type in removed_armor)
+ removed_armor[armor_type] = -removed_armor[armor_type]
+ for(var/obj/item/part as anything in parts)
+ part.armor = part.armor.modifyRating(arglist(removed_armor))
+ if(!remove_pressure_protection || !isclothing(part))
+ continue
+ var/obj/item/clothing/clothing_part = part
+ if(spaceproofed[clothing_part])
+ clothing_part.clothing_flags |= STOPSPRESSUREDAMAGE
+ clothing_part.heat_protection = initial(clothing_part.heat_protection)
+ clothing_part.cold_protection = initial(clothing_part.cold_protection)
+ spaceproofed = list()
+
+/obj/item/mod/module/armor_booster/elite
+ name = "MOD elite armor booster module"
+ armor_values = list(MELEE = 60, BULLET = 60, LASER = 50, ENERGY = 60)
+ added_slowdown = -0.25
+
+//Energy Shield
+
+/obj/item/mod/module/energy_shield
+ name = "MOD energy shield module"
+ desc = "A personal, protective forcefield typically seen in military applications. \
+ This advanced deflector shield is essentially a scaled down version of those seen on starships, \
+ and the power cost can be an easy indicator of this. However, it is capable of blocking nearly any incoming attack, \
+ though with its' low amount of separate charges, the user remains mortal."
+ icon_state = "energy_shield"
+ complexity = 3
+ idle_power_cost = DEFAULT_CELL_DRAIN * 0.5
+ use_power_cost = DEFAULT_CELL_DRAIN * 2
+ incompatible_modules = list(/obj/item/mod/module/energy_shield)
+ var/max_charges = 3
+ var/recharge_start_delay = 20 SECONDS
+ var/charge_increment_delay = 1 SECONDS
+ var/charge_recovery = 1
+ var/lose_multiple_charges = FALSE
+ var/recharge_path = null
+ var/shield_icon_file = 'icons/effects/effects.dmi'
+ var/shield_icon = "shield-red"
+ var/charges
+
+/obj/item/mod/module/energy_shield/Initialize(mapload)
+ . = ..()
+ charges = max_charges
+
+/obj/item/mod/module/energy_shield/on_suit_activation()
+ mod.AddComponent(/datum/component/shielded, max_charges = max_charges, recharge_start_delay = recharge_start_delay, charge_increment_delay = charge_increment_delay, \
+ charge_recovery = charge_recovery, lose_multiple_charges = lose_multiple_charges, recharge_path = recharge_path, starting_charges = charges, shield_icon_file = shield_icon_file, shield_icon = shield_icon)
+ RegisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS, .proc/shield_reaction)
+
+/obj/item/mod/module/energy_shield/on_suit_deactivation()
+ var/datum/component/shielded/shield = mod.GetComponent(/datum/component/shielded)
+ charges = shield.current_charges
+ qdel(shield)
+ UnregisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS)
+
+/obj/item/mod/module/energy_shield/proc/shield_reaction(mob/living/carbon/human/owner, atom/movable/hitby, damage = 0, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0)
+ if(SEND_SIGNAL(mod, COMSIG_ITEM_HIT_REACT, owner, hitby, attack_text, 0, damage, attack_type) & COMPONENT_HIT_REACTION_BLOCK)
+ drain_power(use_power_cost)
+ return SHIELD_BLOCK
+ return NONE
+
+/obj/item/mod/module/energy_shield/wizard
+ name = "MOD battlemage shield module"
+ desc = "The caster wielding this spell gains a visible barrier around them, channeling arcane power through \
+ specialized runes engraved onto the surface of the suit to generate a wall of force. \
+ This shield can perfectly nullify attacks ranging from high-caliber rifles to magic missiles, \
+ though can also be drained by more mundane attacks. It will not protect the caster from social ridicule."
+ icon_state = "battlemage_shield"
+ idle_power_cost = DEFAULT_CELL_DRAIN * 0 //magic
+ use_power_cost = DEFAULT_CELL_DRAIN * 0 //magic too
+ max_charges = 15
+ recharge_start_delay = 0 SECONDS
+ charge_recovery = 8
+ shield_icon_file = 'icons/effects/magic.dmi'
+ shield_icon = "mageshield"
+ recharge_path = /obj/item/wizard_armour_charge
+
+//Magic Nullifier
+
+/obj/item/mod/module/anti_magic
+ name = "MOD magic nullifier module"
+ desc = "A series of obsidian rods installed into critical points around the suit, \
+ vibrated at a certain low frequency to enable them to resonate. \
+ This creates a low-range, yet strong, magic nullification field around the user, \
+ aided by a full replacement of the suit's normal coolant with holy water. \
+ Spells will spall right off this field, though it'll do nothing to help others believe you about all this."
+ 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)
+
+/obj/item/mod/module/anti_magic/on_suit_deactivation()
+ REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, MOD_TRAIT)
+ REMOVE_TRAIT(mod.wearer, TRAIT_HOLY, MOD_TRAIT)
+
+/obj/item/mod/module/anti_magic/wizard
+ name = "MOD magic neutralizer module"
+ desc = "The caster wielding this spell gains an invisible barrier around them, channeling arcane power through \
+ specialized runes engraved onto the surface of the suit to generate anti-magic field. \
+ The field will neutralize all magic that comes into contact with the user. \
+ It will not protect the caster from social ridicule."
+ icon_state = "magic_neutralizer"
+
+/obj/item/mod/module/anti_magic/wizard/on_suit_activation()
+ ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT)
+
+/obj/item/mod/module/anti_magic/wizard/on_suit_deactivation()
+ REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT)
+
+//Insignia
+
+/obj/item/mod/module/insignia
+ name = "MOD insignia module"
+ desc = "Despite the existence of IFF systems, radio communique, and modern methods of deductive reasoning involving \
+ the wearer's own eyes, colorful paint jobs remain a popular way for different factions in the galaxy to display who \
+ they are. This system utilizes a series of tiny moving paint sprayers to both apply and remove different \
+ color patterns to and from the suit."
+ icon_state = "insignia"
+ removable = FALSE
+ incompatible_modules = list(/obj/item/mod/module/insignia)
+ overlay_state_inactive = "insignia"
+
+/obj/item/mod/module/insignia/generate_worn_overlay(mutable_appearance/standing)
+ overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
+ . = ..()
+ for(var/mutable_appearance/appearance as anything in .)
+ appearance.color = color
+
+/obj/item/mod/module/insignia/commander
+ color = "#4980a5"
+
+/obj/item/mod/module/insignia/security
+ color = "#b30d1e"
+
+/obj/item/mod/module/insignia/engineer
+ color = "#e9c80e"
+
+/obj/item/mod/module/insignia/medic
+ color = "#ebebf5"
+
+/obj/item/mod/module/insignia/janitor
+ color = "#7925c7"
+
+/obj/item/mod/module/insignia/clown
+ color = "#ff1fc7"
+
+/obj/item/mod/module/insignia/chaplain
+ color = "#f0a00c"
diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm
new file mode 100644
index 00000000000..294c2a21861
--- /dev/null
+++ b/code/modules/mod/modules/modules_engineering.dm
@@ -0,0 +1,207 @@
+//Engineering modules for MODsuits
+
+//Welding Protection
+
+/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 = FLASH_PROTECTION_WELDER
+
+/obj/item/mod/module/welding/on_suit_deactivation()
+ mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
+
+//T-Ray Scan
+
+/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 = 2
+ active_power_cost = DEFAULT_CELL_DRAIN * 0.2
+ incompatible_modules = list(/obj/item/mod/module/t_ray)
+ cooldown_time = 0.5 SECONDS
+ var/range = 2
+
+/obj/item/mod/module/t_ray/on_active_process(delta_time)
+ t_ray_scan(mod.wearer, 8, range)
+
+//Magnetic Stability
+
+/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_CELL_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/magboot)
+ cooldown_time = 0.5 SECONDS
+ 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()
+ . = ..()
+ 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
+
+//Emergency Tether
+
+/obj/item/mod/module/tether
+ name = "MOD emergency tether module"
+ desc = "A custom-built grappling-hook powered by a winch capable of hauling the user. \
+ While some older models of cargo-oriented grapples have capacities of a few tons, \
+ these are only capable of working in zero-gravity environments, a blessing to some Engineers."
+ icon_state = "tether"
+ module_type = MODULE_ACTIVE
+ complexity = 3
+ use_power_cost = DEFAULT_CELL_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/tether)
+ cooldown_time = 1.5 SECONDS
+
+/obj/item/mod/module/tether/on_use()
+ if(mod.wearer.has_gravity(get_turf(src)))
+ balloon_alert(mod.wearer, "too much gravity!")
+ playsound(src, 'sound/weapons/gun/general/dry_fire.ogg', 25, TRUE)
+ return FALSE
+ return ..()
+
+/obj/item/mod/module/tether/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ var/obj/projectile/tether = new /obj/projectile/tether(mod.wearer.loc)
+ tether.preparePixelProjectile(target, mod.wearer)
+ tether.firer = mod.wearer
+ INVOKE_ASYNC(tether, /obj/projectile.proc/fire)
+ drain_power(use_power_cost)
+
+/obj/projectile/tether
+ name = "tether"
+ icon_state = "tether_projectile"
+ icon = 'icons/obj/mod.dmi'
+ pass_flags = PASSTABLE
+ damage = 0
+ nodamage = TRUE
+ range = 10
+ hitsound = 'sound/weapons/batonextend.ogg'
+ hitsound_wall = 'sound/weapons/batonextend.ogg'
+ suppressed = SUPPRESSED_VERY
+ hit_threshhold = LATTICE_LAYER
+ var/line
+
+/obj/projectile/tether/fire(setAngle)
+ if(firer)
+ line = firer.Beam(src, "line", 'icons/obj/mod.dmi')
+ ..()
+
+/obj/projectile/tether/on_hit(atom/target)
+ . = ..()
+ if(firer)
+ firer.throw_at(target, 10, 1, firer, FALSE, FALSE, null, MOVE_FORCE_NORMAL, TRUE)
+
+/obj/projectile/tether/Destroy()
+ QDEL_NULL(line)
+ return ..()
+
+//Radiation Protection
+
+/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; \
+ a common danger in space. This comes with software to notify the wearer that they're even in a radioactive area, \
+ giving a voice to an otherwise silent killer."
+ icon_state = "radshield"
+ complexity = 2
+ idle_power_cost = DEFAULT_CELL_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/rad_protection)
+ tgui_id = "rad_counter"
+ var/perceived_threat_level
+
+/obj/item/mod/module/rad_protection/on_suit_activation()
+ AddComponent(/datum/component/geiger_sound)
+ ADD_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT)
+ RegisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION, .proc/on_pre_potential_irradiation)
+ for(var/obj/item/part in mod.mod_parts)
+ ADD_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT)
+
+/obj/item/mod/module/rad_protection/on_suit_deactivation()
+ qdel(GetComponent(/datum/component/geiger_sound))
+ REMOVE_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT)
+ UnregisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION)
+ for(var/obj/item/part in mod.mod_parts)
+ REMOVE_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT)
+
+/obj/item/mod/module/rad_protection/add_ui_data()
+ . = ..()
+ .["userradiated"] = mod.wearer ? HAS_TRAIT(mod.wearer, TRAIT_IRRADIATED) : 0
+ .["usertoxins"] = mod.wearer ? mod.wearer.getToxLoss() : 0
+ .["threatlevel"] = perceived_threat_level
+
+/obj/item/mod/module/rad_protection/proc/on_pre_potential_irradiation(datum/source, datum/radiation_pulse_information/pulse_information, insulation_to_target)
+ SIGNAL_HANDLER
+
+ perceived_threat_level = get_perceived_radiation_danger(pulse_information, insulation_to_target)
+ addtimer(VARSET_CALLBACK(src, perceived_threat_level, null), TIME_WITHOUT_RADIATION_BEFORE_RESET, TIMER_UNIQUE | TIMER_OVERRIDE)
+
+//Constructor
+
+/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 functions as an \
+ extremely advanced construction hologram scanner, as well as containing 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_CELL_DRAIN * 0.2
+ use_power_cost = DEFAULT_CELL_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()
+ REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT)
+
+/obj/item/mod/module/constructor/on_use()
+ . = ..()
+ if(!.)
+ return
+ rcd_scan(src, fade_time = 10 SECONDS)
+ drain_power(use_power_cost)
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
new file mode 100644
index 00000000000..0aa781070a6
--- /dev/null
+++ b/code/modules/mod/modules/modules_general.dm
@@ -0,0 +1,657 @@
+//General modules for MODsuits
+
+//Storage
+
+/obj/item/mod/module/storage
+ name = "MOD storage 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)
+ var/datum/component/storage/concrete/storage
+ var/max_w_class = WEIGHT_CLASS_NORMAL
+ var/max_combined_w_class = 15
+ var/max_items = 7
+
+/obj/item/mod/module/storage/Initialize(mapload)
+ . = ..()
+ storage = AddComponent(/datum/component/storage/concrete)
+ storage.max_w_class = max_w_class
+ storage.max_combined_w_class = max_combined_w_class
+ storage.max_items = max_items
+ storage.allow_big_nesting = TRUE
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
+
+/obj/item/mod/module/storage/on_install()
+ var/datum/component/storage/modstorage = mod.AddComponent(/datum/component/storage, storage)
+ modstorage.max_w_class = max_w_class
+ modstorage.max_combined_w_class = max_combined_w_class
+ modstorage.max_items = max_items
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE)
+
+/obj/item/mod/module/storage/on_uninstall()
+ var/datum/component/storage/modstorage = mod.GetComponent(/datum/component/storage)
+ storage.slaves -= modstorage
+ qdel(modstorage)
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)
+
+/obj/item/mod/module/storage/large_capacity
+ name = "MOD expanded storage module"
+ desc = "Reverse engineered by Nakamura Engineering from Donk Corporation designs, this system of hidden compartments \
+ is entirely within the suit, distributing items and weight evenly to ensure a comfortable experience for the user; \
+ whether smuggling, or simply hauling."
+ icon_state = "storage_large"
+ max_combined_w_class = 21
+ max_items = 14
+
+/obj/item/mod/module/storage/syndicate
+ name = "MOD syndicate storage module"
+ desc = "A storage system using nanotechnology developed by Cybersun Industries, these compartments use \
+ esoteric technology to compress the physical matter of items put inside of them, \
+ essentially shrinking items for much easier and more portable storage."
+ icon_state = "storage_syndi"
+ max_combined_w_class = 30
+ max_items = 21
+
+/obj/item/mod/module/storage/bluespace
+ name = "MOD bluespace storage module"
+ desc = "A storage system developed by Nanotrasen, these compartments employ \
+ miniaturized bluespace pockets for the ultimate in storage technology; regardless of the weight of objects put inside."
+ icon_state = "storage_large"
+ max_w_class = WEIGHT_CLASS_GIGANTIC
+ max_combined_w_class = 60
+ max_items = 21
+
+
+//Ion Jetpack
+
+/obj/item/mod/module/jetpack
+ name = "MOD ion jetpack module"
+ desc = "A series of electric thrusters installed across the suit, this is a module highly anticipated by trainee Engineers. \
+ Rather than using gasses for combustion thrust, these jets are capable of accelerating ions using \
+ charge from the suit's cell. Some say this isn't Nakamura Engineering's first foray into jet-enabled suits."
+ icon_state = "jetpack"
+ module_type = MODULE_TOGGLE
+ complexity = 3
+ active_power_cost = DEFAULT_CELL_DRAIN * 0.5
+ use_power_cost = DEFAULT_CELL_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/jetpack)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_jetpack"
+ overlay_state_active = "module_jetpack_on"
+ var/stabilizers = FALSE
+ var/full_speed = FALSE
+ var/datum/effect_system/trail_follow/ion/ion_trail
+
+/obj/item/mod/module/jetpack/Initialize(mapload)
+ . = ..()
+ ion_trail = new
+ ion_trail.auto_process = FALSE
+ ion_trail.set_up(src)
+
+/obj/item/mod/module/jetpack/Destroy()
+ QDEL_NULL(ion_trail)
+ return ..()
+
+/obj/item/mod/module/jetpack/on_activation()
+ . = ..()
+ if(!.)
+ return
+ ion_trail.start()
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/move_react)
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_PRE_MOVE, .proc/pre_move_react)
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_SPACEMOVE, .proc/spacemove_react)
+ if(full_speed)
+ mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
+
+/obj/item/mod/module/jetpack/on_deactivation(mob/user)
+ . = ..()
+ if(!.)
+ return
+ ion_trail.stop()
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_PRE_MOVE)
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_SPACEMOVE)
+ if(full_speed)
+ mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
+
+/obj/item/mod/module/jetpack/get_configuration()
+ . = ..()
+ .["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers)
+
+/obj/item/mod/module/jetpack/configure_edit(key, value)
+ switch(key)
+ if("stabilizers")
+ stabilizers = text2num(value)
+
+/obj/item/mod/module/jetpack/proc/move_react(mob/user)
+ SIGNAL_HANDLER
+
+ if(!active)//If jet dont work, it dont work
+ return
+ if(!isturf(mod.wearer.loc))//You can't use jet in nowhere or from mecha/closet
+ return
+ if(!(mod.wearer.movement_type & FLOATING) || mod.wearer.buckled)//You don't want use jet in gravity or while buckled.
+ return
+ if(mod.wearer.pulledby)//You don't must use jet if someone pull you
+ return
+ if(mod.wearer.throwing)//You don't must use jet if you thrown
+ return
+ if(user.client && length(user.client.keys_held & user.client.movement_keys))//You use jet when press keys. yes.
+ allow_thrust()
+
+/obj/item/mod/module/jetpack/proc/pre_move_react(mob/user)
+ SIGNAL_HANDLER
+
+ ion_trail.oldposition = get_turf(src)
+
+/obj/item/mod/module/jetpack/proc/spacemove_react(mob/user, movement_dir)
+ SIGNAL_HANDLER
+
+ if(active && (stabilizers || movement_dir))
+ return COMSIG_MOVABLE_STOP_SPACEMOVE
+
+/obj/item/mod/module/jetpack/proc/allow_thrust()
+ if(!drain_power(use_power_cost))
+ return
+ ion_trail.generate_effect()
+ return TRUE
+
+//Eating Apparatus
+
+/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 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"
+ var/former_flags = NONE
+ 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()
+ if(!(former_flags & HEADCOVERSMOUTH))
+ mod.helmet.flags_cover |= HEADCOVERSMOUTH
+ if(!(former_visor_flags & HEADCOVERSMOUTH))
+ mod.helmet.visor_flags_cover |= HEADCOVERSMOUTH
+
+//EMP Shield
+
+/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 devices on the wearer. \
+ However, it will take from the suit's power to do so. Luckily, your PDA already has one of these."
+ icon_state = "empshield"
+ complexity = 1
+ idle_power_cost = DEFAULT_CELL_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()
+ mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS)
+
+//Flashlight
+
+/obj/item/mod/module/flashlight
+ name = "MOD flashlight module"
+ desc = "A simple pair of 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_CELL_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/flashlight)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_light"
+ light_system = MOVABLE_LIGHT_DIRECTIONAL
+ light_color = COLOR_WHITE
+ light_range = 3
+ light_power = 1
+ light_on = FALSE
+ var/base_power = DEFAULT_CELL_DRAIN * 0.1
+ var/min_range = 2
+ var/max_range = 5
+
+/obj/item/mod/module/flashlight/on_activation()
+ . = ..()
+ if(!.)
+ return
+ set_light_flags(light_flags | LIGHT_ATTACHED)
+ set_light_on(active)
+ active_power_cost = base_power * light_range
+
+/obj/item/mod/module/flashlight/on_deactivation()
+ . = ..()
+ if(!.)
+ return
+ set_light_flags(light_flags & ~LIGHT_ATTACHED)
+ set_light_on(active)
+
+/obj/item/mod/module/flashlight/on_process(delta_time)
+ . = ..()
+ if(!.)
+ return
+ active_power_cost = base_power * light_range
+
+/obj/item/mod/module/flashlight/generate_worn_overlay(mutable_appearance/standing)
+ . = ..()
+ if(!active)
+ return
+ var/mutable_appearance/light_icon = mutable_appearance('icons/mob/mod.dmi', "module_light_on", layer = standing.layer + 0.2)
+ 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
+ if(is_color_dark(value, 50))
+ balloon_alert(mod.wearer, "too dark!")
+ return
+ set_light_color(value)
+ mod.wearer.update_inv_back()
+ if("light_range")
+ set_light_range(clamp(value, min_range, max_range))
+
+//Dispenser
+
+/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 cell 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_CELL_DRAIN * 2
+ incompatible_modules = list(/obj/item/mod/module/dispenser)
+ cooldown_time = 5 SECONDS
+ var/dispense_type = /obj/item/food/burger/plain
+ 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
+ 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)
+
+//Longfall
+
+/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_CELL_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()
+ 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
+
+/obj/item/mod/module/thermal_regulator
+ name = "MOD thermal regulator module"
+ desc = "Advanced climate control, using an inner body glove interwoven with thousands of tiny, \
+ flexible cooling lines. This circulates coolant at various user-controlled temperatures, \
+ ensuring they're comfortable; even if they're some that like it hot."
+ icon_state = "regulator"
+ module_type = MODULE_TOGGLE
+ complexity = 2
+ active_power_cost = DEFAULT_CELL_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/thermal_regulator)
+ cooldown_time = 0.5 SECONDS
+ var/temperature_setting = BODYTEMP_NORMAL
+ var/min_temp = 293.15
+ var/max_temp = 318.15
+
+/obj/item/mod/module/thermal_regulator/get_configuration()
+ . = ..()
+ .["temperature_setting"] = add_ui_configuration("Temperature", "number", temperature_setting - T0C)
+
+/obj/item/mod/module/thermal_regulator/configure_edit(key, value)
+ switch(key)
+ if("temperature_setting")
+ temperature_setting = clamp(value + T0C, min_temp, max_temp)
+
+/obj/item/mod/module/thermal_regulator/on_active_process(delta_time)
+ mod.wearer.adjust_bodytemperature(get_temp_change_amount((temperature_setting - mod.wearer.bodytemperature), 0.08 * delta_time))
+
+//Pathfinder
+
+/obj/item/mod/module/pathfinder
+ name = "MOD pathfinder module"
+ desc = "This module, brought to you by Nakamura Engineering, has two components. \
+ The first component is a series of thrusters and a computerized location subroutine installed into the \
+ very control unit of the suit, allowing it flight at highway speeds, \
+ and to be able to locate the second part of the system; \
+ a pathfinding implant installed into the base of the user's spine, \
+ broadcasting their location to the suit and allowing them to recall it to their back at any time. \
+ Nakamura Engineering swears up and down there's airbrakes."
+ icon_state = "pathfinder"
+ complexity = 2
+ use_power_cost = DEFAULT_CELL_DRAIN * 10
+ incompatible_modules = list(/obj/item/mod/module/pathfinder)
+ var/obj/item/implant/mod/implant
+
+/obj/item/mod/module/pathfinder/Initialize(mapload)
+ . = ..()
+ implant = new(src)
+
+/obj/item/mod/module/pathfinder/Destroy()
+ implant = null
+ return ..()
+
+/obj/item/mod/module/pathfinder/examine(mob/user)
+ . = ..()
+ if(implant)
+ . += span_notice("Use it on a human to implant them.")
+ else
+ . += span_warning("The implant is missing.")
+
+/obj/item/mod/module/pathfinder/attack(mob/living/target, mob/living/user, params)
+ if(!ishuman(target) || !implant)
+ return
+ if(!do_after(user, 1.5 SECONDS, target = target))
+ balloon_alert(user, "interrupted!")
+ return
+ if(!implant.implant(target, user))
+ balloon_alert(user, "can't implant!")
+ return
+ if(target == user)
+ to_chat(user, span_notice("You implant yourself with [implant]."))
+ else
+ target.visible_message(span_notice("[user] implants [target]."), span_notice("[user] implants you with [implant]."))
+ playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6)
+ icon_state = "pathfinder_empty"
+ implant = null
+
+/obj/item/mod/module/pathfinder/proc/attach(mob/living/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/human_user = user
+ if(human_user.back && !human_user.dropItemToGround(human_user.back))
+ return
+ if(!human_user.equip_to_slot_if_possible(mod, mod.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
+ return
+ human_user.update_action_buttons(TRUE)
+ balloon_alert(human_user, "[mod] attached")
+ playsound(mod, 'sound/machines/ping.ogg', 50, TRUE)
+ drain_power(use_power_cost)
+
+/obj/item/implant/mod
+ name = "MOD pathfinder implant"
+ desc = "Lets you recall a MODsuit to you at any time."
+ actions_types = list(/datum/action/item_action/mod_recall)
+ var/obj/item/mod/module/pathfinder/module
+ var/image/jet_icon
+
+/obj/item/implant/mod/Initialize(mapload)
+ . = ..()
+ if(!istype(loc, /obj/item/mod/module/pathfinder))
+ return INITIALIZE_HINT_QDEL
+ module = loc
+ jet_icon = image(icon = 'icons/obj/mod.dmi', icon_state = "mod_jet", layer = LOW_ITEM_LAYER)
+
+/obj/item/implant/mod/Destroy()
+ if(module?.mod?.ai_controller)
+ end_recall(successful = FALSE)
+ module = null
+ jet_icon = null
+ return ..()
+
+/obj/item/implant/mod/get_data()
+ var/dat = {"Implant Specifications:
+ Name: Nakamura Engineering Pathfinder Implant
+ Implant Details: Allows for the recall of a Modular Outerwear Device by the implant owner at any time.
"}
+ return dat
+
+/obj/item/implant/mod/proc/recall()
+ if(!module?.mod)
+ balloon_alert(imp_in, "no connected suit!")
+ return FALSE
+ if(module.mod.open)
+ balloon_alert(imp_in, "suit is open!")
+ return FALSE
+ if(module.mod.ai_controller)
+ balloon_alert(imp_in, "already in transit!")
+ return FALSE
+ if(ismob(get_atom_on_turf(module.mod)))
+ balloon_alert(imp_in, "already on someone!")
+ return FALSE
+ if(module.z != z || get_dist(imp_in, module.mod) > MOD_AI_RANGE)
+ balloon_alert(imp_in, "too far away!")
+ return FALSE
+ var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod)
+ module.mod.ai_controller = mod_ai
+ mod_ai.current_movement_target = imp_in
+ mod_ai.blackboard[BB_MOD_TARGET] = imp_in
+ mod_ai.blackboard[BB_MOD_IMPLANT] = src
+ module.mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
+ module.mod.AddElement(/datum/element/movetype_handler)
+ ADD_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
+ animate(module.mod, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y)
+ module.mod.add_overlay(jet_icon)
+ RegisterSignal(module.mod, COMSIG_MOVABLE_MOVED, .proc/on_move)
+ balloon_alert(imp_in, "suit recalled")
+ return TRUE
+
+/obj/item/implant/mod/proc/end_recall(successful = TRUE)
+ if(!module?.mod)
+ return
+ QDEL_NULL(module.mod.ai_controller)
+ module.mod.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
+ REMOVE_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
+ module.mod.RemoveElement(/datum/element/movetype_handler)
+ module.mod.cut_overlay(jet_icon)
+ module.mod.transform = matrix()
+ UnregisterSignal(module.mod, COMSIG_MOVABLE_MOVED)
+ if(!successful)
+ balloon_alert(imp_in, "suit lost connection!")
+
+/obj/item/implant/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced)
+ SIGNAL_HANDLER
+
+ var/matrix/mod_matrix = matrix()
+ mod_matrix.Turn(get_angle(source, imp_in))
+ source.transform = mod_matrix
+
+/datum/action/item_action/mod_recall
+ name = "Recall MOD"
+ desc = "Recall a MODsuit anyplace, anytime."
+ check_flags = AB_CHECK_CONSCIOUS
+ background_icon_state = "bg_tech_blue"
+ icon_icon = 'icons/mob/actions/actions_mod.dmi'
+ button_icon_state = "recall"
+ COOLDOWN_DECLARE(recall_cooldown)
+ var/obj/item/implant/mod/implant
+
+/datum/action/item_action/mod_recall/New(Target)
+ ..()
+ implant = Target
+
+/datum/action/item_action/mod_recall/Trigger()
+ . = ..()
+ if(!.)
+ return
+ if(!COOLDOWN_FINISHED(src, recall_cooldown))
+ implant.balloon_alert(implant.imp_in, "on cooldown!")
+ return
+ if(implant.recall())
+ COOLDOWN_START(src, recall_cooldown, 15 SECONDS)
+
+//DNA Lock
+
+/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_CELL_DRAIN * 3
+ incompatible_modules = list(/obj/item/mod/module/dna_lock)
+ cooldown_time = 0.5 SECONDS
+ 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()
+ 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()
+ if(!dna || (mod.wearer.has_dna() && mod.wearer.dna.unique_enzymes == dna))
+ return TRUE
+ balloon_alert(mod.wearer, "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)
+ SIGNAL_HANDLER
+
+ if(!dna_check())
+ return MOD_CANCEL_ACTIVATE
+
+/obj/item/mod/module/dna_lock/proc/on_mod_removal(datum/source)
+ SIGNAL_HANDLER
+
+ if(!dna_check())
+ return MOD_CANCEL_REMOVAL
+
+//Plasma Stabilizer
+
+/obj/item/mod/module/plasma_stabilizer
+ name = "MOD plasma stabilizer module"
+ desc = "This system essentially forms an atmosphere of its' own inside the suit, \
+ safely ejecting oxygen from the inside and allowing the wearer, a plasmaman, \
+ to have their internal plasma circulate around them somewhat like a sauna. \
+ This prevents them from self-igniting, and leads to greater comfort overall. \
+ The purple glass of the visor seems to be constructed for nostalgic purposes."
+ icon_state = "plasma_stabilizer"
+ complexity = 1
+ idle_power_cost = DEFAULT_CELL_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer)
+ overlay_state_inactive = "module_plasma"
+
+/obj/item/mod/module/plasma_stabilizer/on_equip()
+ ADD_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION, MOD_TRAIT)
+
+/obj/item/mod/module/plasma_stabilizer/on_unequip()
+ REMOVE_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION, MOD_TRAIT)
+
+//Kinesis
+
+/obj/item/mod/module/kinesis //TODO POST-MERGE MAKE NOT SUCK ASS, MAKE BALLER AS FUCK
+ name = "MOD kinesis module"
+ desc = "A modular plug-in to the forearm, this module was presumed lost for many years, \
+ despite the suits it used to be mounted on still seeing some circulation. \
+ This piece of technology allows the user to generate precise anti-gravity fields, \
+ letting them move objects as small as a titanium rod to as large as industrial machinery. \
+ Oddly enough, it doesn't seem to work on living creatures."
+ icon_state = "kinesis"
+// module_type = MODULE_ACTIVE
+ module_type = MODULE_TOGGLE
+// complexity = 3
+ complexity = 0
+ active_power_cost = DEFAULT_CELL_DRAIN*0.75
+// use_power_cost = DEFAULT_CELL_DRAIN*3
+ removable = FALSE
+ incompatible_modules = list(/obj/item/mod/module/kinesis)
+ cooldown_time = 0.5 SECONDS
+ var/has_tk = FALSE
+
+/obj/item/mod/module/kinesis/on_activation()
+ . = ..()
+ if(!.)
+ return
+ if(mod.wearer.dna.check_mutation(TK))
+ has_tk = TRUE
+ else
+ mod.wearer.dna.add_mutation(TK)
+
+/obj/item/mod/module/kinesis/on_deactivation()
+ . = ..()
+ if(!.)
+ return
+ if(has_tk)
+ has_tk = FALSE
+ return
+ mod.wearer.dna.remove_mutation(TK)
diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm
new file mode 100644
index 00000000000..d3f1c465fbe
--- /dev/null
+++ b/code/modules/mod/modules/modules_medical.dm
@@ -0,0 +1,203 @@
+//Medical modules for MODsuits
+
+//Health Analyzer
+
+#define HEALTH_SCAN "Health"
+#define WOUND_SCAN "Wound"
+#define CHEM_SCAN "Chemical"
+
+/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_CELL_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/health_analyzer)
+ cooldown_time = 0.5 SECONDS
+ var/mode = HEALTH_SCAN
+ var/static/list/modes = list(HEALTH_SCAN, WOUND_SCAN, CHEM_SCAN)
+
+/obj/item/mod/module/health_analyzer/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!isliving(target))
+ 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
+
+/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_CELL_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_QUICK_CARRY, MOD_TRAIT)
+
+/obj/item/mod/module/quick_carry/on_suit_deactivation()
+ REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT)
+
+/obj/item/mod/module/quick_carry/advanced
+ name = "MOD advanced quick carry module"
+ removable = FALSE
+ complexity = 0
+
+/obj/item/mod/module/quick_carry/on_suit_activation()
+ ADD_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT)
+ ADD_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT)
+
+/obj/item/mod/module/quick_carry/on_suit_deactivation()
+ REMOVE_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT)
+ REMOVE_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT)
+
+//Injector
+
+/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_CELL_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
+ inject_flags = INJECT_CHECK_PENETRATE_THICK
+
+//Organ Thrower
+
+/obj/item/mod/module/organ_thrower
+ name = "MOD organ thrower module"
+ desc = "A device recovered from a crashed Interdyne Pharmaceuticals vessel, \
+ this module has been unearthed for better or for worse. \
+ It's an arm-mounted device utilizing technology similar to modern-day part replacers, \
+ capable of storing and inserting organs into open patients. \
+ It's recommended by the DeForest Medical Corporation to not inform patients it has been used."
+ icon_state = "organ_thrower"
+ module_type = MODULE_ACTIVE
+ complexity = 2
+ use_power_cost = DEFAULT_CELL_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/organ_thrower)
+ cooldown_time = 0.5 SECONDS
+ var/max_organs = 5
+ var/organ_list = list()
+
+/obj/item/mod/module/organ_thrower/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ var/mob/living/carbon/human/wearer_human = mod.wearer
+ if(istype(target, /obj/item/organ))
+ if(!wearer_human.Adjacent(target))
+ return
+ var/atom/movable/organ = target
+ if(length(organ_list) >= max_organs)
+ balloon_alert(mod.wearer, "too many organs!")
+ return
+ organ_list += organ
+ organ.forceMove(src)
+ balloon_alert(mod.wearer, "picked up [organ]")
+ playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
+ drain_power(use_power_cost)
+ return
+ if(!length(organ_list))
+ return
+ var/atom/movable/fired_organ = pop(organ_list)
+ var/obj/projectile/organ/projectile = new /obj/projectile/organ(mod.wearer.loc, fired_organ)
+ projectile.preparePixelProjectile(target, mod.wearer)
+ projectile.firer = mod.wearer
+ playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
+ INVOKE_ASYNC(projectile, /obj/projectile.proc/fire)
+ drain_power(use_power_cost)
+
+/obj/projectile/organ
+ name = "organ"
+ damage = 0
+ nodamage = TRUE
+ hitsound = 'sound/effects/attackblob.ogg'
+ hitsound_wall = 'sound/effects/attackblob.ogg'
+ var/obj/item/organ/organ
+
+/obj/projectile/organ/Initialize(mapload, obj/item/stored_organ)
+ . = ..()
+ if(!stored_organ)
+ return INITIALIZE_HINT_QDEL
+ appearance = stored_organ.appearance
+ stored_organ.forceMove(src)
+ organ = stored_organ
+
+/obj/projectile/organ/Destroy()
+ organ = null
+ return ..()
+
+/obj/projectile/organ/on_hit(atom/target)
+ . = ..()
+ if(!ishuman(target))
+ organ.forceMove(drop_location())
+ organ = null
+ return
+ var/mob/living/carbon/human/organ_receiver = target
+ var/succeed = FALSE
+ if(organ_receiver.surgeries.len)
+ for(var/datum/surgery/procedure as anything in organ_receiver.surgeries)
+ if(procedure.location != organ.zone)
+ continue
+ if(!istype(procedure, /datum/surgery/organ_manipulation))
+ continue
+ var/datum/surgery_step/surgery_step = procedure.get_surgery_step()
+ if(!istype(surgery_step, /datum/surgery_step/manipulate_organs))
+ continue
+ succeed = TRUE
+ break
+ if(succeed)
+ var/list/organs_to_boot_out = organ_receiver.getorganslot(organ.slot)
+ for(var/obj/item/organ/organ_evacced as anything in organs_to_boot_out)
+ if(organ_evacced.organ_flags & ORGAN_UNREMOVABLE)
+ continue
+ organ_evacced.Remove(target)
+ organ_evacced.forceMove(get_turf(target))
+ organ.Insert(target)
+ else
+ organ.forceMove(drop_location())
+ organ = null
diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm
new file mode 100644
index 00000000000..62c86b81975
--- /dev/null
+++ b/code/modules/mod/modules/modules_science.dm
@@ -0,0 +1,162 @@
+//Science modules for MODsuits
+
+//Reagent Scanner
+
+/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_CELL_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()
+ . = ..()
+ 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
+
+/obj/item/mod/module/reagent_scanner/advanced/on_activation()
+ . = ..()
+ if(!.)
+ return
+ mod.wearer.research_scanner++
+ RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, .proc/sense_explosion)
+
+/obj/item/mod/module/reagent_scanner/advanced/on_deactivation()
+ . = ..()
+ if(!.)
+ return
+ mod.wearer.research_scanner--
+ RegisterSignal(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]"))
+
+//Circuit Adapter
+
+/obj/item/mod/module/circuit
+ name = "MOD circuit adapter module"
+ desc = "A popular aftermarket module, seen in wide varieties with wide applications by those across the galaxy. \
+ This is able to fit any sort of integrated circuit, hooking it into controls in the suit and displaying information \
+ to the HUD. Useful for universal translation, or perhaps as a calculator."
+ module_type = MODULE_USABLE
+ complexity = 3
+ idle_power_cost = DEFAULT_CELL_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/circuit)
+ cooldown_time = 0.5 SECONDS
+ var/obj/item/integrated_circuit/circuit
+
+/obj/item/mod/module/circuit/Initialize(mapload)
+ . = ..()
+ circuit = new()
+ AddComponent(/datum/component/shell, \
+ list(new /obj/item/circuit_component/mod()), \
+ capacity = SHELL_CAPACITY_LARGE, \
+ shell_flags = SHELL_FLAG_CIRCUIT_UNREMOVABLE, \
+ starting_circuit = circuit, \
+ )
+
+/obj/item/mod/module/circuit/on_install()
+ circuit.set_cell(mod.cell)
+
+/obj/item/mod/module/circuit/on_uninstall()
+ circuit.set_cell(mod.cell)
+
+/obj/item/mod/module/circuit/on_suit_activation()
+ circuit.set_on(TRUE)
+
+/obj/item/mod/module/circuit/on_suit_deactivation()
+ circuit.set_on(FALSE)
+
+/obj/item/mod/module/circuit/on_use()
+ . = ..()
+ if(!.)
+ return
+ circuit.interact(mod.wearer)
+
+/obj/item/circuit_component/mod
+ display_name = "MOD"
+ desc = "Used to send and receive signals from a MODsuit."
+
+ var/obj/item/mod/module/attached_module
+
+ var/datum/port/input/module_to_select
+ var/datum/port/input/toggle_suit
+ var/datum/port/input/select_module
+
+ var/datum/port/output/wearer
+ var/datum/port/output/selected_module
+
+/obj/item/circuit_component/mod/populate_ports()
+ // Input Signals
+ module_to_select = add_input_port("Module to Select", PORT_TYPE_STRING)
+ toggle_suit = add_input_port("Toggle Suit", PORT_TYPE_SIGNAL)
+ select_module = add_input_port("Select Module", PORT_TYPE_SIGNAL)
+ // States
+ wearer = add_output_port("Wearer", PORT_TYPE_ATOM)
+ selected_module = add_output_port("Selected Module", PORT_TYPE_ATOM)
+
+/obj/item/circuit_component/mod/register_shell(atom/movable/shell)
+ if(istype(shell, /obj/item/mod/module))
+ attached_module = shell
+ RegisterSignal(attached_module, COMSIG_MOVABLE_MOVED, .proc/on_move)
+
+/obj/item/circuit_component/mod/unregister_shell(atom/movable/shell)
+ UnregisterSignal(attached_module, COMSIG_MOVABLE_MOVED)
+ attached_module = null
+
+/obj/item/circuit_component/mod/input_received(datum/port/input/port)
+ var/obj/item/mod/module/module
+ for(var/obj/item/mod/module/potential_module as anything in attached_module.mod.modules)
+ if(potential_module.name == module_to_select.value)
+ module = potential_module
+ if(COMPONENT_TRIGGERED_BY(toggle_suit, port))
+ INVOKE_ASYNC(attached_module.mod, /obj/item/mod/control.proc/toggle_activate, attached_module.mod.wearer)
+ if(module && COMPONENT_TRIGGERED_BY(select_module, port))
+ INVOKE_ASYNC(module, /obj/item/mod/module.proc/on_select)
+
+/obj/item/circuit_component/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced)
+ SIGNAL_HANDLER
+ if(istype(source.loc, /obj/item/mod/control))
+ RegisterSignal(source.loc, COMSIG_MOD_MODULE_SELECTED, .proc/on_module_select)
+ RegisterSignal(source.loc, COMSIG_ITEM_EQUIPPED, .proc/equip_check)
+ equip_check()
+ else if(istype(old_loc, /obj/item/mod/control))
+ UnregisterSignal(old_loc, list(COMSIG_MOD_MODULE_SELECTED, COMSIG_ITEM_EQUIPPED))
+ selected_module.set_output(null)
+ wearer.set_output(null)
+
+/obj/item/circuit_component/mod/proc/on_module_select()
+ SIGNAL_HANDLER
+ selected_module.set_output(attached_module.mod.selected_module)
+
+/obj/item/circuit_component/mod/proc/equip_check()
+ SIGNAL_HANDLER
+
+ if(!attached_module.mod?.wearer)
+ return
+ wearer.set_output(attached_module.mod.wearer)
diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm
new file mode 100644
index 00000000000..8daa732ee81
--- /dev/null
+++ b/code/modules/mod/modules/modules_security.dm
@@ -0,0 +1,125 @@
+//Security modules for MODsuits
+
+//Cloaking
+
+/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_CELL_DRAIN * 2
+ use_power_cost = DEFAULT_CELL_DRAIN * 10
+ incompatible_modules = list(/obj/item/mod/module/stealth)
+ cooldown_time = 5 SECONDS
+ var/bumpoff = TRUE
+ 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_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED), .proc/unstealth)
+ animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS)
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/stealth/on_deactivation()
+ . = ..()
+ if(!.)
+ return
+ if(bumpoff)
+ UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP)
+ UnregisterSignal(mod.wearer, list(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED))
+ 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()
+
+/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/projectile/projectile)
+ SIGNAL_HANDLER
+
+ if(projectile.nodamage)
+ return
+ unstealth(source)
+
+/obj/item/mod/module/stealth/ninja
+ name = "MOD advanced cloaking module"
+ desc = "The latest in stealth technology, this module is a definite upgrade over previous versions. \
+ The field has been tuned to be even more responsive and fast-acting, with enough stability to \
+ continue operation of the field even if the user bumps into others. \
+ The draw on the power cell has been reduced drastically, \
+ making this perfect for activities like standing near sentry turrets for extended periods of time."
+ icon_state = "cloak_ninja"
+ bumpoff = FALSE
+ stealth_alpha = 20
+ active_power_cost = DEFAULT_CELL_DRAIN
+ use_power_cost = DEFAULT_CELL_DRAIN * 5
+ cooldown_time = 3 SECONDS
+
+//Holster
+
+/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
+ use_power_cost = DEFAULT_CELL_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/holster)
+ cooldown_time = 0.5 SECONDS
+ 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, FALSE, FALSE))
+ holstered = holding
+ balloon_alert(mod.wearer, "weapon holstered")
+ playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE)
+ drain_power(use_power_cost)
+ else if(mod.wearer.put_in_active_hand(holstered, FALSE, TRUE))
+ balloon_alert(mod.wearer, "weapon drawn")
+ holstered = null
+ playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE)
+ drain_power(use_power_cost)
+ else
+ balloon_alert(mod.wearer, "holster full!")
+
+/obj/item/mod/module/holster/on_uninstall()
+ if(holstered)
+ holstered.forceMove(drop_location())
+ holstered = null
+
+/obj/item/mod/module/holster/Destroy()
+ QDEL_NULL(holstered)
+ return ..()
diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm
new file mode 100644
index 00000000000..6712837aa07
--- /dev/null
+++ b/code/modules/mod/modules/modules_service.dm
@@ -0,0 +1,58 @@
+//Service modules for MODsuits
+
+//Bike Horn
+
+/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_CELL_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
+
+/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_CELL_DRAIN * 5
+ incompatible_modules = list(/obj/item/mod/module/microwave_beam)
+ cooldown_time = 10 SECONDS
+
+/obj/item/mod/module/microwave_beam/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!istype(target, /obj/item))
+ 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())
+ 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 00000000000..5b6096b9d79
--- /dev/null
+++ b/code/modules/mod/modules/modules_supply.dm
@@ -0,0 +1,169 @@
+//Supply modules for MODsuits
+
+//Internal GPS
+
+/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_ACTIVE
+ complexity = 1
+ active_power_cost = DEFAULT_CELL_DRAIN * 0.3
+ device = /obj/item/gps/mod
+ incompatible_modules = list(/obj/item/mod/module/gps)
+ cooldown_time = 0.5 SECONDS
+
+/obj/item/gps/mod
+ name = "MOD internal GPS"
+ desc = "Common Nanotrasen technology that calcaulates the user's position from anywhere in space, down to their coordinates."
+ icon_state = "gps-b"
+ gpstag = "MOD0"
+
+//Hydraulic Clamp
+
+/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_CELL_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/clamp)
+ cooldown_time = 0.5 SECONDS
+ var/max_crates = 5
+ 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))
+ var/atom/movable/picked_crate = target
+ if(length(stored_crates) >= max_crates)
+ balloon_alert(mod.wearer, "too many crates!")
+ return
+ if(!do_after(mod.wearer, 1 SECONDS, target = target))
+ balloon_alert(mod.wearer, "interrupted!")
+ return
+ stored_crates += picked_crate
+ picked_crate.forceMove(src)
+ balloon_alert(mod.wearer, "picked up [picked_crate]")
+ playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
+ drain_power(use_power_cost)
+ else if(length(stored_crates))
+ var/turf/target_turf = get_turf(target)
+ if(target_turf.is_blocked_turf())
+ return
+ if(!do_after(mod.wearer, 1 SECONDS, target = target))
+ balloon_alert(mod.wearer, "interrupted!")
+ return
+ if(target_turf.is_blocked_turf())
+ return
+ var/atom/movable/dropped_crate = pop(stored_crates)
+ dropped_crate.forceMove(target_turf)
+ balloon_alert(mod.wearer, "dropped [dropped_crate]")
+ playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/clamp/on_uninstall()
+ for(var/atom/movable/crate as anything in stored_crates)
+ crate.forceMove(drop_location())
+ stored_crates -= crate
+
+//Drill
+
+/obj/item/mod/module/drill
+ name = "MOD drill module"
+ desc = "An integrated drill, typically extending over the user's hand. While useful for drilling through rock, \
+ your drill is surely the one that both pierces and creates the heavens."
+ icon_state = "drill"
+ module_type = MODULE_ACTIVE
+ complexity = 2
+ use_power_cost = DEFAULT_CELL_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/drill)
+ cooldown_time = 0.5 SECONDS
+
+/obj/item/mod/module/drill/on_activation()
+ . = ..()
+ if(!.)
+ return
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, .proc/bump_mine)
+
+/obj/item/mod/module/drill/on_deactivation()
+ . = ..()
+ if(!.)
+ return
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP)
+
+/obj/item/mod/module/drill/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!mod.wearer.Adjacent(target))
+ return
+ if(istype(target, /turf/closed/mineral))
+ var/turf/closed/mineral/mineral_turf = target
+ mineral_turf.gets_drilled(mod.wearer)
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity)
+ SIGNAL_HANDLER
+ if(!istype(bumped_into, /turf/closed/mineral) || !drain_power(use_power_cost))
+ return
+ var/turf/closed/mineral/mineral_turf = bumped_into
+ mineral_turf.gets_drilled(mod.wearer)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+//Ore Bag
+
+/obj/item/mod/module/orebag
+ name = "MOD ore bag module"
+ desc = "An integrated ore storage system installed into the suit, \
+ this utilizes precise electromagnets and storage compartments to automatically collect and deposit ore. \
+ It's recommended by Nakamura Engineering to actually deposit that ore at local refineries."
+ icon_state = "ore"
+ module_type = MODULE_USABLE
+ complexity = 2
+ use_power_cost = DEFAULT_CELL_DRAIN * 0.2
+ incompatible_modules = list(/obj/item/mod/module/orebag)
+ cooldown_time = 0.5 SECONDS
+ var/list/ores = list()
+
+/obj/item/mod/module/orebag/on_equip()
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/ore_pickup)
+
+/obj/item/mod/module/orebag/on_unequip()
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
+
+/obj/item/mod/module/orebag/proc/ore_pickup(atom/movable/source, atom/old_loc, dir, forced)
+ SIGNAL_HANDLER
+
+ for(var/obj/item/stack/ore/ore in get_turf(mod.wearer))
+ INVOKE_ASYNC(src, .proc/move_ore, ore)
+ playsound(src, "rustle", 50, TRUE)
+
+/obj/item/mod/module/orebag/proc/move_ore(obj/item/stack/ore)
+ for(var/obj/item/stack/stored_ore as anything in ores)
+ if(!ore.can_merge(stored_ore))
+ continue
+ ore.merge(stored_ore)
+ if(QDELETED(ore))
+ return
+ break
+ ore.forceMove(src)
+ ores += ore
+
+/obj/item/mod/module/orebag/on_use()
+ . = ..()
+ if(!.)
+ return
+ for(var/obj/item/ore as anything in ores)
+ ore.forceMove(drop_location())
+ ores -= ore
+ drain_power(use_power_cost)
diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm
new file mode 100644
index 00000000000..858963cb45f
--- /dev/null
+++ b/code/modules/mod/modules/modules_visor.dm
@@ -0,0 +1,99 @@
+//Visor modules for MODsuits
+
+//Base Visor
+
+/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_CELL_DRAIN*0.3
+ incompatible_modules = list(/obj/item/mod/module/visor)
+ cooldown_time = 0.5 SECONDS
+ var/hud_type
+ 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()
+ . = ..()
+ 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
+
+/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
+ visor_traits = list(TRAIT_MEDICAL_HUD)
+
+//Diagnostic Visor
+
+/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
+ visor_traits = list(TRAIT_DIAGNOSTIC_HUD)
+
+//Security Visor
+
+/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
+ visor_traits = list(TRAIT_SECURITY_HUD)
+
+//Meson Visor
+
+/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, TRAIT_SUPERMATTER_MADNESS_IMMUNE)
+
+//Thermal Visor
+
+/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
+
+/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/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm
index 0bcae529653..cef3f1d3015 100644
--- a/code/modules/paperwork/handlabeler.dm
+++ b/code/modules/paperwork/handlabeler.dm
@@ -88,6 +88,9 @@
qdel(I)
labels_left = initial(labels_left) //Yes, it's capped at its initial value
+/obj/item/hand_labeler/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
+ return !mode
+
/obj/item/hand_labeler/borg
name = "cyborg-hand labeler"
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 4c389562530..efac11ad1fa 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -1150,6 +1150,7 @@
id = "mod_welding"
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/mod/module/welding
+ department_type = MODULE_ENGINEERING
/datum/design/module/mod_t_ray
name = "MOD Module: T-Ray Scanner"
@@ -1229,7 +1230,7 @@
id = "mod_reagent_scanner"
materials = list(/datum/material/glass = 1000)
build_path = /obj/item/mod/module/reagent_scanner
- department_type = MODULE_MEDICAL
+ department_type = MODULE_SCIENCE
/datum/design/module/mod_gps
name = "MOD Module: Internal GPS"
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 65926816ef7..42ef60e1ea0 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -886,8 +886,9 @@
if(!proximity)
return
if(!istype(C))
- // applying this to vehicles is handled in the ridable element, see [/datum/element/ridable/proc/check_potion]
- to_chat(user, span_warning("The potion can only be used on items or vehicles!"))
+ to_chat(user, span_warning("The potion can only be used on objects!"))
+ return
+ if(SEND_SIGNAL(C, COMSIG_SPEED_POTION_APPLIED, src, user) & SPEED_POTION_SUCCESSFUL)
return
if(isitem(C))
var/obj/item/I = C
@@ -901,6 +902,13 @@
C.add_atom_colour("#FF0000", FIXED_COLOUR_PRIORITY)
qdel(src)
+/obj/item/slimepotion/speed/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user)
+ . = ..()
+ if(!isitem(storage_holder))
+ return
+ var/obj/item/storage_item = storage_holder
+ return storage_item.slowdown <= 0
+
/obj/item/slimepotion/fireproof
name = "slime chill potion"
desc = "A potent chemical mix that will fireproof any article of clothing. Has three uses."
diff --git a/tgstation.dme b/tgstation.dme
index 34c59dbabe4..7852d99ae8e 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3299,7 +3299,15 @@
#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_antag.dm"
+#include "code\modules\mod\modules\modules_engineering.dm"
+#include "code\modules\mod\modules\modules_general.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"