From fcc3202153864465b43d204b1cd79b0fb08f80ca Mon Sep 17 00:00:00 2001
From: MLGTASTICa <61350382+MLGTASTICa@users.noreply.github.com>
Date: Fri, 15 Dec 2023 20:32:48 +0200
Subject: [PATCH] Mech update 3 (The forklifting update) (#8378)
* p1
* p2
* sisyphus struggle
* Fixes and HUD additions
* final fixes to mech huds.
* ssa
* the pulling , the medical and the ugly framework
* fixes
* fix 2
* more code , more fuel , more modules , more stuff.
* content
* fix
* fixes for multiple bugs
* Fixes mech breathing , lts mechs be built without arms/head, Additional handlign for no head / arms , component gibbing , mech interaction for generators./
* fixes + the beginning of the forklifts.
* forklifting championship part 1
* Sacrifices in the name of forklifting championships..
* fixes
* more mech fixes
* more fixes
* Unleahs the forklift upon this universe
* fix
* i heard you like to lift..
* more fixes + fix for throwing shit whilst inside of other stuff.
* extra mech examine data.
* more fixes.
* Add functionality for removing people from forklifts / towing hooks + more examine text fixe.s
* Fixes + FORKLIFTING CHECKS!!!!!
* last Fixes.
* More examines + a new seat on the forklift chassis.
* Flip this around so it doesnt always give energy.
* Fix UI sometimes glitching , fix door forcing being do-able with light arms and clamper arms, fixes draining fuel generators
* Update utility.dm
* Update frame.dm
* fixes for bugs.
* fix for integrations
* Add all the modules to research + give them research data and material costs.
* f
* f2
* fix + reduce use delay on auto mender
* fix integrations
* Update mech_damage.dm
* Update utility.dm
* better balance for wheels
* update b-shield sprites + make undeploying it much faster.
* Update utility.dm
* Update shield generator sprites.
* switch around a icon_state update and shield icons.
---
code/__DEFINES/dcs/signals.dm | 3 +
code/_onclick/click.dm | 2 +-
code/datums/components/_component.dm | 1 +
code/datums/movement/mob.dm | 6 +
code/datums/wires/wires.dm | 6 +-
code/game/atoms.dm | 2 +-
code/game/machinery/doors/door.dm | 4 +-
code/game/objects/structures/catwalk.dm | 4 +-
.../structures/crates_lockers/closets.dm | 8 +-
code/game/objects/structures/lattice.dm | 4 +-
code/game/objects/structures/window.dm | 6 +-
code/game/turfs/simulated/wall_attacks.dm | 5 +-
code/modules/materials/materials.dm | 357 +++++++++++
code/modules/mechs/_mech_defines.dm | 10 +
code/modules/mechs/components/_components.dm | 52 +-
code/modules/mechs/components/arms.dm | 10 +
code/modules/mechs/components/body.dm | 36 +-
code/modules/mechs/components/frame.dm | 25 +-
code/modules/mechs/components/head.dm | 11 +-
code/modules/mechs/components/legs.dm | 14 +
code/modules/mechs/equipment/_equipment.dm | 17 +-
code/modules/mechs/equipment/combat.dm | 72 ++-
code/modules/mechs/equipment/medical.dm | 147 +++++
code/modules/mechs/equipment/utility.dm | 592 ++++++++++++++++++
code/modules/mechs/interface/_mech_HUD.dm | 8 +-
code/modules/mechs/interface/datum_HUD.dm | 1 +
.../modules/mechs/interface/screen_objects.dm | 123 +++-
code/modules/mechs/mech.dm | 57 +-
code/modules/mechs/mech_construction.dm | 5 +-
code/modules/mechs/mech_damage.dm | 17 +-
code/modules/mechs/mech_interaction.dm | 195 +++++-
code/modules/mechs/mech_life.dm | 88 ++-
code/modules/mechs/mech_movement.dm | 35 +-
code/modules/mechs/premade/_premade.dm | 12 +-
code/modules/mechs/premade/powerloader.dm | 18 +
.../mob/living/carbon/human/human_helpers.dm | 4 +
code/modules/mob/living/carbon/resist.dm | 5 +
code/modules/mob/mob.dm | 9 +-
code/modules/multiz/structures.dm | 4 +
code/modules/multiz/turf.dm | 16 +-
code/modules/power/cell.dm | 1 -
.../designs/mechs2/exosuits_components.dm | 6 +
.../designs/mechs2/exosuits_equipment.dm | 27 +
code/modules/research/nodes/robotics.dm | 16 +-
icons/mechs/bshield.dmi | Bin 1129 -> 1810 bytes
icons/mechs/mech_equipment.dmi | Bin 26228 -> 29051 bytes
icons/mechs/mech_hud.dmi | Bin 9206 -> 9837 bytes
icons/mechs/mech_parts.dmi | Bin 17901 -> 23087 bytes
icons/mechs/mech_parts_held.dmi | Bin 2816 -> 3192 bytes
icons/mechs/mech_weapon_overlays.dmi | Bin 27064 -> 32107 bytes
icons/mechs/shield.dmi | Bin 9613 -> 29732 bytes
sound/mechs/mech-shutdown.ogg | Bin 0 -> 117896 bytes
sound/mechs/mech_generator.ogg | Bin 0 -> 28805 bytes
53 files changed, 1874 insertions(+), 167 deletions(-)
create mode 100644 sound/mechs/mech-shutdown.ogg
create mode 100644 sound/mechs/mech_generator.ogg
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 7f229590013..6a7ed02f7bf 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -67,6 +67,9 @@
#define COMSIG_MOVABLE_Z_CHANGED "movable_z_moved" //from atom/movable/Move and forceMove): (oldz, newz)
#define COMSIG_MOVABLE_PREMOVE "moveable_boutta_move"
+#define COMSIG_ATTEMPT_PULLING "attempt_pulling"
+ #define COMSIG_PULL_CANCEL (1<<0)
+
// /mob signals
#define COMSIG_MOB_LIFE "mob_life" //from mob/Life()
#define COMSIG_MOB_LOGIN "mob_login" //from mob/Login()
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 085a2797f75..bdea8016c83 100755
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -124,7 +124,7 @@
return 1
if(in_throw_mode)
- if(isturf(A) || isturf(A.loc))
+ if(isturf(A) || isturf(A.loc) && isturf(loc))
throw_item(A)
return 1
throw_mode_off()
diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm
index bf0fa7a49b8..b999aee03c6 100644
--- a/code/datums/components/_component.dm
+++ b/code/datums/components/_component.dm
@@ -200,6 +200,7 @@
var/list/target_procs = (procs[target] ||= list())
var/list/lookup = (target.comp_lookup ||= list())
+
if(!override && target_procs[signal_type])
var/override_message = "[signal_type] overridden. Use override = TRUE to suppress this warning.\nTarget: [target] ([target.type]) Proc: [proctype]"
//log_signal(override_message)
diff --git a/code/datums/movement/mob.dm b/code/datums/movement/mob.dm
index 6e2ba7fa6ce..4d348bbd4f2 100755
--- a/code/datums/movement/mob.dm
+++ b/code/datums/movement/mob.dm
@@ -268,6 +268,12 @@
else
M.stop_pulling()
+ if(istype(mob.loc, /obj/item/mech_equipment/forklifting_system))
+ if(mover == mob && isliving(mob))
+ mob:resist()
+ return MOVEMENT_STOP
+
+
return MOVEMENT_PROCEED
diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm
index 85110919d66..cfe262016f2 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -145,7 +145,11 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown"
var/mob/living/L = usr
if(CanUse(L) && href_list["action"])
var/obj/item/I = L.get_active_hand()
- holder.add_hiddenprint(L)
+ if(!ismech(L.loc))
+ holder.add_hiddenprint(L)
+ else
+ var/mob/living/exosuit/mech = L.loc
+ I = mech.get_active_hand()
if(href_list["cut"]) // Toggles the cut/mend status
if (!istype(I))
return
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index fe7d94739c7..043ba3acf83 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -649,7 +649,7 @@ its easier to just keep the beam vertical.
/atom/movable/proc/fall_impact(turf/from, turf/dest)
//If atom stands under open space, it can prevent fall, or not
-/atom/proc/can_prevent_fall()
+/atom/proc/can_prevent_fall(above, atom/movable/thing)
return FALSE
// Show a message to all mobs and objects in sight of this atom
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 6946ff4d385..b3cd1833cc3 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -53,8 +53,8 @@
GLOB.all_doors -= src
..()
-/obj/machinery/door/can_prevent_fall()
- return density
+/obj/machinery/door/can_prevent_fall(above)
+ return above ? density : null
/obj/machinery/door/attack_generic(mob/user, var/damage)
if(damage >= resistance)
diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm
index c920cc018de..f55105c31fb 100644
--- a/code/game/objects/structures/catwalk.dm
+++ b/code/game/objects/structures/catwalk.dm
@@ -91,5 +91,5 @@
return
-/obj/structure/catwalk/can_prevent_fall()
- return FALSE
+/obj/structure/catwalk/can_prevent_fall(above)
+ return above ? FALSE : TRUE
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 5236cf9b3b7..eff2df7b10f 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -42,8 +42,8 @@
var/store_mobs = 1
var/old_chance = 0 //Chance to have rusted closet content in it, from 0 to 100. Keep in mind that chance increases in maints
-/obj/structure/closet/can_prevent_fall()
- return TRUE
+/obj/structure/closet/can_prevent_fall(above)
+ return above ? TRUE : FALSE
/obj/structure/closet/Initialize(mapload)
..()
@@ -350,6 +350,10 @@
//Empty gripper attacks will call attack_AI
return FALSE
+ /// So mechs dont open these when attacking.
+ if(istype(I, /obj/item/mech_equipment/forklifting_system))
+ return FALSE
+
var/list/usable_qualities = list(QUALITY_WELDING)
if(opened)
usable_qualities += QUALITY_SAWING
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index 11b79502719..469fdd9493b 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -81,5 +81,5 @@
return
-/obj/structure/lattice/can_prevent_fall()
- return TRUE
+/obj/structure/lattice/can_prevent_fall(above)
+ return above ? FALSE : TRUE
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 3c8f5da4ce5..10502621d96 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -24,8 +24,8 @@
atmos_canpass = CANPASS_PROC
-/obj/structure/window/can_prevent_fall()
- return !is_fulltile()
+/obj/structure/window/can_prevent_fall(above)
+ return above ? !is_fulltile() : FALSE
/obj/structure/window/get_fall_damage(var/turf/from, var/turf/dest)
var/damage = health * 0.4 * get_health_ratio()
@@ -357,7 +357,7 @@ proc/end_grab_onto(mob/living/user, mob/living/target)
usable_qualities.Add(QUALITY_SEALING)
//If you set intent to harm, you can hit the window with tools to break it. Set to any other intent to use tools on it
- if (usr.a_intent != I_HURT)
+ if (user.a_intent != I_HURT)
var/tool_type = I.get_tool_type(user, usable_qualities, src)
switch(tool_type)
if(QUALITY_SEALING)
diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm
index 397b0b21bac..6b93f1a8afa 100644
--- a/code/game/turfs/simulated/wall_attacks.dm
+++ b/code/game/turfs/simulated/wall_attacks.dm
@@ -97,7 +97,10 @@
return
//get the user's location
- if(!istype(user.loc, /turf)) return //can't do this stuff whilst inside objects and such
+ if(!istype(user.loc, /turf))
+ if(!(ismech(user.loc) && istype(I, /obj/item/tool/mech_kit)))
+ return
+
if(I)
radiate()
diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm
index e42111baf92..3d0b90fecc2 100644
--- a/code/modules/materials/materials.dm
+++ b/code/modules/materials/materials.dm
@@ -113,6 +113,15 @@ var/list/name_to_material
var/explosion_resistance = 5 // Only used by walls currently.
var/conductive = 1 // Objects with this var add CONDUCTS to flags on spawn.
var/list/composite_material // If set, object matter var will be a list containing these values.
+ /// Armor values for this material whenever its applied on something.
+ var/datum/armor/armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 1,
+ bio = 1,
+ rad = 1
+ )
// Placeholder vars for the time being, todo properly integrate windows/light tiles/rods.
var/created_window
@@ -258,6 +267,16 @@ var/list/name_to_material
hardness = 80
stack_origin_tech = list(TECH_MATERIAL = 5)
door_icon_base = "stone"
+ // it is a metal and it does conduct , but it does very poorly
+ conductive = FALSE
+ armor = list(
+ melee = 6,
+ bullet = 6,
+ energy = 10,
+ bomb = 25,
+ bio = 25,
+ rad = 0
+ )
/material/diamond
name = MATERIAL_DIAMOND
@@ -271,6 +290,14 @@ var/list/name_to_material
hardness = 100
weight = 50
stack_origin_tech = list(TECH_MATERIAL = 6)
+ armor = list(
+ melee = 15,
+ bullet = 15,
+ energy = 0,
+ bomb = 80,
+ bio = 0,
+ rad = 0
+ )
/material/gold
name = MATERIAL_GOLD
@@ -281,10 +308,28 @@ var/list/name_to_material
stack_origin_tech = list(TECH_MATERIAL = 4)
sheet_singular_name = "ingot"
sheet_plural_name = "ingots"
+ conductive = TRUE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 1,
+ bio = 45,
+ rad = 1
+ )
/material/gold/bronze //placeholder for ashtrays
name = "bronze"
icon_colour = "#EDD12F"
+ conductive = TRUE
+ armor = list(
+ melee = 2,
+ bullet = 2,
+ energy = 3,
+ bomb = 10,
+ bio = 30,
+ rad = 0
+ )
/material/silver
name = MATERIAL_SILVER
@@ -295,6 +340,15 @@ var/list/name_to_material
stack_origin_tech = list(TECH_MATERIAL = 3)
sheet_singular_name = "ingot"
sheet_plural_name = "ingots"
+ conductive = TRUE
+ armor = list(
+ melee = 2,
+ bullet = 2,
+ energy = 2,
+ bomb = 10,
+ bio = 80,
+ rad = 0
+ )
/material/plasma
name = MATERIAL_PLASMA
@@ -309,6 +363,15 @@ var/list/name_to_material
door_icon_base = "stone"
sheet_singular_name = "crystal"
sheet_plural_name = "crystals"
+ conductive = TRUE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 6,
+ bomb = 1,
+ bio = 80,
+ rad = 45
+ )
/*
// Commenting this out while fires are so spectacularly lethal, as I can't seem to get this balanced appropriately.
@@ -339,6 +402,15 @@ var/list/name_to_material
door_icon_base = "stone"
sheet_singular_name = "brick"
sheet_plural_name = "bricks"
+ conductive = FALSE
+ armor = list(
+ melee = 2,
+ bullet = 0,
+ energy = 1,
+ bomb = 25,
+ bio = 0,
+ rad = 0
+ )
/material/stone/marble
name = MATERIAL_MARBLE
@@ -358,12 +430,30 @@ var/list/name_to_material
icon_reinf = "reinf_over"
icon_colour = PLASTEEL_COLOUR
hitsound = 'sound/weapons/genhit.ogg'
+ conductive = TRUE
+ armor = list(
+ melee = 5,
+ bullet = 5,
+ energy = 5,
+ bomb = 35,
+ bio = 0,
+ rad = 0
+ )
/material/steel/holographic
name = "holo" + MATERIAL_STEEL
display_name = MATERIAL_STEEL
stack_type = null
shard_type = SHARD_NONE
+ // wish.com steel
+ armor = list(
+ melee = 0,
+ bullet = 0,
+ energy = 0,
+ bomb = 0,
+ bio = 0,
+ rad = 0
+ )
/material/plasteel
name = MATERIAL_PLASTEEL
@@ -377,7 +467,16 @@ var/list/name_to_material
hardness = 80
weight = 23
stack_origin_tech = list(TECH_MATERIAL = 2)
+ conductive = TRUE
hitsound = 'sound/weapons/genhit.ogg'
+ armor = list(
+ melee = 8,
+ bullet = 8,
+ energy = 4,
+ bomb = 75,
+ bio = 35,
+ rad = 25
+ )
/material/plasteel/titanium
name = "titanium"
@@ -388,6 +487,15 @@ var/list/name_to_material
door_icon_base = "metal"
icon_colour = "#D1E6E3"
icon_reinf = "reinf_metal"
+ conductive = TRUE
+ armor = list(
+ melee = 16,
+ bullet = 10,
+ energy = 6,
+ bomb = 125,
+ bio = 35,
+ rad = 0
+ )
/material/glass
name = MATERIAL_GLASS
@@ -406,7 +514,16 @@ var/list/name_to_material
created_window = /obj/structure/window/basic
created_window_full = /obj/structure/window/basic/full
rod_product = /obj/item/stack/material/glass/reinforced
+ conductive = FALSE
hitsound = 'sound/effects/Glasshit.ogg'
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 0,
+ bomb = 0,
+ bio = 0,
+ rad = 0
+ )
/material/glass/build_windows(var/mob/living/user, var/obj/item/stack/used_stack)
@@ -522,8 +639,17 @@ var/list/name_to_material
window_options = list("One Direction" = 1, "Full Window" = 6, "Windoor" = 5)
created_window = /obj/structure/window/reinforced
created_window_full = /obj/structure/window/reinforced/full
+ conductive = FALSE
wire_product = null
rod_product = null
+ armor = list(
+ melee = 4,
+ bullet = 4,
+ energy = 0,
+ bomb = 35,
+ bio = 0,
+ rad = 0
+ )
/material/glass/plasma
name = MATERIAL_PLASMAGLASS
@@ -539,6 +665,15 @@ var/list/name_to_material
created_window_full = /obj/structure/window/plasmabasic/full
wire_product = null
rod_product = /obj/item/stack/material/glass/plasmarglass
+ conductive = FALSE
+ armor = list(
+ melee = 6,
+ bullet = 6,
+ energy = 0,
+ bomb = 45,
+ bio = 0,
+ rad = 0
+ )
/material/glass/plasma/reinforced
name = MATERIAL_RPLASMAGLASS
@@ -550,8 +685,17 @@ var/list/name_to_material
created_window_full = /obj/structure/window/reinforced/plasma/full
hardness = 60
weight = 50
+ conductive = FALSE
//composite_material = list() //todo
rod_product = null
+ armor = list(
+ melee = 8,
+ bullet = 8,
+ energy = 0,
+ bomb = 50,
+ bio = 0,
+ rad = 0
+ )
/material/plastic
name = MATERIAL_PLASTIC
@@ -563,13 +707,31 @@ var/list/name_to_material
hardness = 10
weight = 12
melting_point = T0C+371 //assuming heat resistant plastic
+ conductive = FALSE
stack_origin_tech = list(TECH_MATERIAL = 3)
+ armor = list(
+ melee = 3,
+ bullet = 3,
+ energy = 3,
+ bomb = 20,
+ bio = 5,
+ rad = 0
+ )
/material/plastic/holographic
name = "holoplastic"
display_name = "plastic"
stack_type = null
shard_type = SHARD_NONE
+ conductive = FALSE
+ armor = list(
+ melee = 0,
+ bullet = 0,
+ energy = 0,
+ bomb = 0,
+ bio = 0,
+ rad = 0
+ )
/material/osmium
name = MATERIAL_OSMIUM
@@ -580,6 +742,15 @@ var/list/name_to_material
hardness = 90
sheet_singular_name = "ingot"
sheet_plural_name = "ingots"
+ conductive = TRUE
+ armor = list(
+ melee = 20,
+ bullet = 14,
+ energy = 7,
+ bomb = 200,
+ bio = 0,
+ rad = 25
+ )
/material/tritium
name = MATERIAL_TRITIUM
@@ -588,6 +759,15 @@ var/list/name_to_material
stack_origin_tech = list(TECH_MATERIAL = 5)
sheet_singular_name = "ingot"
sheet_plural_name = "ingots"
+ conductive = FALSE
+ armor = list(
+ melee = 0,
+ bullet = 0,
+ energy = 10,
+ bomb = 0,
+ bio = 100,
+ rad = 50
+ )
/material/mhydrogen
name = MATERIAL_MHYDROGEN
@@ -596,7 +776,16 @@ var/list/name_to_material
stack_origin_tech = list(TECH_MATERIAL = 6, TECH_POWER = 6, TECH_MAGNET = 5)
weight = 10
hardness = 200
+ conductive = TRUE
display_name = "metallic hydrogen"
+ armor = list(
+ melee = 35,
+ bullet = 18,
+ energy = 8,
+ bomb = 350,
+ bio = 0,
+ rad = 35
+ )
/material/platinum
name = MATERIAL_PLATINUM
@@ -607,6 +796,14 @@ var/list/name_to_material
stack_origin_tech = list(TECH_MATERIAL = 2)
sheet_singular_name = "ingot"
sheet_plural_name = "ingots"
+ armor = list(
+ melee = 3,
+ bullet = 3,
+ energy = 15,
+ bomb = 25,
+ bio = 55,
+ rad = 25
+ )
/material/iron
name = MATERIAL_IRON
@@ -617,6 +814,15 @@ var/list/name_to_material
sheet_singular_name = "ingot"
sheet_plural_name = "ingots"
hitsound = 'sound/weapons/smash.ogg'
+ conductive = TRUE
+ armor = list(
+ melee = 3,
+ bullet = 3,
+ energy = 3,
+ bomb = 35,
+ bio = 0,
+ rad = 0
+ )
// Adminspawn only, do not let anyone get this.
/material/voxalloy
@@ -629,6 +835,14 @@ var/list/name_to_material
explosion_resistance = 200 // Hull plating.
hardness = 500
weight = 500
+ armor = list(
+ melee = 50,
+ bullet = 35,
+ energy = 25,
+ bomb = 500,
+ bio = 0,
+ rad = 85
+ )
/material/wood
name = MATERIAL_WOOD
@@ -650,12 +864,30 @@ var/list/name_to_material
sheet_singular_name = "plank"
sheet_plural_name = "planks"
hitsound = 'sound/effects/woodhit.ogg'
+ conductive = FALSE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 5,
+ bomb = 10,
+ bio = 0,
+ rad = 0
+ )
/material/wood/holographic
name = "holowood"
display_name = "wood"
stack_type = null
shard_type = SHARD_NONE
+ conductive = FALSE
+ armor = list(
+ melee = 0,
+ bullet = 0,
+ energy = 0,
+ bomb = 0,
+ bio = 0,
+ rad = 0
+ )
/material/cardboard
name = MATERIAL_CARDBOARD
@@ -672,6 +904,15 @@ var/list/name_to_material
stack_origin_tech = list(TECH_MATERIAL = 1)
door_icon_base = "wood"
destruction_desc = "crumples"
+ conductive = FALSE
+ armor = list(
+ melee = 0,
+ bullet = 0,
+ energy = 0,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
/material/cloth //todo
name = MATERIAL_CLOTH
@@ -680,6 +921,16 @@ var/list/name_to_material
ignition_point = T0C+232
melting_point = T0C+300
flags = MATERIAL_PADDING
+ conductive = FALSE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
+
/material/biomatter
name = MATERIAL_BIOMATTER
@@ -688,6 +939,14 @@ var/list/name_to_material
stack_origin_tech = list(TECH_MATERIAL = 2, TECH_BIO = 2)
sheet_singular_name = "sheet"
sheet_plural_name = "sheets"
+ armor = list(
+ melee = 2,
+ bullet = 2,
+ energy = 2,
+ bomb = 15,
+ bio = 100,
+ rad = 0
+ )
/material/compressed
name = MATERIAL_COMPRESSED
@@ -695,6 +954,15 @@ var/list/name_to_material
icon_colour = "#00E1FF"
sheet_singular_name = "cartrigde"
sheet_plural_name = "cartridges"
+ conductive = TRUE
+ armor = list(
+ melee = 18,
+ bullet = 10,
+ energy = 10,
+ bomb = 150,
+ bio = 0,
+ rad = 100
+ )
//TODO PLACEHOLDERS:
/material/leather
@@ -704,6 +972,14 @@ var/list/name_to_material
flags = MATERIAL_PADDING
ignition_point = T0C+300
melting_point = T0C+300
+ armor = list(
+ melee = 4,
+ bullet = 2,
+ energy = 2,
+ bomb = 10,
+ bio = 10,
+ rad = 0
+ )
/material/carpet
name = "carpet"
@@ -715,6 +991,15 @@ var/list/name_to_material
melting_point = T0C+300
sheet_singular_name = "tile"
sheet_plural_name = "tiles"
+ conductive = FALSE
+ armor = list(
+ melee = 0,
+ bullet = 0,
+ energy = 0,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
/material/cotton
name = "cotton"
@@ -723,6 +1008,15 @@ var/list/name_to_material
flags = MATERIAL_PADDING
ignition_point = T0C+232
melting_point = T0C+300
+ conductive = FALSE
+ armor = list(
+ melee = 0,
+ bullet = 0,
+ energy = 0,
+ bomb = 0,
+ bio = 0,
+ rad = 0
+ )
/material/cloth_teal
name = "teal"
@@ -732,6 +1026,15 @@ var/list/name_to_material
flags = MATERIAL_PADDING
ignition_point = T0C+232
melting_point = T0C+300
+ conductive = FALSE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
/material/cloth_black
name = "black"
@@ -741,6 +1044,15 @@ var/list/name_to_material
flags = MATERIAL_PADDING
ignition_point = T0C+232
melting_point = T0C+300
+ conductive = FALSE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
/material/cloth_green
name = "green"
@@ -750,6 +1062,15 @@ var/list/name_to_material
flags = MATERIAL_PADDING
ignition_point = T0C+232
melting_point = T0C+300
+ conductive = FALSE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
/material/cloth_puple
name = "purple"
@@ -759,6 +1080,15 @@ var/list/name_to_material
flags = MATERIAL_PADDING
ignition_point = T0C+232
melting_point = T0C+300
+ conductive = FALSE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
/material/cloth_blue
name = "blue"
@@ -768,6 +1098,15 @@ var/list/name_to_material
flags = MATERIAL_PADDING
ignition_point = T0C+232
melting_point = T0C+300
+ conductive = FALSE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
/material/cloth_beige
name = "beige"
@@ -777,6 +1116,15 @@ var/list/name_to_material
flags = MATERIAL_PADDING
ignition_point = T0C+232
melting_point = T0C+300
+ conductive = FALSE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
/material/cloth_lime
name = "lime"
@@ -786,3 +1134,12 @@ var/list/name_to_material
flags = MATERIAL_PADDING
ignition_point = T0C+232
melting_point = T0C+300
+ conductive = FALSE
+ armor = list(
+ melee = 1,
+ bullet = 1,
+ energy = 1,
+ bomb = 5,
+ bio = 0,
+ rad = 0
+ )
diff --git a/code/modules/mechs/_mech_defines.dm b/code/modules/mechs/_mech_defines.dm
index 1bde101c220..d1217122fa1 100644
--- a/code/modules/mechs/_mech_defines.dm
+++ b/code/modules/mechs/_mech_defines.dm
@@ -7,6 +7,7 @@
#define MECH_HUD_ICON 'icons/mechs/mech_hud.dmi'
#define HARDPOINT_BACK "back"
+#define HARDPOINT_FRONT "front"
#define HARDPOINT_LEFT_HAND "left hand"
#define HARDPOINT_RIGHT_HAND "right hand"
#define HARDPOINT_LEFT_SHOULDER "left shoulder"
@@ -51,3 +52,12 @@ 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(MECH_WEAPON_OVERLAYS_ICON))
+
+#define MECH_POWER_OFF 0
+#define MECH_POWER_TRANSITION 1
+#define MECH_POWER_ON 2
+
+/// It will make update_icon be called on the equipment after every move
+#define EQUIPFLAG_UPDTMOVE 1
+/// It will have pretick() called on it before the mech checks wheter or not is powered
+#define EQUIPFLAG_PRETICK 2
diff --git a/code/modules/mechs/components/_components.dm b/code/modules/mechs/components/_components.dm
index efcd5483133..0f3d0d78ca0 100644
--- a/code/modules/mechs/components/_components.dm
+++ b/code/modules/mechs/components/_components.dm
@@ -17,8 +17,14 @@
var/max_damage = 60
var/damage_state = 1
var/list/has_hardpoints = list()
+ //var/material/reinforcement = null
var/decal
var/power_use = 0
+ /// how many hits do we have to get to gib once we hit max damage
+ var/gib_hits_needed = 7
+ var/gib_hits = 0
+ /// wheter or not this component can just blow up
+ var/can_gib = FALSE
/obj/item/mech_component/proc/set_colour(new_colour)
var/last_colour = color
@@ -35,13 +41,35 @@
if(.)
if(ready_to_install())
- to_chat(usr, SPAN_NOTICE("It is ready for installation."))
+ to_chat(user, SPAN_NOTICE("It is ready for installation."))
else
show_missing_parts(usr)
+ /*
+ if(reinforcement)
+ to_chat(user, SPAN_NOTICE("It is reinforced with sheets of [reinforcement.material_display_name]."))
+ else
+ to_chat(user, SPAN_NOTICE("It can be reinforced with 5 sheets of a material for additional protection."))
+ */
var/damage_string = src.get_damage_string()
to_chat(user, "The [src.name] [src.gender == PLURAL ? "are" : "is"] [damage_string].")
+/*
+
+/obj/item/mech_component/attackby(obj/item/I, mob/living/user)
+ . = ..()
+
+ if(!reinforcement && istype(I, /obj/item/stack/material))
+ var/obj/item/stack/material/mat = I
+ if(!mat.can_use(5))
+ to_chat(user, SPAN_NOTICE("You need 5 sheets of reinforcing material!"))
+ return
+ to_chat(user, SPAN_NOTICE("You start reinforcing \the src."))
+*/
+
+
+
+
//These icons have multiple directions but before they're attached we only want south.
/obj/item/mech_component/set_dir()
@@ -70,6 +98,26 @@
if(damage_state == MECH_COMPONENT_DAMAGE_DAMAGED_TOTAL)
playsound(loc, 'sound/mechs/critdestr.ogg', 50)
+ if(total_damage == max_damage)
+ if(gib_hits > gib_hits_needed && can_gib)
+ var/mob/living/exosuit/owner = loc
+ if(!istype(owner))
+ return
+ forceMove(NULLSPACE)
+ switch(type)
+ if(/obj/item/mech_component/manipulators)
+ owner.arms = null
+ if(/obj/item/mech_component/sensors)
+ owner.head = null
+ if(/obj/item/mech_component/propulsion)
+ owner.legs = null
+ if(/obj/item/mech_component/chassis)
+ owner.body = null
+ for(var/hardpoint in has_hardpoints)
+ owner.remove_system(hardpoint, null, TRUE)
+ owner.update_icon()
+ qdel(src)
+
/obj/item/mech_component/proc/ready_to_install()
return TRUE
@@ -89,6 +137,7 @@
update_health()
if(total_damage >= max_damage)
take_component_damage(amt,0)
+ gib_hits += (total_damage / 10)
return
/obj/item/mech_component/proc/take_burn_damage(amt)
@@ -96,6 +145,7 @@
update_health()
if(total_damage >= max_damage)
take_component_damage(0,amt)
+ gib_hits += (total_damage / 10)
return
/obj/item/mech_component/proc/take_component_damage(brute, burn)
diff --git a/code/modules/mechs/components/arms.dm b/code/modules/mechs/components/arms.dm
index 3f7918ffbfe..a9c80304720 100644
--- a/code/modules/mechs/components/arms.dm
+++ b/code/modules/mechs/components/arms.dm
@@ -6,9 +6,17 @@
power_use = 10
matter = list(MATERIAL_STEEL = 10)
+ can_gib = TRUE
+ gib_hits_needed = 10
var/melee_damage = WEAPON_FORCE_PAINFUL
var/action_delay = 15
+ /// if they can force open powered doors
+ var/can_force_doors = TRUE
var/obj/item/robot_parts/robot_component/actuator/motivator
+ tool_qualities = list(
+ QUALITY_HAMMERING = 30,
+ QUALITY_PRYING = 20
+ )
var/punch_sound = ('sound/mechs/mech_punch.ogg')
/obj/item/mech_component/manipulators/Destroy()
@@ -50,6 +58,7 @@
desc = "Industrial lifter arms that allow you to crudely manipulate things from the safety of your cockpit."
exosuit_desc_string = "industrial lifter arms"
icon_state = "loader_arms"
+ can_force_doors = FALSE
max_damage = 90
power_use = 30
@@ -59,6 +68,7 @@
desc = "As flexible as they are fragile, these manipulators can follow a pilot's movements in close to real time."
icon_state = "light_arms"
action_delay = 5
+ can_force_doors = FALSE
max_damage = 45
power_use = 10
matter = list(MATERIAL_STEEL = 10, MATERIAL_PLASTIC = 5)
diff --git a/code/modules/mechs/components/body.dm b/code/modules/mechs/components/body.dm
index be7214a9543..a29da3e5146 100644
--- a/code/modules/mechs/components/body.dm
+++ b/code/modules/mechs/components/body.dm
@@ -40,6 +40,10 @@
var/min_pilot_size = MOB_SMALL
var/max_pilot_size = MOB_LARGE
var/climb_time = 25
+ /// does this mech chassis have support for charging all cells inside of its storage? if its 0 it doesnt
+ var/cell_charge_rate = 200
+ /// Wheter chassis blocks sight from a outside POV (aka can see behind mech or not ?)
+ var/opaque_chassis = TRUE
/obj/item/mech_component/chassis/New()
..()
@@ -103,7 +107,7 @@
. = ..()
air_supply = new /obj/machinery/portable_atmospherics/canister/air(src)
storage_compartment = new(src)
- cockpit = new(20)
+ cockpit = new(250)
if(loc)
cockpit.equalize(loc.return_air())
@@ -281,3 +285,33 @@
power_use = 50
climb_time = 35 //Takes longer to climb into, but is beefy as HELL.
matter = list(MATERIAL_STEEL = 50, MATERIAL_URANIUM = 20, MATERIAL_PLASTEEL = 20)
+
+/obj/item/mech_component/chassis/forklift
+ name = "forklift chassis"
+ desc = "Has an integrated forklift clamp for the industrial relocation of resources. Are you ready to lift?"
+ icon_state = "seat-cockpit"
+ has_hardpoints = list(HARDPOINT_FRONT)
+ exosuit_desc_string = "a forklifting chassis"
+ pilot_coverage = 30
+ max_damage = 100
+ mech_health = 200
+ opaque_chassis = FALSE
+ matter = list(MATERIAL_STEEL = 20, MATERIAL_PLASTIC = 10)
+
+
+/obj/item/mech_component/chassis/forklift/Initialize()
+ pilot_positions = list(
+ list(
+ "[NORTH]" = list("x" = 9, "y" = 5),
+ "[SOUTH]" = list("x" = 9, "y" = 5),
+ "[EAST]" = list("x" = 6, "y" = 5),
+ "[WEST]" = list("x" = 8, "y" = 5)
+ ),
+ list(
+ "[NORTH]" = list("x" = 9, "y" = 5),
+ "[SOUTH]" = list("x" = 9, "y" = 10),
+ "[EAST]" = list("x" = 0, "y" = 5),
+ "[WEST]" = list("x" = 16, "y" = 5)
+ )
+ )
+ . = ..()
diff --git a/code/modules/mechs/components/frame.dm b/code/modules/mechs/components/frame.dm
index c972ff5f4b1..37474d2ef15 100644
--- a/code/modules/mechs/components/frame.dm
+++ b/code/modules/mechs/components/frame.dm
@@ -98,7 +98,7 @@
if(is_wired)
usable_qualities += QUALITY_WIRE_CUTTING
- if(is_wired == FRAME_WIRED_ADJUSTED && is_reinforced == FRAME_REINFORCED_WELDED && arms && legs && head && body)
+ if(is_wired == FRAME_WIRED_ADJUSTED && is_reinforced == FRAME_REINFORCED_WELDED && legs && body)
usable_qualities += QUALITY_SCREW_DRIVING
var/tool_type = I.get_tool_type(user, usable_qualities, src)
@@ -225,7 +225,7 @@
// Final construction step
if(QUALITY_SCREW_DRIVING)
// Check for basic components.
- if(!(arms && legs && head && body))
+ if(!(legs && body))
to_chat(user, SPAN_WARNING("There are still parts missing from \the [src]."))
return
@@ -251,7 +251,7 @@
if(!I.use_tool(user, src, WORKTIME_INSTANT, tool_type, FAILCHANCE_ZERO))
return
- if(is_reinforced < FRAME_REINFORCED_WELDED || is_wired < FRAME_WIRED_ADJUSTED || !(arms && legs && head && body))
+ if(is_reinforced < FRAME_REINFORCED_WELDED || is_wired < FRAME_WIRED_ADJUSTED || !(legs && body))
return
// We're all done. Finalize the exosuit and pass the frame to the new system.
@@ -318,6 +318,9 @@
// Installing basic components.
if(istype(I, /obj/item/mech_component/manipulators))
+ if(istype(body, /obj/item/mech_component/chassis/forklift))
+ to_chat(user, SPAN_WARNING("\The [src]'s chassis can not support manipulators!"))
+ return
if(arms)
to_chat(user, SPAN_WARNING("\The [src] already has manipulators installed."))
return
@@ -330,12 +333,18 @@
if(legs)
to_chat(user, SPAN_WARNING("\The [src] already has a propulsion system installed."))
return
+ if(istype(body, /obj/item/mech_component/chassis/forklift) && !istype(I, /obj/item/mech_component/propulsion/wheels))
+ to_chat(user, SPAN_WARNING("\The [src]'s chassis can not support this type of propulsation, only wheels!"))
+ return
if(install_component(I, user))
if(legs)
user.unEquip(I, loc)
return
legs = I
else if(istype(I, /obj/item/mech_component/sensors))
+ if(istype(body, /obj/item/mech_component/chassis/forklift))
+ to_chat(user, SPAN_WARNING("\The [src]'s chassis can not support sensors!"))
+ return
if(head)
to_chat(user, SPAN_WARNING("\The [src] already has a sensor array installed."))
return
@@ -369,6 +378,16 @@
if(!user.unEquip(I))
return
I.forceMove(src)
+ if(istype(MC, /obj/item/mech_component/chassis/forklift))
+ if(arms)
+ arms.forceMove(get_turf(src))
+ arms = null
+ if(head)
+ head.forceMove(get_turf(src))
+ head = null
+ if(legs && !istype(legs, /obj/item/mech_component/propulsion/wheels))
+ legs.forceMove(get_turf(src))
+ legs = null
visible_message(SPAN_NOTICE("\The [user] installs \the [I] into \the [src]."))
playsound(user.loc, 'sound/machines/click.ogg', 50, 1)
return 1
diff --git a/code/modules/mechs/components/head.dm b/code/modules/mechs/components/head.dm
index d18ebd0684c..eb1d7d55a9c 100644
--- a/code/modules/mechs/components/head.dm
+++ b/code/modules/mechs/components/head.dm
@@ -6,6 +6,8 @@
has_hardpoints = list(HARDPOINT_HEAD)
power_use = 15
matter = list(MATERIAL_STEEL = 5, MATERIAL_GLASS = 4)
+ can_gib = TRUE
+ gib_hits_needed = 5
var/vision_flags = NONE
var/see_invisible = 0
var/active_sensors = FALSE
@@ -32,18 +34,19 @@
radio = locate() in src
camera = locate() in src
-/obj/item/mech_component/sensors/proc/get_sight()
+/obj/item/mech_component/sensors/proc/get_sight(powered)
var/flags = 0
- if(total_damage >= 0.8 * max_damage)
+ var/mob/living/exosuit/mech = loc
+ if(total_damage >= 0.8 * max_damage || (!powered && mech.hatch_closed))
flags |= BLIND
else if(active_sensors)
flags |= vision_flags
return flags
-/obj/item/mech_component/sensors/proc/get_invisible()
+/obj/item/mech_component/sensors/proc/get_invisible(powered)
var/invisible = 0
- if((total_damage <= 0.8 * max_damage) && active_sensors)
+ if((total_damage <= 0.8 * max_damage) && active_sensors && powered)
invisible = see_invisible
return invisible
diff --git a/code/modules/mechs/components/legs.dm b/code/modules/mechs/components/legs.dm
index 6e987ab8c5a..980e2ee5252 100644
--- a/code/modules/mechs/components/legs.dm
+++ b/code/modules/mechs/components/legs.dm
@@ -106,3 +106,17 @@
power_use = 100
matter = list(MATERIAL_STEEL = 20, MATERIAL_URANIUM = 8)
can_climb = FALSE
+
+/obj/item/mech_component/propulsion/wheels
+ name = "wheels"
+ exosuit_desc_string = "wheels"
+ desc = "A pair of wheels for any mobile vehicle"
+ icon_state = "wheels"
+ move_delay = 1.5
+ turn_delay = 4
+ max_damage = 60
+ stomp_damage = 15
+ power_use = 10
+ can_strafe = FALSE
+ matter = list(MATERIAL_STEEL = 4, MATERIAL_PLASTIC = 16)
+ can_climb = FALSE
diff --git a/code/modules/mechs/equipment/_equipment.dm b/code/modules/mechs/equipment/_equipment.dm
index d7dc768d600..aec638bad08 100644
--- a/code/modules/mechs/equipment/_equipment.dm
+++ b/code/modules/mechs/equipment/_equipment.dm
@@ -19,6 +19,17 @@
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_COCKPIT_LAYER //For the part where it's rendered as mech gear
+ var/active = FALSE
+ var/equipment_flags = 0
+
+/obj/item/mech_equipment/proc/activate()
+ active = TRUE
+
+/obj/item/mech_equipment/proc/deactivate()
+ active = FALSE
+
+/obj/item/mech_equipment/proc/pretick()
+ return FALSE
/obj/item/mech_equipment/attack() //Generally it's not desired to be able to attack with items
return 0
@@ -38,6 +49,8 @@
if(target in owner.contents)
return FALSE
var/obj/item/cell/C = owner.get_cell()
+ if(!C && active_power_use == 0)
+ return TRUE
if(!(C && C.check_charge(active_power_use * CELLRATE)))
to_chat(user, SPAN_WARNING("The power indicator flashes briefly as you attempt to use \the [src]"))
return FALSE
@@ -55,6 +68,8 @@
/obj/item/mech_equipment/attack_self(var/mob/user)
if (owner && loc == owner && ((user in owner.pilots) || user == owner))
var/obj/item/cell/C = owner.get_cell()
+ if(C && active_power_use == 0)
+ return TRUE
if(!(C && C.check_charge(active_power_use * CELLRATE)))
to_chat(user, SPAN_WARNING("The power indicator flashes briefly as you attempt to use \the [src]"))
return FALSE
@@ -130,7 +145,7 @@
/obj/item/mech_equipment/mounted_system/resolve_attackby(atom/A, mob/user, params)
// foward attackbys only when we are installed .
if(ismech(loc))
- return holding.attackby(A, user, params)
+ return A.attackby(holding, user, params)
else ..()
/obj/item/mech_equipment/mounted_system/Destroy()
diff --git a/code/modules/mechs/equipment/combat.dm b/code/modules/mechs/equipment/combat.dm
index 950bb31934e..8d5b57928c1 100644
--- a/code/modules/mechs/equipment/combat.dm
+++ b/code/modules/mechs/equipment/combat.dm
@@ -813,6 +813,8 @@
icon_state = "mech_atmoshield"
restricted_hardpoints = list(HARDPOINT_BACK)
origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 6, TECH_PLASMA = 5)
+ // so it has update icon called everytime it moves
+ equipment_flags = EQUIPFLAG_UPDTMOVE
/// Defines the amount of power drained per hit thats blocked
var/damage_to_power_drain = 30
/// Are we toggled on ?
@@ -830,8 +832,6 @@
visual_bluff.icon_state = "shield_null"
visual_bluff.vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_ID | VIS_INHERIT_PLANE
visual_bluff.layer = ABOVE_ALL_MOB_LAYER
- // the mech default offset is -8 , this neeeds 8 for some reason.
- visual_bluff.pixel_x = 8
/obj/item/mech_equipment/shield_generator/Destroy()
. = ..()
@@ -840,6 +840,14 @@
mech.vis_contents.Remove(visual_bluff)
QDEL_NULL(visual_bluff)
+/obj/item/mech_equipment/shield_generator/uninstalled()
+ owner.vis_contents.Remove(visual_bluff)
+ if(on)
+ on = FALSE
+ update_icon()
+ . = ..()
+
+
/obj/item/mech_equipment/shield_generator/attack_self(mob/user)
. = ..()
if(.)
@@ -848,13 +856,6 @@
last_toggle = world.time
update_icon()
-/obj/item/mech_equipment/shield_generator/proc/updateVisualBluff(targetDir)
- visual_bluff.dir = targetDir
- if(targetDir == NORTH)
- visual_bluff.layer = MECH_UNDER_LAYER
- else
- visual_bluff.layer = MECH_ABOVE_LAYER
-
// Used to tell how effective we are against damage,
/obj/item/mech_equipment/shield_generator/proc/getEffectiveness()
return on
@@ -870,6 +871,11 @@
return
if(!(visual_bluff in mech.vis_contents))
mech.vis_contents.Add(visual_bluff)
+ visual_bluff.dir = mech.dir
+ if(visual_bluff.dir == NORTH)
+ visual_bluff.layer = MECH_UNDER_LAYER
+ else
+ visual_bluff.layer = MECH_ABOVE_LAYER
if(last_toggle > world.time - 1 SECOND)
if(on)
flick("shield_raise", visual_bluff)
@@ -918,7 +924,7 @@
if(!(visual_bluff in _owner.vis_contents))
_owner.vis_contents.Add(visual_bluff)
visual_bluff.icon_state = "mech_shield_[hardpoint]"
- updateVisualBluff(_owner.dir)
+ update_icon()
/obj/item/mech_equipment/shield_generator/ballistic/uninstalled()
owner.vis_contents.Remove(visual_bluff)
@@ -937,50 +943,48 @@
playsound(get_turf(src), 'sound/weapons/shield/shieldblock.ogg', 50, 8)
return damages
-/obj/item/mech_equipment/shield_generator/ballistic/updateVisualBluff(targetDir)
- visual_bluff.dir = targetDir
+/obj/item/mech_equipment/shield_generator/ballistic/update_icon()
+ /// Not needed since we already have handling for visual bluffs layering
+ /// and since we dont use a shield.
+
+ ..(skip = TRUE)
+ var/mob/living/exosuit/mech = loc
+ if(!istype(mech))
+ return
+ if(!(visual_bluff in mech.vis_contents))
+ mech.vis_contents.Add(visual_bluff)
+ visual_bluff.dir = mech.dir
+ visual_bluff.icon_state = "mech_shield_[on ? "on_" : ""][get_hardpoint()]"
switch(get_hardpoint())
if(HARDPOINT_RIGHT_HAND)
// i used a switch before and it doesnt work as intended for some fucking reason FOR EAST AND WEST >:( -SPCR
- if(targetDir == NORTH)
+ if(visual_bluff.dir == NORTH)
visual_bluff.layer = MECH_UNDER_LAYER
- if(targetDir == EAST)
+ if(visual_bluff.dir == EAST)
visual_bluff.layer = MECH_ABOVE_LAYER
- if(targetDir == SOUTH)
+ if(visual_bluff.dir == SOUTH)
visual_bluff.layer = MECH_ABOVE_LAYER
- if(targetDir == WEST)
+ if(visual_bluff.dir == WEST)
visual_bluff.layer = MECH_UNDER_LAYER
return
if(HARDPOINT_LEFT_HAND)
- if(targetDir == NORTH)
+ if(visual_bluff.dir == NORTH)
visual_bluff.layer = MECH_UNDER_LAYER
- if(targetDir == EAST)
+ if(visual_bluff.dir == EAST)
visual_bluff.layer = MECH_UNDER_LAYER
- if(targetDir == SOUTH)
+ if(visual_bluff.dir == SOUTH)
visual_bluff.layer = MECH_ABOVE_LAYER
- if(targetDir == WEST)
+ if(visual_bluff.dir == WEST)
visual_bluff.layer = MECH_ABOVE_LAYER
return
-
-/obj/item/mech_equipment/shield_generator/ballistic/update_icon()
- /// Not needed since we already have handling for visual bluffs layering
- /// and since we dont use a shield.
-
- ..(skip = TRUE)
- var/mob/living/exosuit/mech = loc
- if(!istype(mech))
- return
- if(!(visual_bluff in mech.vis_contents))
- mech.vis_contents.Add(visual_bluff)
- visual_bluff.icon_state = "mech_shield_[get_hardpoint()]"
-
/obj/item/mech_equipment/shield_generator/ballistic/attack_self(mob/user)
var/mob/living/exosuit/mech = loc
if(!istype(mech))
return
to_chat(user , SPAN_NOTICE("[on ? "Retracting" : "Deploying"] \the [src]..."))
- if(do_after(user, 3 SECOND, src, FALSE))
+ var/time = on ? 0.5 SECONDS : 3 SECONDS
+ if(do_after(user, time, src, FALSE))
on = !on
to_chat(user, "You [on ? "deploy" : "retract"] \the [src].")
mech.visible_message(SPAN_DANGER("\The [mech] [on ? "deploys" : "retracts"] \the [src]!"), "", "You hear the sound of a heavy metal plate hitting the floor!", 8)
diff --git a/code/modules/mechs/equipment/medical.dm b/code/modules/mechs/equipment/medical.dm
index 7371290f795..a44242c794b 100644
--- a/code/modules/mechs/equipment/medical.dm
+++ b/code/modules/mechs/equipment/medical.dm
@@ -77,3 +77,150 @@
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].")
+
+/obj/item/mech_equipment/sleeper/upgraded
+ name = "\improper MK2 mounted sleeper"
+ desc = "An exosuit-mounted sleeper designed to heal patients"
+ origin_tech = list(TECH_MATERIAL = 1, TECH_ENGINEERING = 3, TECH_BIO = 5)
+ spawn_frequency = 80
+ spawn_blacklisted = FALSE
+ spawn_tags = SPAWN_MECH_QUIPMENT
+ matter = list(MATERIAL_PLASTEEL = 10, MATERIAL_PLASTIC = 10, MATERIAL_GLASS = 5, MATERIAL_SILVER = 3, MATERIAL_PLATINUM = 1)
+
+/obj/item/mech_equipment/sleeper/upgraded/Initialize()
+ . = ..()
+ // delete old one
+ qdel(sleeper)
+ sleeper = new /obj/machinery/sleeper/mounted/upgraded(src)
+ sleeper.forceMove(src)
+
+/obj/machinery/sleeper/mounted/upgraded
+ name = "\improper MK2 mounted sleeper"
+ available_chemicals = list("inaprovaline2" = "Synth-Inaprovaline",
+ "quickclot" = "Quick-Clot",
+ "stoxin" = "Soporific",
+ "tramadol" = "Tramadol",
+ "anti_toxin" = "Dylovene",
+ "dexalin" = "Dexalin",
+ "tricordrazine" = "Tricordrazine",
+ "polystem" = "PolyStem")
+
+/obj/item/mech_equipment/auto_mender
+ name = "\improper exosuit auto-mender"
+ desc = "A mech-designed and equipped medical system for fast and automatic application of advanced trauma treatments to pacients. Makes use of medical gear found in trauma kits."
+ icon_state = "mech_mender"
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_MEDICAL)
+ equipment_delay = 10 //don't spam it on people pls
+ active_power_use = 0 //Usage doesn't really require power.
+ origin_tech = list(TECH_DATA = 3, TECH_BIO = 5, TECH_ENGINEERING = 3)
+ spawn_frequency = 80
+ spawn_blacklisted = FALSE
+ spawn_tags = SPAWN_MECH_QUIPMENT
+ matter = list(MATERIAL_PLASTEEL = 10, MATERIAL_PLASTIC = 10, MATERIAL_SILVER = 8, MATERIAL_GLASS = 5)
+ passive_power_use = 1.5 KILOWATTS
+ var/mob/living/carbon/human/mending_target = null
+ var/mob/living/exosuit/mech = null
+ var/obj/item/organ/external/affecting = null
+ var/trauma_charges_stored = 0
+ var/trauma_storage_max = 30
+
+/obj/item/mech_equipment/auto_mender/afterattack(atom/target, mob/living/user, inrange, params)
+ . = ..()
+ if(. && ishuman(target))
+ if(mending_target == target)
+ mending_target = null
+ to_chat(user, SPAN_NOTICE("You cancel \the [src]'s mending on [target]."))
+ return
+ if(mending_target)
+ to_chat(user, SPAN_NOTICE("\The [src] is already mending someone,you can stop it by clicking the person again!"))
+ return
+ if(!target.Adjacent(mech))
+ to_chat(user, SPAN_NOTICE("You need to be next to \the [target] to start mending them!"))
+ mending_target = target
+ mending_loop()
+
+/obj/item/mech_equipment/auto_mender/attackby(obj/item/I, mob/living/user, params)
+ . = ..()
+ if(istype(I, /obj/item/stack/medical/advanced/bruise_pack))
+ var/obj/item/stack/medical/advanced/bruise_pack/pack = I
+ var/substract = clamp(pack.amount, 0, trauma_storage_max - trauma_charges_stored)
+ if(substract && pack.use(substract))
+ trauma_charges_stored += substract
+ to_chat(user, SPAN_NOTICE("You restock \the [src]'s internal medicine storage with \the [I], using [substract] charges."))
+
+
+/obj/item/mech_equipment/auto_mender/installed(mob/living/exosuit/_owner, hardpoint)
+ . = ..()
+ mech = _owner
+
+/obj/item/mech_equipment/auto_mender/uninstalled()
+ . = ..()
+ mech = null
+
+/obj/item/mech_equipment/auto_mender/proc/mending_loop()
+ if(!mending_target || !mech)
+ return
+ if(!mech.Adjacent(mending_target))
+ mending_target = null
+ affecting = null
+ return
+ var/obj/item/organ/external/checking
+ if(!affecting || (affecting && affecting.is_bandaged()))
+ for(var/zone in BP_ALL_LIMBS)
+ checking = mending_target.organs_by_name[zone]
+ if(checking.is_bandaged() && checking.damage < 1)
+ continue
+ if(affecting)
+ if(checking.damage > affecting.damage)
+ affecting = checking
+ else
+ affecting = checking
+
+ if(!affecting)
+ mending_target = null
+
+ return
+
+ for(var/datum/wound/W in affecting.wounds)
+ if(!mech.Adjacent(mending_target))
+ mending_target = null
+ affecting = null
+ return
+ //if(W.internal || W.bandaged)
+ // continue
+ if(W.damage < 1)
+ continue
+ if(!trauma_charges_stored)
+ break
+ if(!do_mob(mech.get_mob(), mending_target, W.damage/5))
+ to_chat(mech.get_mob(), SPAN_NOTICE("You must stand still to bandage wounds."))
+ mending_target = null
+ affecting = null
+ break
+ //if(W.internal || W.bandaged)
+ // continue
+ if (W.current_stage <= W.max_bleeding_stage)
+ mech.visible_message(
+ SPAN_NOTICE("\The [mech] cleans \a [W.desc] on [mending_target]'s [affecting.name] and seals the edges with bioglue."),
+ SPAN_NOTICE("You clean and seal \a [W.desc] on [mending_target]'s [affecting.name].")
+ )
+ else if (W.damage_type == BRUISE)
+ mech.visible_message(
+ SPAN_NOTICE("\The [mech] places a medical patch over \a [W.desc] on [mending_target]'s [affecting.name]."),
+ SPAN_NOTICE("You place a medical patch over \a [W.desc] on [mending_target]'s [affecting.name].")
+ )
+ else
+ mech.visible_message(
+ SPAN_NOTICE("\The [mech] smears some bioglue over \a [W.desc] on [mending_target]'s [affecting.name]."),
+ SPAN_NOTICE("You smear some bioglue over \a [W.desc] on [mending_target]'s [affecting.name].")
+ )
+ W.bandage()
+ W.heal_damage(10)
+ // If it doesn't cancel or run out of kits just repeat for every external organ.
+ if(affecting.is_bandaged() && affecting.damage < 1)
+ affecting = null
+ mending_loop()
+
+
+
diff --git a/code/modules/mechs/equipment/utility.dm b/code/modules/mechs/equipment/utility.dm
index a1d4ee83dd9..269457e6244 100644
--- a/code/modules/mechs/equipment/utility.dm
+++ b/code/modules/mechs/equipment/utility.dm
@@ -465,3 +465,595 @@
/obj/item/extinguisher/mech/get_hardpoint_status_value()
return reagents.total_volume/max_water
+
+/obj/item/mech_equipment/power_generator
+ name = "debug power generator"
+ desc = "If you see this tell coders to fix code!"
+ icon_state = "mech_power"
+ restricted_hardpoints = list(HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER)
+ restricted_software = list(MECH_SOFTWARE_UTILITY)
+ equipment_flags = EQUIPFLAG_PRETICK
+ spawn_blacklisted = TRUE
+ var/obj/item/cell/internal_cell
+ /// 50 power per mech life tick , adjust for cell RATE
+ var/generation_rate = 50
+
+/obj/item/mech_equipment/power_generator/Initialize()
+ . = ..()
+ internal_cell = new /obj/item/cell/small
+ internal_cell.matter = list()
+
+/obj/item/mech_equipment/power_generator/attackby(obj/item/I, mob/living/user, params)
+ . = ..()
+ if(istype(I, /obj/item/cell) && !internal_cell)
+ user.drop_from_inventory(I)
+ I.forceMove(src)
+ internal_cell = I
+ to_chat(user, SPAN_NOTICE("You replace [src]'s cell!"))
+ return
+
+/obj/item/mech_equipment/power_generator/pretick()
+ var/ungiven_power = internal_cell?.give(generation_rate)
+ if(owner && internal_cell)
+ var/obj/item/cell/batt = owner.get_cell(TRUE)
+ if(batt && batt != internal_cell)
+ batt.give(internal_cell.use(batt.maxcharge - batt.charge))
+
+ return ungiven_power
+
+/obj/item/mech_equipment/power_generator/fueled
+ name = "fueled debug power generator"
+ // YES WE NEED VISCONTENTS FOR THE ANIMATIONS
+ equipment_flags = EQUIPFLAG_UPDTMOVE | EQUIPFLAG_PRETICK
+ var/fuel_amount = 0
+ var/fuel_max = 1000
+ var/fuel_usage_per_tick = 5
+ var/mode = 0
+ var/datum/repeating_sound/sound_loop = null
+ var/obj/visual_bluff = null
+ spawn_blacklisted = TRUE
+
+/obj/item/mech_equipment/power_generator/fueled/Initialize()
+ . = ..()
+ visual_bluff = new(src)
+ visual_bluff.forceMove(src)
+ visual_bluff.icon = MECH_WEAPON_OVERLAYS_ICON
+ visual_bluff.layer = MECH_ABOVE_LAYER
+
+/obj/item/mech_equipment/power_generator/fueled/pretick()
+ // for when we arențt on
+ if(!mode)
+ sound_loop?.stop()
+ return
+ if(fuel_amount > fuel_usage_per_tick)
+ . = ..()
+ /// if we had a extremely minimal use
+ if(. > generation_rate - generation_rate * 0.01 || . == null)
+ return
+ else
+ fuel_amount -= fuel_usage_per_tick
+ if(QDELETED(sound_loop))
+ sound_loop = new(_interval = 2 SECONDS, duration = 10 SECONDS, interval_variance = 0,
+ _source = owner, _soundin = 'sound/mechs/mech_generator.ogg' , _vol = 25 * mode, _vary = 0, _extrarange = mode * 3,
+ _falloff = 0, _is_global = FALSE, _use_pressure = TRUE)
+ else
+ // extend it artificially.
+ sound_loop.end_time = world.time + 10 SECONDS
+ sound_loop.vol = mode * 25
+ sound_loop.extrarange = mode * 3
+
+/obj/item/mech_equipment/power_generator/fueled/installed(mob/living/exosuit/_owner, hardpoint)
+ . = ..()
+ _owner.tickers.Add(src)
+ _owner.vis_contents.Add(visual_bluff)
+
+/obj/item/mech_equipment/power_generator/fueled/uninstalled()
+ . = ..()
+ owner.tickers.Remove(src)
+ owner.vis_contents.Remove(visual_bluff)
+
+/obj/item/mech_equipment/power_generator/fueled/update_icon()
+ ..()
+ icon_state = "[initial(icon_state)]"
+ visual_bluff.icon_state = "[initial(icon_state)]_[mode ? "on" : ""]_[get_hardpoint()]"
+ if(owner)
+ visual_bluff.dir = owner.dir
+
+/obj/item/mech_equipment/power_generator/fueled/attack_self(mob/user)
+ . = ..()
+ if(. && owner)
+ switch(mode)
+ /// Eco mode , very slow generation but doubled power output ( 20% of power production at cost of 10% of fuel usage)
+ if(0)
+ mode = 1
+ fuel_usage_per_tick = initial(fuel_usage_per_tick) * 0.1
+ generation_rate = initial(generation_rate) * 0.2
+ to_chat(user, SPAN_NOTICE("You switch \the [src]'s power production mode to ECO. 10% Fuel usage, 20% power output."))
+ /// Default
+ if(1)
+ mode = 2
+ fuel_usage_per_tick = initial(fuel_usage_per_tick)
+ generation_rate = initial(generation_rate)
+ to_chat(user, SPAN_NOTICE("You switch \the [src]'s power production mode to NORMAL. 100% Fuel usage, 100% power output."))
+ /// Turbo mode, 2x fuel usage at 1.6x power output
+ if(2)
+ mode = 3
+ fuel_usage_per_tick = initial(fuel_usage_per_tick) * 2
+ generation_rate = initial(generation_rate) * 1.6
+ to_chat(user, SPAN_NOTICE("You switch \the [src]'s power production mode to TURBO. 200% Fuel usage, 160% power output."))
+ /// back to eco.
+ if(3)
+ mode = 0
+ fuel_usage_per_tick = initial(fuel_usage_per_tick)
+ generation_rate = initial(generation_rate)
+ to_chat(user, SPAN_NOTICE("You switch \the [src]'s power production mode to OFF. 0% Fuel usage, 0% power output."))
+ update_icon()
+
+
+/obj/item/mech_equipment/power_generator/fueled/get_hardpoint_maptext()
+ return "[fuel_amount]/[fuel_max] - [fuel_usage_per_tick]"
+
+/obj/item/mech_equipment/power_generator/fueled/plasma
+ name = "plasma powered mech-mountable power generator"
+ desc = "a plasma-fueled mech power generator, creates 5 KW out of 1 sheet of plasma at a rate of 0.25 KW. Fully stocked it generates 35 KW in total."
+ icon_state = "mech_generator_plasma"
+ spawn_frequency = 80
+ spawn_blacklisted = FALSE
+ spawn_tags = SPAWN_MECH_QUIPMENT
+ matter = list(MATERIAL_PLASTEEL = 15, MATERIAL_GLASS = 6, MATERIAL_PLASTIC = 3, MATERIAL_SILVER = 2, MATERIAL_GOLD = 3, MATERIAL_STEEL = 4)
+ origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 5, TECH_POWER = 3)
+ generation_rate = 250
+ // each sheet is 5000 watts
+ fuel_usage_per_tick = 50
+ // 1 sheet = 1000 fuel
+ // 35000 max power out of a fully loaded generator
+ fuel_max = 7000
+
+/obj/item/mech_equipment/power_generator/fueled/plasma/attackby(obj/item/I, mob/living/user, params)
+ . = ..()
+ if(istype(I, /obj/item/stack/material/plasma))
+ var/obj/item/stack/material/plasma/stck = I
+ var/amount_to_use = round((fuel_max - fuel_amount)/1000)
+ amount_to_use = clamp(stck.amount, 0, amount_to_use)
+ if(amount_to_use && stck.use(amount_to_use))
+ fuel_amount += amount_to_use * 1000
+
+/obj/item/mech_equipment/power_generator/fueled/welding
+ name ="welding fuel powered mech-mountable power generator"
+ desc = "a mech mounted generator that runs off welding fuel, creates 1 KW out of 10 units of welding fuel, at a rate of 0.1 KW. Fully stocked it generates 20 KW in total."
+ icon_state = "mech_generator_welding"
+ spawn_frequency = 80
+ spawn_blacklisted = FALSE
+ spawn_tags = SPAWN_MECH_QUIPMENT
+ matter = list(MATERIAL_PLASTEEL = 10, MATERIAL_GLASS = 3, MATERIAL_PLASTIC = 3, MATERIAL_SILVER = 2, MATERIAL_STEEL = 4)
+ origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 3, TECH_POWER = 2)
+ generation_rate = 100
+ fuel_usage_per_tick = 1
+ reagent_flags = DRAINABLE | REFILLABLE
+ /// can generate 20000 power
+ fuel_max = 200
+ /// The "explosion" chamber , used for when the fuel is mixed with something else
+ var/datum/reagents/chamberReagent = null
+
+/obj/item/mech_equipment/power_generator/fueled/welding/Initialize()
+ . = ..()
+ // max volume
+ create_reagents(200)
+ chamberReagent = new(1, src)
+
+
+/obj/item/mech_equipment/power_generator/fueled/welding/attackby(obj/item/I, mob/living/user, params)
+ . = ..()
+ /// Only needed when we attack from outside
+ if(owner)
+ if(I.is_drainable() && I.reagents.total_volume)
+ to_chat(user, SPAN_NOTICE("You transfer 10 units of substance from \the [I] to \the [src]'s internal fuel storage."))
+ I.reagents.trans_to_holder(reagents, 10, 1, FALSE)
+ else if(I.reagents && I.reagent_flags & REFILLABLE && user.a_intent == I_GRAB)
+ to_chat(user, SPAN_NOTICE("You drain 10 units of substance from \the [src] to \the [I]."))
+ reagents.trans_to_holder(I.reagents, 10, 1, FALSE)
+ else
+ to_chat(user, SPAN_NOTICE("You need to be on GRAB intent to drain from \the [src]."))
+ else if(I.is_refillable() && reagents.total_volume && user.a_intent == I_GRAB)
+ return FALSE
+ else
+ to_chat(user, SPAN_NOTICE("You need to be on GRAB intent to drain from \the [src]."))
+
+
+/obj/item/mech_equipment/power_generator/fueled/welding/pretick()
+ // dont run if we aren't on
+ if(!mode)
+ sound_loop?.stop()
+ return
+ chamberReagent.clear_reagents()
+ reagents.trans_to_holder(chamberReagent, 1, 1, FALSE)
+ if(chamberReagent.has_reagent("fuel"))
+ var/fuel = chamberReagent.get_reagent_amount("fuel")
+ // for the future just add any other reagent here with + * explosion_power_multiplier
+ var/explosives = chamberReagent.get_reagent_amount("plasma") * 3
+ if(explosives > 0.5)
+ // if its full plasma if just fucking blows instantly
+ health -= maxHealth * (explosives / 3)
+ if(health < 1)
+ owner.remove_system(src, null, TRUE)
+ qdel(src)
+ return
+ // min needed for combustion
+ if(fuel > 0.25)
+ var/amountUsed = internal_cell?.give(generation_rate * fuel)
+ // refund if none of it gets turned into power for qol reasons (its never exact returnal due to float errors)
+ if(amountUsed < generation_rate * 0.1)
+ chamberReagent.trans_to_holder(reagents, 1, 1, FALSE)
+ if(fuel > fuel_usage_per_tick)
+ chamberReagent.trans_id_to(reagents, "fuel", chamberReagent.total_volume - fuel_usage_per_tick, TRUE)
+ if(internal_cell && owner)
+ var/obj/item/cell/batt = owner.get_cell(TRUE)
+ if(batt && batt != internal_cell)
+ batt.give(internal_cell.use(batt.maxcharge - batt.charge))
+ if(QDELETED(sound_loop))
+ sound_loop = new(_interval = 2 SECONDS, duration = 10 SECONDS, interval_variance = 0,
+ _source = owner, _soundin = 'sound/mechs/mech_generator.ogg' , _vol = 25 * mode, _vary = 0, _extrarange = mode * 3,
+ _falloff = 0, _is_global = FALSE, _use_pressure = TRUE)
+ else
+ // extend it artificially.
+ sound_loop.end_time = world.time + 10 SECONDS
+ sound_loop.vol = mode * 25
+ sound_loop.extrarange = mode * 3
+ fuel_amount = reagents.total_volume
+
+/obj/item/mech_equipment/towing_hook
+ name = "mounted towing hook"
+ desc = "A mech mounted towing hook, usually found in cars. Can hook to anything that isn't anchored down."
+ icon_state = "mech_tow"
+ restricted_hardpoints = list(HARDPOINT_BACK)
+ restricted_software = list(MECH_SOFTWARE_UTILITY)
+ origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 4)
+ spawn_frequency = 80
+ spawn_blacklisted = FALSE
+ spawn_tags = SPAWN_MECH_QUIPMENT
+ matter = list(MATERIAL_PLASTEEL = 10, MATERIAL_PLASTIC = 10)
+ var/atom/movable/currentlyTowing = null
+
+/obj/item/mech_equipment/towing_hook/installed(mob/living/exosuit/_owner, hardpoint)
+ . = ..()
+ RegisterSignal(_owner, COMSIG_MOVABLE_MOVED, PROC_REF(onMechMove))
+
+/obj/item/mech_equipment/towing_hook/uninstalled()
+ UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
+ if(currentlyTowing)
+ UnregisterSignal(currentlyTowing, list(COMSIG_MOVABLE_MOVED,COMSIG_ATTEMPT_PULLING))
+ currentlyTowing = null
+ . = ..()
+
+/// Yes you can hook onto other mechs that are hooked onto you, and yes it won't break anything , SPCR-2023
+/obj/item/mech_equipment/towing_hook/proc/onTowingMove(atom/movable/mover, atom/oldLocation, atom/newLocation)
+ SIGNAL_HANDLER
+ if(newLocation.Adjacent(src))
+ return
+ UnregisterSignal(mover, list(COMSIG_MOVABLE_MOVED,COMSIG_ATTEMPT_PULLING))
+ to_chat(owner.get_mob(), SPAN_NOTICE("You lose your hook ono \the [currentlyTowing]!"))
+ currentlyTowing = null
+
+/obj/item/mech_equipment/towing_hook/proc/onMechMove(atom/movable/mover, atom/oldLocation, atom/newLocation)
+ SIGNAL_HANDLER
+ if(!currentlyTowing)
+ return
+ if(!oldLocation.Adjacent(currentlyTowing))
+ UnregisterSignal(currentlyTowing, list(COMSIG_MOVABLE_MOVED,COMSIG_ATTEMPT_PULLING))
+ to_chat(owner.get_mob(), SPAN_NOTICE("You lose your hook ono \the [currentlyTowing]!"))
+ currentlyTowing = null
+ return
+ // Protection against move loops caused by 2 mechs towing eachother.
+ if(COMSIG_PULL_CANCEL == SEND_SIGNAL(src, COMSIG_ATTEMPT_PULLING))
+ UnregisterSignal(currentlyTowing, list(COMSIG_MOVABLE_MOVED,COMSIG_ATTEMPT_PULLING))
+ to_chat(owner.get_mob(), SPAN_NOTICE("You lose your hook ono \the [currentlyTowing]!"))
+ currentlyTowing = null
+ return
+ // If we move up a z-level we want to instantly pull it up with us as to prevent possible abuse.
+ // Protection against move loops caused by 2 mechs towing eachother walking diagonally
+ // this gets triggered by z-moves too ,so inbuilt check for that
+ /*
+ if(lastMove > world.time - 0.1 SECONDS)
+ UnregisterSignal(currentlyTowing, list(COMSIG_MOVABLE_MOVED,COMSIG_ATTEMPT_PULLING))
+ currentlyTowing = null
+ return
+ */
+ if(oldLocation == newLocation)
+ return
+ if(oldLocation.z != newLocation.z)
+ currentlyTowing.forceMove(newLocation)
+ else
+ currentlyTowing.Move(oldLocation)
+
+/// We get supreme priority on pulling
+/obj/item/mech_equipment/towing_hook/proc/onTowingPullAttempt()
+ SIGNAL_HANDLER
+ return COMSIG_PULL_CANCEL
+
+/obj/item/mech_equipment/towing_hook/afterattack(atom/movable/target, mob/living/user, inrange, params)
+ . = ..()
+ if(!owner || target == owner)
+ return
+ if(!istype(target))
+ to_chat(user, SPAN_NOTICE("You cannot hook onto this!"))
+ return
+ if(!currentlyTowing)
+ if(target.Adjacent(src.owner) && !target.anchored)
+ to_chat(user, SPAN_NOTICE("You hook \the [src] onto \the [target]!"))
+ currentlyTowing = target
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(onTowingMove))
+ RegisterSignal(target, COMSIG_ATTEMPT_PULLING, PROC_REF(onTowingPullAttempt))
+ else if(currentlyTowing == target)
+ to_chat(user, SPAN_NOTICE("You unhook \the [src] from \the [target]."))
+ UnregisterSignal(currentlyTowing,list(COMSIG_MOVABLE_MOVED,COMSIG_ATTEMPT_PULLING))
+ currentlyTowing = null
+ else
+ to_chat(user, SPAN_NOTICE("You are already towing \the [currentlyTowing]. Unhook from it first by attacking it again!"))
+
+/obj/item/mech_equipment/mounted_system/toolkit
+ name = "mounted toolkit"
+ desc = "A automatic suite of tools suited for installation on a mech."
+ icon_state = "mech_tools"
+ holding_type = /obj/item/tool/mech_kit
+ restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
+ restricted_software = list(MECH_SOFTWARE_UTILITY)
+ origin_tech = list(TECH_ENGINEERING = 5, TECH_MAGNET = 2, TECH_MATERIAL = 2)
+ matter = list(MATERIAL_PLASTEEL = 25, MATERIAL_PLASTIC = 10, MATERIAL_SILVER = 5)
+ spawn_frequency = 80
+ spawn_blacklisted = FALSE
+ spawn_tags = SPAWN_MECH_QUIPMENT
+
+/obj/item/tool/mech_kit
+ name = "mech toolkit"
+ desc = "A robust selection of mech-sized tools."
+ icon_state = "engimplant"
+ force = WEAPON_FORCE_DANGEROUS
+ worksound = WORKSOUND_DRIVER_TOOL
+ flags = CONDUCT
+ tool_qualities = list(
+ QUALITY_SCREW_DRIVING = 70,
+ QUALITY_BOLT_TURNING = 70,
+ QUALITY_DRILLING = 10,
+ QUALITY_WELDING = 100,
+ QUALITY_CAUTERIZING = 5,
+ QUALITY_PRYING = 100,
+ QUALITY_DIGGING = 50,
+ QUALITY_PULSING = 50,
+ QUALITY_WIRE_CUTTING = 100,
+ QUALITY_HAMMERING = 75)
+ degradation = 0
+ workspeed = 1
+ max_upgrades = 1
+ spawn_blacklisted = TRUE
+
+/// Fancy way to move someone up a z-level if you think about it..
+/obj/item/mech_equipment/forklifting_system
+ name = "forklifting bars"
+ desc = "a set of forklifts bars. Can be used to elevate crates above by a level.. or people! You can forklift a z-level up by attacking this with itself."
+ icon_state = "forklift"
+ restricted_hardpoints = list(HARDPOINT_FRONT)
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_MATERIAL = 2)
+ spawn_frequency = 80
+ spawn_blacklisted = FALSE
+ spawn_tags = SPAWN_MECH_QUIPMENT
+ matter = list(MATERIAL_PLASTEEL = 15, MATERIAL_STEEL = 15, MATERIAL_PLASTIC = 10)
+ equipment_flags = EQUIPFLAG_UPDTMOVE
+ var/atom/movable/currentlyLifting = null
+ var/obj/structure/forklift_platform/platform = null
+ var/lastZ = null
+ var/lastDir = null
+ var/lifted = FALSE
+
+/obj/item/mech_equipment/forklifting_system/Initialize()
+ . = ..()
+ platform = new(NULLSPACE)
+ platform.master = src
+ platform.forceMove(src)
+
+/obj/item/mech_equipment/forklifting_system/Destroy()
+ if(currentlyLifting)
+ ejectLifting(get_turf(src))
+ if(platform)
+ QDEL_NULL(platform)
+ . = ..()
+
+
+/obj/item/mech_equipment/forklifting_system/proc/ejectLifting(atom/target)
+ currentlyLifting.forceMove(target)
+ currentlyLifting.transform = null
+ currentlyLifting.pixel_x = initial(currentlyLifting.pixel_x)
+ currentlyLifting.pixel_y = initial(currentlyLifting.pixel_y)
+ currentlyLifting.mouse_opacity = initial(currentlyLifting.mouse_opacity)
+ owner.vis_contents.Remove(currentlyLifting)
+ var/mob/targ = currentlyLifting
+ targ.update_icon()
+ targ.update_plane()
+ targ.layer = initial(targ.layer)
+ if(ismob(targ) && targ.client)
+ targ.client.perspective = MOB_PERSPECTIVE
+ targ.client.eye = src
+ currentlyLifting = null
+ update_icon()
+
+/obj/item/mech_equipment/forklifting_system/proc/startLifting(atom/movable/target)
+ currentlyLifting = target
+ // No clicking this whilst lifted
+ currentlyLifting.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ currentlyLifting.forceMove(src)
+ var/mob/targ = currentlyLifting
+ if(ismob(targ) && targ.client)
+ targ.client.perspective = EYE_PERSPECTIVE
+ targ.client.eye = src
+ to_chat(targ, SPAN_DANGER("You can resist out of the forklift to instantly get out!"))
+ update_icon()
+
+/obj/item/mech_equipment/forklifting_system/uninstalled()
+ . = ..()
+ if(currentlyLifting)
+ ejectLifting(get_turf(owner))
+
+
+/obj/structure/forklift_platform
+ layer = TURF_LAYER + 0.5
+ icon = MECH_EQUIPMENT_ICON
+ icon_state = "forklift_platform"
+ name = "forklift platform"
+ desc = "A fun way to reach new horizons. Mind the gap between.. small things fall through."
+ density = FALSE
+ anchored = TRUE
+ health = 200
+ var/obj/item/mech_equipment/forklifting_system/master = null
+
+/obj/structure/forklift_platform/Destroy()
+ if(master)
+ master.platform = null
+ master.update_icon()
+ if(master.owner)
+ master.owner.update_icon()
+ master = null
+ . = ..()
+
+/// We can prevent the fall of humans and structures , but everything else will just fall down
+/obj/structure/forklift_platform/can_prevent_fall(above, atom/movable/mover)
+ if(!above)
+ if(ishuman(mover))
+ return TRUE
+ if(isstructure(mover))
+ return TRUE
+ return FALSE
+
+
+/obj/item/mech_equipment/forklifting_system/update_icon()
+ . = ..()
+ if(owner)
+ if(!platform)
+ icon_state = "forklift_platformless"
+ return
+ if(lifted)
+ icon_state = "forklift_lifted"
+ else
+ icon_state = "forklift"
+ if(currentlyLifting)
+ if(!locate(currentlyLifting) in owner.vis_contents)
+ owner.vis_contents.Add(currentlyLifting)
+ if(owner.dir != lastDir)
+ lastDir = owner.dir
+ currentlyLifting.dir = owner.dir
+ if(lastDir == NORTH)
+ currentlyLifting.pixel_x = 8
+ currentlyLifting.pixel_y = 10
+ currentlyLifting.layer = MECH_UNDER_LAYER
+ if(lastDir == EAST)
+ currentlyLifting.pixel_x = 33
+ currentlyLifting.pixel_y = 8
+ currentlyLifting.layer = MECH_ABOVE_LAYER
+ if(lastDir == SOUTH)
+ currentlyLifting.pixel_x = 8
+ currentlyLifting.pixel_y = 0
+ currentlyLifting.layer = MECH_ABOVE_LAYER
+ if(lastDir == WEST)
+ currentlyLifting.pixel_x = -15
+ currentlyLifting.pixel_y = 8
+ currentlyLifting.layer = MECH_ABOVE_LAYER
+ if(owner.z != lastZ)
+ lastZ = owner.z
+ currentlyLifting.z = owner.z
+ currentlyLifting.update_plane()
+
+/obj/item/mech_equipment/forklifting_system/attack_self(mob/user)
+ . = ..()
+ if(!owner)
+ return
+ if(platform)
+ if(!lifted)
+ /// We are checking the turf above first
+ var/turf/aboveSpace = GetAbove(get_turf(owner))
+ if(!aboveSpace)
+ to_chat(user, SPAN_NOTICE("The universe runs out of fabric here! You cannot possibly elevate something here."))
+ return
+ if(!istype(aboveSpace, /turf/simulated/open) || locate(/obj/structure/catwalk) in aboveSpace)
+ to_chat(user, SPAN_NOTICE("Something dense prevents lifting up."))
+ return
+ /// Then the one infront + above
+ aboveSpace = get_step(owner, owner.dir)
+ aboveSpace = GetAbove(aboveSpace)
+ if(!aboveSpace)
+ to_chat(user, SPAN_NOTICE("The universe runs out of fabric here! You cannot possibly elevate something here."))
+ return
+ if(!istype(aboveSpace, /turf/simulated/open) || locate(/obj/structure/catwalk) in aboveSpace)
+ to_chat(user, SPAN_NOTICE("Something dense prevents lifting up."))
+ return
+ to_chat(user, SPAN_NOTICE("You start elevating \the [src] platform."))
+ if(do_after(user, 2 SECONDS, owner, TRUE))
+ to_chat(user, SPAN_NOTICE("You elevate \the [src]'s platform"))
+ platform.dir = owner.dir
+ platform.forceMove(aboveSpace)
+ owner.update_icon()
+ if(currentlyLifting)
+ ejectLifting(aboveSpace)
+ lifted = TRUE
+ else
+ to_chat(user, SPAN_NOTICE("You start retracting the forklift!"))
+ var/turf/targ = get_turf(platform)
+ if(do_after(user, 2 SECONDS, owner, TRUE))
+ if(!platform)
+ return
+ to_chat(user, SPAN_NOTICE("You retract the forklift!"))
+ lifted = FALSE
+ platform.forceMove(src)
+ owner.update_icon()
+ var/atom/whoWeBringingBack
+ /// Pick up the first mob , else just get the last atom returned
+ for(var/atom/movable/A in targ)
+ if(A == platform)
+ continue
+ if(A.anchored)
+ continue
+ if(ismob(A))
+ whoWeBringingBack = A
+ break
+ whoWeBringingBack = A
+ if(whoWeBringingBack)
+ startLifting(whoWeBringingBack)
+ else
+ to_chat(user, SPAN_NOTICE("You can't lift without a platform!"))
+
+/obj/item/mech_equipment/forklifting_system/afterattack(atom/movable/target, mob/living/user, inrange, params)
+ . = ..()
+ if(.)
+ if(currentlyLifting && isturf(target) && inrange)
+ for(var/atom/A in target)
+ if(A.density)
+ to_chat(user, SPAN_NOTICE("[A] is taking up space, preventing you from dropping \the [currentlyLifting] here!"))
+ return
+ ejectLifting(target)
+ return
+ if(currentlyLifting)
+ to_chat(user, SPAN_NOTICE("You are already lifting something!"))
+ return
+ if(!platform)
+ to_chat(user, SPAN_NOTICE("There is no forklift platform to lift on! You should get it replaced"))
+ return
+ if(lifted)
+ to_chat(user, SPAN_NOTICE("You can't lift someone whilst the forklift is lifted!"))
+ return
+ if(inrange && istype(target))
+ if(target.anchored)
+ to_chat(user, SPAN_NOTICE("\The [target] is anchored!"))
+ return
+ if(ismob(target))
+ var/mob/living/trg = target
+ if(trg.mob_size >= MOB_HUGE)
+ to_chat(user, SPAN_NOTICE("\The [target] is far too big to fit on the forklift clamps!"))
+ return
+ to_chat(user, SPAN_NOTICE("You start lifting \the [target] onto the hooks."))
+ if(do_after(user, 2 SECONDS, target))
+ startLifting(target)
+ update_icon()
+
+
+
+
+
+
+
diff --git a/code/modules/mechs/interface/_mech_HUD.dm b/code/modules/mechs/interface/_mech_HUD.dm
index 37f919e9b92..fbaea551813 100644
--- a/code/modules/mechs/interface/_mech_HUD.dm
+++ b/code/modules/mechs/interface/_mech_HUD.dm
@@ -55,9 +55,13 @@
/mob/living/exosuit/proc/update_mech_hud_4(var/mob/living/M)
if(M.client && M != src && HUDneed.len)
if(M in pilots)
- for(var/i in HUDneed) if(HUDneed[i]) M.client.screen |= HUDneed[i]
+ for(var/i in HUDneed)
+ if(HUDneed[i])
+ M.client.screen |= HUDneed[i]
+ var/obj/screen/movable/exosuit/thing = HUDneed[i]
+ thing.update_icon()
M.reset_view(src)
else
for(var/i in HUDneed) if(HUDneed[i]) M.client.screen -= HUDneed[i]
M.update_hud()
- M.check_HUD()
\ No newline at end of file
+ M.check_HUD()
diff --git a/code/modules/mechs/interface/datum_HUD.dm b/code/modules/mechs/interface/datum_HUD.dm
index f6a6f3c1909..c22a0096cda 100644
--- a/code/modules/mechs/interface/datum_HUD.dm
+++ b/code/modules/mechs/interface/datum_HUD.dm
@@ -9,6 +9,7 @@
"mech radio" = list("type" = /obj/screen/movable/exosuit/radio, "loc" = "WEST:4,CENTER-3:58"),
"rename mech" = list("type" = /obj/screen/movable/exosuit/rename, "loc" = "WEST:4,CENTER-3:69"),
"mech camera" = list("type" = /obj/screen/movable/exosuit/toggle/camera, "loc" = "WEST:4,CENTER-3:80"),
+ "mech power switch" = list("type" = /obj/screen/movable/exosuit/toggle/power_control, "loc" = "WEST:4,CENTER-3:-12"),
"mech health" = list("type" = /obj/screen/movable/exosuit/health, "loc" = "WEST:4,BOTTOM+3"),
"mech power" = list("type" = /obj/screen/movable/exosuit/power, "loc" = "WEST+1:4,BOTTOM+3"),
"strafe" = list("type" = /obj/screen/movable/exosuit/toggle/strafe, "loc" = "WEST:4,CENTER-3:92"),
diff --git a/code/modules/mechs/interface/screen_objects.dm b/code/modules/mechs/interface/screen_objects.dm
index 48acaff5c43..ceffb89b6e6 100644
--- a/code/modules/mechs/interface/screen_objects.dm
+++ b/code/modules/mechs/interface/screen_objects.dm
@@ -1,8 +1,11 @@
// Screen objects hereon out.
+
+#define MECH_UI_STYLE(X) "" + X + ""
+
/obj/screen/movable/exosuit
name = "hardpoint"
icon = MECH_HUD_ICON
- icon_state = "hardpoint"
+ icon_state = "base"
var/mob/living/exosuit/owner
/obj/screen/movable/exosuit/proc/on_handle_hud(var/mob/living/exosuit/E)
@@ -13,7 +16,10 @@
/obj/screen/movable/exosuit/radio
name = "radio"
- icon_state = "radio"
+ //icon_state = "radio"
+ maptext = MECH_UI_STYLE("RADIO")
+ maptext_x = 5
+ maptext_y = 12
/obj/screen/movable/exosuit/radio/Click()
if(..())
@@ -22,6 +28,7 @@
/obj/screen/movable/exosuit/hardpoint
name = "hardpoint"
+ icon_state = "hardpoint"
desc = "To activate additional hardpoint's options click on it with shift-button."
var/hardpoint_tag
var/obj/item/holding
@@ -44,6 +51,7 @@
if(holding) holding.screen_loc = screen_loc
/obj/screen/movable/exosuit/hardpoint/proc/update_system_info()
+ maptext = null
// No point drawing it if we have no item to use or nobody to see it.
if(!holding || !owner)
@@ -120,7 +128,9 @@
var/modifiers = params2list(params)
if(modifiers["ctrl"])
if(owner.hardpoints_locked) to_chat(usr, SPAN_WARNING("Hardpoint ejection system is locked."))
- else if(owner.remove_system(hardpoint_tag)) to_chat(usr, SPAN_NOTICE("You disengage and discard the system mounted to your [hardpoint_tag] hardpoint."))
+ else if(owner.remove_system(hardpoint_tag))
+ update_system_info()
+ 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."))
else if(modifiers["shift"] && holding) holding.attack_self(usr)
else if(owner.selected_hardpoint == hardpoint_tag)
@@ -128,16 +138,38 @@
owner.clear_selected_hardpoint()
else if(owner.set_hardpoint(hardpoint_tag)) icon_state = "hardpoint_selected"
+
+/obj/screen/movable/exosuit/toggle/power_control
+ name = "Power control"
+ icon_state = "small_important"
+ maptext = MECH_UI_STYLE("POWER")
+ maptext_x = 3
+ maptext_y = 13
+
+/obj/screen/movable/exosuit/toggle/power_control/toggled()
+ . = ..()
+ owner.toggle_power(usr)
+
+/obj/screen/movable/exosuit/toggle/power_control/update_icon()
+ toggled = (owner.power == MECH_POWER_ON)
+ . = ..()
+
/obj/screen/movable/exosuit/eject
name = "eject"
- icon_state = "eject"
+ //icon_state = "eject"
+ maptext = MECH_UI_STYLE("EJECT")
+ maptext_x = 5
+ maptext_y = 12
/obj/screen/movable/exosuit/eject/Click()
if(..()) owner.eject(usr)
/obj/screen/movable/exosuit/rename
name = "rename"
- icon_state = "rename"
+ //icon_state = "rename"
+ maptext = MECH_UI_STYLE("RENAME")
+ maptext_x = 1
+ maptext_y = 12
/obj/screen/movable/exosuit/power
name = "power"
@@ -146,33 +178,46 @@
maptext_width = 64
maptext_x = 2
maptext_y = 20
- maptext = "power"
+
/obj/screen/movable/exosuit/power/on_handle_hud(var/mob/living/exosuit/E)
. = ..()
if(owner)
var/obj/item/cell/C = owner.get_cell()
- if(C && istype(C)) maptext = "[round(C.charge)]/[round(C.maxcharge)]"
- else maptext = "CHECK POWER"
+ if(C && istype(C)) maptext = MECH_UI_STYLE("[round(C.charge)]/[round(C.maxcharge)]")
+ else maptext = MECH_UI_STYLE("CHECK POWER")
/obj/screen/movable/exosuit/rename/Click()
if(..()) owner.rename(usr)
/obj/screen/movable/exosuit/toggle
name = "toggle"
- var/toggled
+ var/toggled = FALSE
+
+/obj/screen/movable/exosuit/toggle/LateInitialize()
+ . = ..()
+ update_icon()
+
+/obj/screen/movable/exosuit/toggle/update_icon()
+ . = ..()
+ icon_state = "[initial(icon_state)][toggled ? "_enabled" : ""]"
+ maptext = FONT_COLORED(toggled ? COLOR_WHITE : COLOR_GRAY, initial(maptext))
/obj/screen/movable/exosuit/toggle/Click()
if(..()) toggled()
/obj/screen/movable/exosuit/toggle/proc/toggled()
toggled = !toggled
- icon_state = "[initial(icon_state)][toggled ? "_enabled" : ""]"
+ update_icon()
return toggled
/obj/screen/movable/exosuit/toggle/air
name = "air"
- icon_state = "air"
+ //icon_state = "air"
+ icon_state = "small_important"
+ maptext = MECH_UI_STYLE("AIR")
+ maptext_x = 9
+ maptext_y = 13
/obj/screen/movable/exosuit/toggle/air/toggled()
owner.use_air = ..()
@@ -180,7 +225,11 @@
/obj/screen/movable/exosuit/toggle/maint
name = "toggle maintenance protocol"
- icon_state = "maint"
+ //icon_state = "maint"
+ icon_state = "small"
+ maptext = MECH_UI_STYLE("MAINT")
+ maptext_x = 5
+ maptext_y = 13
/obj/screen/movable/exosuit/toggle/maint/toggled()
owner.maintenance_protocols = ..()
@@ -188,7 +237,10 @@
/obj/screen/movable/exosuit/toggle/hardpoint
name = "toggle hardpoint lock"
- icon_state = "hardpoint_lock"
+ //icon_state = "hardpoint_lock"
+ maptext = MECH_UI_STYLE("GEAR")
+ maptext_x = 5
+ maptext_y = 12
/obj/screen/movable/exosuit/toggle/hardpoint/toggled()
owner.hardpoints_locked = ..()
@@ -196,7 +248,10 @@
/obj/screen/movable/exosuit/toggle/hatch
name = "toggle hatch lock"
- icon_state = "hatch_lock"
+ //icon_state = "hatch_lock"
+ maptext = MECH_UI_STYLE("LOCK")
+ maptext_x = 5
+ maptext_y = 12
/obj/screen/movable/exosuit/toggle/hatch/toggled()
if(!owner.hatch_locked && !owner.hatch_closed)
@@ -205,20 +260,39 @@
if(owner.body && owner.body.total_damage >= owner.body.max_damage)
to_chat(usr, SPAN_WARNING("\The body of [owner] is far too damaged to close its hatch!"))
return
- owner.hatch_locked = ..()
+ owner.hatch_locked = owner.toggle_hatch_lock()
to_chat(usr, SPAN_NOTICE("The [owner.body.hatch_descriptor] is [owner.hatch_locked ? "now" : "no longer" ] locked."))
+ update_icon()
+
+/obj/screen/movable/exosuit/toggle/hatch/update_icon()
+ toggled = owner.hatch_locked
+ . = ..()
/obj/screen/movable/exosuit/toggle/hatch_open
name = "open or close hatch"
- icon_state = "hatch_status"
+ //icon_state = "hatch_status"
+ maptext = MECH_UI_STYLE("CLOSE")
+ maptext_x = 4
+ maptext_y = 12
/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 = ..()
+ owner.hatch_closed = owner.toggle_hatch()
to_chat(usr, SPAN_NOTICE("The [owner.body.hatch_descriptor] is now [owner.hatch_closed ? "closed" : "open" ]."))
owner.update_icon()
+ update_icon()
+
+/obj/screen/movable/exosuit/toggle/hatch_open/update_icon()
+ toggled = owner.hatch_closed
+ . = ..()
+ if(toggled)
+ maptext = MECH_UI_STYLE("OPEN")
+ maptext_x = 5
+ else
+ maptext = MECH_UI_STYLE("CLOSE")
+ maptext_x = 4
// This is basically just a holder for the updates the exosuit does.
/obj/screen/movable/exosuit/health
@@ -268,7 +342,11 @@
//Controls if cameras set the vision flags
/obj/screen/movable/exosuit/toggle/camera
name = "toggle camera matrix"
- icon_state = "camera"
+ //icon_state = "camera"
+ icon_state = "small_important"
+ maptext = MECH_UI_STYLE("SENSOR")
+ maptext_x = 1
+ maptext_y = 13
/obj/screen/movable/exosuit/toggle/camera/toggled()
if(!owner.head)
@@ -277,8 +355,15 @@
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 = ..()
+ owner.head.active_sensors = owner.toggle_sensors()
to_chat(usr, SPAN_NOTICE("[owner.head.name] advanced sensor mode is [owner.head.active_sensors ? "now" : "no longer" ] active."))
+ update_icon()
+
+/obj/screen/movable/exosuit/toggle/camera/update_icon()
+ if(owner.head)
+ toggled = owner.head.active_sensors
+ else toggled = FALSE
+ . = ..()
/obj/screen/movable/exosuit/needle
vis_flags = VIS_INHERIT_ID
diff --git a/code/modules/mechs/mech.dm b/code/modules/mechs/mech.dm
index 8fa752ca3bf..8aca6d835c4 100644
--- a/code/modules/mechs/mech.dm
+++ b/code/modules/mechs/mech.dm
@@ -50,6 +50,8 @@
var/list/hardpoints = list()
var/hardpoints_locked
var/maintenance_protocols
+ /// For equipment that has a process based on mech Life tick
+ var/list/obj/item/mech_equipment/tickers = list()
// Material
var/material/material = MATERIAL_STEEL
@@ -73,6 +75,8 @@
var/obj/screen/movable/exosuit/toggle/power_control/hud_power_control
var/obj/screen/movable/exosuit/toggle/camera/hud_camera
+ var/power = MECH_POWER_OFF
+
// Strafing - Is the mech currently strafing?
var/strafing = FALSE
@@ -80,6 +84,26 @@
for(var/mob/i in pilots)
to_chat(i, msg)
+/mob/living/exosuit/proc/toggle_power(mob/living/user)
+ if(power == MECH_POWER_TRANSITION)
+ to_chat(user, SPAN_NOTICE("Power transition in progress. Please wait."))
+ else if(power == MECH_POWER_ON) //Turning it off is instant
+ playsound(src, 'sound/mechs/mech-shutdown.ogg', 100, 0)
+ power = MECH_POWER_OFF
+ else if(get_cell(TRUE))
+ //Start power up sequence
+ power = MECH_POWER_TRANSITION
+ playsound(src, 'sound/mechs/powerup.ogg', 50, 0)
+ if(do_after(user, 1.5 SECONDS, src) && power == MECH_POWER_TRANSITION)
+ playsound(src, 'sound/mechs/nominal.ogg', 50, 0)
+ power = MECH_POWER_ON
+ else
+ to_chat(user, SPAN_WARNING("You abort the powerup sequence."))
+ power = MECH_POWER_OFF
+ //hud_power_control?.queue_icon_update()
+ else
+ to_chat(user, SPAN_WARNING("Error: No power cell was detected."))
+
/*
@@ -138,6 +162,9 @@
if(head && head.radio)
radio = new(src)
+ if(body)
+ opacity = body.opaque_chassis
+
if(LAZYLEN(component_descriptions))
desc = "[desc] It has been built with [english_list(component_descriptions)]."
@@ -197,12 +224,30 @@
to_chat(user, "It menaces with reinforcements of [material].")
to_chat(user, SPAN_NOTICE("You can remove people inside by HARM intent clicking with your hand. The hatch must be opened."))
- to_chat(user, SPAN_NOTICE("You can insert ammo into any ballistic weapon by attacking this with ammunition"))
+ to_chat(user, SPAN_NOTICE("You can eject any module from its UI by CtrlClicking the hardpoint button."))
+ if(body.storage_compartment)
+ to_chat(user, SPAN_NOTICE("You can acces its internal storage by click-dragging onto your character."))
+ if(body && body.cell_charge_rate)
+ to_chat(user, SPAN_NOTICE("This mech can recharge any cell storaged in its internal storage at a rate of [body.cell_charge_rate]."))
+ if(arms && arms.can_force_doors)
+ to_chat(user, SPAN_NOTICE("The arms on this mech can force open any unbolted door."))
+ if(locate(/obj/item/mech_equipment/mounted_system/ballistic) in contents)
+ to_chat(user, SPAN_NOTICE("You can insert ammo into any ballistic weapon by attacking this with ammunition."))
+ if(locate(/obj/item/mech_equipment/auto_mender) in contents)
+ to_chat(user, SPAN_NOTICE("You can refill its auto mender by attacking the mech with trauma kits."))
+ if(locate(/obj/item/mech_equipment/forklifting_system) in contents)
+ to_chat(user, SPAN_NOTICE("You can remove objects from this mech's forklifting system by using grab intent."))
+ if(locate(/obj/item/mech_equipment/towing_hook) in contents)
+ to_chat(user, SPAN_NOTICE("You can remove objects from this mech's towing system by using grab intent."))
+ if(locate(/obj/item/mech_equipment/power_generator/fueled) in contents)
+ to_chat(user, SPAN_NOTICE("You can refill the mounted power generators by attacking \the [src] with the fuel they use."))
+ if(locate(/obj/item/mech_equipment/power_generator/fueled/welding) in contents)
+ to_chat(user, SPAN_NOTICE("You can drain from the mounted fuel welding fuel generator by attacking with a beaker on GRAB intent"))
/mob/living/exosuit/return_air()
if(src && loc)
- if(ispath(body) || !hatch_closed)
+ if(ispath(body) || !hatch_closed || body.pilot_coverage < 100)
var/turf/current_loc = get_turf(src)
return current_loc.return_air()
if(body.pilot_coverage >= 100 && hatch_closed)
@@ -212,14 +257,6 @@
/mob/living/exosuit/GetIdCard()
return access_card
-/mob/living/exosuit/set_dir()
- . = ..()
- if(.)
- update_pilots()
- for(var/obj/item/mech_equipment/shield_generator/gen in contents)
- if(gen && gen.visual_bluff)
- gen.updateVisualBluff(dir)
-
/mob/living/exosuit/proc/return_temperature()
return bodytemperature
diff --git a/code/modules/mechs/mech_construction.dm b/code/modules/mechs/mech_construction.dm
index 900a9690d75..600780d1690 100644
--- a/code/modules/mechs/mech_construction.dm
+++ b/code/modules/mechs/mech_construction.dm
@@ -128,9 +128,10 @@
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 0
- hardpoints[system_hardpoint] = null
-
if(system_hardpoint == selected_hardpoint) clear_selected_hardpoint()
+ hardpoints[system_hardpoint] = null
+ // Remove this from screens. Would just be left on a player screen before , SPCR - 2023
+ system.screen_loc = null
var/obj/item/mech_equipment/ME = system
if(istype(ME)) ME.uninstalled()
diff --git a/code/modules/mechs/mech_damage.dm b/code/modules/mechs/mech_damage.dm
index 4ac5b950af8..dfb30a50d74 100644
--- a/code/modules/mechs/mech_damage.dm
+++ b/code/modules/mechs/mech_damage.dm
@@ -83,7 +83,12 @@
/mob/living/exosuit/adjustFireLoss(amount, obj/item/mech_component/MC = null)
if(!MC)
- MC = pick(list(arms, legs, body, head))
+ var/list/picklist = list()
+ if(arms) picklist.Add(arms)
+ if(legs) picklist.Add(legs)
+ if(head) picklist.Add(head)
+ if(body) picklist.Add(body)
+ MC = pick(picklist)
if(amount < 1)
return FALSE
MC.take_burn_damage(amount)
@@ -91,7 +96,12 @@
/mob/living/exosuit/adjustBruteLoss(amount, obj/item/mech_component/MC = null)
if(!MC)
- MC = pick(list(arms, legs, body, head))
+ var/list/picklist = list()
+ if(arms) picklist.Add(arms)
+ if(legs) picklist.Add(legs)
+ if(head) picklist.Add(head)
+ if(body) picklist.Add(body)
+ MC = pick(picklist)
if(amount < 1)
return FALSE
MC.take_brute_damage(amount)
@@ -124,6 +134,9 @@
/mob/living/exosuit/bullet_act(obj/item/projectile/P, var/def_zone)
var/hit_dir = get_dir(P.starting, src)
def_zone = zoneToComponent(def_zone)
+ /// aiming for soemthing the mech doesnt have
+ if(!def_zone)
+ return PROJECTILE_FORCE_MISS
if (P.is_hot() >= HEAT_MOBIGNITE_THRESHOLD)
IgniteMob()
diff --git a/code/modules/mechs/mech_interaction.dm b/code/modules/mechs/mech_interaction.dm
index aee217d58ed..fee206731ea 100644
--- a/code/modules/mechs/mech_interaction.dm
+++ b/code/modules/mechs/mech_interaction.dm
@@ -49,15 +49,19 @@
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
+ if(!selected_system)
+ if(arms)
+ if(!get_cell()?.checked_use(arms.power_use * CELLRATE))
+ to_chat(user, power == MECH_POWER_ON ? SPAN_WARNING("Error: Power levels insufficient.") : SPAN_WARNING("\The [src] is powered off."))
+ 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
+ else
+ to_chat(user, SPAN_WARNING("\The [src] has no manipulators!"))
+ setClickCooldown(3)
+ return
var/obj/item/cell/cell = get_cell()
if(!cell)
@@ -65,11 +69,6 @@
setClickCooldown(3)
return
- if(!cell.checked_use(arms.power_use * CELLRATE))
- to_chat(user, SPAN_WARNING("Error: Power levels insufficient."))
- setClickCooldown(3)
- return
-
if(istype(selected_system, /obj/item/mech_equipment) && !check_equipment_software(selected_system))
to_chat(user, SPAN_WARNING("Error: No control software was found for [selected_system]."))
setClickCooldown(3)
@@ -110,7 +109,7 @@
var/resolved
if(adj)
- resolved = selected_system.resolve_attackby(A, src, params)
+ resolved = selected_system.resolve_attackby(A, user, params)
if(!resolved && A && selected_system)
selected_system.afterattack(A,user,adj,params)
@@ -124,11 +123,23 @@
if(A == src)
setClickCooldown(5)
return attack_self(user)
- else if(adj)
+ else if(adj && arms)
setClickCooldown(arms_action_delay())
playsound(src.loc, arms.punch_sound, 45 + 25 * (arms.melee_damage / 50), -1)
- if(arms)
+ if(user.a_intent == I_HURT)
return A.attack_generic(src, arms.melee_damage, "attacked")
+ else if(user.a_intent == I_DISARM && arms.can_force_doors)
+ if(istype(A, /obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/door = A
+ if(!door.locked)
+ to_chat(user, SPAN_NOTICE("You start forcing \the [door] open!"))
+ visible_message(SPAN_WARNING("\The [src] starts forcing \the [door] open!"))
+ playsound(src, 'sound/machines/airlock_creaking.ogg', 100, 1, 5,5)
+ if(do_after(user, 3 SECONDS, A, FALSE))
+ door.open(TRUE)
+ return
+ else
+ return A.attackby(arms, user, params)
/// Checks the mech for places to store the ore.
/mob/living/exosuit/proc/getOreCarrier()
@@ -266,6 +277,7 @@
to_chat(user, SPAN_WARNING("\The [I] could not be installed in that hardpoint."))
return
+ /// Gun reloading handling
if(istype(I, /obj/item/ammo_magazine)|| istype(I, /obj/item/ammo_casing))
if(!maintenance_protocols)
to_chat(user, SPAN_NOTICE("\The [src] needs to be in maintenance mode to reload its guns!"))
@@ -281,21 +293,87 @@
chosen = loadable_guns[chosen]
else
chosen = loadable_guns[loadable_guns[1]]
- switch(chosen.loadMagazine(I,user))
- if(-1)
- to_chat(user, SPAN_NOTICE("\The [chosen] does not accept this type of magazine."))
- if(0)
- to_chat(user, SPAN_NOTICE("\The [chosen] has no slots left in its ammunition storage."))
- if(1)
- to_chat(user, SPAN_NOTICE("You load \the [I] into \the [chosen]."))
- if(2)
- to_chat(user, SPAN_NOTICE("You partially reload one of the existing ammo magazines inside of \the [chosen]."))
-
- else if(user.a_intent != I_HURT)
+ if(chosen)
+ switch(chosen.loadMagazine(I,user))
+ if(-1)
+ to_chat(user, SPAN_NOTICE("\The [chosen] does not accept this type of magazine."))
+ if(0)
+ to_chat(user, SPAN_NOTICE("\The [chosen] has no slots left in its ammunition storage."))
+ if(1)
+ to_chat(user, SPAN_NOTICE("You load \the [I] into \the [chosen]."))
+ if(2)
+ to_chat(user, SPAN_NOTICE("You partially reload one of the existing ammo magazines inside of \the [chosen]."))
+
+ /// Medical mender handling
+ if(istype(I, /obj/item/stack/medical/advanced/bruise_pack))
+ var/list/choices = list()
+ for(var/hardpoint in hardpoints)
+ if(istype(hardpoints[hardpoint], /obj/item/mech_equipment/auto_mender))
+ var/obj/item/mech_equipment/auto_mender/mend = hardpoints[hardpoint]
+ choices["[hardpoint] - [mend.trauma_charges_stored]/[mend.trauma_storage_max] charges"] = mend
+ var/obj/item/mech_equipment/auto_mender/choice = null
+ if(!length(choices))
+ return
+ if(length(choices) == 1)
+ choice = choices[choices[1]]
+ else
+ var/chosenMender = input("Select mech mender to refill") as null|anything in choices
+ if(chosenMender)
+ choice = choices[chosenMender]
+ if(choice)
+ choice.attackby(I, user)
+ return
+
+ /// Plasma generator handling
+ if(istype(I, /obj/item/stack/material/plasma))
+ var/list/choices = list()
+ for(var/hardpoint in hardpoints)
+ if(istype(hardpoints[hardpoint], /obj/item/mech_equipment/power_generator/fueled/plasma))
+ var/obj/item/mech_equipment/power_generator/fueled/plasma/gen = hardpoints[hardpoint]
+ choices["[hardpoint] - [gen.fuel_amount]/[gen.fuel_max]"] = gen
+ var/obj/item/mech_equipment/power_generator/fueled/plasma/chosen = null
+ if(!length(choices))
+ return
+ if(length(choices)==1)
+ chosen = choices[choices[1]]
+ else
+ var/chosenGen = input("Select generator to refill") as null|anything in choices
+ if(chosenGen)
+ chosen = choices[chosenGen]
+ if(chosen)
+ chosen.attackby(I, user)
+ return
+
+ /// Welding generator handling
+ /// Double negation to turn into 0/1 format since if its more than 1 it doesn't count as true.
+ if(I.is_drainable())
+ if(!maintenance_protocols)
+ to_chat(user, SPAN_NOTICE("\The [src] needs to be in maintenance mode for you to refill its internal generator!"))
+ return
+ var/list/choices = list()
+ for(var/hardpoint in hardpoints)
+ if(istype(hardpoints[hardpoint], /obj/item/mech_equipment/power_generator/fueled/welding))
+ var/obj/item/mech_equipment/power_generator/fueled/welding/gen = hardpoints[hardpoint]
+ choices["[hardpoint] - [gen.fuel_amount]/[gen.fuel_max]"] = gen
+ var/obj/item/mech_equipment/power_generator/fueled/welding/chosen = null
+ if(!length(choices))
+ return
+ if(length(choices)==1)
+ chosen = choices[choices[1]]
+ else
+ var/chosenGen = input("Select generator to refill") as null|anything in choices
+ if(chosenGen)
+ chosen = choices[chosenGen]
+ if(chosen)
+ chosen.attackby(I, user)
+ return
+
+
+ else if(user.a_intent != I_HELP)
if(attack_tool(I, user))
return
// we use BP_CHEST cause we dont need to convert targeted organ to mech format def zoning
- else if(user.a_intent == I_HURT && !hatch_closed && get_dir(user, src) == reverse_dir[dir] && get_mob() && !(user in pilots) && user.targeted_organ == BP_CHEST)
+ else if(user.a_intent != I_HELP && !hatch_closed && get_dir(user, src) == reverse_dir[dir] && get_mob() && !(user in pilots) && user.targeted_organ == BP_CHEST)
var/mob/living/target = get_mob()
target.attackby(I, user)
return
@@ -306,8 +384,10 @@
if(!maintenance_protocols)
to_chat(user, SPAN_WARNING("The power cell bay is locked while maintenance protocols are disabled."))
return TRUE
-
- var/obj/item/cell/cell = get_cell()
+ if(!body)
+ to_chat(user, SPAN_NOTICE("\The [src] has no slot for a battery to be installed unto!"))
+ return
+ var/obj/item/cell/cell = body.cell
if(cell)
to_chat(user, SPAN_WARNING("\The [src] already has [cell] installed!"))
return TRUE
@@ -445,13 +525,17 @@
if(QUALITY_PRYING)
- var/obj/item/cell/cell = get_cell()
+ if(!body)
+ to_chat(user, SPAN_NOTICE("\The [src] has no body to pry out a cell from!"))
+ return
+ var/obj/item/cell/cell = body.cell
if(cell)
if(!maintenance_protocols)
to_chat(user, SPAN_WARNING("The power cell bay is locked while maintenance protocols are disabled."))
return TRUE
to_chat(user, SPAN_NOTICE("You start removing [cell] from \the [src]."))
if(do_mob(user, src, 30) && cell == body.cell && body.eject_item(cell, user))
+ power = MECH_POWER_OFF
body.cell = null
return
@@ -469,9 +553,56 @@
if(ABORT_CHECK)
return
+/// Used by hatch lock UI button
+/mob/living/exosuit/proc/toggle_hatch_lock()
+ if(hatch_locked)
+ hatch_locked = FALSE
+ else
+ if(body && body.total_damage >= body.max_damage)
+ return FALSE
+ hatch_locked = TRUE
+ return hatch_locked
+
+/// Used by hatch toggle mech UI button
+/mob/living/exosuit/proc/toggle_hatch()
+ if(hatch_locked)
+ return hatch_closed
+ else
+ hatch_closed = !hatch_closed
+ return hatch_closed
+
+/// Used by camera toglge UI button
+/mob/living/exosuit/proc/toggle_sensors()
+ if(head)
+ if(!head.active_sensors)
+ if(get_cell().drain_power(0,0,head.power_use))
+ head.active_sensors = TRUE
+ return TRUE
+ return FALSE
+ else
+ head.active_sensors = FALSE
+ return FALSE
+ return FALSE
/mob/living/exosuit/attack_hand(mob/living/user)
// Drag the pilot out if possible.
+ if(user.a_intent == I_GRAB)
+ for(var/obj/item/mech_equipment/towing_hook/towing in contents)
+ if(towing.currentlyTowing)
+ to_chat(user, SPAN_NOTICE("You start removing \the [towing.currentlyTowing] from \the [src]'s towing hook."))
+ if(do_after(user, 3 SECONDS, src, TRUE))
+ to_chat(user, SPAN_NOTICE("You remove \the [towing.currentlyTowing] from \the [src]'s towing hook."))
+ towing.UnregisterSignal(towing.currentlyTowing,list(COMSIG_MOVABLE_MOVED,COMSIG_ATTEMPT_PULLING))
+ towing.currentlyTowing = null
+ return
+ for(var/obj/item/mech_equipment/forklifting_system/fork in contents)
+ if(fork.currentlyLifting)
+ to_chat(user, SPAN_NOTICE("You start removing \the [fork.currentlyLifting] from \the [src]'s forklift."))
+ if(do_after(user, 3 SECONDS ,src , TRUE))
+ to_chat(user, SPAN_NOTICE("You remove \the [fork.currentlyLifting] from \the [src]'s forklift!"))
+ fork.ejectLifting(get_turf(user))
+ return
+
if(user.a_intent == I_HURT)
if(!LAZYLEN(pilots))
to_chat(user, SPAN_WARNING("There is nobody inside \the [src]."))
diff --git a/code/modules/mechs/mech_life.dm b/code/modules/mechs/mech_life.dm
index 74cb759ce44..4354609b478 100644
--- a/code/modules/mechs/mech_life.dm
+++ b/code/modules/mechs/mech_life.dm
@@ -13,23 +13,64 @@
update_pilots()
if(radio)
- radio.on = (head && head.radio && head.radio.is_functional())
+ radio.on = (head && head.radio && head.radio.is_functional() && get_cell())
- body.update_air(hatch_closed && use_air)
+ var/powered = FALSE
+ var/obj/item/cell/mech_cell = get_cell()
+ for(var/hardpoint in hardpoints)
+ var/obj/item/mech_equipment/equip = hardpoints[hardpoint]
+ if(QDELETED(equip))
+ continue
+ if(!(equip.equipment_flags & EQUIPFLAG_PRETICK))
+ continue
+ equip.pretick()
+
+ if(mech_cell)
+ powered = mech_cell.drain_power(0, 0, calc_power_draw()) > 0
+
+ if(!powered)
+ //Shut down all systems
+ if(head)
+ head.active_sensors = FALSE
+ for(var/hardpoint in hardpoints)
+ var/obj/item/mech_equipment/M = hardpoints[hardpoint]
+ if(istype(M) && M.active && M.passive_power_use)
+ M.deactivate()
+ // for chassis charging cells
+ var/chargeUsed = 0
+ if(powered && body && body.cell_charge_rate && mech_cell.charge > 1000)
+ for(var/obj/item/cell/to_charge in body.storage_compartment)
+ if(mech_cell.charge < 1000)
+ break
+ if(chargeUsed > body.cell_charge_rate)
+ break
+ var/chargeNeeded = min(to_charge.maxcharge - to_charge.charge, body.cell_charge_rate)
+ if(!chargeNeeded)
+ continue
+ chargeUsed += to_charge.give(mech_cell.drain_power(0,0, chargeNeeded / CELLRATE))
- if((client || LAZYLEN(pilots)) && get_cell())
- var/obj/item/cell/c = get_cell()
- c.drain_power(0, 0, calc_power_draw())
+
+ body.update_air(hatch_closed && use_air)
updatehealth()
if(health <= 0 && stat != DEAD)
death()
. = ..() //Handles stuff like environment
lying = FALSE // Fuck off, carp.
- handle_vision()
+ handle_vision(powered)
+
+/mob/living/exosuit/get_cell(force)
+ RETURN_TYPE(/obj/item/cell)
+
+ if(power == MECH_POWER_ON || force) //For most intents we can assume that a powered off exosuit acts as if it lacked a cell
+ . = body ? body.cell : null
+ if(!.)
+ for(var/obj/item/mech_equipment/power_generator/gen in tickers)
+ if(!. && gen.internal_cell)
+ . = gen.internal_cell
+ return .
+ return null
-/mob/living/exosuit/get_cell()
- return body?.get_cell()
/mob/living/exosuit/proc/calc_power_draw()
//Passive power stuff here. You can also recharge cells or hardpoints if those make sense
@@ -38,7 +79,8 @@
var/obj/item/mech_equipment/I = hardpoints[hardpoint]
if(!istype(I))
continue
- total_draw += I.passive_power_use
+
+ total_draw += I.active ? I.active_power_use : I.passive_power_use
if(head && head.active_sensors)
total_draw += head.power_use
@@ -77,14 +119,18 @@
var/obj/wreck = new wreckage_path(drop_location(), 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
+ if(arms)
+ if(arms.loc != src)
+ arms = null
+ if(legs)
+ if(legs.loc != src)
+ legs = null
+ if(head)
+ if(head.loc != src)
+ head = null
+ if(body)
+ if(body.loc != src)
+ body = null
// Handle the rest of things.
..(gibbed, (gibbed ? "explodes!" : "grinds to a halt before collapsing!"))
@@ -116,10 +162,12 @@
qdel(src)
return
-/mob/living/exosuit/handle_vision()
+/mob/living/exosuit/handle_vision(powered)
if(head)
- sight = head.get_sight()
- see_invisible = head.get_invisible()
+ sight = head.get_sight(powered)
+ see_invisible = head.get_invisible(powered)
+ else if(hatch_closed)
+ sight &= BLIND
if(body && (body.pilot_coverage < 100 || body.transparent_cabin) || !hatch_closed)
sight &= ~BLIND
diff --git a/code/modules/mechs/mech_movement.dm b/code/modules/mechs/mech_movement.dm
index dac935b20b4..40663ce6d1c 100644
--- a/code/modules/mechs/mech_movement.dm
+++ b/code/modules/mechs/mech_movement.dm
@@ -40,6 +40,25 @@
victim.forceMove(theDepths)
visible_message("The [src] pushes [victim] downwards.")
occupant_message("You can feel \the [src] step onto something.")
+ for(var/hardpoint in hardpoints)
+ if(!hardpoints[hardpoint])
+ continue
+ var/obj/item/mech_equipment/thing = hardpoints[hardpoint]
+ if(!(thing.equipment_flags & EQUIPFLAG_UPDTMOVE))
+ continue
+ thing.update_icon()
+
+/mob/living/exosuit/set_dir()
+ . = ..()
+ if(.)
+ update_pilots()
+ for(var/hardpoint in hardpoints)
+ if(!hardpoints[hardpoint])
+ continue
+ var/obj/item/mech_equipment/thing = hardpoints[hardpoint]
+ if(!(thing.equipment_flags & EQUIPFLAG_UPDTMOVE))
+ continue
+ thing.update_icon()
/mob/living/exosuit/get_jetpack()
for(var/hardpoint_thing in hardpoints)
@@ -178,11 +197,17 @@
/mob/living/exosuit/proc/moveBlocked()
for(var/hardpoint in hardpoints)
- var/obj/item/mech_equipment/shield_generator/ballistic/blocker = hardpoints[hardpoint]
- if(!istype(blocker))
- continue
- if(blocker.on)
- return "\The [blocker] is deployed! Immobilizing you. "
+ var/obj/item/mech_equipment/equip = hardpoints[hardpoint]
+ if(equip)
+ switch(equip.type)
+ if(/obj/item/mech_equipment/shield_generator/ballistic)
+ var/obj/item/mech_equipment/shield_generator/ballistic/blocker = equip
+ if(blocker.on)
+ return "\The [blocker] is deployed! Immobilizing you. "
+ if(/obj/item/mech_equipment/forklifting_system)
+ var/obj/item/mech_equipment/forklifting_system/fork = equip
+ if(fork.lifted)
+ return "\The [fork] is lifted, locking you in place!"
return ""
diff --git a/code/modules/mechs/premade/_premade.dm b/code/modules/mechs/premade/_premade.dm
index 5f36892beb3..49ddd4f4df3 100644
--- a/code/modules/mechs/premade/_premade.dm
+++ b/code/modules/mechs/premade/_premade.dm
@@ -26,10 +26,14 @@
/mob/living/exosuit/premade/Initialize()
- arms = new arms(src)
- body = new body(src)
- head = new head(src)
- legs = new legs(src)
+ if(arms)
+ arms = new arms(src)
+ if(body)
+ body = new body(src)
+ if(head)
+ head = new head(src)
+ if(legs)
+ legs = new legs(src)
for(var/obj/item/mech_component/C in list(arms, legs, head, body))
if(decal)
diff --git a/code/modules/mechs/premade/powerloader.dm b/code/modules/mechs/premade/powerloader.dm
index 4475beb9411..c94f8775d30 100644
--- a/code/modules/mechs/premade/powerloader.dm
+++ b/code/modules/mechs/premade/powerloader.dm
@@ -44,3 +44,21 @@
HARDPOINT_RIGHT_HAND = /obj/item/mech_equipment/mounted_system/extinguisher,
HARDPOINT_HEAD = /obj/item/mech_equipment/light,
)
+
+/mob/living/exosuit/premade/forklift
+ name = "Aster's Guild \"Forklift\""
+ desc = "A modernized forklift for usage on space-ships. Are you ready to lift?"
+ rarity_value = 40
+ material = MATERIAL_PLASTIC
+ installed_armor = /obj/item/robot_parts/robot_component/armour/exosuit/plain
+ exosuit_color = "#c6c37b"
+ body = /obj/item/mech_component/chassis/forklift
+ legs = /obj/item/mech_component/propulsion/wheels
+ arms = null
+ head = null
+ installed_software_boards = list(
+ /obj/item/electronics/circuitboard/exosystem/utility
+ )
+ installed_systems = list(
+ HARDPOINT_FRONT = /obj/item/mech_equipment/forklifting_system
+ )
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 9f6e69b5ade..36764b163a6 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -112,6 +112,10 @@
process_glasses(G,TRUE)
/mob/living/carbon/human/reset_layer()
+ /// The forklift handles it for us here.
+ if(istype(loc, /obj/item/mech_equipment/forklifting_system))
+ if(lying) set_plane(LYING_HUMAN_PLANE)
+ return
if(hiding)
set_plane(HIDING_MOB_PLANE)
layer = HIDING_MOB_LAYER
diff --git a/code/modules/mob/living/carbon/resist.dm b/code/modules/mob/living/carbon/resist.dm
index d3604bcf7eb..0c0c56467d9 100644
--- a/code/modules/mob/living/carbon/resist.dm
+++ b/code/modules/mob/living/carbon/resist.dm
@@ -14,6 +14,11 @@
escape_inventory(src.loc)
return
+ if(istype(loc, /obj/item/mech_equipment/forklifting_system))
+ var/obj/item/mech_equipment/forklifting_system/fork = loc
+ fork.ejectLifting(get_turf(fork))
+ return
+
//unbuckling yourself
if(buckled)
if (buckled.resist_buckle(src))
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 68d451ddc45..30b67f081e2 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -91,8 +91,8 @@
messageturfs += turf
-
-
+
+
for(var/mob/M in getMobsInRangeChunked(get_turf(src), range, FALSE, TRUE))
if(!M.client)
continue
@@ -610,6 +610,11 @@
to_chat(src, "It won't budge!")
return
+ if(SEND_SIGNAL(AM, COMSIG_ATTEMPT_PULLING) == COMSIG_PULL_CANCEL)
+ to_chat(src, SPAN_WARNING("It won't budge!"))
+ return
+
+
var/mob/M = AM
if(ismob(AM))
diff --git a/code/modules/multiz/structures.dm b/code/modules/multiz/structures.dm
index 17377ed1302..59ad0902313 100644
--- a/code/modules/multiz/structures.dm
+++ b/code/modules/multiz/structures.dm
@@ -242,6 +242,10 @@
icon_state = "ramptop"
layer = 2.4
+/obj/structure/multiz/stairs/can_prevent_fall(above)
+ return above ? FALSE : TRUE
+
+
/obj/structure/multiz/stairs/enter
icon_state = "ramptop"
diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm
index 57f1b12bc1f..da01d672368 100755
--- a/code/modules/multiz/turf.dm
+++ b/code/modules/multiz/turf.dm
@@ -90,14 +90,14 @@ see multiz/movement.dm for some info.
if(!below)
return
- if(catwalk != (locate(/obj/structure/catwalk) in src))
- return
-
- if(locate(/obj/structure/multiz/stairs) in src)
- return
+ /// If anything on our turf stops falls downwards
+ for(var/atom/A in contents)
+ if(A.can_prevent_fall(FALSE, null))
+ return
+ /// If anything below stops falls from above
for(var/atom/A in below)
- if(A.can_prevent_fall())
+ if(A.can_prevent_fall(TRUE,null))
return
return TRUE
@@ -117,6 +117,10 @@ see multiz/movement.dm for some info.
P.height = HEIGHT_LOW // We are shooting from above, this protects windows from damage
return // We are done here
+ for(var/atom/A in contents)
+ if(A.can_prevent_fall(FALSE, mover))
+ return
+
if(!mover.can_fall())
return
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index d47f4372d37..70987683fec 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -135,7 +135,6 @@
explode()
return FALSE
- if(maxcharge < amount) return FALSE
var/amount_used = min(maxcharge-charge,amount)
charge += amount_used
update_icon()
diff --git a/code/modules/research/designs/mechs2/exosuits_components.dm b/code/modules/research/designs/mechs2/exosuits_components.dm
index c3be59c7d8b..38fe446e2c7 100644
--- a/code/modules/research/designs/mechs2/exosuits_components.dm
+++ b/code/modules/research/designs/mechs2/exosuits_components.dm
@@ -86,6 +86,9 @@
/datum/design/research/item/mechfab/exosuit/chassis/pod
build_path = /obj/item/mech_component/chassis/pod
+/datum/design/research/item/mechfab/exosuit/chassis/forklift
+ build_path = /obj/item/mech_component/chassis/forklift
+
//Manipulators
/datum/design/research/item/mechfab/exosuit/manipulators
category = "Exosuit Manipulators"
@@ -124,3 +127,6 @@
/datum/design/research/item/mechfab/exosuit/propulsion/tracks
build_path = /obj/item/mech_component/propulsion/tracks
+
+/datum/design/research/item/mechfab/exosuit/propulsion/wheels
+ build_path = /obj/item/mech_component/propulsion/wheels
diff --git a/code/modules/research/designs/mechs2/exosuits_equipment.dm b/code/modules/research/designs/mechs2/exosuits_equipment.dm
index bd0fc4305d9..ee9afd41de1 100644
--- a/code/modules/research/designs/mechs2/exosuits_equipment.dm
+++ b/code/modules/research/designs/mechs2/exosuits_equipment.dm
@@ -88,13 +88,40 @@
name = "mounted ion thrusters"
build_path = /obj/item/mech_equipment/thrusters
+/datum/design/research/item/exosuit/forklift
+ name = "forklift clamps"
+ build_path = /obj/item/mech_equipment/forklifting_system
+
+/datum/design/research/item/exosuit/fuel_generator
+ name = "welding fuel mech generator"
+ build_path = /obj/item/mech_equipment/power_generator/fueled/welding
+
+/datum/design/research/item/exosuit/plasma_generator
+ name = "plasma mech generator"
+ build_path = /obj/item/mech_equipment/power_generator/fueled/plasma
+
+/datum/design/research/item/exosuit/towing_hook
+ name = "mech towing hook"
+ build_path = /obj/item/mech_equipment/towing_hook
+
//MEDICAL
/datum/design/research/item/exosuit/sleeper
name = "mounted sleeper"
build_path = /obj/item/mech_equipment/sleeper
+/datum/design/research/item/exosuit/sleeper/upgraded
+ name = "mounted sleeper MK2"
+ build_path = /obj/item/mech_equipment/sleeper/upgraded
+
+/datum/design/research/item/exosuit/automender
+ name = "mech auto-mender"
+ build_path = /obj/item/mech_equipment/auto_mender
//ENGINEERING
/datum/design/research/item/exosuit/rcd
name = "mounted RCD"
build_path = /obj/item/mech_equipment/mounted_system/rcd
+
+/datum/design/research/item/exosuit/toolkit
+ name = "mounted toolkit"
+ build_path = /obj/item/mech_equipment/mounted_system/toolkit
diff --git a/code/modules/research/nodes/robotics.dm b/code/modules/research/nodes/robotics.dm
index d7c8377b1ce..94bd7401670 100644
--- a/code/modules/research/nodes/robotics.dm
+++ b/code/modules/research/nodes/robotics.dm
@@ -60,7 +60,9 @@
/datum/design/research/item/mechfab/exosuit/sensors/light,
/datum/design/research/item/mechfab/exosuit/chassis/light,
/datum/design/research/item/mechfab/exosuit/manipulators/light,
- /datum/design/research/item/mechfab/exosuit/propulsion/light
+ /datum/design/research/item/mechfab/exosuit/propulsion/light,
+ /datum/design/research/item/mechfab/exosuit/propulsion/wheels,
+ /datum/design/research/item/mechfab/exosuit/chassis/forklift
)
@@ -205,7 +207,9 @@
unlocks_designs = list(
/datum/design/research/circuit/exosuit/medical,
- /datum/design/research/item/exosuit/sleeper
+ /datum/design/research/item/exosuit/sleeper,
+ /datum/design/research/item/exosuit/sleeper/upgraded,
+ /datum/design/research/item/exosuit/automender
)
/datum/technology/mech_utility_modules
@@ -231,7 +235,13 @@
/datum/design/research/item/mechfab/exosuit/drillbit/steel,
/datum/design/research/item/mechfab/exosuit/drillbit/plasteel,
/datum/design/research/item/mechfab/exosuit/drillbit/diamond,
- /datum/design/research/item/exosuit/ion_thruster
+ /datum/design/research/item/exosuit/ion_thruster,
+ /datum/design/research/item/exosuit/forklift,
+ /datum/design/research/item/exosuit/fuel_generator,
+ /datum/design/research/item/exosuit/plasma_generator,
+ /datum/design/research/item/exosuit/towing_hook,
+ /datum/design/research/item/exosuit/rcd,
+ /datum/design/research/item/exosuit/toolkit
)
/datum/technology/mech_teleporter_modules
diff --git a/icons/mechs/bshield.dmi b/icons/mechs/bshield.dmi
index dda9fa60bc93daf32e0bcbc51776fdcb41c2ce76..2f998c7a7f35d625f189ab3c60854bc90bb77844 100644
GIT binary patch
delta 1712
zcmV;h22c6v2$Bw8iBL{Q4GJ0x0000DNk~Le0002+0002M1Oos70LTIJ3;+NCAy74PCc?qV>+9=2JwJPUdr?tQqobo37a6?AVB7!z00DGTPE!Ct=GbNc
z004`TO?Q7tGB-buOa}oq5_Xfaf~%hkIK%+_pkC%)$79w200sz2L_t(|ob8-XcH%e?
z#$k9bi>zXk0}O&yuiAK4vunn=K@O0W@d;x30>pEIJa+SXc0T1uHa7ZGQk7(2ve368
zLgycITHYCIf8Lm1k2g
zcKA9cHbzB7MMXtLMMXtLMMe4QCQk6|p)5;x53&=CdgvVWHon?7P4njLPIQ714_!=<
zqmKO_{MFy5PXtVIf>95>YMPJJ=`etQo<8fk#=rz881>LAj^(KL9$fb#fMq8b1|xqS
z`o2ebgJmapknB369y$a*1c3(=yBBKgp26p1{1FVknAk*c1%T02ZGt5e+Y3CdYW}i*
zAEMeK-3b=C@obJvWUvXm0Y2gf!?9Bh0M-Nb)R*Lmigai2gA=bxJAu_hGkX=0b%LRP_qpBeyr
zn%F(KzMLe2!JZ~|rDU!tU{4cU&0LefeitSc07eoBC~p(H2fq&qL|{W^>@S`$>i7NS
z7?({QG-GdZ=KkmBWyW>bG4@xx)R&!}8=l~3soVdp5H>{hn$a7Ob;Rd4!r
zX(L@HZVyWCJb>NCuHwb25~hEjNE=V12ju`uohO4AEZr0X7+cj+=jq_$`7&c{aY>yA
zu-j2qtQ2D`<^yJ51>88T?CL|hzlX*m)?-VZ2XHASb+o03QaFSMFuyE{l#%PPrOpGm
zyJNRn*}?=aOE7^L_1NwiYrc2qDA#r?+mS%jdTb<)4c{Aclob=$7;As9vI%i)6mL%g
z4^}p1ap}G{c9L*~(`aRrz?$!kfSs9Zw6aNH&G()(W1l$IeeY>t5t<{o{l^VHcOJl{
z)5m
z&&F6<-gkqIlSKG2PJMsCdS?ubotzN(ENt1DtqVP3wl0Q
z@y!*x!JRSXe|n7M)j29EDk>@}Shp!4#dVvl1PGeGl|Z1YPu@zvGJdeDPuxntGX4Zt
zKNB}YSOKYZ{0XjpH)94AkdnrSU47Dy2c*hX>r(^R)hF$EK&pRSwLUcfeG}S_2T&a+
zP`PUHWx$*Hx%mHobjO44Kl&4>a#dQzRcGIj#fulmS+YI=N->rM`2{mLJiCzwj@{Gi|5g{Dt6g}R=Xf3e@t=;-SC-E?EdT%Y@m~)(HC|YhN%x(%
zY3$c~U2kQ_x$LVU53|CIYg3cA+Z~(-v?*}@CVtB{hItIEy9@R_=bZFnIma<`fg|Y~
zJXka`&TAS)v@z^)HD&o7{iL4H_PYJ;mM5ec*xG%Va9oS2)YNwu7ys`^>zbM-
zCY0TtbaI}j}2McxO-|D*Gc$>SpX1C?iR|2=z6*&pzN`AQZ&9mTQ*oC;B$%#*D
zZ>Mui(_)JDDiF)R;MRP(@`>+Bvr|_Eb+j7q%j&JPYAG|CGogNqr%l0iVYi8!Ca-Yz
z5P#I7viri#Ak)be#TS=N&^yBE8Dbpae9LWO((F~vJ1!I{cB-uwiPF2LaI@`lWlE=d
zOqi(S9og3`>zu^;@I(mPM35GTv8`X~!%h7L)o))49b>
z?@L6(E=@4Y;Z9k^rx@EIJn7JN<0Xo7&+q!9o4e=H!>)T97J|XU_~Y|Gi7(8LIUd@m
zxAa21N?nVHJ=-m#dO;KMqdGT!2E5Q0>UCUH75qZKN|!PD|4OwBe_ZPqefGE$BKE60
zi!%hG@WXbu+Pa2Z9-oQj?&pB(b#@Lh2{Jun~H?(5XI9w=wI^1u^Tbd?xi1
zodRM`zMtLrOn;SXz+cx3F(L{PJzOY^FME7447I+9U$tYt8oxyF#Ojro|GI|i#I|aG
u5O*vNJj1kPN%YSBD^`7ETKq5S4^vlZ{L=)UqIbZ2%HZkh=d#Wzp$PyCM8{hI
diff --git a/icons/mechs/mech_equipment.dmi b/icons/mechs/mech_equipment.dmi
index 78cddcd85c416ebce12dccdef511b734735120be..0c2c6540a36913ffc4eeeaee298638eddb2f6dda 100644
GIT binary patch
literal 29051
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