diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm
index ed0636dcb75..776b01d3a11 100644
--- a/code/__defines/_planes+layers.dm
+++ b/code/__defines/_planes+layers.dm
@@ -92,6 +92,13 @@ What is the naming convention for planes or layers?
// Mob planes
#define MOB_PLANE -25
#define BELOW_MOB_LAYER 3.9 // Should be converted to plane swaps
+ #define MECH_BASE_LAYER 4.01
+ #define MECH_INTERMEDIATE_LAYER 4.02
+ #define MECH_PILOT_LAYER 4.03
+ #define MECH_LEG_LAYER 4.04
+ #define MECH_COCKPIT_LAYER 4.05
+ #define MECH_ARM_LAYER 4.06
+ #define MECH_GEAR_LAYER 4.07
#define ABOVE_MOB_LAYER 4.1 // Should be converted to plane swaps
#define ABOVE_MOB_PLANE -24
diff --git a/code/_onclick/MouseDrag.dm b/code/_onclick/MouseDrag.dm
new file mode 100644
index 00000000000..8dec586d207
--- /dev/null
+++ b/code/_onclick/MouseDrag.dm
@@ -0,0 +1,15 @@
+//If we intercept it return true else return false
+/atom/proc/RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, var/mob/user)
+ return FALSE
+
+/mob/proc/OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params)
+ if(istype(loc, /atom))
+ var/atom/A = loc
+ if(A.RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, src))
+ return
+
+ if(over_object && !incapacitated())
+ var/obj/item/gun/gun = get_active_hand()
+ if(istype(gun) && gun.can_autofire())
+ set_dir(get_dir(src, over_object))
+ gun.Fire(get_turf(over_object), src, params, (get_dist(over_object, src) <= 1), FALSE)
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index d7759ed12f6..9bdb8fa8a70 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -79,6 +79,11 @@
var/obj/mecha/M = loc
return M.click_action(A, src, params)
+ else if(!isturf(loc) && !isnull(loc)) // If you are in Nullspace you should be adminhelping not clicking.
+ var/atom/movable/AM = loc
+ if(AM.allow_click_through(A, src))
+ return AM.click_through(A, src, params)
+
if(restrained())
setClickCooldown(10)
RestrainedClickOn(A)
@@ -141,6 +146,12 @@
trigger_aiming(TARGET_CAN_CLICK)
return 1
+/atom/movable/proc/click_through(var/atom/target, var/atom/movable/source, var/params)
+ return FALSE
+
+/atom/movable/proc/allow_click_through(var/atom/target, var/atom/movable/source)
+ return FALSE
+
/mob/proc/setClickCooldown(var/timeout)
next_click = max(world.time + timeout, next_click)
diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm
index d1822f88ab2..e7ae7b0f3b4 100644
--- a/code/game/mecha/mech_bay.dm
+++ b/code/game/mecha/mech_bay.dm
@@ -13,6 +13,7 @@
var/repair = 0
var/list/chargable_types = list(
/obj/mecha,
+ /mob/living/exosuit,
/mob/living/silicon/robot/platform
)
@@ -58,7 +59,7 @@
var/done = FALSE
var/obj/mecha/mech = charging
var/obj/item/cell/cell = charging.get_cell()
- if(cell)
+ if(cell)
var/t = min(charge, cell.maxcharge - cell.charge)
if(t > 0)
if(istype(mech))
diff --git a/code/game/objects/items/weapons/id cards/station_ids.dm b/code/game/objects/items/weapons/id cards/station_ids.dm
index 41c68343f7f..264fc239359 100644
--- a/code/game/objects/items/weapons/id cards/station_ids.dm
+++ b/code/game/objects/items/weapons/id cards/station_ids.dm
@@ -8,7 +8,7 @@
SPECIES_TESHARI = 'icons/mob/species/teshari/id.dmi'
)
- var/access = list()
+ var/list/access = list()
var/registered_name = "Unknown" // The name registered_name on the card
var/registered_id_string = "ID Card"
slot_flags = SLOT_ID | SLOT_EARS
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index c0a2f0cbc5d..e844d0f9000 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -361,6 +361,12 @@
else
. = ..()
+/client/MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params)
+ . = ..()
+ var/mob/living/M = mob
+ if(istype(M))
+ M.OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params)
+
/client/proc/last_activity_seconds()
return inactivity / 10
diff --git a/code/modules/exosuits/_exosuit_setup.dm b/code/modules/exosuits/_exosuit_setup.dm
new file mode 100644
index 00000000000..c052b069bc7
--- /dev/null
+++ b/code/modules/exosuits/_exosuit_setup.dm
@@ -0,0 +1,44 @@
+GLOBAL_DATUM_INIT(default_hardpoint_background, /image, null)
+GLOBAL_DATUM_INIT(hardpoint_error_icon, /image, null)
+GLOBAL_DATUM_INIT(hardpoint_bar_empty, /image, null)
+
+GLOBAL_LIST_INIT(hardpoint_bar_cache, new)
+GLOBAL_LIST_INIT(mech_damage_overlay_cache, new)
+GLOBAL_LIST_INIT(mech_image_cache, new)
+GLOBAL_LIST_INIT(mech_icon_cache, new)
+GLOBAL_LIST_INIT(mech_weapon_overlays, icon_states('icons/mob/mecha/mech_weapon_overlays.dmi'))
+
+#define HARDPOINT_BACK "back"
+#define HARDPOINT_LEFT_HAND "left hand"
+#define HARDPOINT_RIGHT_HAND "right hand"
+#define HARDPOINT_LEFT_SHOULDER "left shoulder"
+#define HARDPOINT_RIGHT_SHOULDER "right shoulder"
+#define HARDPOINT_HEAD "head"
+
+// No software required: taser. light, radio.
+#define MECH_SOFTWARE_UTILITY "utility equipment" // Plasma torch, clamp, drill.
+#define MECH_SOFTWARE_MEDICAL "medical support systems" // Sleeper.
+#define MECH_SOFTWARE_WEAPONS "standard weapon systems" // Ballistics and energy weapons.
+#define MECH_SOFTWARE_ADVWEAPONS "advanced weapon systems" // Railguns, missile launcher.
+#define MECH_SOFTWARE_ENGINEERING "advanced engineering systems" // RCD.
+
+// EMP damage points before various effects occur.
+#define EMP_GUI_DISRUPT 5 // 1 ion rifle shot == 8.
+#define EMP_MOVE_DISRUPT 10 // 2 shots.
+#define EMP_ATTACK_DISRUPT 20 // 3 shots.
+
+//About components
+#define MECH_COMPONENT_DAMAGE_UNDAMAGED 1
+#define MECH_COMPONENT_DAMAGE_DAMAGED 2
+#define MECH_COMPONENT_DAMAGE_DAMAGED_BAD 3
+#define MECH_COMPONENT_DAMAGE_DAMAGED_TOTAL 4
+
+//Construction
+#define EXOFRAME_REINFORCED 1
+#define EXOFRAME_REINFORCED_SECURE 2
+#define EXOFRAME_REINFORCED_WELDED 3
+
+#define EXOFRAME_WIRED 1
+#define EXOFRAME_WIRED_ADJUSTED 2
+
+#define BAR_CAP 12
diff --git a/code/modules/exosuits/component/_components.dm b/code/modules/exosuits/component/_components.dm
new file mode 100644
index 00000000000..c07a5187140
--- /dev/null
+++ b/code/modules/exosuits/component/_components.dm
@@ -0,0 +1,160 @@
+/obj/item/mech_component
+ icon = 'icons/mob/mecha/mech_parts_held.dmi'
+ w_class = ITEMSIZE_HUGE
+ gender = PLURAL
+ color = COLOR_GUNMETAL
+
+ var/on_mech_icon = 'icons/mob/mecha/mech_parts.dmi'
+ var/exosuit_desc_string
+ var/total_damage = 0
+ var/brute_damage = 0
+ var/burn_damage = 0
+ var/max_damage = 60
+ var/damage_state = MECH_COMPONENT_DAMAGE_UNDAMAGED
+ var/list/has_hardpoints = list()
+ var/decal
+ var/power_use = 0
+ matter = list(MATERIAL_STEEL = 15000, MATERIAL_PLASTIC = 1000, MATERIAL_OSMIUM = 500)
+ dir = SOUTH
+
+/obj/item/mech_component/proc/query_color(var/mob/living/user)
+ if(istype(user))
+ var/input = input("Modify part color:", "Color", color) as color|null
+ if(input)
+ return set_colour(input)
+
+/obj/item/mech_component/proc/set_colour(new_colour)
+ var/last_colour = color
+ color = new_colour
+ return color != last_colour
+
+/obj/item/mech_component/emp_act(var/severity)
+ take_burn_damage(rand((10 - (severity*3)),15-(severity*4)))
+ for(var/obj/item/thing in contents)
+ thing.emp_act(severity)
+
+/obj/item/mech_component/examine(mob/user)
+ . = ..()
+ if(ready_to_install())
+ . += SPAN_NOTICE("It is ready for installation.")
+ else
+ . += show_missing_parts(user)
+
+//These icons have multiple directions but before they're attached we only want south.
+/obj/item/mech_component/set_dir()
+ ..(SOUTH)
+
+/obj/item/mech_component/proc/show_missing_parts(var/mob/user)
+ return
+
+/obj/item/mech_component/proc/prebuild()
+ return
+
+/obj/item/mech_component/proc/install_component(var/obj/item/thing, var/mob/user)
+ if(!user.unEquip(thing, src))
+ return FALSE
+ user.visible_message(SPAN_NOTICE("\The [user] installs \the [thing] in \the [src]."))
+ thing.forceMove(src)
+ update_components()
+ return TRUE
+
+/obj/item/mech_component/proc/update_health()
+ total_damage = min(max_damage, brute_damage + burn_damage)
+ damage_state = between(MECH_COMPONENT_DAMAGE_UNDAMAGED, round((total_damage/max_damage) * 4), MECH_COMPONENT_DAMAGE_DAMAGED_TOTAL)
+
+/obj/item/mech_component/proc/ready_to_install()
+ return TRUE
+
+/obj/item/mech_component/proc/repair_brute_damage(var/amt)
+ take_brute_damage(-amt)
+
+/obj/item/mech_component/proc/repair_burn_damage(var/amt)
+ take_burn_damage(-amt)
+
+/obj/item/mech_component/proc/take_brute_damage(var/amt)
+ brute_damage = max(0, brute_damage + amt)
+ update_health()
+ if(total_damage == max_damage)
+ take_component_damage(amt,0)
+
+/obj/item/mech_component/proc/take_burn_damage(var/amt)
+ burn_damage = max(0, burn_damage + amt)
+ update_health()
+ if(total_damage == max_damage)
+ take_component_damage(0,amt)
+
+/obj/item/mech_component/proc/take_component_damage(var/brute, var/burn)
+ var/list/damageable_components = list()
+ for(var/obj/item/robot_parts/robot_component/RC in contents)
+ damageable_components += RC
+ if(!damageable_components.len) return
+ var/obj/item/robot_parts/robot_component/RC = pick(damageable_components)
+ if(RC.take_damage(brute, burn))
+ qdel(RC)
+ update_components()
+
+/obj/item/mech_component/attackby(var/obj/item/thing, var/mob/user)
+ if(thing.has_tool_quality(TOOL_SCREWDRIVER))
+ if(contents.len)
+ var/obj/item/removed = input("Which component would you like to remove") as null|anything in contents
+ if(!removed)
+ return
+ user.visible_message(SPAN_NOTICE("\The [user] removes \the [removed] from \the [src]."))
+ removed.forceMove(user.loc)
+ playsound(user.loc, 'sound/effects/pop.ogg', 50, 0)
+ update_components()
+ else
+ to_chat(user, SPAN_WARNING("There is nothing to remove."))
+ return
+ if(istype(thing, /obj/item/weldingtool))
+ repair_brute_generic(thing, user)
+ return
+ if(istype(thing, /obj/item/stack/cable_coil))
+ repair_burn_generic(thing, user)
+ return
+
+ if(thing.has_tool_quality(TOOL_MULTITOOL))
+ query_color(user)
+ return
+
+ return ..()
+
+/obj/item/mech_component/proc/update_components()
+ return
+
+/obj/item/mech_component/proc/repair_brute_generic(var/obj/item/weldingtool/WT, var/mob/user)
+ if(!istype(WT))
+ return
+ if(!brute_damage)
+ to_chat(user, SPAN_NOTICE("You inspect \the [src] but find nothing to repair."))
+ return
+ if(!WT.welding)
+ to_chat(user, SPAN_WARNING("Turn \the [WT] on, first."))
+ return
+ if(WT.remove_fuel(1, user))
+ if(do_after(user, 10, src) && brute_damage)
+ repair_brute_damage(10)
+ to_chat(user, SPAN_NOTICE("You mend the damage to \the [src]."))
+ playsound(user.loc, 'sound/items/Welder.ogg', 25, 1)
+
+/obj/item/mech_component/proc/repair_burn_generic(var/obj/item/stack/cable_coil/CC, var/mob/user)
+ if(!istype(CC))
+ return
+ if(!burn_damage)
+ to_chat(user, SPAN_NOTICE("\The [src]'s wiring doesn't need replacing."))
+ return
+
+ if(CC.get_amount() < 6)
+ to_chat(user, SPAN_WARNING("You need at least 6 units of cable to repair this section."))
+ return
+
+ user.visible_message("\The [user] begins replacing the wiring of \the [src]...")
+
+ if(do_after(user, 10, src) && burn_damage)
+ if(QDELETED(CC) || QDELETED(src) || !CC.use(6))
+ return
+
+ repair_burn_damage(25)
+ to_chat(user, SPAN_NOTICE("You mend the damage to \the [src]'s wiring."))
+ playsound(user.loc, 'sound/items/Deconstruct.ogg', 25, 1)
+ return
diff --git a/code/modules/exosuits/component/armour.dm b/code/modules/exosuits/component/armour.dm
new file mode 100644
index 00000000000..3caf7df715e
--- /dev/null
+++ b/code/modules/exosuits/component/armour.dm
@@ -0,0 +1,54 @@
+/obj/item/robot_parts/robot_component/armour/exosuit
+ name = "exosuit armour plating"
+ armor = list(
+ melee = 70,
+ bullet = 30,
+ laser = 40,
+ energy = 10,
+ bomb = 30,
+ bio = 100,
+ rad = 20
+ )
+ origin_tech = list(TECH_MATERIAL = 1)
+
+/obj/item/robot_parts/robot_component/armour/exosuit/radproof
+ name = "radiation-proof armour plating"
+ desc = "A fully enclosed radiation hardened shell designed to protect the pilot from radiation"
+ armor = list(
+ melee = 70,
+ bullet = 30,
+ laser = 40,
+ energy = 10,
+ bomb = 30,
+ bio = 100,
+ rad = 100
+ )
+ origin_tech = list(TECH_MATERIAL = 3)
+
+/obj/item/robot_parts/robot_component/armour/exosuit/em
+ name = "EM-shielded armour plating"
+ desc = "A shielded plating that surrounds the eletronics and protects them from electromagnetic radiation"
+ armor = list(
+ melee =70,
+ bullet = 25,
+ laser = 25,
+ energy = 100,
+ bomb = 10,
+ bio = 100,
+ rad = 20
+ )
+ origin_tech = list(TECH_MATERIAL = 3)
+
+/obj/item/robot_parts/robot_component/armour/exosuit/combat
+ name = "heavy combat plating"
+ desc = "Plating designed to deflect incoming attacks and explosions"
+ armor = list(
+ melee = 70,
+ bullet = 60,
+ laser = 70,
+ energy = 10,
+ bomb = 60,
+ bio = 100,
+ rad = 20
+ )
+ origin_tech = list(TECH_MATERIAL = 5)
diff --git a/code/modules/exosuits/component/arms.dm b/code/modules/exosuits/component/arms.dm
new file mode 100644
index 00000000000..673565c0e78
--- /dev/null
+++ b/code/modules/exosuits/component/arms.dm
@@ -0,0 +1,111 @@
+/obj/item/mech_component/manipulators
+ name = "arms"
+ pixel_y = -12
+ icon_state = "loader_arms"
+ has_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+
+ var/melee_damage = 15
+ var/action_delay = 15
+ var/obj/item/robot_parts/robot_component/actuator/motivator
+ power_use = 10
+
+/obj/item/mech_component/manipulators/Destroy()
+ QDEL_NULL(motivator)
+ . = ..()
+
+/obj/item/mech_component/manipulators/show_missing_parts(var/mob/user)
+ if(!motivator)
+ . += SPAN_WARNING("It is missing an actuator.")
+
+/obj/item/mech_component/manipulators/ready_to_install()
+ return motivator
+
+/obj/item/mech_component/manipulators/prebuild()
+ motivator = new(src)
+
+/obj/item/mech_component/manipulators/attackby(var/obj/item/thing, var/mob/user)
+ if(istype(thing,/obj/item/robot_parts/robot_component/actuator))
+ if(motivator)
+ to_chat(user, SPAN_WARNING("\The [src] already has an actuator installed."))
+ else if(install_component(thing, user))
+ motivator = thing
+ return
+ return ..()
+
+/obj/item/mech_component/manipulators/update_components()
+ motivator = locate() in src
+
+/*
+ * Variants
+ */
+
+/obj/item/mech_component/manipulators/combat
+ name = "combat arms"
+ exosuit_desc_string = "flexible, advanced manipulators"
+ icon_state = "combat_arms"
+ melee_damage = 5
+ action_delay = 10
+ power_use = 50
+
+/obj/item/mech_component/manipulators/industrial
+ name = "exosuit arms"
+ exosuit_desc_string = "heavy-duty industrial lifters"
+ icon_state = "industrial_arms"
+ max_damage = 70
+ power_use = 30
+ desc = "The Industrial Digital Interaction Manifolds allow you poke untold dangers from the relative safety of your cockpit."
+
+/obj/item/mech_component/manipulators/heavy
+ name = "heavy arms"
+ exosuit_desc_string = "super-heavy reinforced manipulators"
+ icon_state = "heavy_arms"
+ desc = "Designed to function where any other piece of equipment would have long fallen apart, the Hephaestus Superheavy Lifter series can take a beating and excel at delivering it."
+ melee_damage = 25
+ action_delay = 15
+ max_damage = 90
+ power_use = 60
+
+/obj/item/mech_component/manipulators/light
+ name = "light arms"
+ exosuit_desc_string = "lightweight, segmented manipulators"
+ icon_state = "light_arms"
+ melee_damage = 5
+ action_delay = 15
+ max_damage = 40
+ power_use = 10
+ desc = "As flexible as they are fragile, these Vey-Med manipulators can follow a pilot's movements in close to real time."
+
+/obj/item/mech_component/manipulators/mercenary
+ name = "mercenary arms"
+ exosuit_desc_string = "super-mercenary reinforced manipulators"
+ icon_state = "mercenary_arms"
+ desc = "Designed to function where any other piece of equipment would have long fallen apart, the Supermercenary Lifter series can take a beating and excel at delivering it."
+ melee_damage = 25
+ action_delay = 15
+ max_damage = 90
+ power_use = 60
+
+/obj/item/mech_component/manipulators/modern
+ name = "modern arms"
+ exosuit_desc_string = "flexible, advanced manipulators"
+ icon_state = "modern_arms"
+ melee_damage = 5
+ action_delay = 10
+ power_use = 50
+
+/obj/item/mech_component/manipulators/powerloader
+ name = "exosuit arms"
+ exosuit_desc_string = "heavy-duty industrial lifters"
+ max_damage = 70
+ power_use = 30
+ desc = "The Xion Industrial Digital Interaction Manifolds allow you poke untold dangers from the relative safety of your cockpit."
+
+/obj/item/mech_component/manipulators/sleek
+ name = "sleek arms"
+ exosuit_desc_string = "sleekweight, segmented manipulators"
+ icon_state = "sleek_arms"
+ melee_damage = 5
+ action_delay = 15
+ max_damage = 40
+ power_use = 10
+ desc = "As flexible as they are fragile, these Vey-Med manipulators can follow a pilot's movements in close to real time."
diff --git a/code/modules/exosuits/component/body.dm b/code/modules/exosuits/component/body.dm
new file mode 100644
index 00000000000..7a695927690
--- /dev/null
+++ b/code/modules/exosuits/component/body.dm
@@ -0,0 +1,389 @@
+/obj/item/mech_component/chassis
+ name = "body"
+ icon_state = "loader_body"
+ gender = NEUTER
+
+ var/mech_health = 300
+ var/obj/item/cell/cell
+ var/obj/item/robot_parts/robot_component/diagnosis_unit/diagnostics
+ var/obj/item/robot_parts/robot_component/armour/exosuit/armour
+ var/obj/machinery/portable_atmospherics/canister/air_supply
+ var/datum/gas_mixture/cockpit
+ var/transparent_cabin = FALSE
+ var/hide_pilot = FALSE
+ var/hatch_descriptor = "cockpit"
+ var/list/pilot_positions
+ var/pilot_coverage = 100
+ var/min_pilot_size = MOB_SMALL
+ var/max_pilot_size = MOB_LARGE
+ has_hardpoints = list(HARDPOINT_BACK, HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER)
+
+/obj/item/mech_component/chassis/Destroy()
+ QDEL_NULL(cell)
+ QDEL_NULL(diagnostics)
+ QDEL_NULL(armour)
+ QDEL_NULL(air_supply)
+ . = ..()
+
+/obj/item/mech_component/chassis/update_components()
+ diagnostics = locate() in src
+ cell = locate() in src
+ armour = locate() in src
+ air_supply = locate() in src
+
+/obj/item/mech_component/chassis/show_missing_parts(var/mob/user)
+ if(!cell)
+ . += SPAN_WARNING("It is missing a power cell.")
+ if(!diagnostics)
+ . += SPAN_WARNING("It is missing a diagnostics unit.")
+ if(!armour)
+ . += SPAN_WARNING("It is missing exosuit armour plating.")
+
+/obj/item/mech_component/chassis/Initialize()
+ . = ..()
+ cockpit = new(20)
+ if(loc)
+ var/datum/gas_mixture/air = loc.return_air()
+ if(air)
+ cockpit.equalize(air)
+ air_supply = new /obj/machinery/portable_atmospherics/canister/air(src)
+
+ if(isnull(pilot_positions))
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 0),
+ "[SOUTH]" = list("x" = 8, "y" = 0),
+ "[EAST]" = list("x" = 8, "y" = 0),
+ "[WEST]" = list("x" = 8, "y" = 0)
+ )
+ )
+
+/obj/item/mech_component/chassis/proc/update_air(var/take_from_supply)
+
+ var/changed
+ if(!take_from_supply || pilot_coverage < 100)
+ var/turf/T = get_turf(src)
+ if(!T)
+ return
+ cockpit.equalize(T.return_air())
+ changed = TRUE
+ else if(air_supply)
+ var/env_pressure = cockpit.return_pressure()
+ var/pressure_delta = air_supply.release_pressure - env_pressure
+ if((air_supply.air_contents.temperature > 0) && (pressure_delta > 0))
+ var/transfer_moles = calculate_transfer_moles(air_supply.air_contents, cockpit, pressure_delta)
+ transfer_moles = min(transfer_moles, (air_supply.release_flow_rate/air_supply.air_contents.volume)*air_supply.air_contents.total_moles)
+ pump_gas_passive(air_supply, air_supply.air_contents, cockpit, transfer_moles)
+ changed = TRUE
+ if(changed)
+ cockpit.react()
+
+/obj/item/mech_component/chassis/ready_to_install()
+ return (cell && diagnostics && armour)
+
+/obj/item/mech_component/chassis/prebuild()
+ diagnostics = new(src)
+ cell = new /obj/item/cell/high(src)
+ cell.charge = cell.maxcharge
+
+/obj/item/mech_component/chassis/attackby(var/obj/item/thing, var/mob/user)
+ if(istype(thing,/obj/item/robot_parts/robot_component/diagnosis_unit))
+ if(diagnostics)
+ to_chat(user, SPAN_WARNING("\The [src] already has a diagnostic system installed."))
+ return
+ if(install_component(thing, user)) diagnostics = thing
+ else if(istype(thing, /obj/item/cell))
+ if(cell)
+ to_chat(user, SPAN_WARNING("\The [src] already has a cell installed."))
+ return
+ if(install_component(thing,user)) cell = thing
+ else if(istype(thing, /obj/item/robot_parts/robot_component/armour/exosuit))
+ if(armour)
+ to_chat(user, SPAN_WARNING("\The [src] already has armour installed."))
+ return
+ if(install_component(thing, user))
+ armour = thing
+ else
+ return ..()
+
+/obj/item/mech_component/chassis/MouseDrop_T(atom/dropping, mob/user)
+ var/obj/machinery/portable_atmospherics/canister/C = dropping
+ if(istype(C) && do_after(user, 5, src))
+ to_chat(user, SPAN_NOTICE("You install the canister in the [src]."))
+ if(air_supply)
+ air_supply.forceMove(get_turf(src))
+ air_supply = null
+ C.forceMove(src)
+ update_components()
+ else . = ..()
+
+/obj/item/mech_component/chassis/proc/get_armor_values()
+ if(armour)
+ return armour.armor
+
+ return list(
+ melee =5,
+ bullet = 5,
+ laser = 5,
+ energy = 5,
+ bomb = 0,
+ bio = 5,
+ rad = 5
+ )
+
+/obj/item/mech_component/chassis/proc/get_armor(var/type)
+ var/list/checking = get_armor_values()
+ return checking[type]
+
+/*
+ * Variants
+ */
+
+/obj/item/mech_component/chassis/combat
+ name = "sealed exosuit chassis"
+ hatch_descriptor = "canopy"
+ pilot_coverage = 100
+ transparent_cabin = TRUE
+ exosuit_desc_string = "an armoured chassis"
+ icon_state = "combat_body"
+ power_use = 40
+ mech_health = 350
+
+/obj/item/mech_component/chassis/combat/prebuild()
+ . = ..()
+ armor = new /obj/item/robot_parts/robot_component/armour/exosuit/combat(src)
+
+/obj/item/mech_component/chassis/combat/Initialize()
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 8),
+ "[SOUTH]" = list("x" = 8, "y" = 8),
+ "[EAST]" = list("x" = 4, "y" = 8),
+ "[WEST]" = list("x" = 12, "y" = 8)
+ )
+ )
+
+ . = ..()
+
+/obj/item/mech_component/chassis/industrial
+ name = "open exosuit chassis"
+ hatch_descriptor = "roll cage"
+ pilot_coverage = 40
+ exosuit_desc_string = "an industrial rollcage"
+ desc = "An industrial roll cage. Technically OSHA compliant. Technically."
+ icon_state = "industrial_body"
+ max_damage = 100
+ power_use = 0
+
+/obj/item/mech_component/chassis/industrial/prebuild()
+ . = ..()
+ armour = new /obj/item/robot_parts/robot_component/armour/exosuit(src)
+
+/obj/item/mech_component/chassis/industrial/Initialize()
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 8),
+ "[SOUTH]" = list("x" = 8, "y" = 8),
+ "[EAST]" = list("x" = 8, "y" = 8),
+ "[WEST]" = list("x" = 8, "y" = 8)
+ ),
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 16),
+ "[SOUTH]" = list("x" = 8, "y" = 16),
+ "[EAST]" = list("x" = 0, "y" = 16),
+ "[WEST]" = list("x" = 16, "y" = 16)
+ )
+ )
+ . = ..()
+
+/obj/item/mech_component/chassis/heavy
+ name = "reinforced exosuit chassis"
+ hatch_descriptor = "hatch"
+ desc = "The HI-Koloss chassis is a veritable juggernaut, capable of protecting a pilot even in the most hostile of environments. It handles like a battlecruiser, however."
+ pilot_coverage = 100
+ exosuit_desc_string = "a heavily armoured chassis"
+ icon_state = "heavy_body"
+ max_damage = 150
+ mech_health = 500
+ power_use = 50
+ has_hardpoints = list(HARDPOINT_BACK)
+
+/obj/item/mech_component/chassis/heavy/prebuild()
+ . = ..()
+ armor = new /obj/item/robot_parts/robot_component/armour/exosuit/combat(src)
+
+/obj/item/mech_component/chassis/light
+ name = "light exosuit chassis"
+ hatch_descriptor = "canopy"
+ pilot_coverage = 100
+ transparent_cabin = TRUE
+ hide_pilot = TRUE //Sprite too small, legs clip through, so for now hide pilot
+ exosuit_desc_string = "an open and light chassis"
+ icon_state = "light_body"
+ max_damage = 50
+ power_use = 5
+ mech_health = 200
+ has_hardpoints = list(HARDPOINT_BACK)
+ desc = "The Veymed Odysseus series cockpits combine ultralight materials and clear aluminum laminates to provide an optimized cockpit experience."
+
+/obj/item/mech_component/chassis/light/prebuild()
+ . = ..()
+ armour = new /obj/item/robot_parts/robot_component/armour/exosuit/radproof(src)
+
+/obj/item/mech_component/chassis/light/Initialize()
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 8, "y" = -2),
+ "[SOUTH]" = list("x" = 8, "y" = -2),
+ "[EAST]" = list("x" = 1, "y" = -2),
+ "[WEST]" = list("x" = 9, "y" = -2)
+ )
+ )
+ . = ..()
+
+/obj/item/mech_component/chassis/mercenary
+ name = "reinforced exosuit chassis"
+ hatch_descriptor = "hatch"
+ desc = "The HI-Koloss chassis is a veritable juggernaut, capable of protecting a pilot even in the most hostile of environments. It handles like a battlecruiser, however."
+ pilot_coverage = 100
+ exosuit_desc_string = "a heavily armoured chassis"
+ icon_state = "mercenary_body"
+ max_damage = 150
+ mech_health = 500
+ power_use = 50
+ has_hardpoints = list(HARDPOINT_BACK)
+
+/obj/item/mech_component/chassis/mercenary/Initialize()
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 8),
+ "[SOUTH]" = list("x" = 9, "y" = 2),
+ "[EAST]" = list("x" = 4, "y" = 8),
+ "[WEST]" = list("x" = 12, "y" = 8)
+ )
+ )
+ . = ..()
+
+/obj/item/mech_component/chassis/mercenary/prebuild()
+ . = ..()
+ armour = new /obj/item/robot_parts/robot_component/armour/exosuit/combat(src)
+
+/obj/item/mech_component/chassis/pod
+ name = "spherical exosuit chassis"
+ hatch_descriptor = "hatch"
+ pilot_coverage = 100
+ transparent_cabin = TRUE
+ exosuit_desc_string = "a spherical chassis"
+ icon_state = "pod_body"
+ max_damage = 70
+ power_use = 5
+ has_hardpoints = list(HARDPOINT_BACK)
+ desc = "The Katamari series cockpits won a massive government tender a few years back. No one is sure why, but these terrible things keep popping up on every government-run facility."
+
+/obj/item/mech_component/chassis/pod/prebuild()
+ . = ..()
+ armour = new /obj/item/robot_parts/robot_component/armour/exosuit/radproof(src)
+
+/obj/item/mech_component/chassis/pod/Initialize()
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 4),
+ "[SOUTH]" = list("x" = 8, "y" = 4),
+ "[EAST]" = list("x" = 12, "y" = 4),
+ "[WEST]" = list("x" = 4, "y" = 4)
+ ),
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 8),
+ "[SOUTH]" = list("x" = 8, "y" = 8),
+ "[EAST]" = list("x" = 10, "y" = 8),
+ "[WEST]" = list("x" = 6, "y" = 8)
+ )
+ )
+ . = ..()
+
+/obj/item/mech_component/chassis/pod/prebuild()
+ . = ..()
+ armour = new /obj/item/robot_parts/robot_component/armour/exosuit/radproof(src)
+
+/obj/item/mech_component/chassis/modern
+ name = "sealed exosuit chassis"
+ hatch_descriptor = "canopy"
+ pilot_coverage = 100
+ hide_pilot = TRUE
+ exosuit_desc_string = "an armoured chassis"
+ icon_state = "modern_body"
+ power_use = 40
+ mech_health = 350
+
+/obj/item/mech_component/chassis/modern/prebuild()
+ . = ..()
+ armor = new /obj/item/robot_parts/robot_component/armour/exosuit/combat(src)
+
+/obj/item/mech_component/chassis/modern/Initialize()
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 8),
+ "[SOUTH]" = list("x" = 8, "y" = 8),
+ "[EAST]" = list("x" = 4, "y" = 8),
+ "[WEST]" = list("x" = 12, "y" = 8)
+ )
+ )
+ . = ..()
+
+/obj/item/mech_component/chassis/powerloader
+ name = "open exosuit chassis"
+ hatch_descriptor = "roll cage"
+ pilot_coverage = 40
+ exosuit_desc_string = "an industrial rollcage"
+ desc = "A Xion industrial brand roll cage. Technically OSHA compliant. Technically."
+ max_damage = 100
+ power_use = 0
+
+/obj/item/mech_component/chassis/powerloader/prebuild()
+ . = ..()
+ armour = new /obj/item/robot_parts/robot_component/armour/exosuit(src)
+
+/obj/item/mech_component/chassis/powerloader/Initialize()
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 8),
+ "[SOUTH]" = list("x" = 8, "y" = 8),
+ "[EAST]" = list("x" = 8, "y" = 8),
+ "[WEST]" = list("x" = 8, "y" = 8)
+ ),
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 16),
+ "[SOUTH]" = list("x" = 8, "y" = 16),
+ "[EAST]" = list("x" = 0, "y" = 16),
+ "[WEST]" = list("x" = 16, "y" = 16)
+ )
+ )
+ . = ..()
+
+/obj/item/mech_component/chassis/sleek
+ name = "sleek exosuit chassis"
+ hatch_descriptor = "canopy"
+ pilot_coverage = 100
+ transparent_cabin = TRUE
+ exosuit_desc_string = "an open and sleek chassis"
+ icon_state = "sleek_body"
+ max_damage = 50
+ mech_health = 200
+ power_use = 5
+ has_hardpoints = list(HARDPOINT_BACK)
+ desc = "The Veymed Odysseus series cockpits combine ultrasleek materials and clear aluminium laminates to provide an optimized cockpit experience."
+
+/obj/item/mech_component/chassis/sleek/prebuild()
+ . = ..()
+ armour = new /obj/item/robot_parts/robot_component/armour/exosuit/radproof(src)
+
+/obj/item/mech_component/chassis/sleek/Initialize()
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 8, "y" = 0),
+ "[SOUTH]" = list("x" = 8, "y" = 0),
+ "[EAST]" = list("x" = 3, "y" = 0),
+ "[WEST]" = list("x" = 13, "y" = 0)
+ )
+ )
+ . = ..()
diff --git a/code/modules/exosuits/component/frame.dm b/code/modules/exosuits/component/frame.dm
new file mode 100644
index 00000000000..783c5700da0
--- /dev/null
+++ b/code/modules/exosuits/component/frame.dm
@@ -0,0 +1,332 @@
+/obj/item/frame_holder
+ matter = list(MATERIAL_STEEL = 175000, MATERIAL_PLASTIC = 50000, MATERIAL_OSMIUM = 30000)
+
+/obj/item/frame_holder/Initialize(mapload, var/newloc)
+ ..()
+ new /obj/structure/heavy_vehicle_frame(newloc)
+ return INITIALIZE_HINT_QDEL
+
+/obj/structure/heavy_vehicle_frame
+ name = "exosuit frame"
+ desc = "The frame for an exosuit, apparently."
+ icon = 'icons/mob/mecha/mech_parts.dmi'
+ icon_state = "backbone"
+ density = 1
+ pixel_x = -8
+
+ var/datum/material/material = null
+
+ // Holders for the final product.
+ var/obj/item/mech_component/manipulators/arms
+ var/obj/item/mech_component/propulsion/legs
+ var/obj/item/mech_component/sensors/head
+ var/obj/item/mech_component/chassis/body
+ var/is_wired = 0
+ var/is_reinforced = 0
+ var/set_name
+ dir = SOUTH
+
+/obj/structure/heavy_vehicle_frame/proc/set_colour(var/new_colour)
+ var/painted_component = FALSE
+ for(var/obj/item/mech_component/comp in list(body, arms, legs, head))
+ if(comp.set_colour(new_colour))
+ painted_component = TRUE
+ if(painted_component)
+ update_icon()
+
+/obj/structure/heavy_vehicle_frame/Destroy()
+ QDEL_NULL(arms)
+ QDEL_NULL(legs)
+ QDEL_NULL(head)
+ QDEL_NULL(body)
+ . = ..()
+
+/obj/structure/heavy_vehicle_frame/examine(mob/user)
+ . = ..()
+ if(!arms)
+ . += SPAN_WARNING("It is missing manipulators.")
+ if(!legs)
+ . += SPAN_WARNING("It is missing propulsion.")
+ if(!head)
+ . += SPAN_WARNING("It is missing sensors.")
+ if(!body)
+ . += SPAN_WARNING("It is missing a chassis.")
+ if(is_wired == EXOFRAME_WIRED)
+ . += SPAN_WARNING("It has not had its wiring adjusted.")
+ else if(!is_wired)
+ . += SPAN_WARNING("It has not yet been wired.")
+ if(is_reinforced == EXOFRAME_REINFORCED)
+ . += SPAN_WARNING("It has not had its internal reinforcement secured.")
+ else if(is_reinforced == EXOFRAME_REINFORCED_SECURE)
+ . += SPAN_WARNING("It has not had its internal reinforcement welded in.")
+ else if(!is_reinforced)
+ . += SPAN_WARNING("It does not have any internal reinforcement.")
+
+/obj/structure/heavy_vehicle_frame/update_icon()
+ var/list/new_overlays = get_mech_images(list(legs, head, body, arms), layer)
+ if(body)
+ density = TRUE
+ overlays += get_mech_image(null, "[body.icon_state]_cockpit", body.icon, body.color)
+ if(body.pilot_coverage < 100 || body.transparent_cabin)
+ new_overlays += get_mech_image(null, "[body.icon_state]_open_overlay", body.icon, body.color)
+ else
+ density = FALSE
+ overlays = new_overlays
+ if(density != opacity)
+ set_opacity(density)
+
+/obj/structure/heavy_vehicle_frame/set_dir()
+ ..(SOUTH)
+
+/obj/structure/heavy_vehicle_frame/attackby(var/obj/item/thing, var/mob/user)
+
+ // Removing components.
+ if(thing.has_tool_quality(TOOL_CROWBAR))
+ if(is_reinforced == EXOFRAME_REINFORCED)
+ if(!do_after(user, 5) || !material)
+ return
+ user.visible_message(SPAN_NOTICE("\The [user] crowbars the reinforcement off \the [src]."))
+ material.place_sheet(src.loc, 10)
+ material = null
+ is_reinforced = 0
+ return
+
+ if(!arms && !body && !legs && !head)
+ to_chat(user, SPAN_WARNING("There are no components to remove."))
+ return
+
+ var/to_remove = input("Which component would you like to remove") as null|anything in list(arms, body, legs, head)
+
+ if(!to_remove)
+ return
+
+ if(uninstall_component(to_remove, user))
+ if(to_remove == arms)
+ arms = null
+ else if(to_remove == body)
+ body = null
+ else if(to_remove == legs)
+ legs = null
+ else if(to_remove == head)
+ head = null
+
+ update_icon()
+ return
+
+ // Final construction step.
+ else if(thing.has_tool_quality(TOOL_SCREWDRIVER))
+
+ // Check for basic components.
+ if(!(arms && legs && head && body))
+ to_chat(user, SPAN_WARNING("There are still parts missing from \the [src]."))
+ return
+
+ // Check for wiring.
+ if(is_wired < EXOFRAME_WIRED_ADJUSTED)
+ if(is_wired == EXOFRAME_WIRED)
+ to_chat(user, SPAN_WARNING("\The [src]'s wiring has not been adjusted!"))
+ else
+ to_chat(user, SPAN_WARNING("\The [src] is not wired!"))
+ return
+
+ // Check for basing metal internal plating.
+ if(is_reinforced < EXOFRAME_REINFORCED_WELDED)
+ if(is_reinforced == EXOFRAME_REINFORCED)
+ to_chat(user, SPAN_WARNING("\The [src]'s internal reinforcement has not been secured!"))
+ else if(is_reinforced == EXOFRAME_REINFORCED_SECURE)
+ to_chat(user, SPAN_WARNING("\The [src]'s internal reinforcement has not been welded down!"))
+ else
+ to_chat(user, SPAN_WARNING("\The [src] has no internal reinforcement!"))
+ return
+
+ visible_message(SPAN_NOTICE("\The [user] begins tightening screws, flipping connectors and finishing off \the [src]."))
+ if(!do_after(user, 50, src))
+ return
+
+ if(is_reinforced < EXOFRAME_REINFORCED_WELDED || is_wired < EXOFRAME_WIRED_ADJUSTED || !(arms && legs && head && body) || QDELETED(src) || QDELETED(user))
+ return
+
+ // We're all done. Finalize the exosuit and pass the frame to the new system.
+ var/mob/living/exosuit/M = new(get_turf(src), src)
+ visible_message(SPAN_NOTICE("\The [user] finishes off \the [M]."))
+ playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1)
+
+ arms = null
+ legs = null
+ head = null
+ body = null
+ qdel(src)
+
+ return
+
+ // Installing wiring.
+ else if(istype(thing, /obj/item/stack/cable_coil))
+
+ if(is_wired)
+ to_chat(user, SPAN_WARNING("\The [src] has already been wired."))
+ return
+
+ var/obj/item/stack/cable_coil/CC = thing
+ if(CC.get_amount() < 10)
+ to_chat(user, SPAN_WARNING("You need at least ten units of cable to complete the exosuit."))
+ return
+
+ user.visible_message("\The [user] begins wiring \the [src]...")
+
+ if(!do_after(user, 30))
+ return
+
+ if(!CC || !user || !src || CC.get_amount() < 10 || is_wired)
+ return
+
+ CC.use(10)
+ user.visible_message("\The [user] installs wiring in \the [src].")
+ playsound(user.loc, 'sound/items/Deconstruct.ogg', 50, 1)
+ is_wired = EXOFRAME_WIRED
+ // Securing wiring.
+ else if(thing.has_tool_quality(TOOL_WIRECUTTER))
+ if(!is_wired)
+ to_chat(user, "There is no wiring in \the [src] to neaten.")
+ return
+
+ user.visible_message("\The [user] begins adjusting the wiring inside \the [src]...")
+ var/last_wiring_state = is_wired
+ if(!do_after(user, 30) || last_wiring_state != is_wired)
+ return
+
+ visible_message("\The [user] [(is_wired == EXOFRAME_WIRED_ADJUSTED) ? "snips some of" : "neatens"] the wiring in \the [src].")
+ playsound(user.loc, 'sound/items/Wirecutter.ogg', 100, 1)
+ is_wired = (is_wired == EXOFRAME_WIRED_ADJUSTED) ? EXOFRAME_WIRED : EXOFRAME_WIRED_ADJUSTED
+ // Installing metal.
+ else if(istype(thing, /obj/item/stack/material))
+ var/obj/item/stack/material/M = thing
+ if(M.material)
+ if(is_reinforced)
+ to_chat(user, SPAN_WARNING("There is already a material reinforcement installed in \the [src]."))
+ return
+ if(M.get_amount() < 10)
+ to_chat(user, SPAN_WARNING("You need at least ten sheets to reinforce \the [src]."))
+ return
+
+ visible_message("\The [user] begins layering the interior of the \the [src] with \the [M].")
+
+ if(!do_after(user, 30) || is_reinforced)
+ return
+
+ visible_message("\The [user] reinforces \the [src] with \the [M].")
+ playsound(user.loc, 'sound/items/Deconstruct.ogg', 50, 1)
+ material = M.material
+ is_reinforced = EXOFRAME_REINFORCED
+ M.use(10)
+ else
+ return ..()
+ // Securing metal.
+ else if(thing.has_tool_quality(TOOL_WRENCH))
+ if(!is_reinforced)
+ to_chat(user, SPAN_WARNING("There is no metal to secure inside \the [src]."))
+ return
+ if(is_reinforced == EXOFRAME_REINFORCED_WELDED)
+ to_chat(user, SPAN_WARNING("\The [src]'s internal reinforcment has been welded in."))
+ return
+
+ var/last_reinforced_state = is_reinforced
+ visible_message("\The [user] begins adjusting the metal reinforcement inside \the [src].")
+
+ if(!do_after(user, 4 SECONDS, src) || last_reinforced_state != is_reinforced)
+ return
+
+ visible_message("\The [user] [(is_reinforced == 2) ? "unsecures" : "secures"] the metal reinforcement inside \the [src].")
+ playsound(user.loc, 'sound/items/Ratchet.ogg', 100, 1)
+ is_reinforced = (is_reinforced == EXOFRAME_REINFORCED_SECURE) ? EXOFRAME_REINFORCED : EXOFRAME_REINFORCED_SECURE
+ // Welding metal.
+ else if(istype(thing, /obj/item/weldingtool))
+ var/obj/item/weldingtool/WT = thing
+ if(!is_reinforced)
+ to_chat(user, SPAN_WARNING("There is no metal to secure inside \the [src]."))
+ return
+ if(is_reinforced == EXOFRAME_REINFORCED)
+ to_chat(user, SPAN_WARNING("The reinforcement inside \the [src] has not been secured."))
+ return
+ if(!WT.isOn())
+ to_chat(user, SPAN_WARNING("Turn \the [WT] on, first."))
+ return
+ if(WT.remove_fuel(1, user))
+
+ var/last_reinforced_state = is_reinforced
+ visible_message("\The [user] begins welding the metal reinforcement inside \the [src].")
+ if(!do_after(user, 20) || last_reinforced_state != is_reinforced)
+ return
+
+ visible_message("\The [user] [(is_reinforced == EXOFRAME_REINFORCED_WELDED) ? "unwelds the reinforcement from" : "welds the reinforcement into"] \the [src].")
+ is_reinforced = (is_reinforced == EXOFRAME_REINFORCED_WELDED) ? EXOFRAME_REINFORCED_SECURE : EXOFRAME_REINFORCED_WELDED
+ playsound(user.loc, 'sound/items/Welder.ogg', 50, 1)
+ else
+ to_chat(user, SPAN_WARNING("Not enough fuel!"))
+ return
+ // Installing basic components.
+ else if(istype(thing,/obj/item/mech_component/manipulators))
+ if(arms)
+ to_chat(user, SPAN_WARNING("\The [src] already has manipulators installed."))
+ return
+ if(install_component(thing, user))
+ if(arms)
+ thing.dropInto(loc)
+ return
+ arms = thing
+ else if(istype(thing,/obj/item/mech_component/propulsion))
+ if(legs)
+ to_chat(user, SPAN_WARNING("\The [src] already has a propulsion system installed."))
+ return
+ if(install_component(thing, user))
+ if(legs)
+ thing.dropInto(loc)
+ return
+ legs = thing
+ else if(istype(thing,/obj/item/mech_component/sensors))
+ if(head)
+ to_chat(user, SPAN_WARNING("\The [src] already has a sensor array installed."))
+ return
+ if(install_component(thing, user))
+ if(head)
+ thing.dropInto(loc)
+ return
+ head = thing
+ else if(istype(thing,/obj/item/mech_component/chassis))
+ if(body)
+ to_chat(user, SPAN_WARNING("\The [src] already has an outer chassis installed."))
+ return
+ if(install_component(thing, user))
+ if(body)
+ thing.dropInto(loc)
+ return
+ body = thing
+ else
+ return ..()
+ update_icon()
+
+/obj/structure/heavy_vehicle_frame/proc/install_component(var/obj/item/thing, var/mob/user)
+ var/obj/item/mech_component/MC = thing
+ if(istype(MC) && !MC.ready_to_install())
+ to_chat(user, SPAN_WARNING("\The [MC] [MC.gender == PLURAL ? "are" : "is"] not ready to install."))
+ return 0
+ if(user)
+ visible_message(SPAN_NOTICE("\The [user] begins installing \the [thing] into \the [src]."))
+ if(!user.canUnEquip(thing) || !do_after(user, 30) || user.get_active_hand() != thing)
+ return
+ if(!user.unEquip(thing))
+ return
+ thing.forceMove(src)
+ visible_message(SPAN_NOTICE("\The [user] installs \the [thing] into \the [src]."))
+ playsound(user.loc, 'sound/machines/click.ogg', 50, 1)
+ return 1
+
+/obj/structure/heavy_vehicle_frame/proc/uninstall_component(var/obj/item/component, var/mob/user)
+ if(!istype(component) || (component.loc != src) || !istype(user))
+ return FALSE
+ if(!do_after(user, 40) || component.loc != src)
+ return FALSE
+ user.visible_message(SPAN_NOTICE("\The [user] crowbars \the [component] off \the [src]."))
+ component.forceMove(get_turf(src))
+ user.put_in_hands(component)
+ playsound(user.loc, 'sound/items/Deconstruct.ogg', 50, 1)
+ return TRUE
diff --git a/code/modules/exosuits/component/head.dm b/code/modules/exosuits/component/head.dm
new file mode 100644
index 00000000000..a6258ca561c
--- /dev/null
+++ b/code/modules/exosuits/component/head.dm
@@ -0,0 +1,237 @@
+/obj/item/mech_component/sensors
+ name = "head"
+ icon_state = "loader_head"
+ gender = NEUTER
+
+ var/vision_flags = 0
+ var/see_invisible = 0
+ var/obj/item/robot_parts/robot_component/radio/radio
+ var/obj/item/robot_parts/robot_component/camera
+ var/obj/item/mech_component/control_module/software
+ has_hardpoints = list(HARDPOINT_HEAD)
+ var/active_sensors = 0
+ power_use = 15
+
+/obj/item/mech_component/sensors/Destroy()
+ QDEL_NULL(camera)
+ QDEL_NULL(radio)
+ QDEL_NULL(software)
+ . = ..()
+
+/obj/item/mech_component/sensors/show_missing_parts(var/mob/user)
+ if(!radio)
+ . += SPAN_WARNING("It is missing a radio.")
+ if(!camera)
+ . += SPAN_WARNING("It is missing a camera.")
+ if(!software)
+ . += SPAN_WARNING("It is missing a software control module.")
+
+/obj/item/mech_component/sensors/prebuild()
+ radio = new(src)
+ camera = new(src)
+
+/obj/item/mech_component/sensors/update_components()
+ radio = locate() in src
+ camera = locate() in src
+ software = locate() in src
+
+/obj/item/mech_component/sensors/proc/get_sight()
+ var/flags = 0
+ if(total_damage >= 0.8 * max_damage)
+ flags |= BLIND
+ else if(active_sensors)
+ flags |= vision_flags
+
+ return flags
+
+/obj/item/mech_component/sensors/proc/get_invisible()
+ var/invisible = 0
+ if((total_damage <= 0.8 * max_damage) && active_sensors)
+ invisible = see_invisible
+ return invisible
+
+
+
+/obj/item/mech_component/sensors/ready_to_install()
+ return (radio && camera)
+
+/obj/item/mech_component/sensors/attackby(var/obj/item/thing, var/mob/user)
+ if(istype(thing, /obj/item/mech_component/control_module))
+ if(software)
+ to_chat(user, SPAN_WARNING("\The [src] already has a control module installed."))
+ return
+ if(install_component(thing, user)) software = thing
+ else if(istype(thing,/obj/item/robot_parts/robot_component/radio))
+ if(radio)
+ to_chat(user, SPAN_WARNING("\The [src] already has a radio installed."))
+ return
+ if(install_component(thing, user)) radio = thing
+ else if(istype(thing,/obj/item/robot_parts/robot_component/camera))
+ if(camera)
+ to_chat(user, SPAN_WARNING("\The [src] already has a camera installed."))
+ return
+ if(install_component(thing, user)) camera = thing
+ else
+ return ..()
+
+/obj/item/mech_component/control_module
+ name = "exosuit control module"
+ desc = "A clump of circuitry and software chip docks, used to program exosuits."
+ icon_state = "control"
+ icon = 'icons/mob/mecha/mech_equipment.dmi'
+ gender = NEUTER
+ var/list/installed_software = list()
+ var/max_installed_software = 2
+
+/obj/item/mech_component/control_module/examine(mob/user)
+ . = ..()
+ . += SPAN_NOTICE("It has [max_installed_software - LAZYLEN(installed_software)] empty slot\s remaining out of [max_installed_software].")
+
+/obj/item/mech_component/control_module/attackby(var/obj/item/thing, var/mob/user)
+
+ if(istype(thing, /obj/item/weapon/circuitboard/exosystem))
+ install_software(thing, user)
+ return
+
+ if(thing.has_tool_quality(TOOL_SCREWDRIVER))
+ var/result = ..()
+ update_software()
+ return result
+ else
+ return ..()
+
+/obj/item/mech_component/control_module/proc/install_software(var/obj/item/weapon/circuitboard/exosystem/software, var/mob/user)
+ if(installed_software.len >= max_installed_software)
+ if(user)
+ to_chat(user, SPAN_WARNING("\The [src] can only hold [max_installed_software] software modules."))
+ return
+ if(user && !user.unEquip(software))
+ return
+
+ if(user)
+ to_chat(user, SPAN_NOTICE("You load \the [software] into \the [src]'s memory."))
+
+ software.forceMove(src)
+ update_software()
+
+/obj/item/mech_component/control_module/proc/update_software()
+ installed_software = list()
+ for(var/obj/item/weapon/circuitboard/exosystem/program in contents)
+ installed_software |= program.contains_software
+
+/*
+ * Variants
+ */
+
+/obj/item/mech_component/sensors/combat
+ name = "combat sensors"
+ gender = PLURAL
+ exosuit_desc_string = "high-resolution thermal sensors"
+ icon_state = "combat_head"
+ vision_flags = SEE_MOBS
+ see_invisible = SEE_INVISIBLE_NOLIGHTING
+ power_use = 200
+
+/obj/item/mech_component/sensors/combat/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_WEAPONS, MECH_SOFTWARE_ADVWEAPONS)
+
+/obj/item/mech_component/sensors/industrial
+ name = "exosuit sensors"
+ gender = PLURAL
+ exosuit_desc_string = "simple collision detection sensors"
+ desc = "A primitive set of sensors designed to work in tandem with most MKI Eyeball platforms."
+ icon_state = "industrial_head"
+ max_damage = 100
+ power_use = 0
+
+/obj/item/mech_component/sensors/industrial/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_ENGINEERING)
+
+/obj/item/mech_component/sensors/heavy
+ name = "heavy sensors"
+ exosuit_desc_string = "a reinforced monoeye"
+ desc = "A solitary sensor moves inside a recessed slit in the armour plates."
+ icon_state = "heavy_head"
+ max_damage = 120
+ power_use = 0
+
+/obj/item/mech_component/sensors/heavy/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_WEAPONS, MECH_SOFTWARE_ADVWEAPONS)
+
+/obj/item/mech_component/sensors/light
+ name = "light sensors"
+ gender = PLURAL
+ exosuit_desc_string = "advanced sensor array"
+ icon_state = "light_head"
+ max_damage = 30
+ vision_flags = SEE_TURFS
+ see_invisible = SEE_INVISIBLE_NOLIGHTING
+ power_use = 50
+ desc = "A series of high resolution optical sensors. They can overlay several images to give the pilot a sense of location even in total darkness. "
+
+/obj/item/mech_component/sensors/light/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_MEDICAL)
+
+/obj/item/mech_component/sensors/mercenary
+ name = "mercenary sensors"
+ exosuit_desc_string = "a reinforced monoeye"
+ desc = "A solitary sensor moves inside a recessed slit in the armour plates."
+ icon_state = "mercenary_head"
+ max_damage = 120
+ power_use = 0
+
+/obj/item/mech_component/sensors/mercenary/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_WEAPONS)
+
+/obj/item/mech_component/sensors/modern
+ name = "modern sensors"
+ gender = PLURAL
+ exosuit_desc_string = "high-resolution thermal sensors"
+ icon_state = "modern_head"
+ vision_flags = SEE_MOBS
+ see_invisible = SEE_INVISIBLE_NOLIGHTING
+ power_use = 200
+
+/obj/item/mech_component/sensors/modern/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_WEAPONS, MECH_SOFTWARE_ADVWEAPONS)
+
+/obj/item/mech_component/sensors/powerloader
+ name = "exosuit sensors"
+ gender = PLURAL
+ exosuit_desc_string = "simple collision detection sensors"
+ desc = "A primitive set of sensors designed to work in tandem with most MKI Eyeball platforms."
+ max_damage = 100
+ power_use = 0
+
+/obj/item/mech_component/sensors/powerloader/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_ENGINEERING)
+
+/obj/item/mech_component/sensors/sleek
+ name = "sleek sensors"
+ gender = PLURAL
+ exosuit_desc_string = "advanced sensor array"
+ icon_state = "sleek_head"
+ max_damage = 30
+ vision_flags = SEE_TURFS
+ see_invisible = SEE_INVISIBLE_NOLIGHTING
+ power_use = 50
+ desc = "A series of high resolution optical sensors. They can overlay several images to give the pilot a sense of location even in total darkness. "
+
+/obj/item/mech_component/sensors/sleek/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_MEDICAL)
diff --git a/code/modules/exosuits/component/legs.dm b/code/modules/exosuits/component/legs.dm
new file mode 100644
index 00000000000..7dc23a78f09
--- /dev/null
+++ b/code/modules/exosuits/component/legs.dm
@@ -0,0 +1,137 @@
+/obj/item/mech_component/propulsion
+ name = "legs"
+ pixel_y = 12
+ icon_state = "loader_legs"
+ var/move_delay = 5
+ var/turn_delay = 5
+ var/obj/item/robot_parts/robot_component/actuator/motivator
+ power_use = 50
+
+/obj/item/mech_component/propulsion/Destroy()
+ QDEL_NULL(motivator)
+ . = ..()
+
+/obj/item/mech_component/propulsion/show_missing_parts(var/mob/user)
+ if(!motivator)
+ . += SPAN_WARNING("It is missing an actuator.")
+
+/obj/item/mech_component/propulsion/ready_to_install()
+ return motivator
+
+/obj/item/mech_component/propulsion/update_components()
+ motivator = locate() in src
+
+/obj/item/mech_component/propulsion/attackby(var/obj/item/thing, var/mob/user)
+ if(istype(thing,/obj/item/robot_parts/robot_component/actuator))
+ if(motivator)
+ to_chat(user, SPAN_WARNING("\The [src] already has an actuator installed."))
+ return
+ if(install_component(thing, user)) motivator = thing
+ else
+ return ..()
+
+/obj/item/mech_component/propulsion/prebuild()
+ motivator = new(src)
+
+/obj/item/mech_component/propulsion/proc/can_move_on(var/turf/location, var/turf/target_loc)
+ return isatom(location) && istype(target_loc)
+
+/*
+ * Variants
+ */
+
+/obj/item/mech_component/propulsion/combat
+ name = "combat legs"
+ exosuit_desc_string = "sleek hydraulic legs"
+ icon_state = "combat_legs"
+ move_delay = 3
+ turn_delay = 3
+ power_use = 20
+
+/obj/item/mech_component/propulsion/industrial
+ name = "exosuit legs"
+ exosuit_desc_string = "reinforced hydraulic legs"
+ desc = "Wide and stable but not particularly fast."
+ icon_state = "industrial_legs"
+ max_damage = 70
+ move_delay = 4
+ turn_delay = 4
+ power_use = 10
+
+/obj/item/mech_component/propulsion/heavy
+ name = "heavy legs"
+ exosuit_desc_string = "heavy hydraulic legs"
+ desc = "Oversized actuators struggle to move these armoured legs. "
+ icon_state = "heavy_legs"
+ move_delay = 5
+ turn_delay = 5
+ max_damage = 160
+ power_use = 100
+
+/obj/item/mech_component/propulsion/spider
+ name = "quadlegs"
+ exosuit_desc_string = "hydraulic quadlegs"
+ desc = "Xion Industrial's arachnid series boasts more leg per leg than the leading competitor."
+ icon_state = "spiderlegs"
+ max_damage = 80
+ move_delay = 4
+ turn_delay = 1
+ power_use = 25
+
+/obj/item/mech_component/propulsion/tracks
+ name = "tracks"
+ exosuit_desc_string = "armored tracks"
+ desc = "A classic brought back. The Hephaestus' Landmaster class tracks are impervious to most damage and can maintain top speed regardless of load. Watch out for corners."
+ icon_state = "tracks"
+ max_damage = 150
+ move_delay = 2 //It´s fast
+ turn_delay = 7
+ power_use = 150
+ color = COLOR_WHITE
+
+/obj/item/mech_component/propulsion/light
+ name = "light legs"
+ exosuit_desc_string = "aerodynamic electromechanic legs"
+ icon_state = "light_legs"
+ move_delay = 2
+ turn_delay = 3
+ max_damage = 40
+ power_use = 5
+ desc = "The electrical systems driving these legs are almost totally silent. Unfortunately slamming a plate of metal against the ground is not."
+
+/obj/item/mech_component/propulsion/mercenary
+ name = "mercenary legs"
+ exosuit_desc_string = "mercenary hydraulic legs"
+ desc = "Oversized actuators struggle to move these armoured legs. "
+ icon_state = "mercenary_legs"
+ move_delay = 5
+ turn_delay = 5
+ max_damage = 160
+ power_use = 100
+
+/obj/item/mech_component/propulsion/modern
+ name = "modern legs"
+ exosuit_desc_string = "sleek hydraulic legs"
+ icon_state = "modern_legs"
+ move_delay = 3
+ turn_delay = 3
+ power_use = 20
+
+/obj/item/mech_component/propulsion/powerloader
+ name = "exosuit legs"
+ exosuit_desc_string = "reinforced hydraulic legs"
+ desc = "Wide and stable but not particularly fast."
+ max_damage = 70
+ move_delay = 4
+ turn_delay = 4
+ power_use = 10
+
+/obj/item/mech_component/propulsion/sleek
+ name = "sleek legs"
+ exosuit_desc_string = "aerodynamic electromechanic legs"
+ icon_state = "sleek_legs"
+ move_delay = 2
+ turn_delay = 3
+ max_damage = 40
+ power_use = 5
+ desc = "The electrical systems driving these legs are almost totally silent. Unfortunately slamming a plate of metal against the ground is not."
diff --git a/code/modules/exosuits/component/software.dm b/code/modules/exosuits/component/software.dm
new file mode 100644
index 00000000000..b6e89a650a4
--- /dev/null
+++ b/code/modules/exosuits/component/software.dm
@@ -0,0 +1,39 @@
+#define T_BOARD_MECH(name) "exosuit software (" + (name) + ")"
+
+/obj/item/weapon/circuitboard/exosystem
+ name = "exosuit software template"
+ icon = 'icons/obj/module.dmi'
+ icon_state = "std_mod"
+ item_state = "electronic"
+ var/list/contains_software = list()
+
+/obj/item/weapon/circuitboard/exosystem/engineering
+ name = T_BOARD_MECH(MECH_SOFTWARE_ENGINEERING)
+ contains_software = list(MECH_SOFTWARE_ENGINEERING)
+ origin_tech = list(TECH_DATA = 1)
+
+/obj/item/weapon/circuitboard/exosystem/utility
+ name = T_BOARD_MECH(MECH_SOFTWARE_UTILITY)
+ contains_software = list(MECH_SOFTWARE_UTILITY)
+ icon_state = "mcontroller"
+ origin_tech = list(TECH_DATA = 1)
+
+/obj/item/weapon/circuitboard/exosystem/medical
+ name = T_BOARD_MECH(MECH_SOFTWARE_MEDICAL)
+ contains_software = list(MECH_SOFTWARE_MEDICAL)
+ icon_state = "mcontroller"
+ origin_tech = list(TECH_DATA = 3,TECH_BIO = 2)
+
+/obj/item/weapon/circuitboard/exosystem/weapons
+ name = T_BOARD_MECH(MECH_SOFTWARE_WEAPONS)
+ contains_software = list(MECH_SOFTWARE_WEAPONS)
+ icon_state = "mainboard"
+ origin_tech = list(TECH_DATA = 4, TECH_COMBAT = 3)
+
+/obj/item/weapon/circuitboard/exosystem/advweapons
+ name = T_BOARD_MECH(MECH_SOFTWARE_ADVWEAPONS)
+ contains_software = list(MECH_SOFTWARE_ADVWEAPONS)
+ icon_state = "mainboard"
+ origin_tech = list(TECH_DATA = 4, TECH_COMBAT = 5)
+
+#undef T_BOARD_MECH
diff --git a/code/modules/exosuits/equipment/_equipment.dm b/code/modules/exosuits/equipment/_equipment.dm
new file mode 100644
index 00000000000..6a9d652aa13
--- /dev/null
+++ b/code/modules/exosuits/equipment/_equipment.dm
@@ -0,0 +1,117 @@
+// Defining all of this here so it's centralized.
+// Used by the exosuit HUD to get a 1-10 value representing charge, ammo, etc.
+/obj/item/mech_equipment
+ name = "exosuit hardpoint system"
+ icon = 'icons/mob/mecha/mech_equipment.dmi'
+ icon_state = ""
+ matter = list(MATERIAL_STEEL = 10000, MATERIAL_PLASTIC = 5000, MATERIAL_OSMIUM = 500)
+ force = 10
+
+ var/restricted_hardpoints
+ var/mob/living/exosuit/owner
+ var/list/restricted_software
+ var/equipment_delay = 0
+ var/active_power_use = 1 KILOWATTS // How much does it consume to perform and accomplish usage
+ var/passive_power_use = 0 // For gear that for some reason takes up power even if it's supposedly doing nothing (mech will idly consume power)
+ var/mech_layer = MECH_GEAR_LAYER //For the part where it's rendered as mech gear
+ var/require_adjacent = TRUE
+
+/obj/item/mech_equipment/attack() //Generally it's not desired to be able to attack with items
+ return FALSE
+
+/obj/item/mech_equipment/proc/powercheck(var/mob/living/user)
+ if (owner && loc == owner && ((user in owner.pilots) || user == owner))
+ var/obj/item/cell/OwnerCell = owner.get_cell()
+ if(OwnerCell && OwnerCell.check_charge(active_power_use * CELLRATE))
+ return TRUE
+ to_chat(user, SPAN_WARNING("The power indicator flashes briefly as you attempt to use \the [src]"))
+ return FALSE
+
+/obj/item/mech_equipment/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params)
+ if(require_adjacent)
+ if(!inrange)
+ return 0
+
+ if(target in owner.contents)
+ return FALSE
+
+ return powercheck(user)
+
+/obj/item/mech_equipment/attack_self(var/mob/user)
+ return powercheck(user)
+
+/obj/item/mech_equipment/proc/installed(var/mob/living/exosuit/_owner)
+ owner = _owner
+ //generally attached. Nothing should be able to grab it
+ canremove = FALSE
+
+/obj/item/mech_equipment/proc/uninstalled()
+ owner = null
+ canremove = TRUE
+
+/obj/item/mech_equipment/Destroy()
+ owner = null
+ . = ..()
+
+/obj/item/mech_equipment/proc/get_effective_obj()
+ return src
+
+/obj/item/mech_equipment/proc/MouseDragInteraction()
+ return FALSE
+
+/obj/item/mech_equipment/mob_can_unequip(mob/M, slot, disable_warning)
+ return ..() && !owner
+
+/obj/item/mech_equipment/mounted_system
+ var/holding_type
+ var/obj/item/holding
+
+/obj/item/mech_equipment/mounted_system/attack_self(var/mob/user)
+ . = ..()
+ if(. && holding)
+ return holding.attack_self(user)
+
+/obj/item/mech_equipment/mounted_system/proc/forget_holding()
+ if(holding) //It'd be strange for this to be called with this var unset
+ GLOB.destroyed_event.unregister(holding, src, .proc/forget_holding)
+ holding = null
+ qdel(src)
+
+/obj/item/mech_equipment/mounted_system/Initialize()
+ . = ..()
+ if(holding_type)
+ holding = new holding_type(src)
+ GLOB.destroyed_event.register(holding, src, .proc/forget_holding)
+ if(holding)
+ if(!icon_state)
+ icon = holding.icon
+ icon_state = holding.icon_state
+ name = holding.name
+ desc = "[holding.desc] This one is suitable for installation on an exosuit."
+
+
+/obj/item/mech_equipment/mounted_system/Destroy()
+ if(holding)
+ QDEL_NULL(holding)
+ . = ..()
+
+
+/obj/item/mech_equipment/mounted_system/get_effective_obj()
+ return (holding ? holding : src)
+
+/obj/item/mech_equipment/mounted_system/get_hardpoint_status_value()
+ return (holding ? holding.get_hardpoint_status_value() : null)
+
+/obj/item/mech_equipment/mounted_system/get_hardpoint_maptext()
+ return (holding ? holding.get_hardpoint_maptext() : null)
+
+/obj/item/proc/get_hardpoint_status_value()
+ return null
+
+/obj/item/proc/get_hardpoint_maptext()
+ return null
+
+/obj/item/mech_equipment/mounted_system/get_cell()
+ if(owner && loc == owner)
+ return owner.get_cell()
+ return null
diff --git a/code/modules/exosuits/equipment/combat.dm b/code/modules/exosuits/equipment/combat.dm
new file mode 100644
index 00000000000..0b9ba3ff265
--- /dev/null
+++ b/code/modules/exosuits/equipment/combat.dm
@@ -0,0 +1,504 @@
+/obj/item/mech_equipment/mounted_system/taser
+ name = "mounted electrolaser carbine"
+ desc = "A dual fire mode electrolaser system connected to the exosuit's targetting system."
+ icon_state = "mech_taser"
+ holding_type = /obj/item/gun/energy/taser/carbine/mounted/mech
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_WEAPONS)
+
+/obj/item/mech_equipment/mounted_system/taser/ion
+ name = "mounted ion rifle"
+ desc = "An exosuit-mounted ion rifle. Handle with care."
+ icon_state = "mech_ionrifle"
+ holding_type = /obj/item/gun/energy/ionrifle/mounted/mech
+
+/obj/item/mech_equipment/mounted_system/taser/laser
+ name = "\improper CH-PS \"Immolator\" laser"
+ desc = "An exosuit-mounted laser rifle. Handle with care."
+ icon_state = "mech_lasercarbine"
+ holding_type = /obj/item/gun/energy/laser/mounted/mech
+
+/obj/item/gun/energy/taser/carbine/mounted/mech
+ name = "mounted electrolaser carbine"
+ use_external_power = TRUE
+ self_recharge = TRUE
+
+/obj/item/gun/energy/ionrifle/mounted/mech
+ use_external_power = TRUE
+ self_recharge = TRUE
+
+/obj/item/gun/energy/laser/mounted/mech
+ name = "\improper CH-PS \"Immolator\" laser"
+ use_external_power = TRUE
+ self_recharge = TRUE
+
+/obj/item/gun/energy/get_hardpoint_maptext()
+ return "[round(power_supply.charge / charge_cost)]/[round(power_supply.maxcharge / charge_cost)]"
+
+/obj/item/gun/energy/get_hardpoint_status_value()
+ var/obj/item/cell/C = get_cell()
+ if(istype(C))
+ return C.charge/C.maxcharge
+ return null
+
+/*
+/obj/item/mech_equipment/shields
+ name = "exosuit shield droid"
+ desc = "The Hephaestus Armature system is a well liked energy deflector system designed to stop any projectile before it has a chance to become a threat."
+ icon_state = "shield_droid"
+ var/obj/aura/mechshield/aura = null
+ var/max_charge = 250
+ var/charge = 250
+ var/last_recharge = 0
+ var/charging_rate = 7500 * CELLRATE
+ var/cooldown = 3.5 SECONDS //Time until we can recharge again after a blocked impact
+ restricted_hardpoints = list(HARDPOINT_BACK)
+ restricted_software = list(MECH_SOFTWARE_WEAPONS)
+
+/obj/item/mech_equipment/shields/installed(var/mob/living/exosuit/_owner)
+ . = ..()
+ aura = new(owner, src)
+
+/obj/item/mech_equipment/shields/uninstalled()
+ QDEL_NULL(aura)
+ . = ..()
+
+/obj/item/mech_equipment/shields/attack_self(var/mob/user)
+ . = ..()
+ if(.)
+ toggle()
+
+/obj/item/mech_equipment/shields/proc/stop_damage(var/damage)
+ var/difference = damage - charge
+ charge = Clamp(charge - damage, 0, max_charge)
+
+ last_recharge = world.time
+
+ if(difference > 0)
+ for(var/mob/pilot in owner.pilots)
+ to_chat(pilot, SPAN_DANGER("Warning: Deflector shield failure detect, shutting down"))
+ toggle()
+ playsound(owner.loc,'sound/mecha/internaldmgalarm.ogg',35,1)
+ return difference
+ else return 0
+
+/obj/item/mech_equipment/shields/proc/toggle()
+ if(!aura)
+ return
+ aura.toggle()
+ playsound(owner,'sounds/flash.ogg',35,1)
+ update_icon()
+ if(aura.active)
+ START_PROCESSING(SSobj, src)
+ else
+ STOP_PROCESSING(SSobj, src)
+ owner.update_icon()
+
+/obj/item/mech_equipment/shields/on_update_icon()
+ . = ..()
+ if(!aura)
+ return
+ if(aura.active)
+ icon_state = "shield_droid_a"
+ else
+ icon_state = "shield_droid"
+
+/obj/item/mech_equipment/shields/Process()
+ if(charge >= max_charge)
+ return
+ if((world.time - last_recharge) < cooldown)
+ return
+ var/obj/item/cell/cell = owner.get_cell()
+
+ var/actual_required_power = Clamp(max_charge - charge, 0, charging_rate)
+
+ charge += cell.use(actual_required_power)
+
+/obj/item/mech_equipment/shields/get_hardpoint_status_value()
+ return charge / max_charge
+
+/obj/item/mech_equipment/shields/get_hardpoint_maptext()
+ return "[(aura && aura.active) ? "ONLINE" : "OFFLINE"]: [round((charge / max_charge) * 100)]%"
+
+/obj/aura/mechshield
+ icon = 'icons/mecha/shield.dmi'
+ name = "mechshield"
+ var/obj/item/mech_equipment/shields/shields = null
+ var/active = 0
+ layer = ABOVE_HUMAN_LAYER
+ var/north_layer = MECH_UNDER_LAYER
+ plane = MOB_PLANE
+ pixel_x = 8
+ pixel_y = 4
+ mouse_opacity = 0
+
+/obj/aura/mechshield/Initialize(var/maploading, var/obj/item/mech_equipment/shields/holder)
+ . = ..()
+ shields = holder
+
+/obj/aura/mechshield/added_to(var/mob/living/target)
+ . = ..()
+ target.vis_contents += src
+ set_dir()
+ GLOB.dir_set_event.register(user, src, /obj/aura/mechshield/proc/update_dir)
+
+/obj/aura/mechshield/proc/update_dir(var/user, var/old_dir, var/dir)
+ set_dir(dir)
+
+/obj/aura/mechshield/set_dir(new_dir)
+ . = ..()
+ if(dir == NORTH)
+ layer = north_layer
+ else layer = initial(layer)
+
+/obj/aura/mechshield/Destroy()
+ if(user)
+ GLOB.dir_set_event.unregister(user, src, /obj/aura/mechshield/proc/update_dir)
+ user.vis_contents -= src
+ shields = null
+ . = ..()
+
+/obj/aura/mechshield/proc/toggle()
+ active = !active
+
+ update_icon()
+
+ if(active)
+ flick("shield_raise", src)
+ else
+ flick("shield_drop", src)
+
+
+/obj/aura/mechshield/on_update_icon()
+ . = ..()
+ if(active)
+ icon_state = "shield"
+ else
+ icon_state = "shield_null"
+
+/obj/aura/mechshield/bullet_act(var/obj/item/projectile/P, var/def_zone)
+ if(!active)
+ return
+ if(shields)
+ if(shields.charge)
+ P.damage = shields.stop_damage(P.damage)
+ user.visible_message(SPAN_WARNING("\The [shields.owner]'s shields flash and crackle."))
+ flick("shield_impact", src)
+ playsound(user,'sound/effects/basscannon.ogg',35,1)
+ //light up the night.
+ new /obj/effect/effect/smoke/illumination(user.loc, 5, 4, 1, "#ffffff")
+ if(P.damage <= 0)
+ return AURA_FALSE|AURA_CANCEL
+
+ var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
+ spark_system.set_up(5, 0, user)
+ spark_system.start()
+ playsound(loc, "sparks", 25, 1)
+
+/obj/aura/mechshield/hitby(atom/movable/M, var/datum/thrownthing/TT)
+ . = ..()
+ if(!active)
+ return
+ if(shields.charge && TT.speed <= 5)
+ user.visible_message(SPAN_WARNING("\The [shields.owner]'s shields flash briefly as they deflect \the [M]."))
+ flick("shield_impact", src)
+ playsound(user,'sound/effects/basscannon.ogg',10,1)
+ return AURA_FALSE|AURA_CANCEL
+ //Too fast!
+*/
+
+
+//Melee! As a general rule I would recommend using regular objects and putting logic in them.
+/obj/item/mech_equipment/mounted_system/melee
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_UTILITY)
+
+/obj/item/material/knife/machete/mech
+ name = "mechete"
+ desc = "That thing is too big to be called a machete. Too big, too thick, too heavy, and too rough, it is more like a large hunk of iron."
+ w_class = ITEMSIZE_HUGE
+ slot_flags = 0
+ force_divisor = 0.34 // 20 with hardness 60 (steel)
+ thrown_force_divisor = 1.75 // 20 with weight 20 (steel)
+ can_dull = FALSE // A blade this massive isn't sharp because it has a honed edge.
+
+/obj/item/material/knife/machete/mech/apply_hit_effect(mob/living/target, mob/living/user, hit_zone)
+ . = ..()
+ if (.)
+ user.do_attack_animation(target)
+ if (target.mob_size < user.mob_size) //Damaging attacks overwhelm smaller mobs
+ target.throw_at(get_edge_target_turf(target,get_dir(user, target)),1, 1)
+
+/obj/item/material/knife/machete/mech/resolve_attackby(atom/A, mob/user, attack_modifier, click_parameters)
+ //Case 1: Default, you are hitting something that isn't a mob. Just do whatever, this isn't dangerous or op.
+ if (!isliving(A))
+ return ..()
+
+ user.visible_message(SPAN_DANGER("\The [user] swings \the [src] at \the [A]!"))
+ playsound(user, 'sound/mecha/mechmove03.ogg', 35, 1)
+ return ..()
+
+/obj/item/material/knife/machete/mech/attack_self(mob/living/user)
+ . = ..()
+ if (user.a_intent != I_HURT)
+ return
+ var/obj/item/mech_equipment/mounted_system/melee/mechete/MC = loc
+ if (istype(MC))
+ //SPIN BLADE ATTACK GO!
+ var/mob/living/exosuit/E = MC.owner
+ if (E)
+ E.setClickCooldown(1.35 SECONDS)
+ E.visible_message(SPAN_DANGER("\The [E] swings \the [src] back, preparing for an attack!"), blind_message = SPAN_DANGER("You hear the loud hissing of hydraulics!"))
+ playsound(E, 'sound/mecha/mechmove03.ogg', 35, 1)
+ if (do_after(E, 1.2 SECONDS, get_turf(user)) && E && MC)
+ for (var/mob/living/M in orange(1, E))
+ attack(M, E, E.zone_sel.selecting, FALSE)
+ E.spin(0.65 SECONDS, 0.125 SECONDS)
+ playsound(E, 'sound/mecha/mechturn.ogg', 40, 1)
+
+/obj/item/material/knife/machete/mech/durasteel/Initialize(ml, material_key)
+ . = ..(ml, MAT_DURASTEEL)
+
+/obj/item/mech_equipment/mounted_system/melee/mechete
+ icon_state = "mech_blade"
+ holding_type = /obj/item/material/knife/machete/mech
+
+/obj/item/mech_equipment/mounted_system/melee/mechete/durasteel
+ holding_type = /obj/item/material/knife/machete/mech/durasteel
+
+/*
+//Ballistic shield
+/obj/item/mech_equipment/ballistic_shield
+ name = "exosuit ballistic shield"
+ desc = "This formidable line of defense, sees widespread use in planetary peacekeeping operations and military formations alike."
+ icon_state = "mech_shield" //Rendering is handled by aura due to layering issues: TODO, figure out a better way to do this
+ var/obj/aura/mech_ballistic/aura = null
+ var/last_push = 0
+ var/chance = 60 //For attacks from the front, diminishing returns
+ var/last_max_block = 0 //Blocking during a perfect block window resets this, else there is an anti spam
+ var/max_block = 60 // Should block most things
+ var/blocking = FALSE
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_UTILITY)
+
+/obj/item/mech_equipment/ballistic_shield/installed(mob/living/exosuit/_owner)
+ . = ..()
+ aura = new(owner, src)
+
+/obj/item/mech_equipment/ballistic_shield/uninstalled()
+ QDEL_NULL(aura)
+ . = ..()
+
+/obj/item/mech_equipment/ballistic_shield/afterattack(atom/target, mob/living/user, inrange, params)
+ . = ..()
+ if (. && user.a_intent == I_HURT && (last_push + 1.6 SECONDS < world.time))
+ owner.visible_message(SPAN_WARNING("\The [owner] retracts \the [src], preparing to push with it!"), blind_message = SPAN_WARNING("You hear the whine of hydraulics and feel a rush of air!"))
+ owner.setClickCooldown(0.7 SECONDS)
+ last_push = world.time
+ if (do_after(owner, 0.5 SECONDS, get_turf(owner)) && owner)
+ owner.visible_message(SPAN_WARNING("\The [owner] slams the area in front \the [src]!"), blind_message = SPAN_WARNING("You hear a loud hiss and feel a strong gust of wind!"))
+ playsound(src ,'sound/effects/bang.ogg',35,1)
+ var/list/turfs = list()
+ var/front = get_step(get_turf(owner), owner.dir)
+ turfs += front
+ turfs += get_step(front, turn(owner.dir, -90))
+ turfs += get_step(front, turn(owner.dir, 90))
+ for(var/turf/T in turfs)
+ for(var/mob/living/M in T)
+ if (!M.Adjacent(owner))
+ continue
+ M.attack_generic(owner, (owner.arms ? owner.arms.melee_damage * 1.2 : 0), "slammed")
+ M.throw_at(get_edge_target_turf(owner ,owner.dir),5, 2)
+ do_attack_effect(T, "smash")
+
+/obj/item/mech_equipment/ballistic_shield/attack_self(mob/user)
+ . = ..()
+ if (.) //FORM A SHIELD WALL!
+ if (last_max_block + 2 SECONDS < world.time)
+ owner.visible_message(SPAN_WARNING("\The [owner] raises \the [src], locking it in place!"), blind_message = SPAN_WARNING("You hear the whir of motors and scratching metal!"))
+ playsound(src ,'sound/effects/bamf.ogg',35,1)
+ owner.setClickCooldown(0.8 SECONDS)
+ blocking = TRUE
+ last_max_block = world.time
+ do_after(owner, 0.75 SECONDS, get_turf(user))
+ blocking = FALSE
+ else
+ to_chat(user, SPAN_WARNING("You are not ready to block again!"))
+
+/obj/item/mech_equipment/ballistic_shield/proc/block_chance(damage, pen, atom/source, mob/attacker)
+ if (damage > max_block || pen > max_block)
+ return 0
+
+ var/effective_block = blocking ? chance * 1.5 : chance
+
+ var/conscious_pilot_exists = FALSE
+ for (var/mob/living/pilot in owner.pilots)
+ if (!pilot.incapacitated())
+ conscious_pilot_exists = TRUE
+ break
+
+ if (!conscious_pilot_exists)
+ effective_block *= 0.5 //Who is going to block anything?
+
+ //Bit copypasta but I am doing something different from normal shields
+ var/attack_dir = 0
+ if (istype(source, /obj/item/projectile))
+ var/obj/item/projectile/P = source
+ attack_dir = get_dir(get_turf(src), P.starting)
+ else if (attacker)
+ attack_dir = get_dir(get_turf(src), get_turf(attacker))
+ else if (source)
+ attack_dir = get_dir(get_turf(src), get_turf(source))
+
+ if (attack_dir == turn(owner.dir, -90) || attack_dir == turn(owner.dir, 90))
+ effective_block *= 0.8
+ else if (attack_dir == turn(owner.dir, 180))
+ effective_block = 0
+
+ return effective_block
+
+/obj/item/mech_equipment/ballistic_shield/proc/on_block_attack()
+ if (blocking)
+ //Reset timer for maximum chainblocks
+ last_max_block = 0
+
+/obj/aura/mech_ballistic
+ icon = 'icons/mecha/ballistic_shield.dmi'
+ name = "mech_ballistic_shield"
+ var/obj/item/mech_equipment/ballistic_shield/shield = null
+ layer = MECH_UNDER_LAYER
+ plane = DEFAULT_PLANE
+ mouse_opacity = 0
+
+/obj/aura/mech_ballistic/Initialize(maploading, obj/item/mech_equipment/ballistic_shield/holder)
+ . = ..()
+ shield = holder
+
+ //Get where we are attached so we know what icon to use
+ if (holder && holder.owner)
+ var/mob/living/exosuit/E = holder.owner
+ for (var/hardpoint in E.hardpoints)
+ var/obj/item/mech_equipment/hardpoint_object = E.hardpoints[hardpoint]
+ if (holder == hardpoint_object)
+ icon_state = "mech_shield_[hardpoint]"
+ var/image/I = image(icon, "[icon_state]_over")
+ I.layer = ABOVE_HUMAN_LAYER
+ overlays.Add(I)
+
+/obj/aura/mech_ballistic/added_to(mob/living/target)
+ . = ..()
+ add_vis_contents(target, src)
+ set_dir(target.dir)
+ global.events_repository.register(/decl/observ/dir_set, user, src, /obj/aura/mech_ballistic/proc/update_dir)
+
+/obj/aura/mech_ballistic/proc/update_dir(user, old_dir, dir)
+ set_dir(dir)
+
+/obj/aura/mech_ballistic/Destroy()
+ if (user)
+ global.events_repository.unregister(/decl/observ/dir_set, user, src, /obj/aura/mech_ballistic/proc/update_dir)
+ remove_vis_contents(user, src)
+ shield = null
+ . = ..()
+
+/obj/aura/mech_ballistic/bullet_act(obj/item/projectile/P, def_zone)
+ . = ..()
+ if (shield)
+ if (prob(shield.block_chance(P.damage, P.armor_penetration, source = P)))
+ user.visible_message(SPAN_WARNING("\The [P] is blocked by \the [user]'s [shield]."))
+ user.bullet_impact_visuals(P, def_zone, 0)
+ shield.on_block_attack()
+ return AURA_FALSE|AURA_CANCEL
+
+/obj/aura/mech_ballistic/hitby(atom/movable/M, datum/thrownthing/TT)
+ . = ..()
+ if (shield)
+ var/throw_damage = 0
+ if (istype(M,/obj/))
+ var/obj/O = M
+ throw_damage = O.throwforce*(TT.speed/THROWFORCE_SPEED_DIVISOR)
+
+ if (prob(shield.block_chance(throw_damage, 0, source = M, attacker = TT.thrower)))
+ user.visible_message(SPAN_WARNING("\The [M] bounces off \the [user]'s [shield]."))
+ playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1)
+ shield.on_block_attack()
+ return AURA_FALSE|AURA_CANCEL
+
+/obj/aura/mech_ballistic/attackby(obj/item/I, mob/user)
+ . = ..()
+ if (shield)
+ if (prob(shield.block_chance(I.force, I.armor_penetration, source = I, attacker = user)))
+ user.visible_message(SPAN_WARNING("\The [I] is blocked by \the [user]'s [shield]."))
+ playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1)
+ return AURA_FALSE|AURA_CANCEL
+*/
+/obj/item/mech_equipment/flash
+ name = "exosuit flash"
+ icon_state = "mech_flash"
+ restricted_hardpoints = list(HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER)
+ restricted_software = list(MECH_SOFTWARE_WEAPONS)
+ active_power_use = 7 KILOWATTS
+ var/flash_range = 5
+ var/next_use = 0
+ var/halloss_per_flash = 25
+ origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 3)
+
+/obj/item/mech_equipment/flash/proc/area_flash()
+ if(world.time < next_use)
+ to_chat(owner, SPAN_WARNING("\The [src] is recharging!"))
+ return
+
+ owner.setClickCooldown(5)
+ next_use = world.time + 15 SECONDS
+
+ playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
+ for (var/mob/living/O in oviewers(flash_range, owner))
+ flash(O)
+
+/obj/item/mech_equipment/flash/proc/flash(var/mob/living/L)
+ if(!istype(L))
+ return
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ if(H.stat == DEAD)
+ return
+ if(H.eyecheck() > 0)
+ return
+ var/flash_strength = 5 * H.species.flash_mod
+ if(flash_strength <= 0)
+ return
+ H.Confuse(flash_strength + 5)
+ H.Blind(flash_strength)
+ H.eye_blurry = max(H.eye_blurry, flash_strength + 5)
+ H.flash_eyes()
+ H.adjustHalLoss(halloss_per_flash * (flash_strength / 5)) // Should take four flashes to stun.
+ H.apply_damage(flash_strength * H.species.flash_burn/5, BURN, BP_HEAD, 0, 0, "Photon burns")
+ else if(isrobot(L) && prob(30))
+ var/mob/living/silicon/robot/R = L
+ if(R.has_active_type(/obj/item/borg/combat/shield))
+ var/obj/item/borg/combat/shield/shield = locate() in R
+ if(shield?.active)
+ shield.adjust_flash_count(R, 1)
+
+/obj/item/mech_equipment/flash/attack_self(mob/user)
+ . = ..()
+ if(.)
+ if(world.time < next_use)
+ to_chat(user, SPAN_WARNING("\The [src] is recharging!"))
+ return
+ next_use = world.time + 20
+ area_flash()
+ owner.setClickCooldown(5)
+
+/obj/item/mech_equipment/flash/afterattack(atom/target, mob/living/user, inrange, params)
+ . = ..()
+ if(.)
+ if(world.time < next_use)
+ to_chat(user, SPAN_WARNING("\The [src] is recharging!"))
+ return
+ var/mob/living/O = target
+ owner.setClickCooldown(5)
+ next_use = world.time + 15
+
+ playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
+ flash(O)
diff --git a/code/modules/exosuits/equipment/combat_projectile.dm b/code/modules/exosuits/equipment/combat_projectile.dm
new file mode 100644
index 00000000000..e18f624e621
--- /dev/null
+++ b/code/modules/exosuits/equipment/combat_projectile.dm
@@ -0,0 +1,165 @@
+/obj/item/mech_equipment/mounted_system/projectile/attackby(var/obj/item/O, var/mob/user)
+ var/obj/item/gun/projectile/automatic/A = holding
+ if(!istype(A))
+ return
+ if(O.has_tool_quality(TOOL_CROWBAR))
+ A.unload_ammo(user)
+ to_chat(user, SPAN_NOTICE("You remove the ammo magazine from the [src]."))
+ else if(istype(O, A.magazine_type))
+ A.load_ammo(O, user)
+ to_chat(user, SPAN_NOTICE("You load the ammo magazine into the [src]."))
+
+ return ..()
+
+/obj/item/mech_equipment/mounted_system/projectile/attack_self(var/mob/user)
+ . = ..()
+ if(. && holding)
+ var/obj/item/gun/M = holding
+ return M.switch_firemodes(user)
+
+/obj/item/gun/projectile/automatic/get_hardpoint_status_value()
+ if(!isnull(ammo_magazine))
+ return ammo_magazine.stored_ammo.len
+
+/obj/item/gun/projectile/automatic/get_hardpoint_maptext()
+ if(!isnull(ammo_magazine))
+ return "[ammo_magazine.stored_ammo.len]/[ammo_magazine.max_ammo]"
+ return 0
+
+//Weapons below this.
+/obj/item/mech_equipment/mounted_system/projectile
+ name = "mounted submachine gun"
+ icon_state = "mech_ballistic"
+ holding_type = /obj/item/gun/projectile/automatic/advanced_smg/mech
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_WEAPONS)
+ origin_tech = list(TECH_DATA = 4, TECH_COMBAT = 6, TECH_ENGINEERING = 5)
+
+/obj/item/gun/projectile/automatic/advanced_smg/mech
+ caliber = "9mm"
+ one_handed_penalty = FALSE
+ magazine_type = /obj/item/ammo_magazine/mech/smg_top
+ allowed_magazines = list(/obj/item/ammo_magazine/mech/smg_top)
+
+ firemodes = list(
+ list(mode_name="semi auto", burst=1, fire_delay=null, burst_accuracy=null, dispersion=null),
+ list(mode_name="3-round bursts", burst=3, fire_delay=null, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 1.6, 2.4, 2.4), autofire_enabled=0),
+ list(mode_name="short bursts", burst=5, fire_delay=null, burst_accuracy=list(0,-1,-1,-1,-2), dispersion=list(1.6, 1.6, 2.0, 2.0, 2.4), autofire_enabled=0),
+ list(mode_name="full auto", burst=1, fire_delay=null, burst_delay=1, burst_accuracy=list(0,-1,-1,-1,-2), dispersion=list(1.6, 1.6, 2.0, 2.0, 2.4), autofire_enabled=1)
+ )
+
+/obj/item/mech_equipment/mounted_system/projectile/assault_rifle
+ name = "mounted assault rifle"
+ icon_state = "mech_ballistic2"
+ holding_type = /obj/item/gun/projectile/automatic/sts35/mech
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_WEAPONS)
+ origin_tech = list(TECH_DATA = 4, TECH_COMBAT = 8, TECH_ENGINEERING = 6)
+
+/obj/item/gun/projectile/automatic/sts35/mech
+ caliber = "5.45mm"
+ magazine_type = /obj/item/ammo_magazine/mech/rifle
+ allowed_magazines = list(/obj/item/ammo_magazine/mech/rifle)
+
+ firemodes = list(
+ list(mode_name="semi auto", burst=1, fire_delay=null, burst_accuracy=null, dispersion=null, autofire_enabled=0),
+ list(mode_name="3-round bursts", burst=3, fire_delay=null, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0), autofire_enabled=0),
+ list(mode_name="full auto", burst=1, fire_delay=null, burst_delay=1, burst_accuracy = list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0), autofire_enabled=1)
+ )
+
+/obj/item/mech_equipment/mounted_system/projectile/machine
+ name = "mounted machine gun"
+ icon_state = "mech_machine_gun"
+ holding_type = /obj/item/gun/projectile/automatic/l6_saw/mech
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_WEAPONS)
+
+/obj/item/gun/projectile/automatic/l6_saw/mech
+ caliber = "5.45mm"
+ one_handed_penalty = FALSE
+ magazine_type = /obj/item/ammo_magazine/mech/rifle/drum
+ allowed_magazines = list(/obj/item/ammo_magazine/mech/rifle/drum)
+
+// Magazines below this.
+
+/obj/item/ammo_magazine/mech
+ icon_state = "ashot-mag"
+ desc = "A large magazine for a mech's gun. Looks way too big for a normal gun."
+
+/obj/item/ammo_magazine/mech/attack_self(mob/user)
+ to_chat(user, SPAN_WARNING("It's pretty hard to extract ammo from a magazine that fits on a mech. You'll have to do it one round at a time."))
+ return
+
+/obj/item/ammo_magazine/mech/smg_top
+ name = "large 7mm magazine"
+ ammo_type = /obj/item/ammo_casing/a9mm
+ caliber = "9mm"
+ max_ammo = 90
+
+/obj/item/ammo_magazine/mech/rifle
+ name = "large assault rifle magazine"
+ caliber = "5.45mm"
+ icon_state = "m545"
+ ammo_type = /obj/item/ammo_casing/a545
+ max_ammo = 100
+
+/obj/item/ammo_magazine/mech/rifle/drum
+ name = "large machine gun magazine"
+ caliber = "5.45mm"
+ icon_state = "a545"
+ ammo_type = /obj/item/ammo_casing/a545
+ max_ammo = 300
+/*
+// Handling for auto-fire mechanic
+/mob/living/exosuit/can_autofire(obj/item/gun/autofiring, atom/autofiring_at)
+ if(autofiring.autofiring_by != src)
+ return FALSE
+ var/client/C = current_user ? current_user.client : client
+
+ if(!C || !C.mob || C.mob.incapacitated())
+ return FALSE
+
+ if(!(autofiring_at in view(C.view, src)))
+ return FALSE
+ if(!(get_dir(src, autofiring_at) & dir))
+ return FALSE
+ if(!(autofiring in selected_system)) // Make sure the gun is still selected.
+ return FALSE
+ return TRUE
+
+/obj/item/mech_equipment/mounted_system/projectile/MouseDownInteraction(atom/object, location, control, params, mob/user)
+ var/obj/item/gun/gun = holding
+ if(istype(object) && (isturf(object) || isturf(object.loc)) && istype(gun))
+ if(user != src)
+ if(!user.incapacitated())
+ gun.set_autofire(object, owner, FALSE) // Passed gun-firer is still the exosuit since all checks need to be done on the suit.
+ owner.current_user = user
+ else
+ if(!owner.incapacitated())
+ gun.set_autofire(object, owner, FALSE)
+ owner.current_user = null
+
+/obj/item/mech_equipment/mounted_system/projectile/MouseUpInteraction(atom/object, location, control, params, mob/user)
+ var/obj/item/gun/gun = holding
+ if(istype(gun))
+ gun.clear_autofire()
+ if(owner) // In case the owning exosuit has been gibbed etc.
+ owner.current_user = null
+
+/obj/item/mech_equipment/mounted_system/projectile/MouseDragInteraction(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user)
+ var/obj/item/gun/gun = holding
+ if(!owner)
+ gun?.clear_autofire()
+ return
+ if(!istype(gun))
+ owner?.current_user = null
+ return
+ if(istype(over_object) && (isturf(over_object) || isturf(over_object.loc)))
+ if(user.incapacitated() || (user != owner && user != owner.current_user))
+ gun.clear_autofire()
+ return
+ gun.set_autofire(over_object, owner, FALSE)
+ return
+
+ gun.clear_autofire()
+*/
diff --git a/code/modules/exosuits/equipment/engineering.dm b/code/modules/exosuits/equipment/engineering.dm
new file mode 100644
index 00000000000..f2c52ff3035
--- /dev/null
+++ b/code/modules/exosuits/equipment/engineering.dm
@@ -0,0 +1,38 @@
+
+/obj/item/mech_equipment/mounted_system/rcd
+ icon_state = "mech_rcd"
+ holding_type = /obj/item/rcd/mounted
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_ENGINEERING)
+
+/obj/item/rcd/mounted/get_hardpoint_maptext()
+ var/obj/item/mech_equipment/mounted_system/MS = loc
+ if(istype(MS) && MS.owner)
+ var/obj/item/cell/C = MS.owner.get_cell()
+ if(istype(C))
+ return "[round(C.charge)]/[round(C.maxcharge)]"
+ return null
+
+/obj/item/rcd/mounted/get_hardpoint_status_value()
+ var/obj/item/mech_equipment/mounted_system/MS = loc
+ if(istype(MS) && MS.owner)
+ var/obj/item/cell/C = MS.owner.get_cell()
+ if(istype(C))
+ return C.charge/C.maxcharge
+ return null
+
+/obj/item/extinguisher/mech
+ max_water = 4000 //Good is gooder
+ icon_state = "mech_exting"
+
+/obj/item/extinguisher/mech/get_hardpoint_maptext()
+ return "[reagents.total_volume]/[max_water]"
+
+/obj/item/extinguisher/mech/get_hardpoint_status_value()
+ return reagents.total_volume/max_water
+
+/obj/item/mech_equipment/mounted_system/extinguisher
+ icon_state = "mech_exting"
+ holding_type = /obj/item/extinguisher/mech
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_ENGINEERING)
diff --git a/code/modules/exosuits/equipment/medical.dm b/code/modules/exosuits/equipment/medical.dm
new file mode 100644
index 00000000000..4c4ab25bc6e
--- /dev/null
+++ b/code/modules/exosuits/equipment/medical.dm
@@ -0,0 +1,77 @@
+/obj/item/mech_equipment/sleeper
+ name = "\improper exosuit sleeper"
+ desc = "An exosuit-mounted sleeper designed to mantain patients stabilized on their way to medical facilities."
+ icon_state = "mech_sleeper"
+ restricted_hardpoints = list(HARDPOINT_BACK)
+ restricted_software = list(MECH_SOFTWARE_MEDICAL)
+ equipment_delay = 30 //don't spam it on people pls
+ active_power_use = 0 //Usage doesn't really require power. We don't want people stuck inside
+ origin_tech = list(TECH_DATA = 2, TECH_BIO = 3)
+ passive_power_use = 1.5 KILOWATTS
+ var/obj/machinery/sleeper/mounted/sleeper = null
+
+/obj/item/mech_equipment/sleeper/Initialize()
+ . = ..()
+ sleeper = new /obj/machinery/sleeper/mounted(src)
+ sleeper.forceMove(src)
+
+/obj/item/mech_equipment/sleeper/Destroy()
+ sleeper.go_out() //If for any reason you weren't outside already.
+ QDEL_NULL(sleeper)
+ . = ..()
+
+/obj/item/mech_equipment/sleeper/uninstalled()
+ . = ..()
+ sleeper.go_out()
+
+/obj/item/mech_equipment/sleeper/attack_self(var/mob/user)
+ . = ..()
+ if(.)
+ sleeper.ui_interact(user)
+
+/obj/item/mech_equipment/sleeper/attackby(var/obj/item/I, var/mob/user)
+ if(istype(I, /obj/item/reagent_containers/glass))
+ sleeper.attackby(I, user)
+ else return ..()
+
+/obj/item/mech_equipment/sleeper/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params)
+ . = ..()
+ if(.)
+ if(ishuman(target) && !sleeper.occupant)
+ owner.visible_message(SPAN_NOTICE("\The [src] is lowered down to load [target]"))
+ sleeper.go_in(target, user)
+ else to_chat(user, SPAN_WARNING("You cannot load that in!"))
+
+/obj/item/mech_equipment/sleeper/get_hardpoint_maptext()
+ if(sleeper && sleeper.occupant)
+ return "[sleeper.occupant]"
+
+/obj/machinery/sleeper/mounted
+ name = "\improper mounted sleeper"
+ density = 0
+ anchored = 0
+ idle_power_usage = 0
+ active_power_usage = 0 //It'd be hard to handle, so for now all power is consumed by mech sleeper object
+ stasis_level = 0
+ interact_offline = TRUE
+
+/obj/machinery/sleeper/mounted/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.mech_state)
+ . = ..()
+
+/obj/machinery/sleeper/mounted/nano_host()
+ var/obj/item/mech_equipment/sleeper/S = loc
+ if(istype(S))
+ return S.owner
+ return null
+
+//You cannot modify these, it'd probably end with something in nullspace. In any case basic meds are plenty for an ambulance
+/obj/machinery/sleeper/mounted/attackby(var/obj/item/I, var/mob/user)
+ if(istype(I, /obj/item/reagent_containers/glass))
+ if(!user.unEquip(I, src))
+ return
+
+ if(beaker)
+ beaker.forceMove(get_turf(src))
+ user.visible_message("\The [user] removes \the [beaker] from \the [src].", "You remove \the [beaker] from \the [src].")
+ beaker = I
+ user.visible_message("\The [user] adds \a [I] to \the [src].", "You add \a [I] to \the [src].")
diff --git a/code/modules/exosuits/equipment/utility.dm b/code/modules/exosuits/equipment/utility.dm
new file mode 100644
index 00000000000..b2ffb9b20d6
--- /dev/null
+++ b/code/modules/exosuits/equipment/utility.dm
@@ -0,0 +1,337 @@
+/obj/item/mech_equipment/clamp
+ name = "mounted clamp"
+ desc = "A large, heavy industrial cargo loading clamp."
+ icon_state = "mech_clamp"
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_UTILITY)
+ var/obj/carrying
+ origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2)
+
+/obj/item/mech_equipment/clamp/resolve_attackby(atom/A, mob/user, attack_modifier, click_parameters)
+ if(istype(A, /obj/structure/closet) && owner)
+ return 0
+ return ..()
+
+/obj/item/mech_equipment/clamp/attack_hand(mob/user)
+ if(owner && (user in owner.pilots))
+ if(!owner.hatch_closed && carrying)
+ if(user.put_in_active_hand(carrying))
+ owner.visible_message(SPAN_NOTICE("\The [user] carefully grabs \the [carrying] from \the [src]."))
+ carrying = null
+ . = ..()
+
+/obj/item/mech_equipment/clamp/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params)
+ . = ..()
+
+ if(. && !carrying)
+ if(istype(target, /obj))
+
+
+ var/obj/O = target
+ if(length(O.buckled_mobs))
+ return
+ if(locate(/mob/living) in O)
+ to_chat(user,"You can't load living things into the cargo compartment.")
+ return
+
+ if(O.anchored)
+ to_chat(user, "[target] is firmly secured.")
+ return
+
+
+ owner.visible_message(SPAN_NOTICE("\The [owner] begins loading \the [O]."))
+ if(do_after(owner, 20, O, 0, 1))
+ O.forceMove(src)
+ carrying = O
+ owner.visible_message(SPAN_NOTICE("\The [owner] loads \the [O] into its cargo compartment."))
+
+
+ //attacking - Cannot be carrying something, cause then your clamp would be full
+ else if(istype(target,/mob/living))
+ var/mob/living/M = target
+ if(user.a_intent == I_HURT)
+ log_and_message_admins(user, "attempted to clamp [M] with [src]")
+ owner.setClickCooldown(owner.arms ? owner.arms.action_delay * 3 : 30) //This is an inefficient use of your powers
+ if(prob(33))
+ owner.visible_message(SPAN_DANGER("[owner] swings its [src] in a wide arc at [target] but misses completely!"))
+ return
+ M.attack_generic(owner, (owner.arms ? owner.arms.melee_damage * 1.5 : 0), "slammed") //Honestly you should not be able to do this without hands, but still
+ M.throw_at(get_edge_target_turf(owner ,owner.dir),5, 2)
+ to_chat(user, "You slam [target] with [src.name].")
+ owner.visible_message(SPAN_DANGER("[owner] slams [target] with the hydraulic clamp."))
+ else
+ step_away(M, owner)
+ to_chat(user, "You push [target] out of the way.")
+ owner.visible_message("[owner] pushes [target] out of the way.")
+
+/obj/item/mech_equipment/clamp/attack_self(var/mob/user)
+ . = ..()
+ if(.)
+ if(!carrying)
+ to_chat(user, SPAN_WARNING("You are not carrying anything in \the [src]."))
+ else
+ owner.visible_message(SPAN_NOTICE("\The [owner] unloads \the [carrying]."))
+ carrying.forceMove(get_turf(src))
+ carrying = null
+
+/obj/item/mech_equipment/clamp/get_hardpoint_maptext()
+ if(carrying)
+ return carrying.name
+ . = ..()
+
+/obj/item/mech_equipment/clamp/uninstalled()
+ if(carrying)
+ carrying.dropInto(loc)
+ carrying = null
+ . = ..()
+
+// A lot of this is copied from floodlights.
+/obj/item/mech_equipment/light
+ name = "floodlight"
+ desc = "An exosuit-mounted light."
+ icon_state = "mech_floodlight"
+ item_state = "mech_floodlight"
+ restricted_hardpoints = list(HARDPOINT_HEAD)
+ mech_layer = MECH_INTERMEDIATE_LAYER
+
+ var/on = 0
+ var/l_max_bright = 0.9
+ var/l_inner_range = 1
+ var/l_outer_range = 6
+ origin_tech = list(TECH_MATERIAL = 1, TECH_ENGINEERING = 1)
+
+/obj/item/mech_equipment/light/attack_self(var/mob/user)
+ . = ..()
+ if(.)
+ on = !on
+ to_chat(user, "You switch \the [src] [on ? "on" : "off"].")
+ update_icon()
+ owner.update_icon()
+
+/obj/item/mech_equipment/light/update_icon()
+ if(on)
+ icon_state = "[initial(icon_state)]-on"
+ set_light(l_max_bright, l_inner_range, l_outer_range)
+ else
+ icon_state = "[initial(icon_state)]"
+ set_light(0, 0)
+
+/obj/item/mech_equipment/light/uninstalled()
+ on = FALSE
+ update_icon()
+ . = ..()
+
+#define CATAPULT_SINGLE 1
+#define CATAPULT_AREA 2
+
+/obj/item/mech_equipment/catapult
+ name = "gravitational catapult"
+ desc = "An exosuit-mounted gravitational catapult."
+ icon_state = "mech_clamp"
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_UTILITY)
+ var/mode = CATAPULT_SINGLE
+ var/atom/movable/locked
+ equipment_delay = 30 //Stunlocks are not ideal
+ origin_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4, TECH_MAGNET = 4)
+ require_adjacent = FALSE
+
+/obj/item/mech_equipment/catapult/get_hardpoint_maptext()
+ var/string
+ if(locked)
+ string = locked.name + " - "
+ if(mode == 1)
+ string += "Single"
+ else string += "Area"
+ return string
+
+
+/obj/item/mech_equipment/catapult/attack_self(var/mob/user)
+ . = ..()
+ if(.)
+ mode = mode == CATAPULT_SINGLE ? CATAPULT_AREA : CATAPULT_SINGLE
+ to_chat(user, SPAN_NOTICE("You set \the [src] to [mode == CATAPULT_SINGLE ? "single" : "multi"]-target mode."))
+ update_icon()
+
+
+/obj/item/mech_equipment/catapult/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params)
+ . = ..()
+ if(.)
+
+ switch(mode)
+ if(CATAPULT_SINGLE)
+ if(!locked)
+ var/atom/movable/AM = target
+ if(!istype(AM) || AM.anchored || !AM.simulated)
+ to_chat(user, SPAN_NOTICE("Unable to lock on [target]."))
+ return
+ locked = AM
+ to_chat(user, SPAN_NOTICE("Locked on [AM]."))
+ return
+ else if(target != locked)
+ if(locked in view(owner))
+ locked.throw_at(target, 14, 1.5, owner)
+ log_and_message_admins("used [src] to throw [locked] at [target].", user, owner.loc)
+ locked = null
+
+ var/obj/item/cell/C = owner.get_cell()
+ if(istype(C))
+ C.use(active_power_use * CELLRATE)
+
+ else
+ locked = null
+ to_chat(user, SPAN_NOTICE("Lock on [locked] disengaged."))
+ if(CATAPULT_AREA)
+
+ var/list/atoms = list()
+ if(isturf(target))
+ atoms = range(target,3)
+ else
+ atoms = orange(target,3)
+ for(var/atom/movable/A in atoms)
+ if(A.anchored || !A.simulated) continue
+ var/dist = 5-get_dist(A,target)
+ A.throw_at(get_edge_target_turf(A,get_dir(target, A)),dist,0.7)
+
+
+ log_and_message_admins("used [src]'s area throw on [target].", user, owner.loc)
+ var/obj/item/cell/C = owner.get_cell()
+ if(istype(C))
+ C.use(active_power_use * CELLRATE * 2) //bit more expensive to throw all
+
+
+
+#undef CATAPULT_SINGLE
+#undef CATAPULT_AREA
+
+
+/obj/item/material/drill_head
+ var/durability = 0
+ name = "drill head"
+ desc = "A replaceable drill head usually used in exosuit drills."
+ icon_state = "drill_head"
+
+/obj/item/material/drill_head/Initialize()
+ . = ..()
+ durability = 2 * material.integrity
+
+/obj/item/mech_equipment/drill
+ name = "drill"
+ desc = "This is the drill that'll pierce the heavens!"
+ icon_state = "mech_drill"
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_UTILITY)
+ equipment_delay = 10
+
+ //Drill can have a head
+ var/obj/item/material/drill_head/drill_head
+ origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2)
+
+
+
+/obj/item/mech_equipment/drill/Initialize()
+ . = ..()
+ drill_head = new /obj/item/material/drill_head(src, MAT_STEEL)//You start with a basic steel head
+
+/obj/item/mech_equipment/drill/attack_self(var/mob/user)
+ . = ..()
+ if(.)
+ if(drill_head)
+ owner.visible_message(SPAN_WARNING("[owner] revs the [drill_head], menancingly."))
+ playsound(src, 'sound/weapons/circsawhit.ogg', 50, 1)
+
+
+/obj/item/mech_equipment/drill/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params)
+ . = ..()
+ if(.)
+ if(isobj(target))
+ var/obj/target_obj = target
+ if(target_obj.unacidable)
+ return
+ if(istype(target,/obj/item/material/drill_head))
+ var/obj/item/material/drill_head/DH = target
+ if(drill_head)
+ owner.visible_message(SPAN_NOTICE("\The [owner] detaches the [drill_head] mounted on the [src]."))
+ drill_head.forceMove(owner.loc)
+ DH.forceMove(src)
+ drill_head = DH
+ owner.visible_message(SPAN_NOTICE("\The [owner] mounts the [drill_head] on the [src]."))
+ return
+
+ if(drill_head == null)
+ to_chat(user, SPAN_WARNING("Your drill doesn't have a head!"))
+ return
+
+ var/obj/item/cell/C = owner.get_cell()
+ if(istype(C))
+ C.use(active_power_use * CELLRATE)
+ owner.visible_message(SPAN_DANGER("\The [owner] starts to drill \the [target]."), SPAN_DANGER("You hear a large drill."))
+
+ var/T = target.loc
+
+ //Better materials = faster drill!
+ var/delay = max(5, 20 - drill_head.material.protectiveness)
+ owner.setClickCooldown(delay) //Don't spamclick!
+ if(drill_head && do_after(owner, delay, target))
+ if(src == owner.selected_system)
+ if(drill_head.durability <= 0)
+ drill_head.shatter()
+ drill_head = null
+ return
+ else if(drill_head.durability <= (drill_head.material.integrity * 0.2) && prob(20))
+ to_chat(user, SPAN_NOTICE("\The [drill_head] emits a terrible screeching!"))
+ if(istype(target, /turf/simulated/wall))
+ var/turf/simulated/wall/W = target
+ if(max(W.material.hardness, W.reinf_material ? W.reinf_material.hardness : 0) > drill_head.material.hardness)
+ to_chat(user, SPAN_WARNING("\The [target] is too hard to drill through with this drill head."))
+ return
+ target.ex_act(2)
+ drill_head.durability -= 1
+ log_and_message_admins("used [src] on the wall [W].", user, owner.loc)
+ else if(istype(target, /turf/simulated/mineral))
+ for(var/turf/simulated/mineral/M in range(target,1))
+ if(get_dir(owner,M)&owner.dir)
+ M.GetDrilled()
+ drill_head.durability -= 1
+ else if(target.loc == T)
+ target.ex_act(2)
+ drill_head.durability -= 1
+ log_and_message_admins("[src] used to drill [target].", user, owner.loc)
+
+
+ if(owner.hardpoints.len) //if this isn't true the drill should not be working to be fair
+ for(var/hardpoint in owner.hardpoints)
+ var/obj/item/I = owner.hardpoints[hardpoint]
+ if(!istype(I))
+ continue
+ var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in I //clamps work, but anythin that contains an ore crate internally is valid
+ if(ore_box)
+ for(var/obj/item/ore/ore in range(T,1))
+ if(get_dir(owner,ore)&owner.dir)
+ ore.Move(ore_box)
+ break // Already placed our ore in a container, don't go to another.
+
+ playsound(src, 'sound/weapons/circsawhit.ogg', 50, 1)
+
+ else
+ to_chat(user, "You must stay still while the drill is engaged!")
+
+
+ return 1
+
+
+
+/*
+/obj/item/mech_equipment/mounted_system/taser/plasma
+ name = "mounted plasma cutter"
+ desc = "An industrial plasma cutter mounted onto the chassis of the mech. "
+ icon_state = "railauto" //TODO: Make a new sprite that doesn't get sec called on you.
+ holding_type = /obj/item/gun/energy/plasmacutter/mounted/mech
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND, HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER)
+ restricted_software = list(MECH_SOFTWARE_UTILITY)
+ origin_tech = list(TECH_MATERIAL = 4, TECH_PHORON = 4, TECH_ENGINEERING = 6, TECH_COMBAT = 3)
+
+/obj/item/gun/energy/plasmacutter/mounted/mech
+ use_external_power = TRUE
+ has_safety = FALSE
+*/
diff --git a/code/modules/exosuits/exosuit.dm b/code/modules/exosuits/exosuit.dm
new file mode 100644
index 00000000000..91a03790758
--- /dev/null
+++ b/code/modules/exosuits/exosuit.dm
@@ -0,0 +1,199 @@
+
+// Big stompy robots.
+/mob/living/exosuit
+ name = "exosuit"
+ desc = "A powerful machine piloted from a cockpit, but worn like a suit of armour."
+ description_info = "Interacting with the exosuit with an empty hand will attempt to remove the pilot, if the hatch is open."
+ density = TRUE
+ opacity = TRUE
+ anchored = TRUE
+ default_pixel_x = -8
+ default_pixel_y = 0
+ status_flags = PASSEMOTES
+ a_intent = I_HURT
+ mob_size = MOB_LARGE
+
+ var/emp_damage = 0
+
+ var/obj/item/radio/radio
+
+ var/datum/material/material
+
+ var/wreckage_path = /obj/structure/exosuit_wreckage
+ var/mech_turn_sound = 'sound/mecha/mechturn.ogg'
+ var/mech_step_sound = 'sound/mecha/mechstep.ogg'
+
+ // Access updating/container.
+ var/obj/item/card/id/access_card
+ var/list/saved_access = list()
+ var/sync_access = 1
+
+ // Mob currently piloting the exosuit.
+ var/list/pilots
+ var/list/pilot_overlays
+
+ // Visible external components. Not strictly accurately named for non-humanoid machines (submarines) but w/e
+ var/obj/item/mech_component/manipulators/arms
+ var/obj/item/mech_component/propulsion/legs
+ var/obj/item/mech_component/sensors/head
+ var/obj/item/mech_component/chassis/body
+
+ // Invisible components.
+ var/datum/effect_system/spark_spread/sparks
+
+ // Equipment tracking vars.
+ var/obj/item/mech_equipment/selected_system
+ var/selected_hardpoint
+ var/list/hardpoints = list()
+ var/hardpoints_locked
+ var/maintenance_protocols
+
+ // Cockpit access vars.
+ var/hatch_closed = FALSE
+ var/hatch_locked = FALSE
+
+ //Air!
+ var/use_air = FALSE
+
+ var/next_step = 0
+
+ // Interface stuff.
+ var/list/hud_elements = list()
+ var/list/hardpoint_hud_elements = list()
+ var/obj/screen/movable/exosuit/health/hud_health
+ var/obj/screen/movable/exosuit/toggle/hatch_open/hud_open
+ var/obj/screen/movable/exosuit/power/hud_power
+
+/mob/living/exosuit/Initialize(mapload, var/obj/structure/heavy_vehicle_frame/source_frame)
+ . = ..()
+
+ if(!access_card) access_card = new (src)
+
+ pixel_x = default_pixel_x
+ pixel_y = default_pixel_y
+ sparks = new(src)
+
+ // Grab all the supplied components.
+ if(source_frame)
+ if(source_frame.set_name)
+ name = source_frame.set_name
+ if(source_frame.arms)
+ source_frame.arms.forceMove(src)
+ arms = source_frame.arms
+ if(source_frame.legs)
+ source_frame.legs.forceMove(src)
+ legs = source_frame.legs
+ if(source_frame.head)
+ source_frame.head.forceMove(src)
+ head = source_frame.head
+ if(source_frame.body)
+ source_frame.body.forceMove(src)
+ body = source_frame.body
+ if(source_frame.material)
+ material = source_frame.material
+
+ updatehealth()
+
+ // Generate hardpoint list.
+ var/list/component_descriptions
+ for(var/obj/item/mech_component/comp in list(arms, legs, head, body))
+ if(comp.exosuit_desc_string)
+ LAZYADD(component_descriptions, comp.exosuit_desc_string)
+ if(LAZYLEN(comp.has_hardpoints))
+ for(var/hardpoint in comp.has_hardpoints)
+ hardpoints[hardpoint] = null
+
+ if(head && head.radio)
+ radio = new(src)
+
+ if(LAZYLEN(component_descriptions))
+ desc = "[desc] It has been built with [english_list(component_descriptions)]."
+
+ // Create HUD.
+ InitializeHud()
+
+ // Build icon.
+ update_icon()
+
+/mob/living/exosuit/Destroy()
+
+ selected_system = null
+
+ for(var/thing in pilots)
+ var/mob/pilot = thing
+ if(pilot.client)
+ pilot.client.screen -= hud_elements
+ pilot.client.images -= hud_elements
+ pilot.forceMove(get_turf(src))
+ pilots = null
+
+ for(var/thing in hud_elements)
+ qdel(thing)
+ hud_elements.Cut()
+
+ for(var/hardpoint in hardpoints)
+ qdel(hardpoints[hardpoint])
+ hardpoints.Cut()
+
+ QDEL_NULL(access_card)
+ QDEL_NULL(arms)
+ QDEL_NULL(legs)
+ QDEL_NULL(head)
+ QDEL_NULL(body)
+
+ for(var/hardpoint in hardpoint_hud_elements)
+ var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[hardpoint]
+ H.owner = null
+ H.holding = null
+ qdel(H)
+ hardpoint_hud_elements.Cut()
+
+ . = ..()
+
+/mob/living/exosuit/IsAdvancedToolUser() // If there's one pilot that's intelligent, assumedly they will keep the gaggle together.
+ if(LAZYLEN(pilots))
+ for(var/mob/living/L in pilots)
+ if(L.IsAdvancedToolUser() && L.stat == CONSCIOUS)
+ return TRUE
+ return FALSE
+ return TRUE
+
+/mob/living/exosuit/examine(mob/user)
+ . = ..()
+ if(LAZYLEN(pilots) && (!hatch_closed || body.pilot_coverage < 100 || body.transparent_cabin))
+ to_chat(user, "It is being piloted by [english_list(pilots, nothing_text = "nobody")].")
+ if(hardpoints.len)
+ to_chat(user, "It has the following hardpoints:")
+ for(var/hardpoint in hardpoints)
+ var/obj/item/I = hardpoints[hardpoint]
+ to_chat(user, "- [hardpoint]: [istype(I) ? "[I]" : "nothing"].")
+ else
+ to_chat(user, "It has no visible hardpoints.")
+
+ for(var/obj/item/mech_component/thing in list(arms, legs, head, body))
+ if(!thing)
+ continue
+ var/damage_string = "destroyed"
+ switch(thing.damage_state)
+ if(MECH_COMPONENT_DAMAGE_UNDAMAGED)
+ damage_string = "undamaged"
+ if(MECH_COMPONENT_DAMAGE_DAMAGED)
+ damage_string = "damaged"
+ if(MECH_COMPONENT_DAMAGE_DAMAGED_BAD)
+ damage_string = "badly damaged"
+ if(MECH_COMPONENT_DAMAGE_DAMAGED_TOTAL)
+ damage_string = "almost destroyed"
+ to_chat(user, "Its [thing.name] [thing.gender == PLURAL ? "are" : "is"] [damage_string].")
+
+ to_chat(user, "It menaces with reinforcements of [material].")
+
+/mob/living/exosuit/return_air()
+ return (body && body.pilot_coverage >= 100 && hatch_closed) ? body.cockpit : loc.return_air()
+
+/mob/living/exosuit/GetIdCard()
+ return access_card
+
+/mob/living/exosuit/set_dir()
+ . = ..()
+ if(.)
+ update_pilots()
diff --git a/code/modules/exosuits/exosuit_construction.dm b/code/modules/exosuits/exosuit_construction.dm
new file mode 100644
index 00000000000..eaf89eee1c8
--- /dev/null
+++ b/code/modules/exosuits/exosuit_construction.dm
@@ -0,0 +1,192 @@
+/mob/living/exosuit/proc/dismantle()
+
+ playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
+ var/obj/structure/heavy_vehicle_frame/frame = new(get_turf(src))
+ for(var/hardpoint in hardpoints)
+ remove_system(hardpoint, force = 1)
+ hardpoints.Cut()
+
+ if(arms)
+ frame.arms = arms
+ arms.forceMove(frame)
+ arms = null
+ if(legs)
+ frame.legs = legs
+ legs.forceMove(frame)
+ legs = null
+ if(body)
+ frame.body = body
+ body.forceMove(frame)
+ body = null
+ if(head)
+ frame.head = head
+ head.forceMove(frame)
+ head = null
+
+ frame.is_wired = EXOFRAME_WIRED_ADJUSTED
+ frame.is_reinforced = EXOFRAME_REINFORCED_WELDED
+ frame.set_name = name
+ frame.name = "frame of \the [frame.set_name]"
+ frame.material = material
+ frame.update_icon()
+
+ qdel(src)
+
+/mob/living/exosuit/proc/forget_module(var/module_to_forget)
+ //Realistically a module disappearing without being uninstalled is wrong and a bug or adminbus
+ var/target = null
+ for(var/hardpoint in hardpoints)
+ if(hardpoints[hardpoint] == module_to_forget)
+ target = hardpoint
+ break
+
+ hardpoints[target] = null
+
+ if(target == selected_hardpoint)
+ clear_selected_hardpoint()
+
+ GLOB.destroyed_event.unregister(module_to_forget, src, .proc/forget_module)
+
+ var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[target]
+ H.holding = null
+
+ hud_elements -= module_to_forget
+ refresh_hud()
+ update_icon()
+
+ for(var/mob/pilot in pilots)
+ pilot?.client.screen -= module_to_forget
+
+/mob/living/exosuit/proc/can_install_system(var/obj/item/system, var/system_hardpoint, var/mob/user)
+ if(hardpoints_locked)
+ if(user)
+ to_chat(user, SPAN_WARNING("Hardpoint system access is locked."))
+ return FALSE
+ if(hardpoints[system_hardpoint])
+ if(user)
+ to_chat(user, SPAN_WARNING("[system_hardpoint] is already in use."))
+ return FALSE
+
+ return TRUE
+
+/mob/living/exosuit/proc/do_install_system(var/obj/item/system, var/system_hardpoint)
+ var/obj/item/mech_equipment/ME = system
+ if(istype(ME))
+ if(ME.restricted_hardpoints && !(system_hardpoint in ME.restricted_hardpoints))
+ return FALSE
+ if(ME.restricted_software)
+ if(!head || !head.software)
+ return FALSE
+ var/list/found_software = ME.restricted_software & head.software.installed_software
+ if(!LAZYLEN(found_software))
+ return FALSE
+ ME.installed(src)
+ GLOB.destroyed_event.register(system, src, .proc/forget_module)
+
+ system.forceMove(src)
+ hardpoints[system_hardpoint] = system
+
+ var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[system_hardpoint]
+ H.holding = system
+
+ system.screen_loc = H.screen_loc
+ system.hud_layerise()
+
+ hud_elements |= system
+ refresh_hud()
+ update_icon()
+
+ return TRUE
+
+/mob/living/exosuit/proc/install_system(var/obj/item/system, var/system_hardpoint, var/mob/user)
+ if(!can_install_system(system, system_hardpoint, user))
+ return FALSE
+
+ if(user)
+ user.visible_message(SPAN_NOTICE("\The [user] begins to install \the [system] into \the [src]."))
+ if(!do_after(user, 30, src) || user.get_active_hand() != system)
+ return FALSE
+
+ if(user.unEquip(system))
+ to_chat(user, SPAN_NOTICE("You install \the [system] in \the [src]'s [system_hardpoint]."))
+ playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1)
+ else return FALSE
+
+ return do_install_system(system, system_hardpoint)
+
+/mob/living/exosuit/proc/install_system_initialize(var/obj/item/system, var/system_hardpoint)
+ if(!can_install_system(system, system_hardpoint))
+ return FALSE
+
+ return do_install_system(system, system_hardpoint)
+
+/mob/living/exosuit/proc/can_remove_system(var/system_hardpoint, var/mob/user, var/force)
+ if(hardpoints_locked && !force)
+ if(user)
+ to_chat(user, SPAN_WARNING("Hardpoint system access is locked."))
+ return FALSE
+ if(!hardpoints[system_hardpoint])
+ if(user)
+ to_chat(user, SPAN_NOTICE("[system_hardpoint] is unoccupied."))
+ return FALSE
+
+ return TRUE
+
+/mob/living/exosuit/proc/do_remove_system(var/system_hardpoint)
+ var/obj/item/system = hardpoints[system_hardpoint]
+ hardpoints[system_hardpoint] = null
+
+ if(system_hardpoint == selected_hardpoint)
+ clear_selected_hardpoint()
+
+ var/obj/item/mech_equipment/ME = system
+ if(istype(ME))
+ ME.uninstalled()
+ system.forceMove(get_turf(src))
+ system.screen_loc = null
+ system.layer = initial(system.layer)
+ GLOB.destroyed_event.unregister(system, src, .proc/forget_module)
+
+ var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[system_hardpoint]
+ H.holding = null
+
+ for(var/thing in pilots)
+ var/mob/pilot = thing
+ if(pilot && pilot.client)
+ pilot.client.screen -= system
+
+ hud_elements -= system
+ refresh_hud()
+ update_icon()
+
+/mob/living/exosuit/proc/remove_system(var/system_hardpoint, var/mob/user, var/force)
+
+ if(!can_remove_system(system_hardpoint, user, force))
+ return FALSE
+
+ var/obj/item/system = hardpoints[system_hardpoint]
+ if(user)
+ var/delay = 30
+ if(delay > 0)
+ user.visible_message(SPAN_NOTICE("\The [user] begins trying to remove \the [system] from \the [src]."))
+ if(!do_after(user, delay, src) || hardpoints[system_hardpoint] != system)
+ return FALSE
+
+ do_remove_system(system_hardpoint)
+
+ if(user)
+ system.forceMove(get_turf(user))
+ user.put_in_hands(system)
+ to_chat(user, SPAN_NOTICE("You remove \the [system] from \the [src]'s [system_hardpoint]."))
+ playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1)
+
+ return TRUE
+
+/mob/living/exosuit/proc/remove_system_initialize(var/system_hardpoint, var/mob/exosuit, var/force)
+
+ if(!can_remove_system(system_hardpoint, null, force))
+ return FALSE
+
+ do_remove_system(system_hardpoint)
+
+ return TRUE
diff --git a/code/modules/exosuits/exosuit_damage.dm b/code/modules/exosuits/exosuit_damage.dm
new file mode 100644
index 00000000000..bc2741cd5f1
--- /dev/null
+++ b/code/modules/exosuits/exosuit_damage.dm
@@ -0,0 +1,144 @@
+/mob/living/exosuit/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1)
+ if(!effect || (blocked >= 100))
+ return 0
+ if(LAZYLEN(pilots) && (!hatch_closed || !prob(body.pilot_coverage)))
+ if(effect > 0 && effecttype == IRRADIATE)
+ effect = max((1-(getarmor(null, "rad")/100))*effect/(blocked+1),0)
+ var/mob/living/pilot = pick(pilots)
+ return pilot.apply_effect(effect, effecttype, blocked)
+ if(!(effecttype in list(AGONY, STUTTER, EYE_BLUR, DROWSY, WEAKEN)))
+ . = ..()
+
+/mob/living/exosuit/resolve_item_attack(var/obj/item/I, var/mob/living/user, var/def_zone)
+ if(!I.force)
+ user.visible_message(SPAN_NOTICE("\The [user] bonks \the [src] harmlessly with \the [I]."))
+ return
+
+ if(LAZYLEN(pilots) && (!hatch_closed || !prob(body.pilot_coverage)))
+ var/mob/living/pilot = pick(pilots)
+ return pilot.resolve_item_attack(I, user, def_zone)
+
+ return def_zone //Careful with effects
+
+/mob/living/exosuit/hitby(var/atom/movable/AM, var/speed)
+ if(LAZYLEN(pilots) && (!hatch_closed || !prob(body.pilot_coverage)))
+ var/mob/living/pilot = pick(pilots)
+ return pilot.hitby(AM, speed)
+ . = ..()
+
+/mob/living/exosuit/getarmor(def_zone, type)
+ var/armorval = 0
+ if(body)
+ armorval = body.get_armor(type)
+ if(isnull(armorval))
+ armorval = 0
+
+ for(var/thing in modifiers)
+ var/datum/modifier/M = thing
+ var/modifier_armor = LAZYACCESS(M.armor_percent, type)
+ if(modifier_armor)
+ armorval += modifier_armor
+
+ return armorval
+
+/mob/living/exosuit/getsoak(def_zone, type)
+ return round(getarmor(def_zone, type) / 10)
+
+/mob/living/exosuit/updatehealth()
+ maxHealth = body.mech_health
+ health = maxHealth-(getFireLoss()+getBruteLoss())
+
+/mob/living/exosuit/adjustFireLoss(var/amount, var/obj/item/mech_component/MC = pick(list(arms, legs, body, head)))
+ if(MC)
+ MC.take_burn_damage(amount)
+ MC.update_health()
+
+/mob/living/exosuit/adjustBruteLoss(var/amount, var/obj/item/mech_component/MC = pick(list(arms, legs, body, head)))
+ if(MC)
+ MC.take_brute_damage(amount)
+ MC.update_health()
+
+/mob/living/exosuit/proc/zoneToComponent(var/zone)
+ switch(zone)
+ if(O_EYES , BP_HEAD)
+ return head
+ if(BP_L_ARM , BP_R_ARM)
+ return arms
+ if(BP_L_LEG , BP_R_LEG)
+ return legs
+ else
+ return body
+
+// Override for exosuits, since they don't take many types of damage a 'living' thing does.
+/mob/living/exosuit/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/used_weapon = null, var/sharp = 0, var/edge = 0, var/obj/used_weapon = null)
+ if(Debug2)
+ to_world_log("## DEBUG: apply_damage() was called on [src], with [damage] damage, and an armor value of [blocked].")
+ if(!damage || (blocked >= 100))
+ return 0
+ if(soaked)
+ if(soaked >= round(damage*0.8))
+ damage -= round(damage*0.8)
+ else
+ damage -= soaked
+
+ var/initial_blocked = blocked
+
+ var/target = zoneToComponent(def_zone)
+
+ if((damagetype == BRUTE || damagetype == BURN) && prob(25+(damage*2)))
+ sparks.set_up(3,0,src)
+ sparks.start()
+
+ blocked = (100-blocked)/100
+ switch(damagetype)
+ if(BRUTE)
+ adjustBruteLoss(damage * blocked, target)
+ if(BURN)
+ adjustFireLoss(damage * blocked, target)
+ if(SEARING)
+ apply_damage(round(damage / 3), BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge)
+ apply_damage(round(damage / 3 * 2), BRUTE, def_zone, initial_blocked, soaked, used_weapon, sharp, edge)
+ if(ELECTROCUTE)
+ electrocute_act(damage, used_weapon, 1.0, def_zone)
+ if(BIOACID)
+ if(isSynthetic())
+ apply_damage(damage, BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) // Handle it as normal burn.
+
+ flash_weak_pain()
+ updatehealth()
+ return 1
+
+/mob/living/exosuit/getFireLoss()
+ var/total = 0
+ for(var/obj/item/mech_component/MC in list(arms, legs, body, head))
+ if(MC)
+ total += MC.burn_damage
+ return total
+
+/mob/living/exosuit/getBruteLoss()
+ var/total = 0
+ for(var/obj/item/mech_component/MC in list(arms, legs, body, head))
+ if(MC)
+ total += MC.brute_damage
+ return total
+
+/mob/living/exosuit/emp_act(var/severity)
+
+ var/ratio = getarmor(null, "energy")/100
+
+ if(ratio >= 0.5)
+ for(var/mob/living/m in pilots)
+ to_chat(m, SPAN_NOTICE("Your Faraday shielding absorbed the pulse!"))
+ return
+ else if(ratio > 0)
+ for(var/mob/living/m in pilots)
+ to_chat(m, SPAN_NOTICE("Your Faraday shielding mitigated the pulse!"))
+
+ emp_damage += round((12 - (severity*3))*( 1 - ratio))
+ if(severity <= 3)
+ for(var/obj/item/thing in list(arms,legs,head,body))
+ thing.emp_act(severity)
+ if(!hatch_closed || !prob(body.pilot_coverage))
+ for(var/thing in pilots)
+ var/mob/pilot = thing
+ pilot.emp_act(severity)
diff --git a/code/modules/exosuits/exosuit_damage_immunity.dm b/code/modules/exosuits/exosuit_damage_immunity.dm
new file mode 100644
index 00000000000..3e6993b4cf5
--- /dev/null
+++ b/code/modules/exosuits/exosuit_damage_immunity.dm
@@ -0,0 +1,58 @@
+
+/*
+/mob/living/exosuit/Stun()
+ return 0
+*/
+
+/mob/living/exosuit/Weaken()
+ return 0
+
+/*
+/mob/living/exosuit/Paralyse()
+ return 0
+*/
+
+/mob/living/exosuit/getOxyLoss()
+ return 0
+
+/mob/living/exosuit/setOxyLoss()
+ return 0
+
+/mob/living/exosuit/adjustOxyLoss()
+ return 0
+
+/mob/living/exosuit/getToxLoss()
+ return 0
+
+/mob/living/exosuit/setToxLoss()
+ return 0
+
+/mob/living/exosuit/adjustToxLoss()
+ return 0
+
+/mob/living/exosuit/getBrainLoss()
+ return 0
+
+/mob/living/exosuit/setBrainLoss()
+ return 0
+
+/mob/living/exosuit/adjustBrainLoss()
+ return 0
+
+/mob/living/exosuit/getCloneLoss()
+ return 0
+
+/mob/living/exosuit/setCloneLoss()
+ return 0
+
+/mob/living/exosuit/adjustCloneLoss()
+ return 0
+
+/mob/living/exosuit/getHalLoss()
+ return 0
+
+/mob/living/exosuit/setHalLoss()
+ return 0
+
+/mob/living/exosuit/adjustHalLoss()
+ return 0
diff --git a/code/modules/exosuits/exosuit_icon.dm b/code/modules/exosuits/exosuit_icon.dm
new file mode 100644
index 00000000000..68b5d93f45e
--- /dev/null
+++ b/code/modules/exosuits/exosuit_icon.dm
@@ -0,0 +1,80 @@
+/proc/get_mech_image(var/decal, var/cache_key, var/cache_icon, var/image_colour, var/overlay_layer = FLOAT_LAYER)
+ var/use_key = "[cache_key]-[cache_icon]-[decal ? decal : "none"]-[image_colour ? image_colour : "none"]"
+ if(!GLOB.mech_image_cache[use_key])
+ var/image/I = image(icon = cache_icon, icon_state = cache_key)
+ if(image_colour)
+ var/image/masked_color = image(icon = cache_icon, icon_state = "[cache_key]_mask")
+ masked_color.color = image_colour
+ masked_color.blend_mode = BLEND_MULTIPLY
+ I.overlays.Add(masked_color)
+ if(decal)
+ var/decal_key = "[decal]-[cache_key]"
+ if(!GLOB.mech_icon_cache[decal_key])
+ var/template_key = "template-[cache_key]"
+ if(!GLOB.mech_icon_cache[template_key])
+ GLOB.mech_icon_cache[template_key] = icon(cache_icon, "[cache_key]_mask")
+ var/icon/decal_icon = icon('icons/mob/mecha/mech_decals.dmi',decal)
+ decal_icon.Blend(GLOB.mech_icon_cache[template_key], ICON_MULTIPLY)
+ GLOB.mech_icon_cache[decal_key] = decal_icon
+ I.overlays += get_mech_image(null, decal_key, GLOB.mech_icon_cache[decal_key])
+ I.appearance_flags |= RESET_COLOR
+ I.layer = overlay_layer
+ I.plane = FLOAT_PLANE
+ GLOB.mech_image_cache[use_key] = I
+ return GLOB.mech_image_cache[use_key]
+
+/proc/get_mech_images(var/list/components = list(), var/overlay_layer = FLOAT_LAYER)
+ var/list/all_images = list()
+ for(var/obj/item/mech_component/comp in components)
+ all_images += get_mech_image(comp.decal, comp.icon_state, comp.on_mech_icon, comp.color, overlay_layer)
+ return all_images
+
+/mob/living/exosuit/update_icon()
+ overlays.Cut()
+ var/list/new_overlays = get_mech_images(list(body, head), MECH_BASE_LAYER)
+ if(body && !hatch_closed)
+ new_overlays += get_mech_image(body.decal, "[body.icon_state]_cockpit", body.on_mech_icon, MECH_BASE_LAYER)
+ update_pilots(FALSE)
+ if(LAZYLEN(pilot_overlays))
+ new_overlays += pilot_overlays
+ if(body)
+ new_overlays += get_mech_image(body.decal, "[body.icon_state]_overlay[hatch_closed ? "" : "_open"]", body.on_mech_icon, body.color, MECH_COCKPIT_LAYER)
+ if(arms)
+ new_overlays += get_mech_image(arms.decal, arms.icon_state, arms.on_mech_icon, arms.color, MECH_ARM_LAYER)
+ if(legs)
+ new_overlays += get_mech_image(legs.decal, legs.icon_state, legs.on_mech_icon, legs.color, MECH_LEG_LAYER)
+ for(var/hardpoint in hardpoints)
+ var/obj/item/mech_equipment/hardpoint_object = hardpoints[hardpoint]
+ if(hardpoint_object)
+ var/use_icon_state = "[hardpoint_object.icon_state]_[hardpoint]"
+ if(use_icon_state in GLOB.mech_weapon_overlays)
+ new_overlays += get_mech_image(null, use_icon_state, 'icons/mob/mecha/mech_weapon_overlays.dmi', null, hardpoint_object.mech_layer )
+
+ . = ..()
+
+ overlays |= new_overlays
+
+/mob/living/exosuit/proc/update_pilots(var/update_overlays = TRUE)
+ if(update_overlays && LAZYLEN(pilot_overlays))
+ overlays -= pilot_overlays
+ pilot_overlays = null
+ if(!body || ((body.pilot_coverage < 100 || body.transparent_cabin) && !body.hide_pilot))
+ for(var/i = 1 to LAZYLEN(pilots))
+ var/mob/pilot = pilots[i]
+ var/image/draw_pilot = new
+ draw_pilot.appearance = pilot
+ draw_pilot.layer = MECH_PILOT_LAYER + (body ? ((LAZYLEN(body.pilot_positions)-i)*0.001) : 0)
+ draw_pilot.plane = FLOAT_PLANE
+ if(body && i <= LAZYLEN(body.pilot_positions))
+ var/list/offset_values = body.pilot_positions[i]
+ var/list/directional_offset_values = offset_values["[dir]"]
+ draw_pilot.pixel_x = pilot.default_pixel_x + directional_offset_values["x"]
+ draw_pilot.pixel_y = pilot.default_pixel_y + directional_offset_values["y"]
+ draw_pilot.pixel_z = 0
+ draw_pilot.transform = null
+ LAZYADD(pilot_overlays, draw_pilot)
+ if(update_overlays && LAZYLEN(pilot_overlays))
+ overlays += pilot_overlays
+
+/mob/living/exosuit/regenerate_icons()
+ return
diff --git a/code/modules/exosuits/exosuit_interaction.dm b/code/modules/exosuits/exosuit_interaction.dm
new file mode 100644
index 00000000000..ac0671f0380
--- /dev/null
+++ b/code/modules/exosuits/exosuit_interaction.dm
@@ -0,0 +1,442 @@
+
+/mob/living/MouseDrop(atom/over)
+ if(usr == src && usr != over)
+ if(istype(over, /mob/living/exosuit))
+ var/mob/living/exosuit/exosuit = over
+ if(exosuit.body)
+ exosuit.enter(src)
+ return
+ to_chat(usr, SPAN_WARNING("You cannot pilot an exosuit that lacks a cockpit."))
+ return
+ return ..()
+
+/mob/living/exosuit/MouseDrop_T(atom/dropping, mob/user)
+ var/obj/machinery/portable_atmospherics/canister/C = dropping
+ if(istype(C))
+ body.MouseDrop_T(dropping, user)
+ else . = ..()
+
+/mob/living/exosuit/RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, var/mob/user)
+ if(user && (user in pilots) && user.loc == src)
+ return OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, user)
+ return ..()
+
+/mob/living/exosuit/OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, var/mob/user)
+ if(!user || incapacitated() || user.incapacitated())
+ return FALSE
+
+ if(!(user in pilots) && user != src)
+ return FALSE
+
+ //This is handled at active module level really, it is the one who has to know if it's supposed to act
+ if(selected_system)
+ return selected_system.MouseDragInteraction(src_object, over_object, src_location, over_location, src_control, over_control, params, user)
+
+/mob/living/exosuit/allow_click_through(atom/A, mob/user)
+ if((user in pilots))
+ return TRUE
+ . = ..()
+
+/mob/living/exosuit/click_through(atom/A, mob/user, params)
+ return ClickOn(A, params, user)
+
+/mob/living/exosuit/proc/incompetent_operation(var/mob/living/user) // Advanced tool users shouldn't currently have a chance of randomly blundering.. a -siffet-, however...
+ if(user.IsAdvancedToolUser())
+ return FALSE
+ return TRUE
+
+/mob/living/exosuit/ClickOn(var/atom/A, var/params, var/mob/user)
+ if(!user)
+ user = src
+
+ if(!checkClickCooldown()) // Hard check, before anything else, to avoid crashing
+ return
+
+ setClickCooldown(1)
+
+ if(!user || incapacitated() || user.incapacitated())
+ return
+
+ if(!loc) return
+ var/adj = A.Adjacent(src) // Why in the fuck isn't Adjacent() commutative.
+
+ var/modifiers = params2list(params)
+
+ if(modifiers["shift"])
+ user.ShiftClickOn(A)
+ return
+
+ if(!(user in pilots) && user != src)
+ return
+
+ // Are we facing the target?
+ if(A.loc != src && !(get_dir(src, A) & dir))
+ return
+
+ if(!arms)
+ to_chat(user, SPAN_WARNING("\The [src] has no manipulators!"))
+ setClickCooldown(3)
+ return
+
+ if(!arms.motivator || !arms.motivator.is_functional())
+ to_chat(user, SPAN_WARNING("Your motivators are damaged! You can't use your manipulators!"))
+ setClickCooldown(15)
+ return
+
+ var/obj/item/cell/MyC = get_cell()
+ if(!MyC.checked_use(arms.power_use * CELLRATE))
+ to_chat(user, SPAN_WARNING("Error: Power levels insufficient."))
+
+ // User is not necessarily the exosuit, or the same person, so update intent.
+ if(user != src)
+ a_intent = user.a_intent
+ if(user.zone_sel)
+ zone_sel.set_selected_zone(user.zone_sel.selecting)
+ else
+ zone_sel.set_selected_zone(BP_TORSO)
+ // You may attack the target with your exosuit FIST if you're malfunctioning.
+ var/atom/movable/AM = A
+ var/fail_prob = (user != src && istype(AM) && AM.loc != src && incompetent_operation(user)) ? 15 : 0
+ var/failed = FALSE
+ if(fail_prob && prob(fail_prob))
+ to_chat(user, SPAN_DANGER("Your incompetence leads you to engage the wrong system with the exosuit!"))
+ failed = TRUE
+ else if(emp_damage > EMP_ATTACK_DISRUPT && prob(emp_damage*2))
+ to_chat(user, SPAN_DANGER("The wiring sparks as you attempt to control the exosuit!"))
+ failed = TRUE
+
+ if(!failed)
+ if(selected_system)
+ if(selected_system == A)
+ selected_system.attack_self(user)
+ setClickCooldown(5)
+ return
+
+ // Mounted non-exosuit systems have some hacky loc juggling
+ // to make sure that they work.
+ var/system_moved = FALSE
+ var/obj/item/temp_system
+ var/obj/item/mech_equipment/ME
+ if(istype(selected_system, /obj/item/mech_equipment))
+ ME = selected_system
+ temp_system = ME.get_effective_obj()
+ if(temp_system in ME)
+ system_moved = 1
+ temp_system.forceMove(src)
+ else
+ temp_system = selected_system
+
+ // Slip up and attack yourself maybe.
+ failed = FALSE
+ if(fail_prob && prob(fail_prob))
+ to_chat(user, SPAN_DANGER("You artlessly shove the exosuit controls the wrong way!"))
+ failed = TRUE
+ else if(emp_damage>EMP_MOVE_DISRUPT && prob(10))
+ failed = TRUE
+
+ if(failed)
+ var/list/other_atoms = orange(1, A)
+ A = null
+ while(LAZYLEN(other_atoms))
+ var/atom/picked = pick_n_take(other_atoms)
+ if(istype(picked) && picked.simulated)
+ A = picked
+ break
+ if(!A)
+ A = src
+ adj = A.Adjacent(src)
+
+ var/resolved
+
+ if(adj) resolved = temp_system.resolve_attackby(A, src, params)
+ if(!resolved && A && temp_system)
+ var/mob/ruser = src
+ if(!system_moved) //It's more useful to pass along clicker pilot when logic is fully mechside
+ ruser = user
+ temp_system.afterattack(A,ruser,adj,params)
+ if(system_moved) //We are using a proxy system that may not have logging like mech equipment does
+ log_and_message_admins("used [temp_system] targetting [A]", user, src.loc)
+ //Mech equipment subtypes can add further click delays
+ var/extra_delay = 0
+ if(ME != null)
+ ME = selected_system
+ extra_delay = ME.equipment_delay
+ setClickCooldown(arms ? arms.action_delay + extra_delay : 15 + extra_delay)
+ if(system_moved)
+ temp_system.forceMove(selected_system)
+ return
+
+ if(A == src)
+ setClickCooldown(5)
+ return attack_self(user)
+ else if(adj)
+ setClickCooldown(arms ? arms.action_delay : 15)
+ return A.attack_generic(src, arms.melee_damage, "attacked")
+ return
+
+/mob/living/exosuit/proc/set_hardpoint(var/hardpoint_tag)
+ clear_selected_hardpoint()
+ if(hardpoints[hardpoint_tag])
+ // Set the new system.
+ selected_system = hardpoints[hardpoint_tag]
+ selected_hardpoint = hardpoint_tag
+ return 1 // The element calling this proc will set its own icon.
+ return 0
+
+/mob/living/exosuit/proc/clear_selected_hardpoint()
+
+ if(selected_hardpoint)
+ for(var/hardpoint in hardpoints)
+ if(hardpoint != selected_hardpoint)
+ continue
+ var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[hardpoint]
+ if(istype(H))
+ H.icon_state = "hardpoint"
+ break
+ selected_system = null
+ selected_hardpoint = null
+
+/mob/living/exosuit/proc/check_enter(var/mob/user)
+ if(!user || user.incapacitated())
+ return FALSE
+ if(!user.Adjacent(src))
+ return FALSE
+ if(hatch_locked)
+ to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked."))
+ return FALSE
+ if(hatch_closed)
+ to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is closed."))
+ return FALSE
+ if(LAZYLEN(pilots) >= LAZYLEN(body.pilot_positions))
+ to_chat(user, SPAN_WARNING("\The [src] is occupied to capacity."))
+ return FALSE
+ if(user.mob_size < body.min_pilot_size || user.mob_size > body.max_pilot_size)
+ to_chat(user, SPAN_WARNING("You cannot pilot an exosuit of this size."))
+ return FALSE
+
+ return TRUE
+
+/mob/living/exosuit/proc/enter(var/mob/user)
+ if(!check_enter(user))
+ return
+ to_chat(user, SPAN_NOTICE("You start climbing into \the [src]..."))
+ if(!do_after(user, 25))
+ return
+ if(!check_enter(user))
+ return
+ to_chat(user, SPAN_NOTICE("You climb into \the [src]."))
+ user.forceMove(src)
+ LAZYDISTINCTADD(pilots, user)
+ synchronize_access()
+ playsound(src, 'sound/mecha/hydraulic.ogg', 50, 1)
+ user.playsound_local(null, 'sound/mecha/nominal.ogg', 50)
+ if(user.client) user.client.screen |= hud_elements
+// LAZYDISTINCTADD(user.additional_vision_handlers, src)
+ update_pilots()
+// user.PushClickHandler(/datum/click_handler/default/mech)
+ return 1
+
+/mob/living/exosuit/proc/synchronize_access()
+ access_card.access.Cut()
+ access_card.access = saved_access.Copy()
+ if(sync_access)
+ for(var/mob/pilot in pilots)
+ access_card.access |= pilot.GetAccess()
+ to_chat(pilot, SPAN_NOTICE("Security access permissions synchronized."))
+
+/mob/living/exosuit/proc/eject(var/mob/user, var/silent)
+ if(!user || !(user in src.contents))
+ return
+ if(hatch_closed)
+ if(hatch_locked)
+ if(!silent)
+ to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked."))
+ return
+ hud_open.toggled()
+ if(!silent)
+ to_chat(user, SPAN_NOTICE("You open the hatch and climb out of \the [src]."))
+ else
+ if(!silent)
+ to_chat(user, SPAN_NOTICE("You climb out of \the [src]."))
+
+// user.RemoveClickHandler(/datum/click_handler/default/mech)
+ user.dropInto(loc)
+// LAZYREMOVE(user.additional_vision_handlers, src)
+ if(user.client)
+ user.client.screen -= hud_elements
+ user.client.eye = user
+ if(user in pilots)
+ a_intent = I_HURT
+ LAZYREMOVE(pilots, user)
+ UNSETEMPTY(pilots)
+ update_pilots()
+ return 1
+/*
+/mob/living/exosuit/proc/handle_equip_install(var/obj/item/mech_equipment/prospective, var/mob/user)
+ if(hardpoints_locked)
+ to_chat(user, SPAN_WARNING("Hardpoint system access is disabled."))
+ return
+
+ var/obj/item/mech_equipment/realThing = prospective
+ if(realThing.owner)
+ return
+
+ var/free_hardpoints = list()
+ for(var/hardpoint in hardpoints)
+ if(hardpoints[hardpoint] == null)
+ free_hardpoints += hardpoint
+ var/to_place = input("Where would you like to install it?") as null|anything in (realThing.restricted_hardpoints & free_hardpoints)
+ if(install_system(prospective, to_place, user))
+ return
+ to_chat(user, SPAN_WARNING("\The [src] could not be installed in that hardpoint."))
+
+/mob/living/exosuit/proc/handle_equip_uninstall(var/mob/user)
+ if(hardpoints_locked)
+ to_chat(user, SPAN_WARNING("Hardpoint system access is disabled."))
+ return
+
+ var/list/parts = list()
+ for(var/hardpoint in hardpoints)
+ if(hardpoints[hardpoint])
+ parts += hardpoint
+
+ var/to_remove = input("Which component would you like to remove") as null|anything in parts
+
+ if(remove_system(to_remove, user))
+ return
+ to_chat(user, SPAN_WARNING("\The [src] has no hardpoint systems to remove."))
+
+/mob/living/exosuit/proc/handle_dismantling(var/obj/item/tool, var/mob/user)
+ if(!maintenance_protocols)
+ to_chat(user, SPAN_WARNING("The securing bolts are not visible while maintenance protocols are disabled."))
+ return
+
+ visible_message(SPAN_WARNING("\The [user] begins unwrenching the securing bolts holding \the [src] together."))
+ var/delay = 60 * tool.toolspeed
+ if(!do_after(user, delay) || !maintenance_protocols)
+ return
+ visible_message(SPAN_NOTICE("\The [user] loosens and removes the securing bolts, dismantling \the [src]."))
+ dismantle()
+
+/mob/living/exosuit/proc/handle_repairing(var/obj/item/tool, var/mob/user)
+ if(istype(tool, /obj/item/weldingtool) && getBruteLoss())
+ var/list/damaged_parts = list()
+ for(var/obj/item/mech_component/MC in list(arms, legs, body, head))
+ if(MC && MC.brute_damage)
+ damaged_parts += MC
+ var/obj/item/mech_component/to_fix = input(user,"Which component would you like to fix") as null|anything in damaged_parts
+ if(Adjacent(user) && user.stat != DEAD && !QDELETED(to_fix) && (to_fix in src) && to_fix.brute_damage)
+ to_fix.repair_brute_generic(thing, user)
+
+ if(istype(thing, /obj/item/stack/cable_coil) && getFireLoss())
+ var/list/damaged_parts = list()
+ for(var/obj/item/mech_component/MC in list(arms, legs, body, head))
+ if(MC && MC.burn_damage)
+ damaged_parts += MC
+ var/obj/item/mech_component/to_fix = input(user,"Which component would you like to fix") as null|anything in damaged_parts
+ if(Adjacent(user) && user.stat != DEAD && !QDELETED(to_fix) && (to_fix in src) && to_fix.burn_damage)
+ to_fix.repair_burn_generic(thing, user)
+
+/mob/living/exosuit/proc/handle_powercell(var/obj/item/thing, var/mob/user)
+ if(!maintenance_protocols)
+ to_chat(user, SPAN_WARNING("The cell compartment remains locked while maintenance protocols are disabled."))
+ return
+ if(thing.has_tool_quality(TOOL_CROWBAR))
+ if(!body)
+ to_chat(user, SPAN_WARNING("The exosuit lacks a cell compartment!"))
+ return
+ if(body.cell) // If there's a cell, remove it.
+ var/delay = 20 * thing.toolspeed
+ if(!do_after(user, delay) || !maintenance_protocols || !body || !body.cell)
+ return
+
+ user.put_in_hands(body.cell)
+ to_chat(user, SPAN_NOTICE("You remove \the [body.cell] from \the [src]."))
+ playsound(user.loc, 'sound/items/Crowbar.ogg', 50, 1)
+ visible_message(SPAN_NOTICE("\The [user] pries out \the [body.cell] using the \the [thing]."))
+ body.cell = null
+ else // No return after this to_chat, incase you have a cell with the tool quality of a crowbar, like some sort of.. emergency cell.
+ to_chat(user, SPAN_WARNING("There is no cell here for you to remove!"))
+
+ if(istype(thing, /obj/item/cell))
+ if(!body || body.cell)
+ to_chat(user, SPAN_WARNING("There is already a cell in there!"))
+ return
+
+ if(user.unEquip(thing))
+ thing.forceMove(body)
+ body.cell = thing
+ to_chat(user, SPAN_NOTICE("You install \the [body.cell] into \the [src]."))
+ playsound(user.loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ visible_message(SPAN_NOTICE("\The [user] installs \the [body.cell] into \the [src]."))
+*/
+/mob/living/exosuit/attackby(var/obj/item/thing, var/mob/user)
+/*
+ if(istype(thing, /obj/item/kit/decal))
+ user.visible_message(SPAN_NOTICE("\The [user] opens \the [thing] and spends some quality time customising \the [src]."))
+ var/obj/item/kit/paint/exo/P = thing
+ P.customize(src, user)
+ return TRUE
+*/
+ if(user.a_intent != I_HURT)
+ if(istype(thing, /obj/item/mech_equipment))
+// handle_equip_install(thing, user)
+ return
+
+ if(thing.has_tool_quality(TOOL_MULTITOOL))
+// handle_equip_uninstall(user)
+ return
+
+ else if(thing.has_tool_quality(TOOL_WRENCH))
+// handle_dismantling(thing, user)
+ return
+
+ else if(istype(thing, /obj/item/weldingtool) || istype(thing, /obj/item/stack/cable_coil))
+// handle_repairing(thing, user)
+ return
+
+ else if(thing.has_tool_quality(TOOL_CROWBAR) || istype(thing, /obj/item/cell))
+// handle_powercell(thing, user)
+ return
+ return ..()
+
+/mob/living/exosuit/attack_hand(var/mob/user)
+ // Drag the pilot out if possible.
+ if(user.a_intent == I_HURT)
+ if(!LAZYLEN(pilots))
+ to_chat(user, SPAN_WARNING("There is nobody inside \the [src]."))
+ else if(!hatch_closed)
+ var/mob/pilot = pick(pilots)
+ user.visible_message(SPAN_DANGER("\The [user] is trying to pull \the [pilot] out of \the [src]!"))
+ if(do_after(user, 30) && user.Adjacent(src) && (pilot in pilots) && !hatch_closed)
+ user.visible_message(SPAN_DANGER("\The [user] drags \the [pilot] out of \the [src]!"))
+ eject(pilot, silent=1)
+ return
+
+ // Otherwise toggle the hatch.
+ if(hatch_locked)
+ to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked."))
+ return
+ hatch_closed = !hatch_closed
+ to_chat(user, SPAN_NOTICE("You [hatch_closed ? "close" : "open"] the [body.hatch_descriptor]."))
+ hud_open.update_icon()
+ update_icon()
+ return
+
+/mob/living/exosuit/proc/attack_self(var/mob/user)
+ return visible_message("\The [src] pokes itself.")
+
+/mob/living/exosuit/proc/rename(var/mob/user)
+ if(user != src && !(user in pilots))
+ return
+ var/new_name = sanitize(input("Enter a new exosuit designation.", "Exosuit Name") as text|null, max_length = MAX_NAME_LEN)
+ if(!new_name || new_name == name || (user != src && !(user in pilots)))
+ return
+ name = new_name
+ to_chat(user, SPAN_NOTICE("You have redesignated this exosuit as \the [name]."))
+
+/mob/living/exosuit/get_inventory_slot(obj/item/I)
+ for(var/h in hardpoints)
+ if(hardpoints[h] == I)
+ return h
+ return 0
diff --git a/code/modules/exosuits/exosuit_life.dm b/code/modules/exosuits/exosuit_life.dm
new file mode 100644
index 00000000000..52b290f2bd6
--- /dev/null
+++ b/code/modules/exosuits/exosuit_life.dm
@@ -0,0 +1,123 @@
+/mob/living/exosuit/handle_disabilities()
+ return
+
+/mob/living/exosuit/Life()
+
+ for(var/thing in pilots)
+ var/mob/pilot = thing
+ if(pilot.loc != src) // Admin jump or teleport/grab.
+ if(pilot.client)
+ pilot.client.screen -= hud_elements
+ LAZYREMOVE(pilots, pilot)
+ UNSETEMPTY(pilots)
+ update_pilots()
+
+ if(radio)
+ radio.on = (head && head.radio && head.radio.is_functional())
+
+ body.update_air(hatch_closed && use_air)
+
+ if((client || LAZYLEN(pilots)) && get_cell())
+ var/obj/item/cell/MyC = get_cell()
+ MyC.drain_power(0, 0, calc_power_draw())
+
+ updatehealth()
+ if(health <= 0 && stat != DEAD)
+ death()
+
+ ..() //Handles stuff like environment
+
+ handle_hud_icons()
+
+ lying = FALSE // Fuck off, carp.
+ handle_vision()
+
+/mob/living/exosuit/get_cell()
+ return body ? body.cell : null
+
+/mob/living/exosuit/proc/calc_power_draw()
+ //Passive power stuff here. You can also recharge cells or hardpoints if those make sense
+ var/total_draw = 0
+ for(var/hardpoint in hardpoints)
+ var/obj/item/mech_equipment/I = hardpoints[hardpoint]
+ if(!istype(I))
+ continue
+ total_draw += I.passive_power_use
+
+ if(head && head.active_sensors)
+ total_draw += head.power_use
+
+ if(body)
+ total_draw += body.power_use
+
+ return total_draw
+
+/mob/living/exosuit/handle_environment(var/datum/gas_mixture/environment)
+ if(!environment) return
+ //Mechs and vehicles in general can be assumed to just tend to whatever ambient temperature
+ if(abs(environment.temperature - bodytemperature) > 10 )
+ bodytemperature += ((environment.temperature - bodytemperature) / 3)
+
+ if(environment.temperature > material.melting_point * 1.25 ) //A bit higher because I like to assume there's a difference between a mech and a wall
+ apply_damage(damage = (environment.temperature - (material.melting_point))/5 , damagetype = BURN)
+ //A possibility is to hook up interface icons here. But this works pretty well in my experience
+ if(prob(5))
+ visible_message(SPAN_DANGER("\The [src]'s hull bends and buckles under the intense heat!"))
+
+
+/mob/living/exosuit/death(var/gibbed)
+
+ // Salvage moves into the wreck unless we're exploding violently.
+ var/obj/wreck = new wreckage_path(get_turf(src), src, gibbed)
+ wreck.name = "wreckage of \the [name]"
+ if(!gibbed)
+ if(arms.loc != src)
+ arms = null
+ if(legs.loc != src)
+ legs = null
+ if(head.loc != src)
+ head = null
+ if(body.loc != src)
+ body = null
+
+ // Eject the pilot.
+ if(LAZYLEN(pilots))
+ hatch_locked = 0 // So they can get out.
+ for(var/pilot in pilots)
+ eject(pilot, silent=1)
+
+ // Handle the rest of things.
+ ..(gibbed, (gibbed ? "explodes!" : "grinds to a halt before collapsing!"))
+ if(!gibbed) qdel(src)
+
+/mob/living/exosuit/gib()
+ death(1)
+
+ // Get a turf to play with.
+ var/turf/T = get_turf(src)
+ if(!T)
+ qdel(src)
+ return
+
+ // Hurl our component pieces about.
+ var/list/stuff_to_throw = list()
+ for(var/obj/item/thing in list(arms, legs, head, body))
+ if(thing) stuff_to_throw += thing
+ for(var/hardpoint in hardpoints)
+ if(hardpoints[hardpoint])
+ var/obj/item/thing = hardpoints[hardpoint]
+ thing.screen_loc = null
+ stuff_to_throw += thing
+ for(var/obj/item/thing in stuff_to_throw)
+ thing.forceMove(T)
+ thing.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(3,6),40)
+ explosion(T, -1, 0, 2)
+ qdel(src)
+ return
+
+/mob/living/exosuit/handle_vision()
+ if(head)
+ sight = head.get_sight()
+ see_invisible = head.get_invisible()
+ if(body && (body.pilot_coverage < 100 || body.transparent_cabin))
+ sight &= ~BLIND
diff --git a/code/modules/exosuits/exosuit_movement.dm b/code/modules/exosuits/exosuit_movement.dm
new file mode 100644
index 00000000000..bd016528806
--- /dev/null
+++ b/code/modules/exosuits/exosuit_movement.dm
@@ -0,0 +1,70 @@
+
+/mob/living/exosuit/can_ztravel()
+ if(Allow_Spacemove()) //Handle here
+ return 1
+
+ for(var/turf/simulated/T in trange(1,src))
+ if(T.density)
+ return 1
+
+/mob/living/exosuit/can_fall() // Ignore anchored.
+ if(is_incorporeal())
+ return FALSE
+ if(hovering)
+ return FALSE
+ return !(can_overcome_gravity())
+
+/mob/living/exosuit/relaymove(var/mob/user, var/direction)
+ . = FALSE
+ if(length(pilots) && (user in pilots) && world.time > next_step)
+ . = Move(get_step(src, direction), direction)
+
+/mob/living/exosuit/Move(atom/newloc, direct, movetime)
+ . = FALSE
+
+ if(incapacitated())
+ return FALSE
+
+ if(!legs)
+ to_chat(src, SPAN_WARNING("\The [src] has no means of propulsion!"))
+ next_step = world.time + 3 // Just to stop them from getting spammed with messages.
+ return FALSE
+
+ if(!legs.motivator || !legs.motivator.is_functional())
+ to_chat(src, SPAN_WARNING("Your motivators are damaged! You can't move!"))
+ next_step = world.time + 15
+ return FALSE
+
+ if(maintenance_protocols)
+ to_chat(src, SPAN_WARNING("Maintenance protocols are in effect."))
+ next_step = world.time + 3 // Just to stop them from getting spammed with messages.
+ return FALSE
+
+ var/obj/item/cell/C = get_cell()
+ if(!C || !C.check_charge(legs.power_use * CELLRATE))
+ to_chat(src, SPAN_WARNING("The power indicator flashes briefly."))
+ next_step = world.time + 3 //On fast exosuits this got annoying fast
+ return FALSE
+
+ if(emp_damage >= EMP_MOVE_DISRUPT && prob(30))
+ direct = pick(GLOB.alldirs - dir) // We have diagonal movement, we're gonna use it damnit.
+
+ C.use(legs.power_use * CELLRATE)
+
+ if(!direct)
+ direct = get_dir(src, newloc)
+
+ if(dir != direct)
+ playsound(loc, mech_turn_sound, 40,1)
+ set_dir(direct)
+ next_step = world.time + legs.turn_delay
+ return FALSE
+ . = FALSE
+ if(!newloc)
+ newloc = get_step(src, direct)
+ if(newloc && legs && legs.can_move_on(loc, newloc))
+ . = ..(newloc, direct)
+ next_step = world.time + legs.move_delay
+ if(. && !istype(loc, /turf/space))
+ playsound(src.loc, mech_step_sound, 40, 1)
+ return .
diff --git a/code/modules/exosuits/exosuit_wreckage.dm b/code/modules/exosuits/exosuit_wreckage.dm
new file mode 100644
index 00000000000..919c9df77be
--- /dev/null
+++ b/code/modules/exosuits/exosuit_wreckage.dm
@@ -0,0 +1,83 @@
+/obj/structure/exosuit_wreckage
+ name = "wreckage"
+ desc = "It might have some salvagable parts."
+ density = 1
+ opacity = 1
+ anchored = 1
+ icon_state = "wreck"
+ icon = 'icons/mob/mecha/mech_part_items.dmi'
+ var/prepared
+
+/obj/structure/exosuit_wreckage/Initialize(var/newloc, var/mob/living/exosuit/exosuit, var/gibbed)
+ if(exosuit)
+ name = "wreckage of \the [exosuit.name]"
+ if(!gibbed)
+ for(var/obj/item/thing in list(exosuit.arms, exosuit.legs, exosuit.head, exosuit.body))
+ if(thing && prob(40))
+ thing.forceMove(src)
+ for(var/hardpoint in exosuit.hardpoints)
+ if(exosuit.hardpoints[hardpoint] && prob(40))
+ var/obj/item/thing = exosuit.hardpoints[hardpoint]
+ if(exosuit.remove_system_initialize(hardpoint))
+ thing.forceMove(src)
+
+ return ..()
+
+/obj/structure/exosuit_wreckage/powerloader/Initialize(var/newloc)
+ var/mob/living/exosuit/Exo = new /mob/living/exosuit/premade/powerloader(src)
+ return ..(newloc, Exo, FALSE)
+
+/obj/structure/exosuit_wreckage/attack_hand(var/mob/user)
+ if(contents.len)
+ var/obj/item/thing = pick(contents)
+ if(istype(thing))
+ thing.forceMove(get_turf(user))
+ user.put_in_hands(thing)
+ to_chat(user, "You retrieve \the [thing] from \the [src].")
+ return
+ return ..()
+
+/obj/structure/exosuit_wreckage/attackby(var/obj/item/W, var/mob/user)
+
+ var/cutting
+ if(istype(W, /obj/item/weldingtool))
+ var/obj/item/weldingtool/WT = W
+ if(WT.welding)
+ WT.remove_fuel(1,user)
+ cutting = TRUE
+ else
+ to_chat(user, SPAN_WARNING("Turn the torch on, first."))
+ else if(istype(W, /obj/item/pickaxe/plasmacutter))
+ cutting = TRUE
+
+ if(cutting)
+ if(!prepared)
+ prepared = 1
+ to_chat(user, SPAN_NOTICE("You partially dismantle \the [src]."))
+ else
+ to_chat(user, SPAN_WARNING("\The [src] has already been weakened."))
+ return 1
+
+ else if(W.has_tool_quality(TOOL_WRENCH))
+ if(prepared)
+ to_chat(user, SPAN_NOTICE("You finish dismantling \the [src]."))
+ new /obj/item/stack/material/steel(get_turf(src),rand(5,10))
+ qdel(src)
+ else
+ to_chat(user, SPAN_WARNING("It's too solid to dismantle. Try cutting through some of the bigger bits."))
+ return 1
+ else if(istype(W) && W.force > 20)
+ visible_message(SPAN_DANGER("\The [src] has been smashed with \the [W] by \the [user]!"))
+ if(prob(20))
+ new /obj/item/stack/material/steel(get_turf(src),rand(1,3))
+ qdel(src)
+ return 1
+ return ..()
+
+/obj/structure/exosuit_wreckage/Destroy()
+ for(var/obj/thing in contents)
+ if(prob(65))
+ thing.forceMove(get_turf(src))
+ else
+ qdel(thing)
+ ..()
diff --git a/code/modules/exosuits/interface/_interface.dm b/code/modules/exosuits/interface/_interface.dm
new file mode 100644
index 00000000000..1bd8be68be2
--- /dev/null
+++ b/code/modules/exosuits/interface/_interface.dm
@@ -0,0 +1,214 @@
+/mob/living/exosuit/proc/refresh_hud()
+ if(LAZYLEN(pilots))
+ for(var/thing in pilots)
+ var/mob/pilot = thing
+ if(pilot.client)
+ pilot.client.screen |= hud_elements
+ if(client)
+ client.screen |= hud_elements
+
+/mob/living/exosuit/proc/InitializeHud()
+ zone_sel = new
+ if(!LAZYLEN(hud_elements))
+ var/i = 1
+ for(var/hardpoint in hardpoints)
+ var/obj/screen/movable/exosuit/hardpoint/H = new(src, hardpoint)
+ H.screen_loc = "1:6,[15-i]" //temp
+ hud_elements |= H
+ hardpoint_hud_elements[hardpoint] = H
+ i++
+
+ var/list/additional_hud_elements = list(
+ /obj/screen/movable/exosuit/toggle/maint,
+ /obj/screen/movable/exosuit/eject,
+ /obj/screen/movable/exosuit/toggle/hardpoint,
+ /obj/screen/movable/exosuit/toggle/hatch,
+ /obj/screen/movable/exosuit/toggle/hatch_open,
+ /obj/screen/movable/exosuit/radio,
+ /obj/screen/movable/exosuit/rename,
+ /obj/screen/movable/exosuit/toggle/camera
+ )
+ if(body && body.pilot_coverage >= 100)
+ additional_hud_elements += /obj/screen/movable/exosuit/toggle/air
+ i = 0
+ var/pos = 7
+ for(var/additional_hud in additional_hud_elements)
+ var/obj/screen/movable/exosuit/M = new additional_hud(src)
+ M.screen_loc = "1:6,[pos]:[i * -12]"
+ hud_elements |= M
+ i++
+ if(i == 3)
+ pos--
+ i = 0
+
+ hud_health = new /obj/screen/movable/exosuit/health(src)
+ hud_health.screen_loc = "EAST-1:28,CENTER-3:11"
+ hud_elements |= hud_health
+ hud_open = locate(/obj/screen/movable/exosuit/toggle/hatch_open) in hud_elements
+ hud_power = new /obj/screen/movable/exosuit/power(src)
+ hud_power.screen_loc = "EAST-1:12,CENTER-4:25"
+ hud_elements |= hud_power
+
+ refresh_hud()
+
+/mob/living/exosuit/create_mob_hud(datum/hud/HUD)
+ ..()
+
+ var/ui_style = 'icons/mob/screen1.dmi'
+ if(HUD.ui_style)
+ ui_style = HUD.ui_style
+ var/ui_color = "#ffffff"
+ var/ui_alpha = 255
+
+ var/obj/screen/using
+ //Intent Backdrop
+ using = new /obj/screen()
+ using.name = "act_intent"
+ using.icon = ui_style
+ using.icon_state = "intent_"+a_intent
+ using.screen_loc = ui_acti
+ using.color = ui_color
+ using.alpha = ui_alpha
+ HUD.adding += using
+ HUD.action_intent = using
+
+ hud_elements |= using
+
+ //Small intent quarters
+ var/icon/ico
+
+ ico = new(ui_style, "black")
+ ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1)
+ ico.DrawBox(rgb(255,255,255,1),1,ico.Height()/2,ico.Width()/2,ico.Height())
+ using = new /obj/screen()
+ using.name = I_HELP
+ using.icon = ico
+ using.screen_loc = ui_acti
+ using.alpha = ui_alpha
+ using.layer = LAYER_HUD_ITEM //These sit on the intent box
+ HUD.adding += using
+ HUD.help_intent = using
+
+ ico = new(ui_style, "black")
+ ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1)
+ ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,ico.Height()/2,ico.Width(),ico.Height())
+ using = new /obj/screen()
+ using.name = I_DISARM
+ using.icon = ico
+ using.screen_loc = ui_acti
+ using.alpha = ui_alpha
+ using.layer = LAYER_HUD_ITEM
+ HUD.adding += using
+ HUD.disarm_intent = using
+
+ ico = new(ui_style, "black")
+ ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1)
+ ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,1,ico.Width(),ico.Height()/2)
+ using = new /obj/screen()
+ using.name = I_GRAB
+ using.icon = ico
+ using.screen_loc = ui_acti
+ using.alpha = ui_alpha
+ using.layer = LAYER_HUD_ITEM
+ HUD.adding += using
+ HUD.grab_intent = using
+
+ ico = new(ui_style, "black")
+ ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1)
+ ico.DrawBox(rgb(255,255,255,1),1,1,ico.Width()/2,ico.Height()/2)
+ using = new /obj/screen()
+ using.name = I_HURT
+ using.icon = ico
+ using.screen_loc = ui_acti
+ using.alpha = ui_alpha
+ using.layer = LAYER_HUD_ITEM
+ HUD.adding += using
+ HUD.hurt_intent = using
+
+ //Move intent (walk/run)
+ using = new /obj/screen()
+ using.name = "mov_intent"
+ using.icon = ui_style
+ using.icon_state = (IS_RUNNING(src) ? "running" : "walking")
+ using.screen_loc = ui_movi
+ using.color = ui_color
+ using.alpha = ui_alpha
+ HUD.adding += using
+ HUD.move_intent = using
+
+ //Resist button
+ using = new /obj/screen()
+ using.name = "resist"
+ using.icon = ui_style
+ using.icon_state = "act_resist"
+ using.screen_loc = ui_pull_resist
+ using.color = ui_color
+ using.alpha = ui_alpha
+ HUD.hotkeybuttons += using
+
+ //Pull button
+ pullin = new /obj/screen()
+ pullin.icon = ui_style
+ pullin.icon_state = "pull0"
+ pullin.name = "pull"
+ pullin.screen_loc = ui_pull_resist
+ HUD.hotkeybuttons += pullin
+ hud_elements |= pullin
+
+ zone_sel = new /obj/screen/zone_sel( null )
+ zone_sel.icon = ui_style
+ zone_sel.color = ui_color
+ zone_sel.alpha = ui_alpha
+ zone_sel.cut_overlays()
+ zone_sel.add_overlay(image('icons/mob/zone_sel.dmi', "[zone_sel.selecting]"))
+ hud_elements |= zone_sel
+
+/mob/living/exosuit/handle_hud_icons()
+ for(var/hardpoint in hardpoint_hud_elements)
+ var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[hardpoint]
+ if(H) H.update_system_info()
+ handle_hud_icons_health()
+ var/obj/item/cell/C = get_cell()
+ if(istype(C))
+ hud_power.maptext = "[round(C.charge)]/[round(C.maxcharge)]"
+ else hud_power.maptext = "CHECK POWER"
+ refresh_hud()
+
+/mob/living/exosuit/handle_hud_icons_health()
+
+ hud_health.overlays.Cut()
+
+ var/obj/item/cell/MyC = get_cell()
+ if(!body || !MyC || (MyC.charge <= 0))
+ return
+
+ if(!body.diagnostics || !body.diagnostics.is_functional() || ((emp_damage>EMP_GUI_DISRUPT) && prob(emp_damage*2)))
+ if(!GLOB.mech_damage_overlay_cache["critfail"])
+ GLOB.mech_damage_overlay_cache["critfail"] = image(icon='icons/mob/mecha/mech_hud.dmi',icon_state="dam_error")
+ hud_health.overlays |= GLOB.mech_damage_overlay_cache["critfail"]
+ return
+
+ var/list/part_to_state = list("legs" = legs,"body" = body,"head" = head,"arms" = arms)
+ for(var/part in part_to_state)
+ var/state = 0
+ var/obj/item/mech_component/MC = part_to_state[part]
+ if(MC)
+ if((emp_damage>EMP_GUI_DISRUPT) && prob(emp_damage*3))
+ state = rand(0,4)
+ else
+ state = MC.damage_state
+ if(!GLOB.mech_damage_overlay_cache["[part]-[state]"])
+ var/image/I = image(icon='icons/mob/mecha/mech_hud.dmi',icon_state="dam_[part]")
+ switch(state)
+ if(1)
+ I.color = "#00ff00"
+ if(2)
+ I.color = "#f2c50d"
+ if(3)
+ I.color = "#ea8515"
+ if(4)
+ I.color = "#ff0000"
+ else
+ I.color = "#f5f5f0"
+ GLOB.mech_damage_overlay_cache["[part]-[state]"] = I
+ hud_health.overlays |= GLOB.mech_damage_overlay_cache["[part]-[state]"]
diff --git a/code/modules/exosuits/interface/screen_objects.dm b/code/modules/exosuits/interface/screen_objects.dm
new file mode 100644
index 00000000000..65006bd96bf
--- /dev/null
+++ b/code/modules/exosuits/interface/screen_objects.dm
@@ -0,0 +1,250 @@
+// Screen objects hereon out.
+/obj/screen/movable/exosuit
+ name = "hardpoint"
+ icon = 'icons/mob/mecha/mech_hud.dmi'
+ icon_state = "hardpoint"
+ var/mob/living/exosuit/owner
+
+/obj/screen/movable/exosuit/radio
+ name = "radio"
+ icon_state = "radio"
+
+/obj/screen/movable/exosuit/radio/Click()
+ if(..())
+ if(owner.radio)
+ owner.radio.attack_self(usr)
+ else
+ to_chat(usr, SPAN_WARNING("There is no radio installed."))
+
+/obj/screen/movable/exosuit/Initialize()
+ . = ..()
+ var/mob/living/exosuit/newowner = loc
+ if(!istype(newowner))
+ return qdel(src)
+ owner = newowner
+
+/obj/screen/movable/exosuit/Click()
+ return (!owner || !usr.incapacitated() && (usr == owner || usr.loc == owner))
+
+/obj/screen/movable/exosuit/hardpoint
+ name = "hardpoint"
+ var/hardpoint_tag
+ var/obj/item/holding
+
+ maptext_x = 34
+ maptext_y = 3
+ maptext_width = 72
+
+/obj/screen/movable/exosuit/hardpoint/MouseDrop()
+ ..()
+ if(holding) holding.screen_loc = screen_loc
+
+/obj/screen/movable/exosuit/hardpoint/proc/update_system_info()
+
+ // No point drawing it if we have no item to use or nobody to see it.
+ if(!holding || !owner)
+ return
+
+ var/has_pilot_with_client = owner.client
+ if(!has_pilot_with_client && LAZYLEN(owner.pilots))
+ for(var/thing in owner.pilots)
+ var/mob/pilot = thing
+ if(pilot.client)
+ has_pilot_with_client = TRUE
+ break
+ if(!has_pilot_with_client)
+ return
+
+ var/list/new_overlays = list()
+ var/obj/item/cell/OwnerCell = owner.get_cell()
+ if(!OwnerCell || (OwnerCell.charge <= 0))
+ overlays.Cut()
+ return
+
+ maptext = holding.get_hardpoint_maptext()
+
+ var/ui_damage = (!owner.body.diagnostics || !owner.body.diagnostics.is_functional() || ((owner.emp_damage>EMP_GUI_DISRUPT) && prob(owner.emp_damage)))
+
+ var/value = holding.get_hardpoint_status_value()
+ if(isnull(value))
+ overlays.Cut()
+ return
+
+ if(ui_damage)
+ value = -1
+ maptext = "ERROR"
+ else
+ if((owner.emp_damage>EMP_GUI_DISRUPT) && prob(owner.emp_damage*2))
+ if(prob(10))
+ value = -1
+ else
+ value = rand(1,BAR_CAP)
+ else
+ value = round(value * BAR_CAP)
+
+ // Draw background.
+ if(!GLOB.default_hardpoint_background)
+ GLOB.default_hardpoint_background = image(icon = 'icons/mob/mecha/mech_hud.dmi', icon_state = "bar_bkg")
+ GLOB.default_hardpoint_background.pixel_x = 34
+ new_overlays += GLOB.default_hardpoint_background
+
+ if(value == 0)
+ if(!GLOB.hardpoint_bar_empty)
+ GLOB.hardpoint_bar_empty = image(icon='icons/mob/mecha/mech_hud.dmi',icon_state="bar_flash")
+ GLOB.hardpoint_bar_empty.pixel_x = 24
+ GLOB.hardpoint_bar_empty.color = "#ff0000"
+ new_overlays += GLOB.hardpoint_bar_empty
+ else if(value < 0)
+ if(!GLOB.hardpoint_error_icon)
+ GLOB.hardpoint_error_icon = image(icon='icons/mob/mecha/mech_hud.dmi',icon_state="bar_error")
+ GLOB.hardpoint_error_icon.pixel_x = 34
+ new_overlays += GLOB.hardpoint_error_icon
+ else
+ value = min(value, BAR_CAP)
+ // Draw statbar.
+ if(!LAZYLEN(GLOB.hardpoint_bar_cache))
+ for(var/i=0;i5)
+ bar.color = "#00ff00"
+ else if(i>1)
+ bar.color = "#ffff00"
+ else
+ bar.color = "#ff0000"
+ GLOB.hardpoint_bar_cache += bar
+ for(var/i=0;i<=value;i++)
+ new_overlays += GLOB.hardpoint_bar_cache[i]
+ overlays = new_overlays
+
+/obj/screen/movable/exosuit/hardpoint/Initialize(mapload, var/newtag)
+ . = ..()
+ hardpoint_tag = newtag
+ name = "hardpoint ([hardpoint_tag])"
+
+/obj/screen/movable/exosuit/hardpoint/Click(var/location, var/control, var/params)
+
+ if(!(..()))
+ return
+
+ var/modifiers = params2list(params)
+ if(modifiers["ctrl"])
+ if(owner.hardpoints_locked)
+ to_chat(usr, SPAN_WARNING("Hardpoint ejection system is locked."))
+ return
+ if(owner.remove_system(hardpoint_tag))
+ to_chat(usr, SPAN_NOTICE("You disengage and discard the system mounted to your [hardpoint_tag] hardpoint."))
+ else
+ to_chat(usr, SPAN_DANGER("You fail to remove the system mounted to your [hardpoint_tag] hardpoint."))
+ return
+
+ if(owner.selected_hardpoint == hardpoint_tag)
+ icon_state = "hardpoint"
+ owner.clear_selected_hardpoint()
+ else
+ if(owner.set_hardpoint(hardpoint_tag))
+ icon_state = "hardpoint_selected"
+
+/obj/screen/movable/exosuit/eject
+ name = "eject"
+ icon_state = "eject"
+
+/obj/screen/movable/exosuit/eject/Click()
+ if(..())
+ owner.eject(usr)
+
+/obj/screen/movable/exosuit/rename
+ name = "rename"
+ icon_state = "rename"
+
+/obj/screen/movable/exosuit/power
+ name = "power"
+ icon_state = null
+
+ maptext_width = 64
+
+/obj/screen/movable/exosuit/rename/Click()
+ if(..())
+ owner.rename(usr)
+
+/obj/screen/movable/exosuit/toggle
+ name = "toggle"
+ var/toggled
+
+/obj/screen/movable/exosuit/toggle/Click()
+ if(..()) toggled()
+
+/obj/screen/movable/exosuit/toggle/proc/toggled()
+ toggled = !toggled
+ icon_state = "[initial(icon_state)][toggled ? "_enabled" : ""]"
+ return toggled
+
+/obj/screen/movable/exosuit/toggle/air
+ name = "air"
+ icon_state = "air"
+
+/obj/screen/movable/exosuit/toggle/air/toggled()
+ owner.use_air = ..()
+ to_chat(usr, SPAN_NOTICE("Auxiliary atmospheric system [owner.use_air ? "enabled" : "disabled"]."))
+
+/obj/screen/movable/exosuit/toggle/maint
+ name = "toggle maintenance protocol"
+ icon_state = "maint"
+
+/obj/screen/movable/exosuit/toggle/maint/toggled()
+ owner.maintenance_protocols = ..()
+ to_chat(usr, SPAN_NOTICE("Maintenance protocols [owner.maintenance_protocols ? "enabled" : "disabled"]."))
+
+/obj/screen/movable/exosuit/toggle/hardpoint
+ name = "toggle hardpoint lock"
+ icon_state = "hardpoint_lock"
+
+/obj/screen/movable/exosuit/toggle/hardpoint/toggled()
+ owner.hardpoints_locked = ..()
+ to_chat(usr, SPAN_NOTICE("Hardpoint system access is now [owner.hardpoints_locked ? "disabled" : "enabled"]."))
+
+/obj/screen/movable/exosuit/toggle/hatch
+ name = "toggle hatch lock"
+ icon_state = "hatch_lock"
+
+/obj/screen/movable/exosuit/toggle/hatch/toggled()
+ if(!owner.hatch_locked && !owner.hatch_closed)
+ to_chat(usr, SPAN_WARNING("You cannot lock the hatch while it is open."))
+ return
+ owner.hatch_locked = ..()
+ to_chat(usr, SPAN_NOTICE("The [owner.body.hatch_descriptor] is [owner.hatch_locked ? "now" : "no longer" ] locked."))
+
+/obj/screen/movable/exosuit/toggle/hatch_open
+ name = "open or close hatch"
+ icon_state = "hatch_status"
+
+/obj/screen/movable/exosuit/toggle/hatch_open/toggled()
+ if(owner.hatch_locked && owner.hatch_closed)
+ to_chat(usr, SPAN_WARNING("You cannot open the hatch while it is locked."))
+ return
+ owner.hatch_closed = ..()
+ to_chat(usr, SPAN_NOTICE("The [owner.body.hatch_descriptor] is now [owner.hatch_closed ? "closed" : "open" ]."))
+ owner.update_icon()
+
+// This is basically just a holder for the updates the exosuit does.
+/obj/screen/movable/exosuit/health
+ name = "exosuit integrity"
+ icon_state = "health"
+
+//Controls if cameras set the vision flags
+/obj/screen/movable/exosuit/toggle/camera
+ name = "toggle camera matrix"
+ icon_state = "camera"
+
+/obj/screen/movable/exosuit/toggle/camera/toggled()
+ if(!owner.head)
+ to_chat(usr, SPAN_WARNING("I/O Error: Camera systems not found."))
+ return
+ if(!owner.head.vision_flags)
+ to_chat(usr, SPAN_WARNING("Alternative sensor configurations not found. Contact manufacturer for more details."))
+ return
+ owner.head.active_sensors = ..()
+ to_chat(usr, SPAN_NOTICE("[owner.head.name] advanced sensor mode is [owner.head.active_sensors ? "now" : "no longer" ] active."))
+
+
+#undef BAR_CAP
diff --git a/code/modules/exosuits/premade/_premade.dm b/code/modules/exosuits/premade/_premade.dm
new file mode 100644
index 00000000000..1a32541abe2
--- /dev/null
+++ b/code/modules/exosuits/premade/_premade.dm
@@ -0,0 +1,156 @@
+//GLOBAL_LIST_INIT(mech_decals, (icon_states('icons/mecha/mech_decals.dmi')-list("template", "mask")))
+
+/mob/living/exosuit/premade
+ name = "impossible exosuit"
+ desc = "It seems to be saying 'please let me die'."
+ var/decal
+
+/mob/living/exosuit/premade/Initialize()
+ if(arms)
+ arms.decal = decal
+ arms.prebuild()
+ if(legs)
+ legs.decal = decal
+ legs.prebuild()
+ if(head)
+ head.decal = decal
+ head.prebuild()
+ if(body)
+ body.decal = decal
+ body.prebuild()
+ if(!material)
+ material = get_material_by_name(MAT_STEEL)
+ . = ..()
+
+ spawn_mech_equipment()
+
+/mob/living/exosuit/premade/proc/spawn_mech_equipment()
+ install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD)
+
+/mob/living/exosuit/premade/random
+ name = "mismatched exosuit"
+ desc = "It seems to have been roughly thrown together and then spraypainted a single colour."
+
+/mob/living/exosuit/premade/random/Initialize(mapload, var/obj/structure/heavy_vehicle_frame/source_frame, var/super_random = FALSE, var/using_boring_colours = FALSE)
+ //if(!prob(100/(LAZYLEN(GLOB.mech_decals)+1)))
+ // decal = pick(GLOB.mech_decals)
+
+ var/list/use_colours
+ if(using_boring_colours)
+ use_colours = list(
+ COLOR_DARK_GRAY,
+ COLOR_GRAY40,
+ COLOR_DARK_BROWN,
+ COLOR_GRAY,
+ COLOR_RED_GRAY,
+ COLOR_BROWN,
+ COLOR_GREEN_GRAY,
+ COLOR_BLUE_GRAY,
+ COLOR_PURPLE_GRAY,
+ COLOR_BEIGE,
+ COLOR_PALE_GREEN_GRAY,
+ COLOR_PALE_RED_GRAY,
+ COLOR_PALE_PURPLE_GRAY,
+ COLOR_PALE_BLUE_GRAY,
+ COLOR_SILVER,
+ COLOR_GRAY80,
+ COLOR_OFF_WHITE,
+ COLOR_GUNMETAL,
+ COLOR_HULL,
+ COLOR_TITANIUM,
+ COLOR_DARK_GUNMETAL,
+ COLOR_BRONZE,
+ COLOR_BRASS
+ )
+ else
+ use_colours = list(
+ COLOR_NAVY_BLUE,
+ COLOR_GREEN,
+ COLOR_DARK_GRAY,
+ COLOR_MAROON,
+ COLOR_PURPLE,
+ COLOR_VIOLET,
+ COLOR_OLIVE,
+ COLOR_BROWN_ORANGE,
+ COLOR_DARK_ORANGE,
+ COLOR_GRAY40,
+ COLOR_SEDONA,
+ COLOR_DARK_BROWN,
+ COLOR_BLUE,
+ COLOR_DEEP_SKY_BLUE,
+ COLOR_LIME,
+ COLOR_CYAN,
+ COLOR_TEAL,
+ COLOR_RED,
+ COLOR_PINK,
+ COLOR_ORANGE,
+ COLOR_YELLOW,
+ COLOR_GRAY,
+ COLOR_RED_GRAY,
+ COLOR_BROWN,
+ COLOR_GREEN_GRAY,
+ COLOR_BLUE_GRAY,
+ COLOR_SUN,
+ COLOR_PURPLE_GRAY,
+ COLOR_BLUE_LIGHT,
+ COLOR_RED_LIGHT,
+ COLOR_BEIGE,
+ COLOR_PALE_GREEN_GRAY,
+ COLOR_PALE_RED_GRAY,
+ COLOR_PALE_PURPLE_GRAY,
+ COLOR_PALE_BLUE_GRAY,
+ COLOR_LUMINOL,
+ COLOR_SILVER,
+ COLOR_GRAY80,
+ COLOR_OFF_WHITE,
+ COLOR_NT_RED,
+ COLOR_BOTTLE_GREEN,
+ COLOR_PALE_BTL_GREEN,
+ COLOR_GUNMETAL,
+ COLOR_MUZZLE_FLASH,
+ COLOR_CHESTNUT,
+ COLOR_BEASTY_BROWN,
+ COLOR_WHEAT,
+ COLOR_CYAN_BLUE,
+ COLOR_LIGHT_CYAN,
+ COLOR_PAKISTAN_GREEN,
+ COLOR_HULL,
+ COLOR_AMBER,
+ COLOR_COMMAND_BLUE,
+ COLOR_SKY_BLUE,
+ COLOR_PALE_ORANGE,
+ COLOR_CIVIE_GREEN,
+ COLOR_TITANIUM,
+ COLOR_DARK_GUNMETAL,
+ COLOR_BRONZE,
+ COLOR_BRASS,
+ COLOR_INDIGO
+ )
+
+ var/mech_colour = super_random ? FALSE : pick(use_colours)
+ if(!arms)
+ var/armstype = pick(typesof(/obj/item/mech_component/manipulators)-/obj/item/mech_component/manipulators)
+ arms = new armstype(src)
+ arms.color = mech_colour ? mech_colour : pick(use_colours)
+ if(!legs)
+ var/legstype = pick(typesof(/obj/item/mech_component/propulsion)-/obj/item/mech_component/propulsion)
+ legs = new legstype(src)
+ legs.color = mech_colour ? mech_colour : pick(use_colours)
+ if(!head)
+ var/headtype = pick(typesof(/obj/item/mech_component/sensors)-/obj/item/mech_component/sensors)
+ head = new headtype(src)
+ head.color = mech_colour ? mech_colour : pick(use_colours)
+ if(!body)
+ var/bodytype = pick(typesof(/obj/item/mech_component/chassis)-/obj/item/mech_component/chassis)
+ body = new bodytype(src)
+ body.color = mech_colour ? mech_colour : pick(use_colours)
+ . = ..()
+
+// Used for spawning/debugging.
+/mob/living/exosuit/premade/random/normal
+
+/mob/living/exosuit/premade/random/boring/Initialize(mapload, var/obj/structure/heavy_vehicle_frame/source_frame)
+ return ..(mapload, source_frame, using_boring_colours = TRUE)
+
+/mob/living/exosuit/premade/random/extra/Initialize(mapload, var/obj/structure/heavy_vehicle_frame/source_frame)
+ return ..(mapload, source_frame, super_random = TRUE)
diff --git a/code/modules/exosuits/premade/combat.dm b/code/modules/exosuits/premade/combat.dm
new file mode 100644
index 00000000000..abcbd41a15b
--- /dev/null
+++ b/code/modules/exosuits/premade/combat.dm
@@ -0,0 +1,24 @@
+/mob/living/exosuit/premade/combat
+ name = "combat exosuit"
+ desc = "A sleek, modern combat exosuit."
+
+/mob/living/exosuit/premade/combat/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/combat(src)
+ arms.color = COLOR_DARK_GUNMETAL
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/combat(src)
+ legs.color = COLOR_DARK_GUNMETAL
+ if(!head)
+ head = new /obj/item/mech_component/sensors/combat(src)
+ head.color = COLOR_DARK_GUNMETAL
+ if(!body)
+ body = new /obj/item/mech_component/chassis/combat(src)
+ body.color = COLOR_DARK_GUNMETAL
+
+ . = ..()
+
+/mob/living/exosuit/premade/combat/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/ion(src), HARDPOINT_RIGHT_HAND)
diff --git a/code/modules/exosuits/premade/exploration.dm b/code/modules/exosuits/premade/exploration.dm
new file mode 100644
index 00000000000..5219a1f2c31
--- /dev/null
+++ b/code/modules/exosuits/premade/exploration.dm
@@ -0,0 +1,36 @@
+/mob/living/exosuit/premade/light/exploration
+ name = "exploration mech"
+ desc = "It looks a bit charred."
+
+/obj/item/mech_component/manipulators/industrial/exploration
+ color = COLOR_PURPLE
+
+/obj/item/mech_component/chassis/pod/exploration
+ color = COLOR_GUNMETAL
+
+/obj/item/mech_component/propulsion/tracks/exploration
+ color = COLOR_GUNMETAL
+
+/mob/living/exosuit/premade/light/exploration/Initialize()
+ if(!body)
+ body = new /obj/item/mech_component/chassis/pod/exploration(src)
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/tracks/exploration(src)
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/industrial/exploration(src)
+
+ . = ..()
+
+ //Damage it
+ var/list/parts = list(arms,legs,head,body)
+ var/obj/item/mech_component/damaged = pick(parts)
+ damaged.take_burn_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED)
+ if(prob(33))
+ parts -= damaged
+ damaged = pick(parts)
+ damaged.take_burn_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED)
+
+/mob/living/exosuit/premade/light/exploration/spawn_mech_equipment()
+ install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD)
+ install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/laser(src), HARDPOINT_LEFT_HAND)
diff --git a/code/modules/exosuits/premade/heavy.dm b/code/modules/exosuits/premade/heavy.dm
new file mode 100644
index 00000000000..b94d23986be
--- /dev/null
+++ b/code/modules/exosuits/premade/heavy.dm
@@ -0,0 +1,25 @@
+/mob/living/exosuit/premade/heavy
+ name = "Heavy exosuit"
+ desc = "A heavily armored combat exosuit."
+
+/mob/living/exosuit/premade/heavy/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/heavy(src)
+ arms.color = COLOR_TITANIUM
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/heavy(src)
+ legs.color = COLOR_TITANIUM
+ if(!head)
+ head = new /obj/item/mech_component/sensors/heavy(src)
+ head.color = COLOR_TITANIUM
+ if(!body)
+ body = new /obj/item/mech_component/chassis/heavy(src)
+ body.color = COLOR_TITANIUM
+
+ return ..()
+
+/mob/living/exosuit/premade/heavy/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/laser(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/ion(src), HARDPOINT_RIGHT_HAND)
+// install_system_initialize(new /obj/item/mech_equipment/shields(src), HARDPOINT_BACK)
diff --git a/code/modules/exosuits/premade/industrial.dm b/code/modules/exosuits/premade/industrial.dm
new file mode 100644
index 00000000000..1bbdd39fb46
--- /dev/null
+++ b/code/modules/exosuits/premade/industrial.dm
@@ -0,0 +1,112 @@
+/mob/living/exosuit/premade/industrial
+ name = "power loader"
+ desc = "An ancient, but well-liked cargo handling exosuit."
+
+/obj/item/mech_component/manipulators/industrial/painted
+ color = "#ffbc37"
+
+/obj/item/mech_component/propulsion/industrial/painted
+ color = "#ffbc37"
+
+/obj/item/mech_component/sensors/industrial/painted
+ color = "#ffbc37"
+
+/obj/item/mech_component/chassis/industrial/painted
+ color = "#ffdc37"
+
+/mob/living/exosuit/premade/industrial/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/industrial/painted(src)
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/industrial/painted(src)
+ if(!head)
+ head = new /obj/item/mech_component/sensors/industrial/painted(src)
+ if(!body)
+ body = new /obj/item/mech_component/chassis/industrial/painted(src)
+ . = ..()
+
+/mob/living/exosuit/premade/industrial/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/drill(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND)
+
+/mob/living/exosuit/premade/industrial/mechete/Initialize()
+ . = ..()
+
+ if (arms)
+ arms.color = "#6c8aaf"
+ if (legs)
+ legs.color = "#6c8aaf"
+ if (head)
+ head.color = "#6c8aaf"
+ if (body)
+ body.color = "#6c8aaf"
+
+/mob/living/exosuit/premade/industrial/mechete/spawn_mech_equipment()
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/melee/mechete(src), HARDPOINT_RIGHT_HAND)
+
+/mob/living/exosuit/premade/industrial/flames_red
+ name = "APLU \"Firestarter\""
+ desc = "An ancient, but well-liked cargo handling exosuit. This one has cool red flames."
+ decal = "flames_red"
+
+/mob/living/exosuit/premade/industrial/flames_blue
+ name = "APLU \"Burning Chrome\""
+ desc = "An ancient, but well-liked cargo handling exosuit. This one has cool blue flames."
+ decal = "flames_blue"
+
+
+/mob/living/exosuit/premade/industrial/firefighter
+ name = "firefighting exosuit"
+ desc = "A mix and match of industrial parts designed to withstand fires."
+
+/mob/living/exosuit/premade/firefighter/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/industrial(src)
+ arms.color = "#385b3c"
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/industrial(src)
+ legs.color = "#385b3c"
+ if(!head)
+ head = new /obj/item/mech_component/sensors/industrial(src)
+ head.color = "#385b3c"
+ if(!body)
+ body = new /obj/item/mech_component/chassis/heavy(src)
+ body.color = "#385b3c"
+
+ material = get_material_by_name(MAT_PLASTEEL)
+
+ return ..()
+
+/mob/living/exosuit/premade/firefighter/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/drill(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/extinguisher(src), HARDPOINT_RIGHT_HAND)
+
+/obj/item/mech_component/sensors/firefighter/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_ENGINEERING)
+
+/mob/living/exosuit/premade/industrial/old
+ name = "weathered power loader"
+ desc = "An ancient, but well-liked cargo handling exosuit. The paint is starting to flake. Perhaps some maintenance is in order?"
+
+/mob/living/exosuit/premade/industrial/old/Initialize()
+ . = ..()
+ var/list/parts = list(arms,legs,head,body)
+ for(var/obj/item/mech_component/MC in parts)
+ if(prob(35))
+ MC.color = rgb(255,rand(188, 225),rand(55, 136))
+ //Damage it
+ var/obj/item/mech_component/damaged = pick(parts)
+ damaged.take_brute_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED)
+ if(prob(33))
+ parts -= damaged
+ damaged = pick(parts)
+ damaged.take_brute_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED)
+
+/mob/living/exosuit/premade/industrial/old/spawn_mech_equipment()
+ install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD)
+ install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND)
diff --git a/code/modules/exosuits/premade/light.dm b/code/modules/exosuits/premade/light.dm
new file mode 100644
index 00000000000..e4eda4fccf3
--- /dev/null
+++ b/code/modules/exosuits/premade/light.dm
@@ -0,0 +1,25 @@
+/mob/living/exosuit/premade/light
+ name = "light exosuit"
+ desc = "A light and agile exosuit."
+
+/mob/living/exosuit/premade/light/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/light(src)
+ arms.color = COLOR_OFF_WHITE
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/light(src)
+ legs.color = COLOR_OFF_WHITE
+ if(!head)
+ head = new /obj/item/mech_component/sensors/light(src)
+ head.color = COLOR_OFF_WHITE
+ if(!body)
+ body = new /obj/item/mech_component/chassis/light(src)
+ body.color = COLOR_OFF_WHITE
+
+ return ..()
+
+/mob/living/exosuit/premade/light/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/catapult(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/sleeper(src), HARDPOINT_BACK)
+ install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD)
diff --git a/code/modules/exosuits/premade/mercenary.dm b/code/modules/exosuits/premade/mercenary.dm
new file mode 100644
index 00000000000..e2cbcfce99c
--- /dev/null
+++ b/code/modules/exosuits/premade/mercenary.dm
@@ -0,0 +1,46 @@
+/mob/living/exosuit/premade/mercenary
+ name = "mercenary exosuit"
+ desc = "A heavily armored combat exosuit."
+
+/obj/item/mech_component/manipulators/mercenary/painted
+ color = COLOR_TITANIUM
+
+/obj/item/mech_component/propulsion/mercenary/painted
+ color = COLOR_TITANIUM
+
+/obj/item/mech_component/sensors/mercenary/painted
+ color = COLOR_TITANIUM
+
+/obj/item/mech_component/chassis/mercenary/painted
+ color = COLOR_TITANIUM
+
+/mob/living/exosuit/premade/mercenary/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/mercenary/painted(src)
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/mercenary/painted(src)
+ if(!head)
+ head = new /obj/item/mech_component/sensors/mercenary/painted(src)
+ if(!body)
+ body = new /obj/item/mech_component/chassis/mercenary/painted(src)
+ . = ..()
+
+/mob/living/exosuit/premade/mercenary/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/laser(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/ion(src), HARDPOINT_RIGHT_HAND)
+
+/mob/living/exosuit/premade/mercenary/merc/Initialize()
+ . = ..()
+ if(arms)
+ arms.color = COLOR_RED
+ if(legs)
+ legs.color = COLOR_RED
+ if(head)
+ head.color = COLOR_RED
+ if(body)
+ body.color = COLOR_DARK_GUNMETAL
+
+/mob/living/exosuit/premade/mercenary/merc/spawn_mech_equipment()
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/laser(src), HARDPOINT_RIGHT_HAND)
diff --git a/code/modules/exosuits/premade/modern.dm b/code/modules/exosuits/premade/modern.dm
new file mode 100644
index 00000000000..5f4458de095
--- /dev/null
+++ b/code/modules/exosuits/premade/modern.dm
@@ -0,0 +1,24 @@
+/mob/living/exosuit/premade/modern
+ name = "modern exosuit"
+ desc = "A sleek, modern modern exosuit."
+
+/mob/living/exosuit/premade/modern/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/modern(src)
+ arms.color = COLOR_DARK_GUNMETAL
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/modern(src)
+ legs.color = COLOR_DARK_GUNMETAL
+ if(!head)
+ head = new /obj/item/mech_component/sensors/modern(src)
+ head.color = COLOR_DARK_GUNMETAL
+ if(!body)
+ body = new /obj/item/mech_component/chassis/modern(src)
+ body.color = COLOR_DARK_GUNMETAL
+
+ return ..()
+
+/mob/living/exosuit/premade/modern/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/ion(src), HARDPOINT_RIGHT_HAND)
diff --git a/code/modules/exosuits/premade/powerloader.dm b/code/modules/exosuits/premade/powerloader.dm
new file mode 100644
index 00000000000..83a21468e7c
--- /dev/null
+++ b/code/modules/exosuits/premade/powerloader.dm
@@ -0,0 +1,92 @@
+/mob/living/exosuit/premade/powerloader
+ name = "power loader"
+ desc = "An ancient, but well-liked cargo handling exosuit."
+
+/mob/living/exosuit/premade/powerloader/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/powerloader(src)
+ arms.color = "#ffbc37"
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/powerloader(src)
+ legs.color = "#ffbc37"
+ if(!head)
+ head = new /obj/item/mech_component/sensors/powerloader(src)
+ head.color = "#ffbc37"
+ if(!body)
+ body = new /obj/item/mech_component/chassis/powerloader(src)
+ body.color = "#ffdc37"
+
+ body.armour = new /obj/item/robot_parts/robot_component/armour/exosuit(src)
+
+ return ..()
+
+/mob/living/exosuit/premade/powerloader/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/drill(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND)
+
+/mob/living/exosuit/premade/powerloader/flames_red
+ name = "APLU \"Firestarter\""
+ desc = "An ancient, but well-liked cargo handling exosuit. This one has cool red flames."
+ decal = "flames_red"
+
+/mob/living/exosuit/premade/powerloader/flames_blue
+ name = "APLU \"Burning Chrome\""
+ desc = "An ancient, but well-liked cargo handling exosuit. This one has cool blue flames."
+ decal = "flames_blue"
+
+
+/mob/living/exosuit/premade/firefighter
+ name = "firefighting exosuit"
+ desc = "A mix and match of industrial parts designed to withstand fires."
+
+/mob/living/exosuit/premade/firefighter/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/powerloader(src)
+ arms.color = "#385b3c"
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/powerloader(src)
+ legs.color = "#385b3c"
+ if(!head)
+ head = new /obj/item/mech_component/sensors/powerloader(src)
+ head.color = "#385b3c"
+ if(!body)
+ body = new /obj/item/mech_component/chassis/heavy(src)
+ body.color = "#385b3c"
+
+ . = ..()
+
+ material = get_material_by_name(MAT_PLASTEEL)
+
+/mob/living/exosuit/premade/firefighter/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/drill(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/mounted_system/extinguisher(src), HARDPOINT_RIGHT_HAND)
+
+/obj/item/mech_component/sensors/firefighter/prebuild()
+ ..()
+ software = new(src)
+ software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_ENGINEERING)
+
+/mob/living/exosuit/premade/powerloader/old
+ name = "weathered power loader"
+ desc = "An ancient, but well-liked cargo handling exosuit. The paint is starting to flake. Perhaps some maintenance is in order?"
+
+/mob/living/exosuit/premade/powerloader/old/Initialize()
+ . = ..()
+ var/list/parts = list(arms,legs,head,body)
+ for(var/obj/item/mech_component/MC in parts)
+ if(prob(35))
+ MC.color = rgb(255,rand(188, 225),rand(55, 136))
+ //Damage it
+ var/obj/item/mech_component/damaged = pick(parts)
+ damaged.take_brute_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED)
+ if(prob(33))
+ parts -= damaged
+ damaged = pick(parts)
+ damaged.take_brute_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED)
+
+/mob/living/exosuit/premade/powerloader/old/spawn_mech_equipment()
+ install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD)
+ install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND)
diff --git a/code/modules/exosuits/premade/sleek.dm b/code/modules/exosuits/premade/sleek.dm
new file mode 100644
index 00000000000..07db10859f1
--- /dev/null
+++ b/code/modules/exosuits/premade/sleek.dm
@@ -0,0 +1,33 @@
+/mob/living/exosuit/premade/sleek
+ name = "sleek exosuit"
+ desc = "A sleek and agile exosuit."
+
+/obj/item/mech_component/manipulators/sleek/painted
+ color = COLOR_OFF_WHITE
+
+/obj/item/mech_component/propulsion/sleek/painted
+ color = COLOR_OFF_WHITE
+
+/obj/item/mech_component/sensors/sleek/painted
+ color = COLOR_OFF_WHITE
+
+/obj/item/mech_component/chassis/sleek/painted
+ color = COLOR_OFF_WHITE
+
+/mob/living/exosuit/premade/sleek/Initialize()
+ if(!arms)
+ arms = new /obj/item/mech_component/manipulators/sleek/painted(src)
+ if(!legs)
+ legs = new /obj/item/mech_component/propulsion/sleek/painted(src)
+ if(!head)
+ head = new /obj/item/mech_component/sensors/sleek/painted(src)
+ if(!body)
+ body = new /obj/item/mech_component/chassis/sleek/painted(src)
+
+ return ..()
+
+/mob/living/exosuit/premade/sleek/spawn_mech_equipment()
+ ..()
+ install_system_initialize(new /obj/item/mech_equipment/catapult(src), HARDPOINT_LEFT_HAND)
+ install_system_initialize(new /obj/item/mech_equipment/sleeper(src), HARDPOINT_BACK)
+ install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD)
diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm
index 09bc9d02748..f95e445fd6f 100644
--- a/code/modules/mob/living/silicon/robot/component.dm
+++ b/code/modules/mob/living/silicon/robot/component.dm
@@ -238,24 +238,35 @@
var/burn = 0
var/icon_state_broken = "broken"
+ var/max_damage
+
+/obj/item/robot_parts/robot_component/proc/is_functional()
+ return (brute + burn) < max_damage
+
/obj/item/robot_parts/robot_component/binary_communication_device
name = "binary communication device"
desc = "A module used for binary communications over encrypted frequencies, commonly used by synthetic robots."
icon_state = "binradio"
icon_state_broken = "binradio_broken"
+ max_damage = 40
+
/obj/item/robot_parts/robot_component/actuator
name = "actuator"
desc = "A modular, hydraulic actuator used by exosuits and robots alike for movement and manipulation."
icon_state = "motor"
icon_state_broken = "motor_broken"
+ max_damage = 50
+
/obj/item/robot_parts/robot_component/armour
name = "armour plating"
desc = "A pair of flexible, adaptable armor plates, used to protect the internals of robots."
icon_state = "armor"
icon_state_broken = "armor_broken"
+ max_damage = 90
+
/obj/item/robot_parts/robot_component/armour_platform
name = "platform armour plating"
desc = "A pair of reinforced armor plates, used to protect the internals of robots."
@@ -263,6 +274,8 @@
icon_state_broken = "armor_broken"
color = COLOR_GRAY80
+ max_damage = 140
+
/obj/item/robot_parts/robot_component/armour_light
name = "lightweight armour plating"
desc = "A pair of flexible, light armor plates, used to protect the internals of robots equipped with anti-gravity frames."
@@ -275,14 +288,21 @@
icon_state = "camera"
icon_state_broken = "camera_broken"
+ max_damage = 40
+
/obj/item/robot_parts/robot_component/diagnosis_unit
name = "diagnosis unit"
desc = "An internal computer and sensors used by robots and exosuits to accurately diagnose any system discrepancies on their components."
icon_state = "analyser"
icon_state_broken = "analyser_broken"
+ max_damage = 30
+
/obj/item/robot_parts/robot_component/radio
name = "radio"
desc = "A modular, multi-frequency radio used by robots and exosuits to enable communication systems. Comes with built-in subspace receivers."
icon_state = "radio"
icon_state_broken = "radio_broken"
+
+ max_damage = 40
+
diff --git a/code/modules/nano/interaction/exosuit.dm b/code/modules/nano/interaction/exosuit.dm
new file mode 100644
index 00000000000..97dbf341387
--- /dev/null
+++ b/code/modules/nano/interaction/exosuit.dm
@@ -0,0 +1,6 @@
+GLOBAL_DATUM_INIT(mech_state, /datum/topic_state/default/mech, new)
+
+/datum/topic_state/default/mech/can_use_topic(var/mob/living/exosuit/src_object, var/mob/user)
+ if(istype(src_object) && (user in src_object.pilots))
+ return ..()
+ return STATUS_CLOSE
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 6c8264212dc..326a3c2eb31 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -225,6 +225,10 @@
Fire(A,user,params) //Otherwise, fire normally.
*/
+// Can the weapon be click-dragged to fire on turfs when able.
+/obj/item/gun/proc/can_autofire()
+ return FALSE
+
/obj/item/gun/attack(atom/A, mob/living/user, def_zone)
if (A == user && user.zone_sel.selecting == O_MOUTH && !mouthshoot)
handle_suicide(user)
@@ -404,7 +408,7 @@
if(muzzle_flash)
set_light(0)
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
// Similar to the above proc, but does not require a user, which is ideal for things like turrets.
/obj/item/gun/proc/Fire_userless(atom/target)
@@ -497,7 +501,7 @@
/obj/item/gun/proc/handle_click_empty(mob/user)
if (user)
user.visible_message("*click click*", "*click*")
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
else
src.visible_message("*click click*")
playsound(src, 'sound/weapons/empty.ogg', 100, 1)
@@ -729,7 +733,7 @@
var/datum/firemode/new_mode = firemodes[sel_mode]
new_mode.apply_to(src)
to_chat(user, "\The [src] is now set to [new_mode.name].")
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
return new_mode
@@ -752,12 +756,12 @@
/obj/item/gun/equipped(mob/living/user, slot) // When a gun is equipped to your hands, we'll add the HUD to the user. Pending porting over TGMC guncode where wielding is far more sensible.
if(user?.hud_used)
if(slot == slot_l_hand || slot == slot_r_hand)
- user.hud_used.add_ammo_hud(user, src)
+ user?.hud_used?.add_ammo_hud(user, src)
else
- user.hud_used.remove_ammo_hud(user, src)
+ user?.hud_used?.remove_ammo_hud(user, src)
return ..()
/obj/item/gun/dropped(mob/living/user) // Ditto as above, we remove the HUD. Pending porting TGMC code to clean up this fucking nightmare of spaghetti.
if(user?.hud_used)
- user.hud_used.remove_ammo_hud(user, src)
+ user?.hud_used?.remove_ammo_hud(user, src)
..()
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index 58369fd5ad9..c974e6c1cc2 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -115,7 +115,7 @@
if(!power_supply.checked_use(charge_cost)) return null
var/mob/living/M = loc // TGMC Ammo HUD
if(istype(M)) // TGMC Ammo HUD
- M?.hud_used.update_ammo_hud(M, src)
+ M?.hud_used?.update_ammo_hud(M, src)
return new projectile_type(src)
/obj/item/gun/energy/proc/load_ammo(var/obj/item/C, mob/user)
@@ -137,7 +137,7 @@
playsound(src, 'sound/weapons/flipblade.ogg', 50, 1)
update_icon()
update_held_icon()
- user.hud_used.update_ammo_hud(user, src) // TGMC Ammo HUD
+ user?.hud_used?.update_ammo_hud(user, src) // TGMC Ammo HUD
else
to_chat(user, "This cell is not fitted for [src].")
return
@@ -154,7 +154,7 @@
playsound(src, 'sound/weapons/empty.ogg', 50, 1)
update_icon()
update_held_icon()
- user.hud_used.update_ammo_hud(user, src) // TGMC Ammo HUD
+ user?.hud_used?.update_ammo_hud(user, src) // TGMC Ammo HUD
else
to_chat(user, "[src] does not have a power cell.")
@@ -172,7 +172,7 @@
if(isrobot(src.loc))
var/mob/living/silicon/robot/R = src.loc
return R.cell
- if(istype(src.loc, /obj/item/rig_module))
+ else if(istype(src.loc, /obj/item/rig_module))
var/obj/item/rig_module/module = src.loc
if(module.holder && module.holder.wearer)
var/mob/living/carbon/human/H = module.holder.wearer
@@ -180,6 +180,11 @@
var/obj/item/rig/suit = H.get_rig()
if(istype(suit))
return suit.cell
+ else if(istype(loc, /atom/movable))
+ var/atom/movable/AM = loc
+ var/obj/item/cell/C = AM.get_cell()
+ if(C)
+ return C
return null
/obj/item/gun/energy/examine(mob/user, distance, infix, suffix)
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index c6f0206b0f7..88e5965822e 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -38,6 +38,29 @@
charge_cost = 800
recharge_time = 0.5 SECONDS
+/obj/item/gun/energy/taser/carbine
+ name = "electrolaser carbine"
+ desc = "The NT Mk44 NL is a high capacity gun used for lesss-than-lethal takedowns. It can switch between high and low intensity stun shots."
+ icon = 'icons/obj/gun2.dmi'
+ icon_state = "tasercarbine"
+ w_class = ITEMSIZE_LARGE
+ slot_flags = SLOT_BELT|SLOT_BACK
+ origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3, TECH_POWER = 3)
+ force = 8
+ accuracy = 1
+ projectile_type = /obj/item/projectile/beam/stun
+ wielded_item_state = "tasercarbine-wielded"
+
+ firemodes = list(
+ list(mode_name="stun", projectile_type=/obj/item/projectile/beam/stun, charge_cost = 300),
+ list(mode_name="shock", projectile_type=/obj/item/projectile/beam/shock, charge_cost = 480),
+ )
+
+/obj/item/gun/energy/taser/carbine/mounted
+ name = "mounted electrolaser carbine"
+ self_recharge = TRUE
+ use_external_power = TRUE
+
/obj/item/gun/energy/crossbow
name = "mini energy-crossbow"
desc = "A weapon favored by many mercenary stealth specialists."
diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm
index 6a64d4fce86..6075782f1e1 100644
--- a/code/modules/projectiles/guns/projectile.dm
+++ b/code/modules/projectiles/guns/projectile.dm
@@ -57,7 +57,7 @@
var/mob/living/M = loc
if(istype(M))
- M?.hud_used.update_ammo_hud(M, src)
+ M?.hud_used?.update_ammo_hud(M, src)
if (chambered)
return chambered.BB
@@ -105,7 +105,7 @@
var/mob/living/M = loc
if(istype(M))
- M?.hud_used.update_ammo_hud(M, src)
+ M?.hud_used?.update_ammo_hud(M, src)
//Attempts to load A into src, depending on the type of thing being loaded and the load_method
@@ -125,7 +125,7 @@
AM.loc = src
ammo_magazine = AM
user.visible_message("[user] inserts [AM] into [src].", "You insert [AM] into [src].")
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
playsound(src, 'sound/weapons/flipblade.ogg', 50, 1)
if(SPEEDLOADER)
if(loaded.len >= max_shells)
@@ -140,10 +140,10 @@
loaded += C
AM.stored_ammo -= C //should probably go inside an ammo_magazine proc, but I guess less proc calls this way...
count++
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
if(count)
user.visible_message("[user] reloads [src].", "You load [count] round\s into [src].")
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
playsound(src, 'sound/weapons/empty.ogg', 50, 1)
AM.update_icon()
else if(istype(A, /obj/item/ammo_casing))
@@ -179,7 +179,7 @@
sleep(1 SECOND)
update_icon()
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
//attempts to unload src. If allow_dump is set to 0, the speedloader unloading method will be disabled
/obj/item/gun/projectile/proc/unload_ammo(mob/user, var/allow_dump=1)
@@ -189,7 +189,7 @@
playsound(src, 'sound/weapons/empty.ogg', 50, 1)
ammo_magazine.update_icon()
ammo_magazine = null
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
else if(loaded.len)
//presumably, if it can be speed-loaded, it can be speed-unloaded.
if(allow_dump && (load_method & SPEEDLOADER))
@@ -208,11 +208,11 @@
user.put_in_hands(C)
user.visible_message("[user] removes \a [C] from [src].", "You remove \a [C] from [src].")
playsound(src, 'sound/weapons/empty.ogg', 50, 1)
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
else
to_chat(user, "[src] is empty.")
update_icon()
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
/obj/item/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob)
..()
@@ -243,7 +243,7 @@
ammo_magazine.update_icon()
ammo_magazine = null
update_icon() //make sure to do this after unsetting ammo_magazine
- user.hud_used.update_ammo_hud(user, src)
+ user?.hud_used?.update_ammo_hud(user, src)
/obj/item/gun/projectile/examine(mob/user, distance, infix, suffix)
. = ..()
diff --git a/code/modules/research/designs/circuits/circuits.dm b/code/modules/research/designs/circuits/circuits.dm
index a56f10dff79..47b47f2f8fa 100644
--- a/code/modules/research/designs/circuits/circuits.dm
+++ b/code/modules/research/designs/circuits/circuits.dm
@@ -669,6 +669,45 @@ CIRCUITS BELOW
build_path = /obj/item/circuitboard/microwave/advanced
sort_string = "HACAA"
+/datum/design/circuit/exosuit_engineering
+ name = "exosuit engineering software"
+ id = "exosoft_engi"
+ req_tech = list(TECH_DATA = 1)
+ build_path = /obj/item/weapon/circuitboard/exosystem/engineering
+ sort_string = "XABAA"
+ materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500)
+
+/datum/design/circuit/exosuit_utility
+ name = "exosuit utility software"
+ id = "exosoft_utility"
+ req_tech = list(TECH_DATA = 1)
+ build_path = /obj/item/weapon/circuitboard/exosystem/utility
+ sort_string = "XABAB"
+ materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500)
+
+/datum/design/circuit/exosuit_medical
+ name = "exosuit medical software"
+ id = "exosoft_medical"
+ req_tech = list(TECH_DATA = 3,TECH_BIO = 2)
+ build_path = /obj/item/weapon/circuitboard/exosystem/medical
+ sort_string = "XABAC"
+ materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500)
+
+/datum/design/circuit/exosuit_weapons
+ name = "exosuit weapons software"
+ id = "exosoft_weapons"
+ req_tech = list(TECH_DATA = 4, TECH_COMBAT = 3)
+ build_path = /obj/item/weapon/circuitboard/exosystem/weapons
+ sort_string = "XABAD"
+ materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500)
+
+/datum/design/circuit/exosuit_advweapons
+ name = "exosuit advweapons software"
+ id = "exosoft_advweapons"
+ req_tech = list(TECH_DATA = 4, TECH_COMBAT = 5)
+ build_path = /obj/item/weapon/circuitboard/exosystem/advweapons
+ sort_string = "XABAE"
+ materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500)
/* I have no idea how this was even running before, but it doesn't seem to be necessary.
///////////////////////////////////
@@ -677,4 +716,4 @@ CIRCUITS BELOW
/datum/design/circuit/shield
req_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3)
materials = list("$glass" = 2000, "sacid" = 20, "$phoron" = 10000, "$diamond" = 5000, "$gold" = 10000)
-*/
\ No newline at end of file
+*/
diff --git a/code/modules/research/designs/exosuits/categories.dm b/code/modules/research/designs/exosuits/categories.dm
new file mode 100644
index 00000000000..5093d6eb82b
--- /dev/null
+++ b/code/modules/research/designs/exosuits/categories.dm
@@ -0,0 +1,15 @@
+/*
+ * Exosuits! Nebbay Mechs, but filling the middle ground.
+ */
+
+/datum/design/item/mechfab/trueexo
+ category = "Exosuit"
+ time = 60
+ req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_DATA = 4)
+
+/datum/design/item/mechfab/trueexo/equip
+ category = "Exosuit Equipment"
+ time = 45
+
+/datum/design/item/mechfab/trueexo/armor
+ category = "Exosuit Armor"
diff --git a/code/modules/research/designs/exosuits/combat.dm b/code/modules/research/designs/exosuits/combat.dm
new file mode 100644
index 00000000000..71dffebdf5e
--- /dev/null
+++ b/code/modules/research/designs/exosuits/combat.dm
@@ -0,0 +1,24 @@
+/datum/design/item/mechfab/trueexo/combat
+ req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 5, TECH_DATA = 4)
+ materials = list(MAT_PLASTEEL = 7500, MAT_COPPER = 1500)
+
+/datum/design/item/mechfab/trueexo/combat/chassis
+ name = "Combat Chassis"
+ id = "exosuit_combat_chassis"
+ build_path = /obj/item/mech_component/chassis/combat
+ materials = list(MAT_PLASTEEL = 10000, MAT_COPPER = 2500)
+
+/datum/design/item/mechfab/trueexo/combat/arms
+ name = "Combat Arms"
+ id = "exosuit_combat_arms"
+ build_path = /obj/item/mech_component/manipulators/combat
+
+/datum/design/item/mechfab/trueexo/combat/legs
+ name = "Combat Legs"
+ id = "exosuit_combat_legs"
+ build_path = /obj/item/mech_component/propulsion/combat
+
+/datum/design/item/mechfab/trueexo/combat/head
+ name = "Combat Head"
+ id = "exosuit_combat_head"
+ build_path = /obj/item/mech_component/sensors/combat
diff --git a/code/modules/research/designs/exosuits/components.dm b/code/modules/research/designs/exosuits/components.dm
new file mode 100644
index 00000000000..f8d7cb3efe2
--- /dev/null
+++ b/code/modules/research/designs/exosuits/components.dm
@@ -0,0 +1,35 @@
+
+/datum/design/item/mechfab/trueexo/spine
+ name = "Exosuit Spine"
+ id = "exosuit_spine"
+ build_path = /obj/structure/heavy_vehicle_frame
+ time = 120
+ materials = list(MAT_STEEL = 10000)
+
+/datum/design/item/mechfab/trueexo/armor/normal
+ name = "Standard Exosuit Armor"
+ id = "exosuit_armor_norm"
+ build_path = /obj/item/robot_parts/robot_component/armour/exosuit
+ req_tech = list(TECH_MATERIAL = 1)
+ materials = list(MAT_STEEL = 7500)
+
+/datum/design/item/mechfab/trueexo/armor/radproof
+ name = "Radiation Hardened Exosuit Armor"
+ id = "exosuit_armor_rad"
+ build_path = /obj/item/robot_parts/robot_component/armour/exosuit/radproof
+ req_tech = list(TECH_MATERIAL = 3, TECH_PHORON = 3)
+ materials = list(MAT_STEEL = 5000, MAT_LEAD = 2500)
+
+/datum/design/item/mechfab/trueexo/armor/em
+ name = "Ionized Exosuit Armor"
+ id = "exosuit_armor_em"
+ build_path = /obj/item/robot_parts/robot_component/armour/exosuit/em
+ req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 5)
+ materials = list(MAT_STEEL = 5000, MAT_METALHYDROGEN = 2500)
+
+/datum/design/item/mechfab/trueexo/armor/combat
+ name = "Combat Exosuit Armor"
+ id = "exosuit_armor_combat"
+ build_path = /obj/item/robot_parts/robot_component/armour/exosuit/combat
+ req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5)
+ materials = list(MAT_TITANIUM = 5000, MAT_DIAMOND = 3000)
diff --git a/code/modules/research/designs/exosuits/engineering.dm b/code/modules/research/designs/exosuits/engineering.dm
new file mode 100644
index 00000000000..e1673702208
--- /dev/null
+++ b/code/modules/research/designs/exosuits/engineering.dm
@@ -0,0 +1,16 @@
+
+/datum/design/item/mechfab/trueexo/equip/engineering
+ req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_POWER = 2)
+
+/datum/design/item/mechfab/trueexo/equip/engineering/rcd
+ name = "Mounted RCD"
+ id = "exosuit_tool_rcd"
+ build_path = /obj/item/mech_equipment/mounted_system/rcd
+ materials = list(MAT_STEEL = 5000, MAT_PLASTEEL = 1500)
+ req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_POWER = 5)
+
+/datum/design/item/mechfab/trueexo/equip/engineering/extinguisher
+ name = "Mounted Extinguisher"
+ id = "exosuit_tool_extinguisher"
+ build_path = /obj/item/mech_equipment/mounted_system/extinguisher
+ materials = list(MAT_STEEL = 5000, MAT_PLASTEEL = 1500)
diff --git a/code/modules/research/designs/exosuits/heavy.dm b/code/modules/research/designs/exosuits/heavy.dm
new file mode 100644
index 00000000000..04cd6588cbc
--- /dev/null
+++ b/code/modules/research/designs/exosuits/heavy.dm
@@ -0,0 +1,24 @@
+/datum/design/item/mechfab/trueexo/heavyhead
+ req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 3, TECH_DATA = 4)
+ materials = list(MAT_TITANIUM = 5000, MAT_PLASTEEL = 2500, MAT_COPPER = 2000)
+
+/datum/design/item/mechfab/trueexo/heavy/chassis
+ name = "heavy Chassis"
+ id = "exosuit_heavy_chassis"
+ build_path = /obj/item/mech_component/chassis/heavy
+ materials = list(MAT_TITANIUM = 10000, MAT_PLASTEEL = 5000, MAT_COPPER = 2500)
+
+/datum/design/item/mechfab/trueexo/heavy/arms
+ name = "heavy Arms"
+ id = "exosuit_heavy_arms"
+ build_path = /obj/item/mech_component/manipulators/heavy
+
+/datum/design/item/mechfab/trueexo/heavy/legs
+ name = "heavy Legs"
+ id = "exosuit_heavy_legs"
+ build_path = /obj/item/mech_component/propulsion/heavy
+
+/datum/design/item/mechfab/trueexo/heavy/head
+ name = "heavy Head"
+ id = "exosuit_heavy_head"
+ build_path = /obj/item/mech_component/sensors/heavy
diff --git a/code/modules/research/designs/exosuits/industrial.dm b/code/modules/research/designs/exosuits/industrial.dm
new file mode 100644
index 00000000000..4fb07a11a9f
--- /dev/null
+++ b/code/modules/research/designs/exosuits/industrial.dm
@@ -0,0 +1,24 @@
+/datum/design/item/mechfab/trueexo/industrial
+ req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_DATA = 4)
+ materials = list(MAT_STEEL = 7500, MAT_COPPER = 1500)
+
+/datum/design/item/mechfab/trueexo/industrial/chassis
+ name = "Industrial Chassis"
+ id = "exosuit_industrial_chassis"
+ build_path = /obj/item/mech_component/chassis/industrial
+ materials = list(MAT_STEEL = 10000, MAT_COPPER = 2500)
+
+/datum/design/item/mechfab/trueexo/industrial/arms
+ name = "Industrial Arms"
+ id = "exosuit_industrial_arms"
+ build_path = /obj/item/mech_component/manipulators/industrial
+
+/datum/design/item/mechfab/trueexo/industrial/legs
+ name = "Industrial Legs"
+ id = "exosuit_industrial_legs"
+ build_path = /obj/item/mech_component/propulsion/industrial
+
+/datum/design/item/mechfab/trueexo/industrial/head
+ name = "Industrial Head"
+ id = "exosuit_industrial_head"
+ build_path = /obj/item/mech_component/sensors/industrial
diff --git a/code/modules/research/designs/exosuits/light.dm b/code/modules/research/designs/exosuits/light.dm
new file mode 100644
index 00000000000..b3123fb1570
--- /dev/null
+++ b/code/modules/research/designs/exosuits/light.dm
@@ -0,0 +1,24 @@
+/datum/design/item/mechfab/trueexo/light
+ req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_DATA = 4, TECH_BIO = 2)
+ materials = list(MAT_STEEL = 5500, MAT_COPPER = 1000)
+
+/datum/design/item/mechfab/trueexo/light/chassis
+ name = "Light Chassis"
+ id = "exosuit_light_chassis"
+ build_path = /obj/item/mech_component/chassis/light
+ materials = list(MAT_STEEL = 7600, MAT_COPPER = 2000)
+
+/datum/design/item/mechfab/trueexo/light/arms
+ name = "Light Arms"
+ id = "exosuit_light_arms"
+ build_path = /obj/item/mech_component/manipulators/light
+
+/datum/design/item/mechfab/trueexo/light/legs
+ name = "Light Legs"
+ id = "exosuit_light_legs"
+ build_path = /obj/item/mech_component/propulsion/light
+
+/datum/design/item/mechfab/trueexo/light/head
+ name = "Light Head"
+ id = "exosuit_light_head"
+ build_path = /obj/item/mech_component/sensors/light
diff --git a/code/modules/research/designs/exosuits/medical.dm b/code/modules/research/designs/exosuits/medical.dm
new file mode 100644
index 00000000000..876a7f6d7c5
--- /dev/null
+++ b/code/modules/research/designs/exosuits/medical.dm
@@ -0,0 +1,9 @@
+
+/datum/design/item/mechfab/trueexo/equip/medical
+ req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_BIO = 3)
+
+/datum/design/item/mechfab/trueexo/equip/medical/sleeper
+ name = "Portable Sleeper"
+ id = "exosuit_tool_sleeper"
+ build_path = /obj/item/mech_equipment/sleeper
+ materials = list(MAT_STEEL = 5000, MAT_ALUMINIUM = 1500)
diff --git a/code/modules/research/designs/exosuits/mercenary.dm b/code/modules/research/designs/exosuits/mercenary.dm
new file mode 100644
index 00000000000..4220d328563
--- /dev/null
+++ b/code/modules/research/designs/exosuits/mercenary.dm
@@ -0,0 +1,24 @@
+/datum/design/item/mechfab/trueexo/mercenaryhead
+ req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 3, TECH_DATA = 4)
+ materials = list(MAT_TITANIUM = 5000, MAT_PLASTEEL = 2500, MAT_COPPER = 2000)
+
+/datum/design/item/mechfab/trueexo/mercenary/chassis
+ name = "mercenary Chassis"
+ id = "exosuit_mercenary_chassis"
+ build_path = /obj/item/mech_component/chassis/mercenary
+ materials = list(MAT_TITANIUM = 10000, MAT_PLASTEEL = 5000, MAT_COPPER = 2500)
+
+/datum/design/item/mechfab/trueexo/mercenary/arms
+ name = "mercenary Arms"
+ id = "exosuit_mercenary_arms"
+ build_path = /obj/item/mech_component/manipulators/mercenary
+
+/datum/design/item/mechfab/trueexo/mercenary/legs
+ name = "mercenary Legs"
+ id = "exosuit_mercenary_legs"
+ build_path = /obj/item/mech_component/propulsion/mercenary
+
+/datum/design/item/mechfab/trueexo/mercenary/head
+ name = "mercenary Head"
+ id = "exosuit_mercenary_head"
+ build_path = /obj/item/mech_component/sensors/mercenary
diff --git a/code/modules/research/designs/exosuits/modern.dm b/code/modules/research/designs/exosuits/modern.dm
new file mode 100644
index 00000000000..88deb7d004c
--- /dev/null
+++ b/code/modules/research/designs/exosuits/modern.dm
@@ -0,0 +1,24 @@
+/datum/design/item/mechfab/trueexo/modern
+ req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 4, TECH_DATA = 4)
+ materials = list(MAT_PLASTEEL = 7500, MAT_COPPER = 1500)
+
+/datum/design/item/mechfab/trueexo/modern/chassis
+ name = "Modern Chassis"
+ id = "exosuit_modern_chassis"
+ build_path = /obj/item/mech_component/chassis/modern
+ materials = list(MAT_PLASTEEL = 10000, MAT_COPPER = 2500)
+
+/datum/design/item/mechfab/trueexo/modern/arms
+ name = "Modern Arms"
+ id = "exosuit_modern_arms"
+ build_path = /obj/item/mech_component/manipulators/modern
+
+/datum/design/item/mechfab/trueexo/modern/legs
+ name = "Modern Legs"
+ id = "exosuit_modern_legs"
+ build_path = /obj/item/mech_component/propulsion/modern
+
+/datum/design/item/mechfab/trueexo/modern/head
+ name = "Modern Head"
+ id = "exosuit_modern_head"
+ build_path = /obj/item/mech_component/sensors/modern
diff --git a/code/modules/research/designs/exosuits/pod.dm b/code/modules/research/designs/exosuits/pod.dm
new file mode 100644
index 00000000000..d1ffd38c579
--- /dev/null
+++ b/code/modules/research/designs/exosuits/pod.dm
@@ -0,0 +1,9 @@
+/datum/design/item/mechfab/trueexo/pod
+ req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3, TECH_DATA = 3)
+ materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 2500, MAT_COPPER = 1500)
+
+/datum/design/item/mechfab/trueexo/pod/chassis
+ name = "Pod Chassis"
+ id = "exosuit_pod_chassis"
+ build_path = /obj/item/mech_component/chassis/pod
+ materials = list(MAT_STEEL = 7500, MAT_PLASTIC = 2500, MAT_COPPER = 2500)
diff --git a/code/modules/research/designs/exosuits/powerloader.dm b/code/modules/research/designs/exosuits/powerloader.dm
new file mode 100644
index 00000000000..9bc0e8668af
--- /dev/null
+++ b/code/modules/research/designs/exosuits/powerloader.dm
@@ -0,0 +1,24 @@
+/datum/design/item/mechfab/trueexo/powerloader
+ req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_DATA = 4)
+ materials = list(MAT_STEEL = 7500, MAT_COPPER = 1500)
+
+/datum/design/item/mechfab/trueexo/powerloader/chassis
+ name = "Powerloader Chassis"
+ id = "exosuit_powerloader_chassis"
+ build_path = /obj/item/mech_component/chassis/powerloader
+ materials = list(MAT_STEEL = 10000, MAT_COPPER = 2500)
+
+/datum/design/item/mechfab/trueexo/powerloader/arms
+ name = "Powerloader Arms"
+ id = "exosuit_powerloader_arms"
+ build_path = /obj/item/mech_component/manipulators/powerloader
+
+/datum/design/item/mechfab/trueexo/powerloader/legs
+ name = "Powerloader Legs"
+ id = "exosuit_powerloader_legs"
+ build_path = /obj/item/mech_component/propulsion/powerloader
+
+/datum/design/item/mechfab/trueexo/powerloader/head
+ name = "Powerloader Head"
+ id = "exosuit_powerloader_head"
+ build_path = /obj/item/mech_component/sensors/powerloader
diff --git a/code/modules/research/designs/exosuits/sleek.dm b/code/modules/research/designs/exosuits/sleek.dm
new file mode 100644
index 00000000000..0ddbb170706
--- /dev/null
+++ b/code/modules/research/designs/exosuits/sleek.dm
@@ -0,0 +1,24 @@
+/datum/design/item/mechfab/trueexo/sleek
+ req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_DATA = 4, TECH_BIO = 3)
+ materials = list(MAT_STEEL = 5500, MAT_COPPER = 1000)
+
+/datum/design/item/mechfab/trueexo/sleek/chassis
+ name = "sleek Chassis"
+ id = "exosuit_sleek_chassis"
+ build_path = /obj/item/mech_component/chassis/sleek
+ materials = list(MAT_STEEL = 7600, MAT_COPPER = 2000)
+
+/datum/design/item/mechfab/trueexo/sleek/arms
+ name = "sleek Arms"
+ id = "exosuit_sleek_arms"
+ build_path = /obj/item/mech_component/manipulators/sleek
+
+/datum/design/item/mechfab/trueexo/sleek/legs
+ name = "sleek Legs"
+ id = "exosuit_sleek_legs"
+ build_path = /obj/item/mech_component/propulsion/sleek
+
+/datum/design/item/mechfab/trueexo/sleek/head
+ name = "sleek Head"
+ id = "exosuit_sleek_head"
+ build_path = /obj/item/mech_component/sensors/sleek
diff --git a/code/modules/research/designs/exosuits/spider.dm b/code/modules/research/designs/exosuits/spider.dm
new file mode 100644
index 00000000000..49cc4a366ba
--- /dev/null
+++ b/code/modules/research/designs/exosuits/spider.dm
@@ -0,0 +1,8 @@
+/datum/design/item/mechfab/trueexo/spider
+ req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3, TECH_DATA = 3)
+ materials = list(MAT_STEEL = 5000, MAT_PLASTEEL = 2500, MAT_COPPER = 1500)
+
+/datum/design/item/mechfab/trueexo/spider/legs
+ name = "Spider Legs"
+ id = "exosuit_spider_legs"
+ build_path = /obj/item/mech_component/propulsion/spider
diff --git a/code/modules/research/designs/exosuits/tracks.dm b/code/modules/research/designs/exosuits/tracks.dm
new file mode 100644
index 00000000000..ad97f420711
--- /dev/null
+++ b/code/modules/research/designs/exosuits/tracks.dm
@@ -0,0 +1,8 @@
+/datum/design/item/mechfab/trueexo/tracks
+ req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3, TECH_DATA = 3)
+ materials = list(MAT_STEEL = 5000, MAT_PLASTEEL = 2500, MAT_COPPER = 1500)
+
+/datum/design/item/mechfab/trueexo/tracks/legs
+ name = "Tank Treads"
+ id = "exosuit_tracks_legs"
+ build_path = /obj/item/mech_component/propulsion/tracks
diff --git a/code/modules/research/designs/exosuits/utility.dm b/code/modules/research/designs/exosuits/utility.dm
new file mode 100644
index 00000000000..b45f206b4fc
--- /dev/null
+++ b/code/modules/research/designs/exosuits/utility.dm
@@ -0,0 +1,31 @@
+
+/datum/design/item/mechfab/trueexo/equip/utility
+ req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 2)
+
+/datum/design/item/mechfab/trueexo/equip/utility/drill
+ name = "Exosuit Drill"
+ id = "exosuit_tool_drill"
+ req_tech = list(TECH_MATERIAL = 1)
+ materials = list(MAT_STEEL = 7500)
+ build_path = /obj/item/mech_equipment/drill
+
+/datum/design/item/mechfab/trueexo/equip/utility/gravipult
+ name = "Exosuit Gravipult"
+ id = "exosuit_tool_gravipult"
+ req_tech = list(TECH_MATERIAL = 4, TECH_BLUESPACE = 3, TECH_ENGINEERING = 4)
+ materials = list(MAT_STEEL = 7500, MAT_PHORON = 4000)
+ build_path = /obj/item/mech_equipment/catapult
+
+/datum/design/item/mechfab/trueexo/equip/utility/light
+ name = "Exosuit Floodlight"
+ id = "exosuit_tool_light"
+ req_tech = list(TECH_MATERIAL = 1)
+ materials = list(MAT_ALUMINIUM = 7000, MAT_GLASS = 2000)
+ build_path = /obj/item/mech_equipment/light
+
+/datum/design/item/mechfab/trueexo/equip/utility/clamp
+ name = "Exosuit Clamp"
+ id = "exosuit_tool_clamp"
+ req_tech = list(TECH_MATERIAL = 1)
+ materials = list(MAT_STEEL = 7500)
+ build_path = /obj/item/mech_equipment/clamp
diff --git a/code/modules/research/designs/exosuits/weapon_melee.dm b/code/modules/research/designs/exosuits/weapon_melee.dm
new file mode 100644
index 00000000000..7e07cfd8dab
--- /dev/null
+++ b/code/modules/research/designs/exosuits/weapon_melee.dm
@@ -0,0 +1,22 @@
+
+/datum/design/item/mechfab/trueexo/equip/melee
+ req_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 2)
+
+/datum/design/item/mechfab/trueexo/equip/melee/mechete
+ name = "Exosuit Machete"
+ id = "exosuit_weapon_mechete"
+ materials = list(MAT_STEEL = 8000, MAT_ALUMINIUM = 3000)
+ build_path = /obj/item/mech_equipment/mounted_system/melee/mechete
+
+/datum/design/item/mechfab/trueexo/equip/melee/mechete/durasteel
+ name = "Durasteel Exosuit Machete"
+ id = "exosuit_weapon_durasteel_mechete"
+ materials = list(MAT_TITANIUM = 5000, MAT_DURASTEEL = 6000)
+ build_path = /obj/item/mech_equipment/mounted_system/melee/mechete/durasteel
+
+/datum/design/item/mechfab/trueexo/equip/melee/flash
+ name = "Exosuit H.I. Flash"
+ id = "exosuit_weapon_flash"
+ materials = list(MAT_STEEL = 5000)
+ build_path = /obj/item/mech_equipment/flash
+ req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 3, TECH_MAGNET = 4)
diff --git a/code/modules/research/designs/exosuits/weapon_ranged.dm b/code/modules/research/designs/exosuits/weapon_ranged.dm
new file mode 100644
index 00000000000..583f33eb3fe
--- /dev/null
+++ b/code/modules/research/designs/exosuits/weapon_ranged.dm
@@ -0,0 +1,63 @@
+
+/datum/design/item/mechfab/trueexo/equip/gun
+ req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 3)
+
+/datum/design/item/mechfab/trueexo/equip/gun/smg
+ name = "Exosuit SMG"
+ id = "exosuit_weapon_smg"
+ materials = list(MAT_STEEL = 5000)
+ build_path = /obj/item/mech_equipment/mounted_system/projectile
+
+/datum/design/item/mechfab/trueexo/equip/gun/smg_ammo
+ name = "Exosuit SMG Ammunition"
+ id = "exosuit_weapon_smg_ammo"
+ materials = list(MAT_STEEL = 4000, MAT_PLASTIC = 500, MAT_GRAPHITE = 500)
+ build_path = /obj/item/ammo_magazine/mech/smg_top
+
+/datum/design/item/mechfab/trueexo/equip/gun/rifle
+ name = "Exosuit Rifle"
+ id = "exosuit_weapon_rifle"
+ materials = list(MAT_STEEL = 7000, MAT_GRAPHITE = 1000)
+ req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 4, TECH_MAGNET = 2)
+ build_path = /obj/item/mech_equipment/mounted_system/projectile/assault_rifle
+
+/datum/design/item/mechfab/trueexo/equip/gun/rifle_ammo
+ name = "Exosuit Rifle Ammunition"
+ id = "exosuit_weapon_rifle_ammo"
+ materials = list(MAT_STEEL = 4000, MAT_PLASTIC = 500, MAT_GRAPHITE = 500)
+ req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 4, TECH_MAGNET = 2)
+ build_path = /obj/item/ammo_magazine/mech/rifle
+
+/datum/design/item/mechfab/trueexo/equip/gun/lmg
+ name = "Exosuit LMG"
+ id = "exosuit_weapon_lmg"
+ materials = list(MAT_STEEL = 7000, MAT_GRAPHITE = 1000)
+ req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_ILLEGAL = 2, TECH_MAGNET = 2)
+ build_path = /obj/item/mech_equipment/mounted_system/projectile/machine
+
+/datum/design/item/mechfab/trueexo/equip/gun/lmg_ammo
+ name = "Exosuit LMG Ammunition"
+ id = "exosuit_weapon_lmg_ammo"
+ materials = list(MAT_PLASTEEL = 4000, MAT_PLASTIC = 500, MAT_GRAPHITE = 500)
+ req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_ILLEGAL = 2, TECH_MAGNET = 2)
+ build_path = /obj/item/ammo_magazine/mech/rifle/drum
+
+/datum/design/item/mechfab/trueexo/equip/gun/taser
+ name = "Exosuit Taser Carbine"
+ id = "exosuit_weapon_taser"
+ materials = list(MAT_ALUMINIUM = 5000, MAT_PLASTIC = 2000)
+ build_path = /obj/item/mech_equipment/mounted_system/taser
+
+/datum/design/item/mechfab/trueexo/equip/gun/ion
+ name = "Exosuit Ion Rifle"
+ id = "exosuit_weapon_ion"
+ materials = list(MAT_ALUMINIUM = 5000, MAT_OSMIUM = 1000, MAT_URANIUM = 1000)
+ req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 4, TECH_MAGNET = 4)
+ build_path = /obj/item/mech_equipment/mounted_system/taser/ion
+
+/datum/design/item/mechfab/trueexo/equip/gun/laser
+ name = "Exosuit Laser Rifle"
+ id = "exosuit_weapon_laser"
+ materials = list(MAT_ALUMINIUM = 5000, MAT_STEEL = 2000)
+ req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 3)
+ build_path = /obj/item/mech_equipment/mounted_system/taser/laser
diff --git a/code/modules/research/mechfab_designs.dm b/code/modules/research/mechfab_designs.dm
index 1c6007ab5f8..32ac13cc117 100644
--- a/code/modules/research/mechfab_designs.dm
+++ b/code/modules/research/mechfab_designs.dm
@@ -292,16 +292,16 @@
/datum/design/item/mecha
build_type = MECHFAB
- category = "Exosuit Equipment"
+ category = "Mecha Equipment"
time = 10
materials = list(MAT_STEEL = 7500)
/datum/design/item/mecha/AssembleDesignDesc()
if(!desc)
- desc = "Allows for the construction of \a '[item_name]' exosuit module."
+ desc = "Allows for the construction of \a '[item_name]' mecha module."
/datum/design/item/mecha/tracking
- name = "Exosuit Tracking Beacon"
+ name = "Mecha Tracking Beacon"
id = "mech_tracker"
time = 5
materials = list(MAT_STEEL = 375)
@@ -364,7 +364,7 @@
build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/taser/rigged
/datum/design/item/mecha/shocker
- name = "Exosuit Electrifier"
+ name = "Mecha Electrifier"
desc = "A device to electrify the external portions of a mecha in order to increase its defensive capabilities."
id = "mech_shocker"
req_tech = list(TECH_COMBAT = 3, TECH_POWER = 6, TECH_MAGNET = 1)
@@ -409,7 +409,7 @@
/datum/design/item/mecha/weapon/laser_rigged
name = "Jury-Rigged Welder-Laser"
- desc = "Allows for the construction of a welder-laser assembly package for non-combat exosuits."
+ desc = "Allows for the construction of a welder-laser assembly package for non-combat mechas."
id = "mech_laser_rigged"
req_tech = list(TECH_COMBAT = 2, TECH_MAGNET = 2)
build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/riggedlaser
@@ -528,28 +528,28 @@
// *** Nonweapon modules
/datum/design/item/mecha/wormhole_gen
name = "Wormhole Generator"
- desc = "An exosuit module that can generate small quasi-stable wormholes."
+ desc = "A mecha module that can generate small quasi-stable wormholes."
id = "mech_wormhole_gen"
req_tech = list(TECH_BLUESPACE = 3, TECH_MAGNET = 2)
build_path = /obj/item/mecha_parts/mecha_equipment/wormhole_generator
/datum/design/item/mecha/teleporter
name = "Teleporter"
- desc = "An exosuit module that allows teleportation to any position in view."
+ desc = "An mecha module that allows teleportation to any position in view."
id = "mech_teleporter"
req_tech = list(TECH_BLUESPACE = 10, TECH_MAGNET = 5)
build_path = /obj/item/mecha_parts/mecha_equipment/teleporter
/datum/design/item/mecha/teleporter
name = "Cloaking Device"
- desc = "A device that renders the exosuit invisible to the naked eye, though not to thermal detection. Uses large amounts of energy."
+ desc = "A device that renders the mecha invisible to the naked eye, though not to thermal detection. Uses large amounts of energy."
id = "mech_cloaking"
req_tech = list(TECH_BLUESPACE = 10, TECH_MAGNET = 5)
build_path = /obj/item/mecha_parts/mecha_equipment/cloak
/datum/design/item/mecha/rcd
name = "RCD"
- desc = "An exosuit-mounted rapid construction device."
+ desc = "An mecha-mounted rapid construction device."
id = "mech_rcd"
time = 120
materials = list(MAT_STEEL = 20000, MAT_PLASTIC = 10000, MAT_PHORON = 18750, MAT_SILVER = 15000, MAT_GOLD = 15000)
@@ -558,14 +558,14 @@
/datum/design/item/mecha/gravcatapult
name = "Gravitational Catapult"
- desc = "An exosuit-mounted gravitational catapult."
+ desc = "An mecha-mounted gravitational catapult."
id = "mech_gravcatapult"
req_tech = list(TECH_BLUESPACE = 2, TECH_MAGNET = 3, TECH_ENGINEERING = 3)
build_path = /obj/item/mecha_parts/mecha_equipment/gravcatapult
/datum/design/item/mecha/repair_droid
name = "Repair Droid"
- desc = "Automated repair droid, exosuits' best companion. BEEP BOOP"
+ desc = "Automated repair droid, mechas' best companion. BEEP BOOP"
id = "mech_repair_droid"
req_tech = list(TECH_MAGNET = 3, TECH_DATA = 3, TECH_ENGINEERING = 3)
materials = list(MAT_STEEL = 7500, MAT_GOLD = 750, MAT_SILVER = 1500, MAT_GLASS = 3750)
@@ -582,7 +582,7 @@
/datum/design/item/mecha/omni_shield
name = "Omni Shield"
- desc = "Integral shield projector. Can only protect the exosuit, but has no weak angles."
+ desc = "Integral shield projector. Can only protect the mecha, but has no weak angles."
id = "mech_shield_omni"
req_tech = list(TECH_PHORON = 3, TECH_MAGNET = 6, TECH_ILLEGAL = 4)
materials = list(MAT_STEEL = 8000, MAT_GOLD = 2000, MAT_SILVER = 3000, MAT_PHORON = 5000, MAT_GLASS = 3750)
@@ -645,7 +645,7 @@
/datum/design/item/mecha/ccw_armor
name = "CCW Armor Booster"
- desc = "Exosuit close-combat armor booster."
+ desc = "Mecha close-combat armor booster."
id = "mech_ccw_armor"
req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 4)
materials = list(MAT_STEEL = 11250, MAT_SILVER = 3750)
@@ -653,7 +653,7 @@
/datum/design/item/mecha/proj_armor
name = "Ranged Armor Booster"
- desc = "Exosuit projectile armor booster."
+ desc = "Mecha projectile armor booster."
id = "mech_proj_armor"
req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_ENGINEERING = 3)
materials = list(MAT_STEEL = 15000, MAT_GOLD = 3750)
@@ -661,7 +661,7 @@
/datum/design/item/mecha/diamond_drill
name = "Diamond Drill"
- desc = "A diamond version of the exosuit drill. It's harder, better, faster, stronger."
+ desc = "A diamond version of the mecha drill. It's harder, better, faster, stronger."
id = "mech_diamond_drill"
req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3)
materials = list(MAT_STEEL = 7500, MAT_DIAMOND = 4875)
@@ -692,8 +692,8 @@
build_path = /obj/item/mecha_parts/mecha_equipment/tool/orescanner/advanced
/datum/design/item/mecha/runningboard
- name = "Powered Exosuit Running Board"
- desc = "A running board with a power-lifter attachment, to quickly catapult exosuit pilots into the cockpit. Only fits to working exosuits."
+ name = "Powered Mecha Running Board"
+ desc = "A running board with a power-lifter attachment, to quickly catapult mecha pilots into the cockpit. Only fits to working mechas."
id = "mech_runningboard"
req_tech = list(TECH_MATERIAL = 6)
materials = list(MAT_STEEL = 10000)
@@ -757,7 +757,7 @@
/datum/design/item/mecha/generator_nuclear
name = "Nuclear Reactor"
- desc = "Exosuit-held nuclear reactor. Converts uranium and everyone's health to energy."
+ desc = "Mecha-held nuclear reactor. Converts uranium and everyone's health to energy."
id = "mech_generator_nuclear"
req_tech = list(TECH_POWER= 3, TECH_ENGINEERING = 3, TECH_MATERIAL = 3)
materials = list(MAT_STEEL = 7500, MAT_SILVER = 375, MAT_GLASS = 750)
@@ -1075,150 +1075,150 @@
req_tech = list(TECH_MATERIAL = 6, TECH_ENGINEERING = 5, TECH_PHORON = 3, TECH_MAGNET = 4, TECH_POWER = 6)
materials = list(MAT_STEEL = 10000, MAT_GLASS = 6000, MAT_SILVER = 4000)
-// Exosuit Internals
+// Mecha Internals
-/datum/design/item/mechfab/exointernal
- category = "Exosuit Internals"
+/datum/design/item/mechfab/mechainternal
+ category = "Mecha Internals"
time = 30
req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3)
-/datum/design/item/mechfab/exointernal/stan_armor
+/datum/design/item/mechfab/mechainternal/stan_armor
name = "Armor Plate (Standard)"
- category = "Exosuit Internals"
- id = "exo_int_armor_standard"
+ category = "Mecha Internals"
+ id = "mecha_int_armor_standard"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2)
materials = list(MAT_STEEL = 10000)
build_path = /obj/item/mecha_parts/component/armor
-/datum/design/item/mechfab/exointernal/light_armor
+/datum/design/item/mechfab/mechainternal/light_armor
name = "Armor Plate (Lightweight)"
- category = "Exosuit Internals"
- id = "exo_int_armor_lightweight"
+ category = "Mecha Internals"
+ id = "mecha_int_armor_lightweight"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 3)
materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 3000)
build_path = /obj/item/mecha_parts/component/armor/lightweight
-/datum/design/item/mechfab/exointernal/reinf_armor
+/datum/design/item/mechfab/mechainternal/reinf_armor
name = "Armor Plate (Reinforced)"
- category = "Exosuit Internals"
- id = "exo_int_armor_reinforced"
+ category = "Mecha Internals"
+ id = "mecha_int_armor_reinforced"
req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4)
materials = list(MAT_STEEL = 20000, MAT_PLASTEEL = 10000)
build_path = /obj/item/mecha_parts/component/armor/reinforced
-/datum/design/item/mechfab/exointernal/mining_armor
+/datum/design/item/mechfab/mechainternal/mining_armor
name = "Armor Plate (Blast)"
- category = "Exosuit Internals"
- id = "exo_int_armor_blast"
+ category = "Mecha Internals"
+ id = "mecha_int_armor_blast"
req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4)
materials = list(MAT_STEEL = 20000, MAT_PLASTEEL = 10000)
build_path = /obj/item/mecha_parts/component/armor/mining
-/datum/design/item/mechfab/exointernal/gygax_armor
+/datum/design/item/mechfab/mechainternal/gygax_armor
name = "Armor Plate (Marshal)"
- category = "Exosuit Internals"
- id = "exo_int_armor_gygax"
+ category = "Mecha Internals"
+ id = "mecha_int_armor_gygax"
req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_COMBAT = 2)
materials = list(MAT_STEEL = 40000, MAT_DIAMOND = 8000)
build_path = /obj/item/mecha_parts/component/armor/marshal
-/datum/design/item/mechfab/exointernal/darkgygax_armor
+/datum/design/item/mechfab/mechainternal/darkgygax_armor
name = "Armor Plate (Blackops)"
- category = "Exosuit Internals"
- id = "exo_int_armor_dgygax"
+ category = "Mecha Internals"
+ id = "mecha_int_armor_dgygax"
req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 4, TECH_ILLEGAL = 2)
materials = list(MAT_PLASTEEL = 20000, MAT_DIAMOND = 10000, MAT_GRAPHITE = 20000)
build_path = /obj/item/mecha_parts/component/armor/marshal/reinforced
-/datum/design/item/mechfab/exointernal/durand_armour
+/datum/design/item/mechfab/mechainternal/durand_armour
name = "Armor Plate (Military)"
- id = "exo_int_armor_durand"
+ id = "mecha_int_armor_durand"
req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_COMBAT = 2)
materials = list(MAT_STEEL = 40000, MAT_PLASTEEL = 9525, MAT_URANIUM = 8000)
build_path = /obj/item/mecha_parts/component/armor/military
-/datum/design/item/mechfab/exointernal/marauder_armour
+/datum/design/item/mechfab/mechainternal/marauder_armour
name = "Armor Plate (Cutting Edge)"
- id = "exo_int_armor_marauder"
+ id = "mecha_int_armor_marauder"
req_tech = list(TECH_MATERIAL = 8, TECH_ENGINEERING = 7, TECH_COMBAT = 6, TECH_ILLEGAL = 4)
materials = list(MAT_DURASTEEL = 40000, MAT_GRAPHITE = 8000, MAT_OSMIUM = 8000)
build_path = /obj/item/mecha_parts/component/armor/military/marauder
-/datum/design/item/mechfab/exointernal/phazon_armour
+/datum/design/item/mechfab/mechainternal/phazon_armour
name = "Armor Plate (Janus)"
- id = "exo_int_armor_phazon"
+ id = "mecha_int_armor_phazon"
req_tech = list(TECH_MATERIAL = 6, TECH_ENGINEERING = 6, TECH_COMBAT = 6, TECH_ILLEGAL = 4)
materials = list(MAT_MORPHIUM = 40000, MAT_DURASTEEL = 8000, MAT_OSMIUM = 8000)
build_path = /obj/item/mecha_parts/component/armor/alien
-/datum/design/item/mechfab/exointernal/stan_hull
+/datum/design/item/mechfab/mechainternal/stan_hull
name = "Hull (Standard)"
- category = "Exosuit Internals"
- id = "exo_int_hull_standard"
+ category = "Mecha Internals"
+ id = "mecha_int_hull_standard"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2)
materials = list(MAT_STEEL = 10000)
build_path = /obj/item/mecha_parts/component/hull
-/datum/design/item/mechfab/exointernal/durable_hull
+/datum/design/item/mechfab/mechainternal/durable_hull
name = "Hull (Durable)"
- category = "Exosuit Internals"
- id = "exo_int_hull_durable"
+ category = "Mecha Internals"
+ id = "mecha_int_hull_durable"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2)
materials = list(MAT_STEEL = 8000, MAT_PLASTEEL = 5000)
build_path = /obj/item/mecha_parts/component/hull/durable
-/datum/design/item/mechfab/exointernal/light_hull
+/datum/design/item/mechfab/mechainternal/light_hull
name = "Hull (Lightweight)"
- category = "Exosuit Internals"
- id = "exo_int_hull_light"
+ category = "Mecha Internals"
+ id = "mecha_int_hull_light"
req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 4)
materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 3000)
build_path = /obj/item/mecha_parts/component/hull/lightweight
-/datum/design/item/mechfab/exointernal/stan_gas
+/datum/design/item/mechfab/mechainternal/stan_gas
name = "Life-Support (Standard)"
- category = "Exosuit Internals"
- id = "exo_int_lifesup_standard"
+ category = "Mecha Internals"
+ id = "mecha_int_lifesup_standard"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2)
materials = list(MAT_STEEL = 10000)
build_path = /obj/item/mecha_parts/component/gas
-/datum/design/item/mechfab/exointernal/reinf_gas
+/datum/design/item/mechfab/mechainternal/reinf_gas
name = "Life-Support (Reinforced)"
- category = "Exosuit Internals"
- id = "exo_int_lifesup_reinforced"
+ category = "Mecha Internals"
+ id = "mecha_int_lifesup_reinforced"
req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4)
materials = list(MAT_STEEL = 8000, MAT_PLASTEEL = 8000, MAT_GRAPHITE = 1000)
build_path = /obj/item/mecha_parts/component/gas/reinforced
-/datum/design/item/mechfab/exointernal/stan_electric
+/datum/design/item/mechfab/mechainternal/stan_electric
name = "Electrical Harness (Standard)"
- category = "Exosuit Internals"
- id = "exo_int_electric_standard"
+ category = "Mecha Internals"
+ id = "mecha_int_electric_standard"
req_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2)
materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 1000)
build_path = /obj/item/mecha_parts/component/electrical
-/datum/design/item/mechfab/exointernal/efficient_electric
+/datum/design/item/mechfab/mechainternal/efficient_electric
name = "Electrical Harness (High)"
- category = "Exosuit Internals"
- id = "exo_int_electric_efficient"
+ category = "Mecha Internals"
+ id = "mecha_int_electric_efficient"
req_tech = list(TECH_POWER = 4, TECH_ENGINEERING = 4, TECH_DATA = 2)
materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 3000, MAT_SILVER = 3000)
build_path = /obj/item/mecha_parts/component/electrical/high_current
-/datum/design/item/mechfab/exointernal/stan_actuator
+/datum/design/item/mechfab/mechainternal/stan_actuator
name = "Actuator Lattice (Standard)"
- category = "Exosuit Internals"
- id = "exo_int_actuator_standard"
+ category = "Mecha Internals"
+ id = "mecha_int_actuator_standard"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2)
materials = list(MAT_STEEL = 10000)
build_path = /obj/item/mecha_parts/component/actuator
-/datum/design/item/mechfab/exointernal/hispeed_actuator
+/datum/design/item/mechfab/mechainternal/hispeed_actuator
name = "Actuator Lattice (Overclocked)"
- category = "Exosuit Internals"
- id = "exo_int_actuator_overclock"
+ category = "Mecha Internals"
+ id = "mecha_int_actuator_overclock"
req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_POWER = 4)
materials = list(MAT_PLASTEEL = 10000, MAT_OSMIUM = 3000, MAT_GOLD = 5000)
build_path = /obj/item/mecha_parts/component/actuator/hispeed
diff --git a/icons/mob/mecha/backup.dmi b/icons/mob/mecha/backup.dmi
new file mode 100644
index 00000000000..a499acda172
Binary files /dev/null and b/icons/mob/mecha/backup.dmi differ
diff --git a/icons/mob/mecha/mech_bay.dmi b/icons/mob/mecha/mech_bay.dmi
new file mode 100644
index 00000000000..e46f668f0d0
Binary files /dev/null and b/icons/mob/mecha/mech_bay.dmi differ
diff --git a/icons/mob/mecha/mech_decals.dmi b/icons/mob/mecha/mech_decals.dmi
new file mode 100644
index 00000000000..a7c85588703
Binary files /dev/null and b/icons/mob/mecha/mech_decals.dmi differ
diff --git a/icons/mob/mecha/mech_equipment.dmi b/icons/mob/mecha/mech_equipment.dmi
new file mode 100644
index 00000000000..8ddc0a6b493
Binary files /dev/null and b/icons/mob/mecha/mech_equipment.dmi differ
diff --git a/icons/mob/mecha/mech_fab.dmi b/icons/mob/mecha/mech_fab.dmi
new file mode 100644
index 00000000000..3a2ab9e4880
Binary files /dev/null and b/icons/mob/mecha/mech_fab.dmi differ
diff --git a/icons/mob/mecha/mech_hud.dmi b/icons/mob/mecha/mech_hud.dmi
new file mode 100644
index 00000000000..9755c384712
Binary files /dev/null and b/icons/mob/mecha/mech_hud.dmi differ
diff --git a/icons/mob/mecha/mech_part_items.dmi b/icons/mob/mecha/mech_part_items.dmi
new file mode 100644
index 00000000000..b6072b7ca02
Binary files /dev/null and b/icons/mob/mecha/mech_part_items.dmi differ
diff --git a/icons/mob/mecha/mech_parts.dmi b/icons/mob/mecha/mech_parts.dmi
new file mode 100644
index 00000000000..59c49387a1e
Binary files /dev/null and b/icons/mob/mecha/mech_parts.dmi differ
diff --git a/icons/mob/mecha/mech_parts_held.dmi b/icons/mob/mecha/mech_parts_held.dmi
new file mode 100644
index 00000000000..35a8f763ce7
Binary files /dev/null and b/icons/mob/mecha/mech_parts_held.dmi differ
diff --git a/icons/mob/mecha/mech_weapon_overlays.dmi b/icons/mob/mecha/mech_weapon_overlays.dmi
new file mode 100644
index 00000000000..448593ae336
Binary files /dev/null and b/icons/mob/mecha/mech_weapon_overlays.dmi differ
diff --git a/icons/mob/mecha/shield.dmi b/icons/mob/mecha/shield.dmi
new file mode 100644
index 00000000000..c4c41e9180b
Binary files /dev/null and b/icons/mob/mecha/shield.dmi differ
diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi
index b662e0e71e0..da2ce69903f 100644
Binary files a/icons/obj/weapons.dmi and b/icons/obj/weapons.dmi differ
diff --git a/polaris.dme b/polaris.dme
index 7632f1ce87b..4f317be6a1e 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -131,6 +131,7 @@
#include "code\_onclick\cyborg.dm"
#include "code\_onclick\drag_drop.dm"
#include "code\_onclick\item_attack.dm"
+#include "code\_onclick\MouseDrag.dm"
#include "code\_onclick\observer.dm"
#include "code\_onclick\other_mobs.dm"
#include "code\_onclick\rig.dm"
@@ -1886,6 +1887,42 @@
#include "code\modules\examine\descriptions\telecomms.dm"
#include "code\modules\examine\descriptions\turfs.dm"
#include "code\modules\examine\descriptions\weapons.dm"
+#include "code\modules\exosuits\_exosuit_setup.dm"
+#include "code\modules\exosuits\exosuit.dm"
+#include "code\modules\exosuits\exosuit_construction.dm"
+#include "code\modules\exosuits\exosuit_damage.dm"
+#include "code\modules\exosuits\exosuit_damage_immunity.dm"
+#include "code\modules\exosuits\exosuit_icon.dm"
+#include "code\modules\exosuits\exosuit_interaction.dm"
+#include "code\modules\exosuits\exosuit_life.dm"
+#include "code\modules\exosuits\exosuit_movement.dm"
+#include "code\modules\exosuits\exosuit_wreckage.dm"
+#include "code\modules\exosuits\component\_components.dm"
+#include "code\modules\exosuits\component\armour.dm"
+#include "code\modules\exosuits\component\arms.dm"
+#include "code\modules\exosuits\component\body.dm"
+#include "code\modules\exosuits\component\frame.dm"
+#include "code\modules\exosuits\component\head.dm"
+#include "code\modules\exosuits\component\legs.dm"
+#include "code\modules\exosuits\component\software.dm"
+#include "code\modules\exosuits\equipment\_equipment.dm"
+#include "code\modules\exosuits\equipment\combat.dm"
+#include "code\modules\exosuits\equipment\combat_projectile.dm"
+#include "code\modules\exosuits\equipment\engineering.dm"
+#include "code\modules\exosuits\equipment\medical.dm"
+#include "code\modules\exosuits\equipment\utility.dm"
+#include "code\modules\exosuits\interface\_interface.dm"
+#include "code\modules\exosuits\interface\screen_objects.dm"
+#include "code\modules\exosuits\premade\_premade.dm"
+#include "code\modules\exosuits\premade\combat.dm"
+#include "code\modules\exosuits\premade\exploration.dm"
+#include "code\modules\exosuits\premade\heavy.dm"
+#include "code\modules\exosuits\premade\industrial.dm"
+#include "code\modules\exosuits\premade\light.dm"
+#include "code\modules\exosuits\premade\mercenary.dm"
+#include "code\modules\exosuits\premade\modern.dm"
+#include "code\modules\exosuits\premade\powerloader.dm"
+#include "code\modules\exosuits\premade\sleek.dm"
#include "code\modules\ext_scripts\irc.dm"
#include "code\modules\ext_scripts\python.dm"
#include "code\modules\fishing\fishing.dm"
@@ -2694,6 +2731,7 @@
#include "code\modules\nano\interaction\conscious.dm"
#include "code\modules\nano\interaction\contained.dm"
#include "code\modules\nano\interaction\default.dm"
+#include "code\modules\nano\interaction\exosuit.dm"
#include "code\modules\nano\interaction\interactive.dm"
#include "code\modules\nano\interaction\inventory.dm"
#include "code\modules\nano\interaction\inventory_deep.dm"
@@ -3063,6 +3101,24 @@
#include "code\modules\research\designs\circuits\ai_modules.dm"
#include "code\modules\research\designs\circuits\circuits.dm"
#include "code\modules\research\designs\circuits\disks.dm"
+#include "code\modules\research\designs\exosuits\categories.dm"
+#include "code\modules\research\designs\exosuits\combat.dm"
+#include "code\modules\research\designs\exosuits\components.dm"
+#include "code\modules\research\designs\exosuits\engineering.dm"
+#include "code\modules\research\designs\exosuits\heavy.dm"
+#include "code\modules\research\designs\exosuits\industrial.dm"
+#include "code\modules\research\designs\exosuits\light.dm"
+#include "code\modules\research\designs\exosuits\medical.dm"
+#include "code\modules\research\designs\exosuits\mercenary.dm"
+#include "code\modules\research\designs\exosuits\modern.dm"
+#include "code\modules\research\designs\exosuits\pod.dm"
+#include "code\modules\research\designs\exosuits\powerloader.dm"
+#include "code\modules\research\designs\exosuits\sleek.dm"
+#include "code\modules\research\designs\exosuits\spider.dm"
+#include "code\modules\research\designs\exosuits\tracks.dm"
+#include "code\modules\research\designs\exosuits\utility.dm"
+#include "code\modules\research\designs\exosuits\weapon_melee.dm"
+#include "code\modules\research\designs\exosuits\weapon_ranged.dm"
#include "code\modules\security levels\keycard authentication.dm"
#include "code\modules\security levels\security levels.dm"
#include "code\modules\shieldgen\directional_shield.dm"