diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_oldstation.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_oldstation.dmm
index 36e4db69397e..2b1180bff3a9 100644
--- a/_maps/RandomRuins/IceRuins/icemoon_underground_oldstation.dmm
+++ b/_maps/RandomRuins/IceRuins/icemoon_underground_oldstation.dmm
@@ -5635,7 +5635,7 @@
dir = 4
},
/obj/structure/rack,
-/obj/item/circuitboard/computer/shuttle/helm,
+/obj/item/circuitboard/computer/ship/helm,
/obj/item/circuitboard/machine/shuttle/engine/electric,
/obj/item/circuitboard/machine/shuttle/engine/electric,
/obj/item/circuitboard/machine/shuttle/engine/plasma,
diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_SPOOKYCRASH.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_SPOOKYCRASH.dmm
index 8423e23714c3..87a160398e35 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_SPOOKYCRASH.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_SPOOKYCRASH.dmm
@@ -125,7 +125,7 @@
/area/lavaland/surface/outdoors)
"D" = (
/obj/structure/frame/computer,
-/obj/item/circuitboard/computer/shuttle/helm,
+/obj/item/circuitboard/computer/ship/helm,
/obj/item/shard,
/turf/open/floor/mineral/titanium/blue,
/area/lavaland/surface/outdoors)
diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm
index e3e8c53bb5ec..5a9b6f6c6468 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm
@@ -2390,7 +2390,7 @@
/turf/open/floor/mineral/titanium/blue,
/area/ruin/powered/golem_ship)
"ZY" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 4
},
/obj/effect/decal/cleanable/dirt,
diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm
index eef6b2d03d5a..c170e666f952 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm
@@ -1911,7 +1911,7 @@
/obj/effect/turf_decal/industrial/warning{
dir = 8
},
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/plastitanium,
/area/ruin/unpowered/syndicate_lava_base/cargo)
"hp" = (
diff --git a/_maps/RandomRuins/SandRuins/whitesands_surface_solgovcrash.dmm b/_maps/RandomRuins/SandRuins/whitesands_surface_solgovcrash.dmm
index 7fcd00912410..8d2452d35e94 100644
--- a/_maps/RandomRuins/SandRuins/whitesands_surface_solgovcrash.dmm
+++ b/_maps/RandomRuins/SandRuins/whitesands_surface_solgovcrash.dmm
@@ -49,7 +49,7 @@
/area/ruin/unpowered)
"t" = (
/obj/structure/frame/computer,
-/obj/item/circuitboard/computer/shuttle/helm,
+/obj/item/circuitboard/computer/ship/helm,
/turf/open/floor/mineral/titanium/blue,
/area/ruin/unpowered)
"v" = (
diff --git a/_maps/RandomRuins/SpaceRuins/astraeus.dmm b/_maps/RandomRuins/SpaceRuins/astraeus.dmm
index fb1f2f411a7f..2fa40ef6395b 100644
--- a/_maps/RandomRuins/SpaceRuins/astraeus.dmm
+++ b/_maps/RandomRuins/SpaceRuins/astraeus.dmm
@@ -254,7 +254,7 @@
dir = 6
},
/obj/effect/decal/cleanable/glass,
-/obj/item/circuitboard/computer/shuttle/helm,
+/obj/item/circuitboard/computer/ship/helm,
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plasteel/dark/airless,
/area/ruin/space/has_grav/astraeus/bridge)
diff --git a/_maps/RandomRuins/SpaceRuins/mechtransport.dmm b/_maps/RandomRuins/SpaceRuins/mechtransport.dmm
index c36a60961c79..66b4977adda2 100644
--- a/_maps/RandomRuins/SpaceRuins/mechtransport.dmm
+++ b/_maps/RandomRuins/SpaceRuins/mechtransport.dmm
@@ -35,7 +35,7 @@
/turf/open/floor/mineral/titanium/blue,
/area/ruin/space/has_grav/powered/mechtransport)
"j" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/mineral/titanium/blue,
diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm
index 47964815dd28..889c1c95d889 100644
--- a/_maps/RandomZLevels/moonoutpost19.dmm
+++ b/_maps/RandomZLevels/moonoutpost19.dmm
@@ -2810,7 +2810,7 @@
/turf/open/floor/plasteel/dark,
/area/awaymission/moonoutpost19/arrivals)
"mG" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 4
},
/turf/open/floor/mineral/titanium/blue,
diff --git a/_maps/RandomZLevels/snowdin.dmm b/_maps/RandomZLevels/snowdin.dmm
index e85cb0933409..510e2cfe6897 100644
--- a/_maps/RandomZLevels/snowdin.dmm
+++ b/_maps/RandomZLevels/snowdin.dmm
@@ -7091,7 +7091,7 @@
/obj/effect/turf_decal/stripes/white/line{
dir = 1
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8;
name = "Excavation Elevator Console"
},
@@ -9820,7 +9820,7 @@
},
/area/awaymission/snowdin/post/mining_main/mechbay)
"KS" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/industrial/warning{
@@ -13469,7 +13469,7 @@
/turf/open/floor/plasteel,
/area/awaymission/snowdin/post/mining_main)
"TJ" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/industrial/warning{
@@ -14863,7 +14863,7 @@
/obj/effect/turf_decal/industrial/warning{
dir = 4
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8;
name = "Excavation Elevator Console"
},
diff --git a/_maps/RandomZLevels/spacebattle.dmm b/_maps/RandomZLevels/spacebattle.dmm
index 8bcaee9f3158..d22283121bef 100644
--- a/_maps/RandomZLevels/spacebattle.dmm
+++ b/_maps/RandomZLevels/spacebattle.dmm
@@ -134,7 +134,7 @@
/turf/open/floor/mineral/plastitanium/red,
/area/awaymission/spacebattle/syndicate3)
"aK" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 1
},
/turf/open/floor/mineral/plastitanium/red,
@@ -295,7 +295,7 @@
/turf/open/floor/mineral/plastitanium/red,
/area/awaymission/spacebattle/syndicate1)
"bu" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 1
},
/turf/open/floor/mineral/plastitanium/red,
@@ -318,7 +318,7 @@
/turf/open/floor/mineral/plastitanium/red,
/area/awaymission/spacebattle/syndicate2)
"bC" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 1
},
/turf/open/floor/mineral/plastitanium/red,
@@ -1155,7 +1155,7 @@
/turf/open/floor/mineral/plastitanium/red,
/area/awaymission/spacebattle/syndicate4)
"eL" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/plastitanium/red,
/area/awaymission/spacebattle/syndicate4)
"eM" = (
@@ -1410,7 +1410,7 @@
/turf/open/floor/plasteel,
/area/awaymission/spacebattle/cruiser)
"fv" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/corner/blue{
@@ -2165,7 +2165,7 @@
},
/area/awaymission/spacebattle/cruiser)
"hS" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 4
},
/turf/open/floor/mineral/plastitanium/red,
@@ -2382,7 +2382,7 @@
/turf/open/floor/mineral/plastitanium/red,
/area/awaymission/spacebattle/syndicate5)
"iM" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/plastitanium/red,
/area/awaymission/spacebattle/syndicate5)
"iP" = (
@@ -2429,7 +2429,7 @@
/turf/open/floor/mineral/plastitanium/red,
/area/awaymission/spacebattle/syndicate6)
"jc" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/plastitanium/red,
/area/awaymission/spacebattle/syndicate6)
"jd" = (
diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm
index 87810f7bb14d..9be39d8ea6f4 100644
--- a/_maps/map_files/generic/CentCom.dmm
+++ b/_maps/map_files/generic/CentCom.dmm
@@ -1351,7 +1351,7 @@
/turf/open/floor/holofloor,
/area/holodeck/rec_center/school)
"afh" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 4
},
/obj/effect/turf_decal/industrial/warning{
@@ -2905,7 +2905,7 @@
/turf/open/floor/engine/cult,
/area/wizard_station)
"ara" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/engine/cult,
/area/wizard_station)
"ard" = (
@@ -7016,7 +7016,7 @@
/turf/open/floor/mineral/titanium/blue,
/area/centcom/evac)
"aLP" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 1
},
/turf/open/floor/mineral/titanium/blue,
@@ -14703,7 +14703,7 @@
/turf/open/floor/plasteel/dark,
/area/ctf)
"gFU" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/turf_decal/industrial/warning{
dir = 6
},
@@ -16168,7 +16168,7 @@
/turf/open/floor/plasteel,
/area/centcom/control)
"nEL" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/turf_decal/industrial/warning{
dir = 10
},
@@ -17746,7 +17746,7 @@
/turf/open/floor/plasteel/dark,
/area/centcom/ferry)
"ukC" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/turf_decal/corner/bar,
/obj/effect/turf_decal/corner/bar{
dir = 1
diff --git a/_maps/shuttles/escape_pod/escape_pod_default.dmm b/_maps/shuttles/escape_pod/escape_pod_default.dmm
index 4f714898f80a..a58c441c9dd0 100644
--- a/_maps/shuttles/escape_pod/escape_pod_default.dmm
+++ b/_maps/shuttles/escape_pod/escape_pod_default.dmm
@@ -9,7 +9,7 @@
/obj/machinery/status_display/evac{
pixel_x = 32
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
pixel_x = -32
},
/turf/open/floor/mineral/titanium/blue,
diff --git a/_maps/shuttles/escape_pod/escape_pod_large.dmm b/_maps/shuttles/escape_pod/escape_pod_large.dmm
index cbad5f3946a0..c85d057d4ee7 100644
--- a/_maps/shuttles/escape_pod/escape_pod_large.dmm
+++ b/_maps/shuttles/escape_pod/escape_pod_large.dmm
@@ -35,11 +35,11 @@
/obj/machinery/light{
dir = 1
},
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/pod_1)
"E" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/pod_1)
"F" = (
diff --git a/_maps/shuttles/hunter/hunter_bounty.dmm b/_maps/shuttles/hunter/hunter_bounty.dmm
index 814106780735..419e3f24c9a3 100644
--- a/_maps/shuttles/hunter/hunter_bounty.dmm
+++ b/_maps/shuttles/hunter/hunter_bounty.dmm
@@ -133,7 +133,7 @@
/turf/open/floor/pod/dark,
/area/shuttle/hunter)
"A" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
icon_state = "computer";
dir = 8
},
diff --git a/_maps/shuttles/hunter/hunter_russian.dmm b/_maps/shuttles/hunter/hunter_russian.dmm
index 6ac6c73929ee..b7e30d8bd7df 100644
--- a/_maps/shuttles/hunter/hunter_russian.dmm
+++ b/_maps/shuttles/hunter/hunter_russian.dmm
@@ -127,7 +127,7 @@
/turf/open/floor/mineral/plastitanium/red,
/area/shuttle/hunter)
"A" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/mineral/plastitanium/red,
diff --git a/_maps/shuttles/hunter/hunter_space_cop.dmm b/_maps/shuttles/hunter/hunter_space_cop.dmm
index c4f5200d6792..31da6c8085f5 100644
--- a/_maps/shuttles/hunter/hunter_space_cop.dmm
+++ b/_maps/shuttles/hunter/hunter_space_cop.dmm
@@ -7,7 +7,7 @@
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/hunter)
"ab" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/mineral/titanium/blue,
diff --git a/_maps/shuttles/infiltrator/infiltrator_advanced.dmm b/_maps/shuttles/infiltrator/infiltrator_advanced.dmm
index 405765dd2a98..273e00c98fa1 100644
--- a/_maps/shuttles/infiltrator/infiltrator_advanced.dmm
+++ b/_maps/shuttles/infiltrator/infiltrator_advanced.dmm
@@ -71,7 +71,7 @@
/turf/open/floor/pod/dark,
/area/shuttle/syndicate/armory)
"aj" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/turf_decal/industrial/outline/yellow,
/turf/open/floor/mineral/plastitanium,
/area/shuttle/syndicate/bridge)
diff --git a/_maps/shuttles/infiltrator/infiltrator_basic.dmm b/_maps/shuttles/infiltrator/infiltrator_basic.dmm
index 66a805ff94ae..426ba407a5c1 100644
--- a/_maps/shuttles/infiltrator/infiltrator_basic.dmm
+++ b/_maps/shuttles/infiltrator/infiltrator_basic.dmm
@@ -47,7 +47,7 @@
/turf/open/floor/plasteel/dark,
/area/shuttle/syndicate/bridge)
"aj" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/turf_decal/bot_white,
/turf/open/floor/plasteel/dark,
/area/shuttle/syndicate/bridge)
diff --git a/_maps/shuttles/mining/mining_box.dmm b/_maps/shuttles/mining/mining_box.dmm
index cc72c17b4774..004cd82fcb94 100644
--- a/_maps/shuttles/mining/mining_box.dmm
+++ b/_maps/shuttles/mining/mining_box.dmm
@@ -11,7 +11,7 @@
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/mining)
"d" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/mining)
"e" = (
diff --git a/_maps/shuttles/mining/mining_delta.dmm b/_maps/shuttles/mining/mining_delta.dmm
index 43876a8d6416..ee100c8ca88c 100644
--- a/_maps/shuttles/mining/mining_delta.dmm
+++ b/_maps/shuttles/mining/mining_delta.dmm
@@ -25,7 +25,7 @@
/obj/effect/turf_decal/industrial/warning{
dir = 1
},
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plasteel,
/area/shuttle/mining)
"e" = (
diff --git a/_maps/shuttles/mining/mining_freight.dmm b/_maps/shuttles/mining/mining_freight.dmm
index 8580463a7161..639185b2596d 100644
--- a/_maps/shuttles/mining/mining_freight.dmm
+++ b/_maps/shuttles/mining/mining_freight.dmm
@@ -13,7 +13,7 @@
/area/shuttle/mining)
"d" = (
/obj/effect/turf_decal/industrial/outline/yellow,
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/plastitanium,
/area/shuttle/mining)
"e" = (
diff --git a/_maps/shuttles/mining/mining_kilo.dmm b/_maps/shuttles/mining/mining_kilo.dmm
index 18c9e0060142..66c257062293 100644
--- a/_maps/shuttles/mining/mining_kilo.dmm
+++ b/_maps/shuttles/mining/mining_kilo.dmm
@@ -322,7 +322,7 @@
/area/ship/bridge)
"Q" = (
/obj/effect/turf_decal/industrial/outline/yellow,
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/plastitanium,
/area/ship/bridge)
"S" = (
diff --git a/_maps/shuttles/mining/mining_large.dmm b/_maps/shuttles/mining/mining_large.dmm
index 30402f6ae127..c896321ce49a 100644
--- a/_maps/shuttles/mining/mining_large.dmm
+++ b/_maps/shuttles/mining/mining_large.dmm
@@ -28,7 +28,7 @@
dir = 8
},
/obj/effect/turf_decal/bot_white,
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plasteel/dark,
/area/shuttle/mining/large)
"e" = (
diff --git a/_maps/shuttles/mining/mining_packed.dmm b/_maps/shuttles/mining/mining_packed.dmm
index 9d6b7fc99dc9..0b5cb5ac0034 100644
--- a/_maps/shuttles/mining/mining_packed.dmm
+++ b/_maps/shuttles/mining/mining_packed.dmm
@@ -28,7 +28,7 @@
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/mining)
"n" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/mining)
"o" = (
diff --git a/_maps/shuttles/pirate/pirate_default.dmm b/_maps/shuttles/pirate/pirate_default.dmm
index 26ea9c21d294..24ee5e22a50c 100644
--- a/_maps/shuttles/pirate/pirate_default.dmm
+++ b/_maps/shuttles/pirate/pirate_default.dmm
@@ -28,7 +28,7 @@
/turf/open/floor/plasteel/dark,
/area/shuttle/pirate)
"ac" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/decal/cleanable/dirt,
/obj/effect/turf_decal/corner/red,
/obj/effect/turf_decal/corner/red{
diff --git a/_maps/shuttles/ruin/ruin_caravan_victim.dmm b/_maps/shuttles/ruin/ruin_caravan_victim.dmm
index fa76c3791d9f..68e392caf1f5 100644
--- a/_maps/shuttles/ruin/ruin_caravan_victim.dmm
+++ b/_maps/shuttles/ruin/ruin_caravan_victim.dmm
@@ -1148,7 +1148,7 @@
/obj/effect/turf_decal/corner/blue{
dir = 8
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/plasteel/dark{
diff --git a/_maps/shuttles/ruin/ruin_pirate_cutter.dmm b/_maps/shuttles/ruin/ruin_pirate_cutter.dmm
index 7fcefb31f592..c999c322bf3f 100644
--- a/_maps/shuttles/ruin/ruin_pirate_cutter.dmm
+++ b/_maps/shuttles/ruin/ruin_pirate_cutter.dmm
@@ -94,7 +94,7 @@
/obj/effect/turf_decal/corner/red{
dir = 8
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/plasteel/dark,
diff --git a/_maps/shuttles/ruin/ruin_solgov_exploration_pod.dmm b/_maps/shuttles/ruin/ruin_solgov_exploration_pod.dmm
index d8f3cbfe4766..d89f760bc924 100644
--- a/_maps/shuttles/ruin/ruin_solgov_exploration_pod.dmm
+++ b/_maps/shuttles/ruin/ruin_solgov_exploration_pod.dmm
@@ -19,7 +19,7 @@
/turf/open/floor/plating,
/area/ship/bridge)
"j" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 4
},
/turf/open/floor/mineral/titanium/blue,
diff --git a/_maps/shuttles/ruin/ruin_syndicate_dropship.dmm b/_maps/shuttles/ruin/ruin_syndicate_dropship.dmm
index 1c97267db010..ebd540a1f511 100644
--- a/_maps/shuttles/ruin/ruin_syndicate_dropship.dmm
+++ b/_maps/shuttles/ruin/ruin_syndicate_dropship.dmm
@@ -171,7 +171,7 @@
/obj/effect/turf_decal/corner/red{
dir = 8
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/plasteel/dark,
diff --git a/_maps/shuttles/ruin/ruin_syndicate_fighter_shiv.dmm b/_maps/shuttles/ruin/ruin_syndicate_fighter_shiv.dmm
index ef7826b0f289..8577be2904de 100644
--- a/_maps/shuttles/ruin/ruin_syndicate_fighter_shiv.dmm
+++ b/_maps/shuttles/ruin/ruin_syndicate_fighter_shiv.dmm
@@ -100,7 +100,7 @@
/obj/effect/decal/cleanable/dirt,
/obj/structure/frame/computer{
anchored = 1;
-
+
},
/turf/open/floor/mineral/plastitanium/red,
/area/ship/security)
@@ -117,7 +117,7 @@
pixel_x = -25;
req_access_txt = "150"
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 1
},
/obj/item/radio/intercom/wideband{
diff --git a/_maps/shuttles/shiptest/amogus_sus.dmm b/_maps/shuttles/shiptest/amogus_sus.dmm
index ec53e43df747..c186ef459646 100644
--- a/_maps/shuttles/shiptest/amogus_sus.dmm
+++ b/_maps/shuttles/shiptest/amogus_sus.dmm
@@ -195,7 +195,7 @@
/turf/open/floor/plasteel/grimy,
/area/ship/crew)
"cA" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/corner/neutral/half{
diff --git a/_maps/shuttles/shiptest/bar_ship.dmm b/_maps/shuttles/shiptest/bar_ship.dmm
index 650595dd065f..4f8770aad63f 100644
--- a/_maps/shuttles/shiptest/bar_ship.dmm
+++ b/_maps/shuttles/shiptest/bar_ship.dmm
@@ -1216,7 +1216,7 @@
/turf/open/floor/plating,
/area/ship/maintenance)
"AA" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/corner/blue{
@@ -1985,7 +1985,7 @@
/turf/open/floor/plasteel/mono/dark,
/area/ship/crew/canteen)
"OB" = (
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_x = -30
},
/turf/open/floor/plasteel/mono/dark,
diff --git a/_maps/shuttles/shiptest/bar_ship_b.dmm b/_maps/shuttles/shiptest/bar_ship_b.dmm
index 8e741e777757..fa7ef4417911 100644
--- a/_maps/shuttles/shiptest/bar_ship_b.dmm
+++ b/_maps/shuttles/shiptest/bar_ship_b.dmm
@@ -17,7 +17,7 @@
/turf/open/floor/carpet/nanoweave/red,
/area/ship/crew)
"aB" = (
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_x = -30
},
/obj/machinery/computer/slot_machine,
@@ -653,7 +653,7 @@
/turf/closed/wall,
/area/ship/crew/canteen)
"mz" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/corner/blue{
diff --git a/_maps/shuttles/shiptest/bogatyr.dmm b/_maps/shuttles/shiptest/bogatyr.dmm
index 511db7857177..dba422361f74 100644
--- a/_maps/shuttles/shiptest/bogatyr.dmm
+++ b/_maps/shuttles/shiptest/bogatyr.dmm
@@ -1242,7 +1242,7 @@
/turf/open/floor/plating,
/area/ship/maintenance/aft)
"OY" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/mineral/plastitanium,
/area/ship/bridge)
"Pa" = (
diff --git a/_maps/shuttles/shiptest/cargotide.dmm b/_maps/shuttles/shiptest/cargotide.dmm
index 1cbd0bdb68e0..b6fd6dbba9d3 100644
--- a/_maps/shuttles/shiptest/cargotide.dmm
+++ b/_maps/shuttles/shiptest/cargotide.dmm
@@ -186,7 +186,7 @@
/obj/structure/cable{
icon_state = "6-10"
},
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plasteel,
/area/ship/bridge)
"gp" = (
diff --git a/_maps/shuttles/shiptest/chapel.dmm b/_maps/shuttles/shiptest/chapel.dmm
index 1c2d6191a395..c1f274547a8a 100644
--- a/_maps/shuttles/shiptest/chapel.dmm
+++ b/_maps/shuttles/shiptest/chapel.dmm
@@ -1330,7 +1330,7 @@
/obj/structure/railing{
dir = 9
},
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/carpet,
/area/ship/bridge)
"tw" = (
diff --git a/_maps/shuttles/shiptest/corp_high.dmm b/_maps/shuttles/shiptest/corp_high.dmm
index 6707e3bf03de..0e68bcf54f90 100644
--- a/_maps/shuttles/shiptest/corp_high.dmm
+++ b/_maps/shuttles/shiptest/corp_high.dmm
@@ -3094,7 +3094,7 @@
/turf/open/floor/carpet/nanoweave/beige,
/area/ship/crew/dorm)
"Vw" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/carpet,
/area/ship/bridge)
"VA" = (
diff --git a/_maps/shuttles/shiptest/engi_moth.dmm b/_maps/shuttles/shiptest/engi_moth.dmm
index e75a1b727b67..4d1a9fa649b5 100644
--- a/_maps/shuttles/shiptest/engi_moth.dmm
+++ b/_maps/shuttles/shiptest/engi_moth.dmm
@@ -236,7 +236,7 @@
/turf/open/floor/engine,
/area/ship/engineering/engine)
"gk" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 1
},
/turf/open/floor/mineral/plastitanium/red,
diff --git a/_maps/shuttles/shiptest/golem_ship.dmm b/_maps/shuttles/shiptest/golem_ship.dmm
index 6acc510adb17..71f70da881b0 100644
--- a/_maps/shuttles/shiptest/golem_ship.dmm
+++ b/_maps/shuttles/shiptest/golem_ship.dmm
@@ -420,7 +420,7 @@
"hF" = (
/obj/effect/decal/cleanable/dirt,
/obj/effect/turf_decal/industrial/outline/yellow,
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 4
},
/turf/open/floor/mineral/plastitanium,
diff --git a/_maps/shuttles/shiptest/hightide.dmm b/_maps/shuttles/shiptest/hightide.dmm
index 3992d696b1bf..32948d051521 100644
--- a/_maps/shuttles/shiptest/hightide.dmm
+++ b/_maps/shuttles/shiptest/hightide.dmm
@@ -432,7 +432,7 @@
/turf/open/floor/plating,
/area/ship/maintenance/aft)
"pZ" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plating,
/area/ship/maintenance/fore)
"qn" = (
diff --git a/_maps/shuttles/shiptest/independent_litieguai.dmm b/_maps/shuttles/shiptest/independent_litieguai.dmm
index 3bb9586960dc..ca1123e69d5b 100644
--- a/_maps/shuttles/shiptest/independent_litieguai.dmm
+++ b/_maps/shuttles/shiptest/independent_litieguai.dmm
@@ -309,7 +309,7 @@
/turf/open/floor/plasteel/dark,
/area/ship/bridge)
"hH" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plasteel/dark,
/area/ship/bridge)
"hT" = (
diff --git a/_maps/shuttles/shiptest/isv_roberts.dmm b/_maps/shuttles/shiptest/isv_roberts.dmm
index dfa157ccd211..bfad23857837 100644
--- a/_maps/shuttles/shiptest/isv_roberts.dmm
+++ b/_maps/shuttles/shiptest/isv_roberts.dmm
@@ -768,7 +768,7 @@
/turf/open/floor/plating,
/area/ship/maintenance/central)
"Vl" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plating,
/area/ship/maintenance/fore)
"VL" = (
diff --git a/_maps/shuttles/shiptest/lamia.dmm b/_maps/shuttles/shiptest/lamia.dmm
index eba0b04b5702..d3c08be46109 100644
--- a/_maps/shuttles/shiptest/lamia.dmm
+++ b/_maps/shuttles/shiptest/lamia.dmm
@@ -10,7 +10,7 @@
/turf/open/floor/plating,
/area/ship/bridge)
"ac" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/carpet/stellar,
/area/ship/bridge)
"ad" = (
diff --git a/_maps/shuttles/shiptest/mining_ship_all.dmm b/_maps/shuttles/shiptest/mining_ship_all.dmm
index 04e2a86839a4..a30f75523c6e 100644
--- a/_maps/shuttles/shiptest/mining_ship_all.dmm
+++ b/_maps/shuttles/shiptest/mining_ship_all.dmm
@@ -741,7 +741,7 @@
/obj/machinery/light/small{
dir = 1
},
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_x = 30
},
/obj/effect/landmark/start/assistant,
@@ -1522,7 +1522,7 @@
dir = 8;
name = "engine fuel pump"
},
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_y = 30
},
/obj/structure/catwalk/over,
@@ -1723,7 +1723,7 @@
/turf/closed/wall,
/area/ship/engineering)
"NK" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/corner/blue/half{
diff --git a/_maps/shuttles/shiptest/ntsv_bubble.dmm b/_maps/shuttles/shiptest/ntsv_bubble.dmm
index baa9e0066aeb..3084a060ccc1 100644
--- a/_maps/shuttles/shiptest/ntsv_bubble.dmm
+++ b/_maps/shuttles/shiptest/ntsv_bubble.dmm
@@ -178,7 +178,7 @@
/turf/open/floor/plating,
/area/ship/maintenance/aft)
"jW" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer2{
diff --git a/_maps/shuttles/shiptest/ntsv_osprey.dmm b/_maps/shuttles/shiptest/ntsv_osprey.dmm
index fe439e44c349..4a5474750a64 100644
--- a/_maps/shuttles/shiptest/ntsv_osprey.dmm
+++ b/_maps/shuttles/shiptest/ntsv_osprey.dmm
@@ -1411,7 +1411,7 @@
/obj/effect/turf_decal/corner/purple{
dir = 4
},
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plasteel/dark,
/area/ship/bridge)
"lk" = (
diff --git a/_maps/shuttles/shiptest/ntsv_skipper.dmm b/_maps/shuttles/shiptest/ntsv_skipper.dmm
index a5f1f3d1e88c..aa699d298052 100644
--- a/_maps/shuttles/shiptest/ntsv_skipper.dmm
+++ b/_maps/shuttles/shiptest/ntsv_skipper.dmm
@@ -1475,7 +1475,7 @@
/turf/open/floor/plasteel/white,
/area/ship/medical)
"rP" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/corner/purple,
diff --git a/_maps/shuttles/shiptest/pirate_libertatia.dmm b/_maps/shuttles/shiptest/pirate_libertatia.dmm
index f8688dd7f5ed..5ef2239837e0 100644
--- a/_maps/shuttles/shiptest/pirate_libertatia.dmm
+++ b/_maps/shuttles/shiptest/pirate_libertatia.dmm
@@ -6,7 +6,7 @@
/turf/open/floor/pod/light,
/area/ship/bridge)
"az" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/decal/cleanable/dirt/dust,
/obj/effect/turf_decal/box,
/turf/open/floor/pod/light,
diff --git a/_maps/shuttles/shiptest/radio_funny.dmm b/_maps/shuttles/shiptest/radio_funny.dmm
index b721ab2e3dc6..8cf73b220c66 100644
--- a/_maps/shuttles/shiptest/radio_funny.dmm
+++ b/_maps/shuttles/shiptest/radio_funny.dmm
@@ -241,7 +241,7 @@
/turf/open/floor/carpet/nanoweave,
/area/ship/bridge)
"Kn" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/carpet/nanoweave/blue,
/area/ship/bridge)
"Kr" = (
diff --git a/_maps/shuttles/shiptest/rigger.dmm b/_maps/shuttles/shiptest/rigger.dmm
index 69ce23c53dae..6ff8ec69b8fc 100644
--- a/_maps/shuttles/shiptest/rigger.dmm
+++ b/_maps/shuttles/shiptest/rigger.dmm
@@ -1129,7 +1129,7 @@
/area/ship/maintenance/starboard)
"pt" = (
/obj/effect/turf_decal/industrial/outline/yellow,
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plasteel/dark,
/area/ship/bridge)
"pv" = (
diff --git a/_maps/shuttles/shiptest/rigger_b.dmm b/_maps/shuttles/shiptest/rigger_b.dmm
index c8b8e02f2914..ce3eb986a0e1 100644
--- a/_maps/shuttles/shiptest/rigger_b.dmm
+++ b/_maps/shuttles/shiptest/rigger_b.dmm
@@ -1268,7 +1268,7 @@
/turf/open/floor/carpet/nanoweave/beige,
/area/ship/security)
"qR" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/carpet/nanoweave/blue,
/area/ship/bridge)
"qS" = (
diff --git a/_maps/shuttles/shiptest/rigger_c.dmm b/_maps/shuttles/shiptest/rigger_c.dmm
index a75307d309a2..3eb08c3d0160 100644
--- a/_maps/shuttles/shiptest/rigger_c.dmm
+++ b/_maps/shuttles/shiptest/rigger_c.dmm
@@ -1375,7 +1375,7 @@
/turf/open/floor/carpet/nanoweave/beige,
/area/ship/security)
"qR" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plating,
/area/ship/bridge)
"qS" = (
diff --git a/_maps/shuttles/shiptest/scrapper.dmm b/_maps/shuttles/shiptest/scrapper.dmm
index 63a5bf2ffd3e..500584deb3e4 100644
--- a/_maps/shuttles/shiptest/scrapper.dmm
+++ b/_maps/shuttles/shiptest/scrapper.dmm
@@ -903,7 +903,7 @@
/area/ship/security)
"lD" = (
/obj/effect/turf_decal/delivery,
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_y = 32
},
/obj/machinery/atmospherics/components/unary/tank/toxins,
@@ -1377,7 +1377,7 @@
pixel_y = 28
},
/obj/effect/landmark/start/assistant,
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_x = 32;
pixel_y = 32
},
@@ -2985,7 +2985,7 @@
/area/ship/external)
"IC" = (
/obj/effect/turf_decal/delivery,
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_y = 32
},
/obj/machinery/atmospherics/components/unary/tank/toxins,
@@ -4065,7 +4065,7 @@
/turf/open/floor/pod/light,
/area/ship/crew/dorm)
"TO" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/turf_decal/box,
/obj/effect/turf_decal/techfloor{
dir = 1
@@ -4284,7 +4284,7 @@
/obj/structure/cable{
icon_state = "1-8"
},
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_x = 32;
pixel_y = 32
},
diff --git a/_maps/shuttles/shiptest/scrapperb.dmm b/_maps/shuttles/shiptest/scrapperb.dmm
index 3181a7695c69..7747adae927e 100644
--- a/_maps/shuttles/shiptest/scrapperb.dmm
+++ b/_maps/shuttles/shiptest/scrapperb.dmm
@@ -268,7 +268,7 @@
/area/ship/hallway/starboard)
"cu" = (
/obj/effect/turf_decal/delivery,
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_y = 32
},
/obj/machinery/atmospherics/components/unary/tank/toxins,
@@ -764,7 +764,7 @@
/turf/closed/wall/mineral/plastitanium/nodiagonal,
/area/ship/engineering/electrical)
"kQ" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/turf_decal/box,
/obj/effect/turf_decal/techfloor{
dir = 1
@@ -3897,7 +3897,7 @@
/obj/structure/cable{
icon_state = "1-8"
},
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_x = 32;
pixel_y = 32
},
@@ -4114,7 +4114,7 @@
/area/ship/crew/dorm)
"Yk" = (
/obj/effect/turf_decal/delivery,
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_y = 32
},
/obj/machinery/atmospherics/components/unary/tank/toxins,
diff --git a/_maps/shuttles/shiptest/solar_class.dmm b/_maps/shuttles/shiptest/solar_class.dmm
index 83b7cdbd380a..c4628c7d6fe7 100644
--- a/_maps/shuttles/shiptest/solar_class.dmm
+++ b/_maps/shuttles/shiptest/solar_class.dmm
@@ -762,7 +762,7 @@
/turf/open/floor/plasteel/mono,
/area/ship/engineering)
"sm" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/corner/neutral{
diff --git a/_maps/shuttles/shiptest/solgov_carina.dmm b/_maps/shuttles/shiptest/solgov_carina.dmm
index be20311201c3..117adbc5f331 100644
--- a/_maps/shuttles/shiptest/solgov_carina.dmm
+++ b/_maps/shuttles/shiptest/solgov_carina.dmm
@@ -3045,7 +3045,7 @@
/turf/closed/wall/mineral/titanium/nodiagonal,
/area/ship/cargo)
"US" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/machinery/button/door{
diff --git a/_maps/shuttles/shiptest/solgov_cricket.dmm b/_maps/shuttles/shiptest/solgov_cricket.dmm
index dbeebb40f110..389c8fbd3ad6 100644
--- a/_maps/shuttles/shiptest/solgov_cricket.dmm
+++ b/_maps/shuttles/shiptest/solgov_cricket.dmm
@@ -1936,7 +1936,7 @@
/turf/open/floor/plasteel/tech,
/area/ship/engineering)
"wE" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/carpet/nanoweave/blue,
diff --git a/_maps/shuttles/shiptest/solgov_liberty.dmm b/_maps/shuttles/shiptest/solgov_liberty.dmm
index cf8914becba5..3a77cc30002e 100644
--- a/_maps/shuttles/shiptest/solgov_liberty.dmm
+++ b/_maps/shuttles/shiptest/solgov_liberty.dmm
@@ -7,7 +7,7 @@
/area/ship/bridge)
"az" = (
/obj/effect/turf_decal/corner/neutral/full,
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/obj/effect/turf_decal/box,
/turf/open/floor/pod/light,
/area/ship/bridge)
diff --git a/_maps/shuttles/shiptest/starfury_class.dmm b/_maps/shuttles/shiptest/starfury_class.dmm
index 604f45ee1670..e3e952d95154 100644
--- a/_maps/shuttles/shiptest/starfury_class.dmm
+++ b/_maps/shuttles/shiptest/starfury_class.dmm
@@ -758,7 +758,7 @@
/turf/open/floor/carpet/nanoweave/red,
/area/ship/security)
"gR" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/plasteel/dark,
/area/ship/bridge)
"gY" = (
diff --git a/_maps/shuttles/shiptest/whiteship_box.dmm b/_maps/shuttles/shiptest/whiteship_box.dmm
index a5f96b49e329..08e360b7b029 100644
--- a/_maps/shuttles/shiptest/whiteship_box.dmm
+++ b/_maps/shuttles/shiptest/whiteship_box.dmm
@@ -913,7 +913,7 @@
/obj/effect/turf_decal/corner/blue{
dir = 8
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/plasteel/dark,
diff --git a/_maps/shuttles/shiptest/whiteship_delta.dmm b/_maps/shuttles/shiptest/whiteship_delta.dmm
index 965f0addac62..bea0881c1654 100644
--- a/_maps/shuttles/shiptest/whiteship_delta.dmm
+++ b/_maps/shuttles/shiptest/whiteship_delta.dmm
@@ -2277,7 +2277,7 @@
/obj/effect/turf_decal/corner/blue{
dir = 8
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/plasteel/dark,
diff --git a/_maps/shuttles/shiptest/whiteship_kilo.dmm b/_maps/shuttles/shiptest/whiteship_kilo.dmm
index dbda33620f38..210a970933f1 100644
--- a/_maps/shuttles/shiptest/whiteship_kilo.dmm
+++ b/_maps/shuttles/shiptest/whiteship_kilo.dmm
@@ -1034,7 +1034,7 @@
"oj" = (
/obj/effect/turf_decal/industrial/outline/yellow,
/obj/effect/decal/cleanable/dirt,
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 4
},
/turf/open/floor/mineral/plastitanium,
diff --git a/_maps/shuttles/shiptest/whiteship_meta.dmm b/_maps/shuttles/shiptest/whiteship_meta.dmm
index d2dfab057c36..4e2ed4cca993 100644
--- a/_maps/shuttles/shiptest/whiteship_meta.dmm
+++ b/_maps/shuttles/shiptest/whiteship_meta.dmm
@@ -1335,7 +1335,7 @@
/obj/effect/turf_decal/corner/bar{
dir = 1
},
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_x = 30;
pixel_y = 30
},
@@ -1873,7 +1873,7 @@
/obj/effect/turf_decal/corner/blue{
dir = 8
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/plasteel/dark,
@@ -2097,7 +2097,7 @@
pixel_x = -2;
pixel_y = 1
},
-/obj/machinery/computer/helm/viewscreen{
+/obj/machinery/computer/ship/helm/viewscreen{
pixel_y = -30
},
/turf/open/floor/plasteel/dark,
diff --git a/_maps/shuttles/shiptest/whiteship_midway.dmm b/_maps/shuttles/shiptest/whiteship_midway.dmm
index 5b908de68b3b..fe204ad24039 100644
--- a/_maps/shuttles/shiptest/whiteship_midway.dmm
+++ b/_maps/shuttles/shiptest/whiteship_midway.dmm
@@ -2177,7 +2177,7 @@
/obj/effect/turf_decal/corner/blue{
dir = 4
},
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/plasteel,
diff --git a/_maps/shuttles/shiptest/whiteship_pubby.dmm b/_maps/shuttles/shiptest/whiteship_pubby.dmm
index ed7700e76080..aa3be0aaf95c 100644
--- a/_maps/shuttles/shiptest/whiteship_pubby.dmm
+++ b/_maps/shuttles/shiptest/whiteship_pubby.dmm
@@ -796,7 +796,7 @@
"JU" = (
/obj/effect/decal/cleanable/dirt/dust,
/obj/effect/decal/cleanable/dirt,
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/turf/open/floor/plasteel/dark,
diff --git a/_maps/shuttles/shiptest/wright.dmm b/_maps/shuttles/shiptest/wright.dmm
index 0ccebd0072dd..81c0ea5e23b7 100644
--- a/_maps/shuttles/shiptest/wright.dmm
+++ b/_maps/shuttles/shiptest/wright.dmm
@@ -1266,7 +1266,7 @@
/turf/open/floor/plating,
/area/ship/maintenance/aft)
"Gy" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
dir = 8
},
/obj/effect/turf_decal/siding/wood{
diff --git a/_maps/shuttles/snowdin/snowdin_excavation.dmm b/_maps/shuttles/snowdin/snowdin_excavation.dmm
index fa5b91880b3b..1aaf38d26898 100644
--- a/_maps/shuttles/snowdin/snowdin_excavation.dmm
+++ b/_maps/shuttles/snowdin/snowdin_excavation.dmm
@@ -29,7 +29,7 @@
/turf/open/floor/engine,
/area/shuttle/snowdin/elevator1)
"f" = (
-/obj/machinery/computer/helm{
+/obj/machinery/computer/ship/helm{
name = "Excavation Elevator Console"
},
/turf/open/floor/engine,
diff --git a/_maps/shuttles/snowdin/snowdin_mining.dmm b/_maps/shuttles/snowdin/snowdin_mining.dmm
index e50cf1a9d626..09888b7aa312 100644
--- a/_maps/shuttles/snowdin/snowdin_mining.dmm
+++ b/_maps/shuttles/snowdin/snowdin_mining.dmm
@@ -27,7 +27,7 @@
/turf/closed/wall,
/area/shuttle/snowdin/elevator2)
"e" = (
-/obj/machinery/computer/helm,
+/obj/machinery/computer/ship/helm,
/turf/open/floor/engine,
/area/shuttle/snowdin/elevator2)
"f" = (
diff --git a/code/__DEFINES/overmap.dm b/code/__DEFINES/overmap.dm
index 4beb0794ed0c..55f41e1c0f5c 100644
--- a/code/__DEFINES/overmap.dm
+++ b/code/__DEFINES/overmap.dm
@@ -7,6 +7,44 @@
#define MEDSTAR 3
#define BIGSTAR 4
+// Ship Damage Types
+/// This only exists for use in overmap armor lists. Dealing damage with this 'type' is not supported
+#define DAMAGE_ALL "All"
+#define DAMAGE_EMP "EMP"
+#define DAMAGE_TESLA "Tesla"
+#define DAMAGE_PLASMA "Plasma"
+#define DAMAGE_PHOTON "Photon"
+#define DAMAGE_PHYSICAL "Physical"
+#define DAMAGE_EXPLOSIVE "Explosive"
+#define DAMAGE_BLOCKED_MOVE "ship-blocked-move.name" // classical localization joke, given this should NEVER BE USED AS OUTPUT
+
+/proc/convert_damage_types_to_readable_string(list/damage_types)
+ var/ret = "("
+ for(var/d_type in damage_types)
+ ret += "[d_type],"
+ ret = copytext(ret, 1, length(ret))
+ ret += ")"
+ return ret
+
+#define IS_DAMAGE_TYPE(t1, t2) (islist(t1) ? (t2 in t1) : t1 == t2)
+
+// Ship COMSIGs
+#define COMSIG_SHIP_DAMAGE "ship-damage"
+#define COMSIG_SHIP_THRUST "ship-thrust"
+#define COMSIG_SHIP_MOVE "ship-move"
+#define COMSIG_SHIP_DOCK "ship-dock"
+#define COMSIG_SHIP_UNDOCK "ship-undock"
+#define COMSIG_SHIP_BLOCKED_MOVE "ship-blocked-move"
+
+// Allow the ship to perform this action
+#define SHIP_ALLOW (1<<0)
+// Do not allow the ship to perform this action
+#define SHIP_BLOCK (1<<1)
+// Always allow the ship to perform this action
+#define SHIP_FORCE_ALLOW (1<<2)
+// This overrides SHIP_FORCE_ALLOW
+#define SHIP_FORCE_BLOCK (1<<3)
+
//Star classes
#define STARO 1 //Extremely bright blue main sequence star or (super)giant
#define STARB 2 //Bright blue main sequence star or (super)giant
@@ -19,3 +57,80 @@
#define START 9 //Methane dwarf
#define STARY 10 //Sad lame brown dwarf
#define STARD 11 //White dwarf
+
+// Bluespace Jump states
+#define JUMP_STATE_OFF 0
+#define JUMP_STATE_CHARGING 1
+#define JUMP_STATE_IONIZING 2
+#define JUMP_STATE_FIRING 3
+#define JUMP_STATE_FINALIZED 4
+// Bluespace Jump numbers
+#define JUMP_CHARGE_DELAY (20 SECONDS)
+#define JUMP_CHARGEUP_TIME (3 MINUTES)
+
+// Ship Module stuff
+#define SHIP_SLOT_NONE "None"
+#define SHIP_SLOT_SHIELD "Shield"
+#define SHIP_SLOT_WEAPON "Weapon"
+#define SHIP_SLOT_AUXILLARY "Auxillary"
+#define SHIP_SLOT_UTILITY "Utility"
+#define SHIP_MODULE_UNIQUE (1<<0)
+
+GLOBAL_LIST_EMPTY_TYPED(ship_modules, /datum/ship_module)
+
+/proc/populate_ship_modules()
+ var/list/ret = new
+ for(var/path in subtypesof(/datum/ship_module))
+ var/datum/ship_module/m_path = path
+ if(initial(m_path.abstract) == path)
+ continue
+ ret[path] = new path
+ GLOB.ship_modules = ret
+
+// Ship Weapon Defines
+/// The weapon's projectile hit the enemy but did not fully impact them
+#define WEAPON_RICHOCHET 1
+/// Hit confirmed
+#define WEAPON_HIT 2
+/// The weapon's projectile did not hit the enemy or missed entirely
+#define WEAPON_MISS 3
+/// The weapon's projectile ricocheted off the enemy and hit us
+#define WEAPON_RICHOCHET_SELF 4
+/// The weapon is currently reloading
+#define WEAPON_RELOADING 5
+/// The weapon attempted to fire but has no ammo
+#define WEAPON_EMPTY 6
+/// The weapon is broken and cannot fire
+#define WEAPON_BROKEN 7
+/// The weapon failed to fire for some other reason
+#define WEAPON_FAIL 8
+
+// Module Install Status Defines
+/// The module can be installed
+#define MODULE_INSTALL_GOOD 0
+/// The module cannot be installed
+#define MODULE_INSTALL_BAD_GENERIC 1
+/// The module cannot be installed due to its cost
+#define MODULE_INSTALL_BAD_COST 2
+/// The module cannot be installed due to slot conflicts
+#define MODULE_INSTALL_BAD_UNIQUE 3
+/// The module cannot be installed due to conflicting with other modules
+#define MODULE_INSTALL_BAD_CONFLICT 4
+/// The module is already installed and cannot be installed more than once
+#define MODULE_INSTALL_BAD_SINGULAR 5
+
+/proc/module_install_reason(reason_code)
+ switch(reason_code)
+ if(MODULE_INSTALL_GOOD)
+ return("Module is able to be installed.")
+ if(MODULE_INSTALL_BAD_GENERIC)
+ return("Module is unable to be installed.")
+ if(MODULE_INSTALL_BAD_COST)
+ return("Module cannot be afforded with current ship modularity.")
+ if(MODULE_INSTALL_BAD_UNIQUE)
+ return("Module cannot be installed alongside other modules.")
+ if(MODULE_INSTALL_BAD_CONFLICT)
+ return("Module cannot be installed because of conflicts existing modules already installed.")
+ if(MODULE_INSTALL_BAD_SINGULAR)
+ return("Module cannot be installed more than once.")
+ return("Coder Error: Unknown Reason Code '[reason_code]'")
diff --git a/code/controllers/configuration/entries/overmap.dm b/code/controllers/configuration/entries/overmap.dm
new file mode 100644
index 000000000000..df5087716e29
--- /dev/null
+++ b/code/controllers/configuration/entries/overmap.dm
@@ -0,0 +1,4 @@
+
+/// Config Value for the default bluespace jump wait
+/datum/config_entry/number/bluespace_jump_wait
+ default = 30 MINUTES
diff --git a/code/game/objects/items/shuttle_creator.dm b/code/game/objects/items/shuttle_creator.dm
index bb62fec454b8..d4211f6b6028 100644
--- a/code/game/objects/items/shuttle_creator.dm
+++ b/code/game/objects/items/shuttle_creator.dm
@@ -58,9 +58,14 @@ GLOBAL_LIST_EMPTY(custom_shuttle_machines) //Machines that require updating (He
if(shuttle_create_docking_port(target, user))
to_chat(user, "Shuttle created!")
return
- else if(istype(target, /obj/machinery/computer/helm))
- var/obj/machinery/computer/helm/console = target
- console.reload_ship()
+ else if(istype(target, /obj/machinery/computer/ship/helm))
+ var/obj/machinery/computer/ship/helm/console = target
+ var/obj/structure/overmap/ship/simulated/old = console.current_ship
+ console.recursive_connect()
+ if(!old || console.current_ship != old)
+ to_chat(user, "Re-Linked Helm to ship.")
+ else
+ to_chat(user, "Helm already linked to ship.")
return
to_chat(user, "The [src] bleeps. Select an airlock to create a docking port, or a valid machine to link.")
return
diff --git a/code/modules/overmap/_overmap.dm b/code/modules/overmap/_overmap.dm
index 98bf730b7a06..d54f3269e1c4 100644
--- a/code/modules/overmap/_overmap.dm
+++ b/code/modules/overmap/_overmap.dm
@@ -53,7 +53,7 @@
* Everything visible on the overmap: stations, ships, ruins, events, and more.
*
* This base class should be the parent of all objects present on the overmap.
- * For the control counterparts, see [/obj/machinery/computer/helm].
+ * For the control counterparts, see [/obj/machinery/computer/ship/helm].
* For the shuttle counterparts (ONLY USED FOR SHIPS), see [/obj/docking_port/mobile].
*
*/
@@ -70,7 +70,7 @@
///Integrity percentage, do NOT modify. Use [/obj/structure/overmap/proc/receive_damage] instead.
var/integrity = 100
///Armor value, reduces integrity damage taken
- var/overmap_armor = 1
+ var/list/overmap_armor = new
///List of other overmap objects in the same tile
var/list/close_overmap_objects
///Vessel approximate mass
@@ -164,9 +164,31 @@
/**
* Reduces overmap object integrity by X amount, divided by armor
* * amount - amount of damage to apply to the ship
+ * * source - The source of the damage
+ * * damage_type - The damage type, or a list of types, that this damage is dealt as
*/
-/obj/structure/overmap/proc/recieve_damage(amount)
- integrity = max(integrity - (amount / overmap_armor), 0)
+/obj/structure/overmap/proc/recieve_damage(amount, atom/source, damage_type = DAMAGE_PHYSICAL)
+ var/reduction
+ var/highest_reduction = 1
+ if(islist(damage_type))
+ for(var/dtype in damage_type)
+ if(!(type in overmap_armor))
+ continue
+ reduction = overmap_armor[dtype]
+ if(reduction > highest_reduction)
+ highest_reduction = reduction
+ else if(type in overmap_armor)
+ highest_reduction = overmap_armor[damage_type]
+ if(DAMAGE_ALL in overmap_armor)
+ reduction = overmap_armor[DAMAGE_ALL]
+ if(reduction > highest_reduction)
+ highest_reduction = reduction
+ var/damage = amount / highest_reduction
+ var/signal_resp = SEND_SIGNAL(src, COMSIG_SHIP_DAMAGE, damage, damage_type, source)
+ if((signal_resp & SHIP_FORCE_BLOCK) || ((signal_resp & SHIP_BLOCK) && !(signal_resp & SHIP_FORCE_ALLOW)))
+ return FALSE
+ integrity = max(integrity - damage, 0)
+ return damage
/**
* The action performed by a ship on this when the helm button is pressed. Returns nothing on success, an error string if one occurs.
diff --git a/code/modules/overmap/consoles/_ship_console.dm b/code/modules/overmap/consoles/_ship_console.dm
new file mode 100644
index 000000000000..fc4f9cb8d012
--- /dev/null
+++ b/code/modules/overmap/consoles/_ship_console.dm
@@ -0,0 +1,111 @@
+/obj/machinery/computer/ship
+ //TODO: icon = 'icons/obj/ship_machinery.dmi'
+ //TODO: icon_screen = "default"
+ //TODO: icon_state = "default"
+ /// The simulated ship this console is connected to, or null if none!
+ var/obj/structure/overmap/ship/simulated/current_ship
+ /// All users currently using this
+ var/list/concurrent_users = list()
+ /// Is this console view only? I.E. cant dock/etc
+ var/viewer = FALSE
+ /// Does this console not function if the ship it is on is bluespace jumping?
+ var/bluespace_interferes = TRUE
+ /// The name of the TGUI Window to initialize for ui_interact. Set to null to disallow new interactions.
+ var/tgui_interface_id
+
+/obj/machinery/computer/ship/Initialize()
+ . = ..()
+ addtimer(CALLBACK(src, .proc/recursive_connect), 1)
+
+/obj/machinery/computer/ship/proc/recursive_connect()
+ SHOULD_NOT_OVERRIDE(TRUE)
+ for(var/obj/structure/overmap/ship/simulated/sim_ship as anything in SSovermap.simulated_ships)
+ for(var/area/ship_area as anything in sim_ship.shuttle.shuttle_areas)
+ if(!(src in ship_area))
+ continue
+ connect_to_parent(sim_ship)
+ return
+
+/**
+ * Handles the connection to a parent ship.
+ * You probably want to call the parent BEFORE you do your logic.
+ */
+/obj/machinery/computer/ship/proc/connect_to_parent(obj/structure/overmap/ship/simulated/parent)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!istype(parent))
+ CRASH("Illegal parent")
+ parent.sync_with_helms_console()
+ if(!parent.helm)
+ return
+ if(parent.helm == src)
+ audible_message("Helms Console connected with Ship mainframe. Updating database.")
+ else
+ audible_message("Console connected with Ship mainframe. Downloading database.")
+ current_ship = parent
+ parent.ship_computers |= src
+ RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/handle_parent_qdel)
+
+/**
+ * This proc handles the disconnection from a parent ship.
+ * You probably want to call the parent AFTER you do your logic.
+ */
+/obj/machinery/computer/ship/proc/disconnect_from_parent()
+ SHOULD_CALL_PARENT(TRUE)
+ UnregisterSignal(current_ship, COMSIG_PARENT_QDELETING)
+ current_ship.ship_computers -= src
+ current_ship = null
+
+/obj/machinery/computer/ship/Destroy()
+ . = ..()
+ UnregisterSignal(current_ship, COMSIG_PARENT_QDELETING)
+
+/obj/machinery/computer/ship/proc/handle_parent_qdel()
+ SIGNAL_HANDLER
+ current_ship = null
+
+/obj/machinery/computer/ship/ui_interact(mob/user, datum/tgui/ui)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ . = ..()
+ if(!current_ship)
+ recursive_connect()
+ if(!current_ship)
+ visible_message("Warning: Connected Ship Database lacks a Helms console. Unable to Initalize.")
+ return
+ if(bluespace_interferes && current_ship.is_jumping())
+ say("Warning: Bluespace Jump in progress. Controls are temporarily suspended.")
+ if(isliving(user))
+ if(!length(concurrent_users))
+ playsound(src, 'sound/machines/terminal_on.ogg', 25, FALSE)
+ use_power(active_power_usage)
+ concurrent_users |= user
+ if(ui)
+ if(ui.interface != tgui_interface_id)
+ ui.close(FALSE)
+ ui = null
+ else
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ if(!tgui_interface_id)
+ say("Console Interface error while loading from Database.")
+ return
+ ui = new(user, src, tgui_interface_id)
+ ui.autoupdate = TRUE
+ ui.open()
+
+/obj/machinery/computer/ship/ui_status(mob/user)
+ if(bluespace_interferes && current_ship.is_jumping())
+ return max(..(), UI_UPDATE)
+ return ..()
+
+/obj/machinery/computer/ship/ui_close(mob/user)
+ . = ..()
+ if(length(concurrent_users) == 0 && isliving(user))
+ playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
+ use_power(0)
+
+/obj/machinery/computer/ship/ui_act()
+ . = ..()
+ if(viewer)
+ return TRUE
+ if(bluespace_interferes && current_ship.is_jumping())
+ return TRUE
diff --git a/code/modules/overmap/consoles/bluespace_jump.dm b/code/modules/overmap/consoles/bluespace_jump.dm
new file mode 100644
index 000000000000..519cbda32dfc
--- /dev/null
+++ b/code/modules/overmap/consoles/bluespace_jump.dm
@@ -0,0 +1,94 @@
+
+
+/obj/machinery/computer/ship/bluespace_jump
+ name = "Bluespace Jump Control"
+ icon_screen = "shuttle"
+ icon_keyboard = "tech_key"
+ circuit = /obj/item/circuitboard/computer/ship/bluespace_jump
+ tgui_interface_id = "BluespaceJumpConsole"
+ /// When are we allowed to jump
+ var/jump_allowed
+ /// Current state of our jump
+ var/jump_state = JUMP_STATE_OFF
+ /// If we are calibrating the jump
+ var/calibrating = FALSE
+ /// Holds the timerid for the jump
+ var/jump_timer
+ /// Expected world.time of state completion
+ var/state_eta
+ /// Expected world.time of jump completion
+ var/jump_eta
+
+/obj/machinery/computer/ship/bluespace_jump/Initialize()
+ . = ..()
+ jump_allowed = world.time + CONFIG_GET(number/bluespace_jump_wait)
+
+/obj/machinery/computer/ship/bluespace_jump/ui_data(mob/user)
+ . = list()
+ .["calibrating"] = calibrating
+ .["state"] = jump_state
+ .["state_eta"] = DisplayTimeText(timeleft(jump_timer), 1)
+ .["final_eta"] = DisplayTimeText(world.time - jump_eta, 1)
+
+/obj/machinery/computer/ship/bluespace_jump/ui_act(action)
+ . = ..()
+ switch(action)
+ if("bluespace_jump")
+ if(calibrating)
+ cancel_jump()
+ return
+ else
+ if(tgui_alert(usr, "Do you want to bluespace jump? Your ship and everything on it will be removed from the round.", "Jump Confirmation", list("Yes", "No")) != "Yes")
+ return
+ calibrate_jump()
+ return
+
+/obj/machinery/computer/ship/bluespace_jump/proc/calibrate_jump(inline = FALSE)
+ if(jump_allowed < 0)
+ say("Bluespace Jump Calibration offline. Please contact your system administrator.")
+ return
+ if(current_ship.state != OVERMAP_SHIP_FLYING)
+ say("Bluespace Jump Calibration detected interference in the local area.")
+ return
+ if(world.time < jump_allowed)
+ var/jump_wait = DisplayTimeText(jump_allowed - world.time)
+ say("Bluespace Jump Calibration is currently recharging. ETA: [jump_wait].")
+ return
+ if(jump_state != JUMP_STATE_OFF && !inline)
+ return // This exists to prefent Href exploits to call process_jump more than once by a client
+ message_admins("[ADMIN_LOOKUPFLW(usr)] has initiated a bluespace jump in [ADMIN_VERBOSEJMP(src)]")
+ jump_state = JUMP_STATE_OFF
+ jump_timer = addtimer(CALLBACK(src, .proc/jump_sequence), JUMP_CHARGEUP_TIME, TIMER_STOPPABLE)
+ state_eta = JUMP_CHARGEUP_TIME
+ jump_eta = JUMP_CHARGEUP_TIME + (JUMP_CHARGE_DELAY * 5)
+ priority_announce("Bluespace jump calibration initialized. Calibration completion in [JUMP_CHARGEUP_TIME/600] minutes.", sender_override="[current_ship.name] Bluespace Pylon", zlevel=get_virtual_z_level())
+ calibrating = TRUE
+ return TRUE
+
+/obj/machinery/computer/ship/bluespace_jump/proc/cancel_jump()
+ priority_announce("Bluespace Pylon spooling down. Jump calibration aborted.", sender_override="[current_ship.name] Bluespace Pylon", zlevel=get_virtual_z_level())
+ calibrating = FALSE
+ deltimer(jump_timer)
+
+/obj/machinery/computer/ship/bluespace_jump/proc/jump_sequence()
+ switch(jump_state)
+ if(JUMP_STATE_OFF)
+ jump_state = JUMP_STATE_CHARGING
+ SStgui.close_uis(src)
+ if(JUMP_STATE_CHARGING)
+ jump_state = JUMP_STATE_IONIZING
+ priority_announce("Bluespace Jump Calibration completed. Ionizing Bluespace Pylon.", sender_override="[current_ship.name] Bluespace Pylon", zlevel=get_virtual_z_level())
+ if(JUMP_STATE_IONIZING)
+ jump_state = JUMP_STATE_FIRING
+ priority_announce("Bluespace Ionization finalized; preparing to fire Bluespace Pylon.", sender_override="[current_ship.name] Bluespace Pylon", zlevel=get_virtual_z_level())
+ if(JUMP_STATE_FIRING)
+ jump_state = JUMP_STATE_FINALIZED
+ priority_announce("Bluespace Pylon launched.", sender_override="[current_ship.name] Bluespace Pylon", sound='sound/magic/lightning_chargeup.ogg', zlevel=get_virtual_z_level())
+ addtimer(CALLBACK(src, .proc/do_jump), 10 SECONDS)
+ return
+ state_eta = world.time + JUMP_CHARGE_DELAY
+ addtimer(CALLBACK(src, .proc/jump_sequence), JUMP_CHARGE_DELAY)
+
+/obj/machinery/computer/ship/bluespace_jump/proc/do_jump()
+ priority_announce("Bluespace Jump Initiated.", sender_override="[current_ship.name] Bluespace Pylon", sound='sound/magic/lightningbolt.ogg', zlevel=get_virtual_z_level())
+ current_ship.shuttle.intoTheSunset()
diff --git a/code/modules/overmap/consoles/helm.dm b/code/modules/overmap/consoles/helm.dm
new file mode 100644
index 000000000000..590973ed048c
--- /dev/null
+++ b/code/modules/overmap/consoles/helm.dm
@@ -0,0 +1,156 @@
+/obj/machinery/computer/ship/helm
+ name = "helm control console"
+ desc = "Used to view or control the ship."
+ icon_screen = "shuttle"
+ icon_keyboard = "tech_key"
+ circuit = /obj/item/circuitboard/computer/ship/helm
+ tgui_interface_id = "HelmConsole"
+ light_color = LIGHT_COLOR_FLARE
+ clicksound = null
+
+/obj/machinery/computer/ship/helm/ui_data(mob/user)
+ // Register the current ship with the user's client's plane master everytime we want to reload the ui.
+ if(current_ship)
+ user.client.register_map_obj(current_ship.cam_screen)
+ user.client.register_map_obj(current_ship.cam_plane_master)
+ user.client.register_map_obj(current_ship.cam_background)
+ current_ship.update_screen()
+ . = list()
+ .["integrity"] = current_ship.integrity
+ .["otherInfo"] = list()
+ for (var/object in current_ship.close_overmap_objects)
+ var/obj/structure/overmap/O = object
+ var/list/other_data = list(
+ name = O.name,
+ integrity = O.integrity,
+ ref = REF(O)
+ )
+ .["otherInfo"] += list(other_data)
+
+ var/turf/T = get_turf(current_ship)
+ .["x"] = T.x
+ .["y"] = T.y
+
+ if(!istype(current_ship, /obj/structure/overmap/ship/simulated))
+ return
+
+ var/obj/structure/overmap/ship/simulated/S = current_ship
+
+ .["state"] = S.state
+ .["docked"] = isturf(S.loc) ? FALSE : TRUE
+ .["heading"] = dir2text(S.get_heading()) || "None"
+ .["speed"] = S.get_speed()
+ .["eta"] = S.get_eta()
+ .["est_thrust"] = S.est_thrust
+ .["engineInfo"] = list()
+ for(var/obj/machinery/power/shuttle/engine/E in S.shuttle.engine_list)
+ var/list/engine_data
+ if(!E.thruster_active)
+ engine_data = list(
+ name = E.name,
+ fuel = 0,
+ maxFuel = 100,
+ enabled = E.enabled,
+ ref = REF(E)
+ )
+ else
+ engine_data = list(
+ name = E.name,
+ fuel = E.return_fuel(),
+ maxFuel = E.return_fuel_cap(),
+ enabled = E.enabled,
+ ref = REF(E)
+ )
+ .["engineInfo"] += list(engine_data)
+
+/obj/machinery/computer/ship/helm/ui_static_data(mob/user)
+ . = list()
+ .["isViewer"] = viewer
+ .["mapRef"] = current_ship.map_name
+
+ var/class_name = istype(current_ship, /obj/structure/overmap/ship) ? "Ship" : "Planetoid"
+ .["shipInfo"] = list(
+ name = current_ship.name,
+ can_rename = class_name == "Ship",
+ class = class_name,
+ mass = current_ship.mass,
+ sensor_range = current_ship.sensor_range,
+ ref = REF(current_ship)
+ )
+ if(class_name == "Ship")
+ .["canFly"] = TRUE
+
+
+/obj/machinery/computer/ship/helm/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ if(viewer)
+ return
+ if(!istype(current_ship, /obj/structure/overmap/ship/simulated))
+ return
+
+ var/obj/structure/overmap/ship/simulated/S = current_ship
+
+ switch(action) // Universal topics
+ if("rename_ship")
+ if(!("newName" in params) || params["newName"] == S.name)
+ return
+ if(!S.set_ship_name(params["newName"]))
+ say("Error: [COOLDOWN_TIMELEFT(S, rename_cooldown)/10] seconds until ship designation can be changed..")
+ update_static_data(usr, ui)
+ return
+ if("reload_ship")
+ update_static_data(usr, ui)
+ return
+ if("reload_engines")
+ S.refresh_engines()
+ return
+
+ switch(S.state) // Ship state-llimited topics
+ if(OVERMAP_SHIP_FLYING)
+ switch(action)
+ if("act_overmap")
+ if(SSshuttle.jump_mode == BS_JUMP_INITIATED)
+ to_chat(usr, "You've already escaped. Never going back to that place again!")
+ return
+ var/obj/structure/overmap/to_act = locate(params["ship_to_act"])
+ say(S.overmap_object_act(usr, to_act))
+ return
+ if("toggle_engine")
+ var/obj/machinery/power/shuttle/engine/E = locate(params["engine"])
+ E.enabled = !E.enabled
+ S.refresh_engines()
+ return
+ if("thrust")
+ if(current_ship.thrust(usr, text2num(params["dir"])))
+ S.current_autopilot_target = null
+ return
+ if("stop")
+ S.current_autopilot_target = null
+ S.burn_engines()
+ return
+ if("dock_empty")
+ say(S.dock_in_empty_space(usr))
+ return
+ if(OVERMAP_SHIP_IDLE)
+ if(action == "undock")
+ S.calculate_avg_fuel()
+ if(S.avg_fuel_amnt < 25 && tgui_alert(usr, "Ship only has ~[round(S.avg_fuel_amnt)]% fuel remaining! Are you sure you want to undock?", name, list("Yes", "No")) != "Yes")
+ return
+ say(S.undock())
+ return
+
+/obj/machinery/computer/ship/helm/ui_close(mob/user)
+ ..()
+ if(current_ship)
+ user.client?.clear_map(current_ship.map_name)
+
+/obj/machinery/computer/ship/helm/viewscreen
+ name = "ship viewscreen"
+ icon = 'icons/obj/stationobjs.dmi'
+ icon_state = "telescreen"
+ layer = SIGN_LAYER
+ density = FALSE
+ viewer = TRUE
+
diff --git a/code/modules/overmap/consoles/weapons.dm b/code/modules/overmap/consoles/weapons.dm
new file mode 100644
index 000000000000..a6c7a5a7a204
--- /dev/null
+++ b/code/modules/overmap/consoles/weapons.dm
@@ -0,0 +1,84 @@
+/obj/machinery/computer/ship/weapons
+ name = "Weapons Control"
+ icon_screen = "weapons-s"
+ icon_keyboard = "weapons-k"
+ circuit = /obj/item/circuitboard/computer/ship/weapons
+ tgui_interface_id = "ShipWeaponsConsole"
+ light_color = LIGHT_COLOR_YELLOW
+ clicksound = null
+ var/list/last_hit_data
+
+/obj/item/circuitboard/computer/ship/weapons
+ name = "Weapons Control (Computer Board)"
+ build_path = /obj/machinery/computer/ship/weapons
+
+/obj/machinery/computer/ship/weapons/ui_static_data(mob/user)
+ // Generate weapon data list; this really shouldnt change very often
+ var/list/wep_data = list(
+ "weapon_lookup" = list()
+ )
+ for(var/datum/ship_module/weapon/weapon as anything in current_ship.modules[SHIP_SLOT_WEAPON])
+ var/wep_ref = ref(weapon)
+ wep_data["weapon_lookup"] += wep_ref
+ wep_data["weapon_data"][wep_ref] = list(
+ "name" = weapon.name,
+ "structure_count" = length(weapon.installed_on[current_ship]),
+ "damage_types" = convert_damage_types_to_readable_string(weapon.damage_types),
+ "damage_base" = weapon.damage,
+ "damage_variance" = weapon.damage_variance,
+ "structure_lookup" = list(),
+ "structure_data" = list()
+ )
+ return wep_data
+
+/obj/machinery/computer/ship/weapons/ui_data(mob/user)
+ var/list/data = list(
+ "structure_data" = list()
+ )
+ for(var/datum/ship_module/weapon/weapon as anything in current_ship.modules[SHIP_SLOT_WEAPON])
+ for(var/obj/structure/ship_module/weapon/struc as anything in weapon.installed_on[current_ship])
+ var/struc_ref = ref(struc)
+ data["structure_data"][struc_ref] = list(
+ "name" = struc.name,
+ "loaded" = struc.check_loaded(),
+ "ammo_max" = struc.mag_size,
+ "reloading" = !!struc.reload_timer_id,
+ "reload_time" = struc.reload_time,
+ "reload_left" = struc.reload_eta - world.time
+ )
+ data["last_hit_data"] = length(last_hit_data) ? last_hit_data : FALSE
+ return data
+
+/obj/machinery/computer/ship/weapons/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+
+ // Active weapon type
+ var/datum/ship_module/weapon/active_module = locate(params["active_module"])
+ if(!istype(active_module))
+ CRASH("Invalid active_module reference. incorrect type: [active_module.type]")
+ // selected weapon id
+ var/selected_weapon = params["weapon_select"]
+ // weapon structure
+ var/obj/structure/ship_module/weapon/weapon = active_module.get_module_structure_by_id(selected_weapon)
+ if(!weapon)
+ CRASH("Unable to find active weapon structure? invalid id: [selected_weapon]")
+
+ switch(action)
+ if("weapon-reload")
+ weapon.try_reload()
+ return TRUE
+ if("weapon-fire")
+ var/obj/structure/overmap/target = locate(params["target"])
+ if(!istype(target))
+ CRASH("Illegal target ref.")
+ var/old_integ = target.integrity
+ var/fire_resp = active_module.weapon_fire(current_ship, weapon, target)
+ last_hit_data = list()
+ last_hit_data["state"] = fire_resp
+ last_hit_data["old_integrity"] = old_integ
+ last_hit_data["new_integrity"] = target.integrity
+ last_hit_data["damage"] = old_integ - target.integrity
+ return TRUE
+ return FALSE
diff --git a/code/modules/overmap/events.dm b/code/modules/overmap/events.dm
index db873121e33e..dcb11b3c4d23 100644
--- a/code/modules/overmap/events.dm
+++ b/code/modules/overmap/events.dm
@@ -64,7 +64,7 @@
/obj/structure/overmap/event/meteor/affect_ship(obj/structure/overmap/ship/simulated/S)
var/area/source_area = pick(S.shuttle.shuttle_areas)
source_area?.set_fire_alarm_effect()
- S.recieve_damage(rand(min_damage, max_damage))
+ S.recieve_damage(rand(min_damage, max_damage), src, DAMAGE_PHYSICAL)
if(S.integrity <= 0 && source_area)
var/source_object = pick(source_area.contents)
dyn_explosion(source_object, rand(min_damage, max_damage) / 2)
@@ -107,7 +107,7 @@
/obj/structure/overmap/event/emp/affect_ship(obj/structure/overmap/ship/simulated/S)
var/area/source_area = pick(S.shuttle.shuttle_areas)
source_area.set_fire_alarm_effect()
- S.recieve_damage(rand(strength * 5, strength * 10))
+ S.recieve_damage(rand(strength * 5, strength * 10), src, DAMAGE_EMP)
if(S.integrity <= 0)
var/source_object = pick(source_area.contents)
empulse(get_turf(source_object), round(rand(strength / 2, strength)), rand(strength, strength * 2))
@@ -146,7 +146,7 @@
/obj/structure/overmap/event/electric/affect_ship(obj/structure/overmap/ship/simulated/S)
var/area/source_area = pick(S.shuttle.shuttle_areas)
source_area.set_fire_alarm_effect()
- S.recieve_damage(rand(min_damage, max_damage))
+ S.recieve_damage(rand(min_damage, max_damage), src, DAMAGE_TESLA)
if(S.integrity <= 0)
var/source_object = pick(source_area.contents)
tesla_zap(source_object, rand(min_damage, max_damage) / 2)
@@ -204,7 +204,7 @@
if(!other_wormhole)
qdel(src)
if(--stability <= 0)
- S.recieve_damage(rand(20, 30))
+ S.recieve_damage(rand(20, 30), src, list(DAMAGE_PHOTON, DAMAGE_EMP))
S.forceMove(SSovermap.get_unused_overmap_square())
QDEL_NULL(other_wormhole)
for(var/MN in GLOB.player_list)
diff --git a/code/modules/overmap/helm.dm b/code/modules/overmap/helm.dm
deleted file mode 100644
index 0e42832488ef..000000000000
--- a/code/modules/overmap/helm.dm
+++ /dev/null
@@ -1,294 +0,0 @@
-#define JUMP_STATE_OFF 0
-#define JUMP_STATE_CHARGING 1
-#define JUMP_STATE_IONIZING 2
-#define JUMP_STATE_FIRING 3
-#define JUMP_STATE_FINALIZED 4
-#define JUMP_CHARGE_DELAY (20 SECONDS)
-#define JUMP_CHARGEUP_TIME (3 MINUTES)
-
-/obj/machinery/computer/helm
- name = "helm control console"
- desc = "Used to view or control the ship."
- icon_screen = "shuttle"
- icon_keyboard = "tech_key"
- circuit = /obj/item/circuitboard/computer/shuttle/helm
- light_color = LIGHT_COLOR_FLARE
- clicksound = null
-
- /// The ship we reside on for ease of access
- var/obj/structure/overmap/ship/simulated/current_ship
- /// All users currently using this
- var/list/concurrent_users = list()
- /// Is this console view only? I.E. cant dock/etc
- var/viewer = FALSE
- /// When are we allowed to jump
- var/jump_allowed
- /// Current state of our jump
- var/jump_state = JUMP_STATE_OFF
- ///if we are calibrating the jump
- var/calibrating = FALSE
- ///holding jump timer ID
- var/jump_timer
-
-/datum/config_entry/number/bluespace_jump_wait
- default = 30 MINUTES
-
-/obj/machinery/computer/helm/Initialize(mapload, obj/item/circuitboard/C)
- . = ..()
- jump_allowed = world.time + CONFIG_GET(number/bluespace_jump_wait)
- addtimer(CALLBACK(src, .proc/reload_ship), 5)
-
-/obj/machinery/computer/helm/proc/calibrate_jump(inline = FALSE)
- if(jump_allowed < 0)
- say("Bluespace Jump Calibration offline. Please contact your system administrator.")
- return
- if(current_ship.state != OVERMAP_SHIP_FLYING)
- say("Bluespace Jump Calibration detected interference in the local area.")
- return
- if(world.time < jump_allowed)
- var/jump_wait = DisplayTimeText(jump_allowed - world.time)
- say("Bluespace Jump Calibration is currently recharging. ETA: [jump_wait].")
- return
- if(jump_state != JUMP_STATE_OFF && !inline)
- return // This exists to prefent Href exploits to call process_jump more than once by a client
- message_admins("[ADMIN_LOOKUPFLW(usr)] has initiated a bluespace jump in [ADMIN_VERBOSEJMP(src)]")
- jump_timer = addtimer(CALLBACK(src, .proc/jump_sequence, TRUE), JUMP_CHARGEUP_TIME, TIMER_STOPPABLE)
- priority_announce("Bluespace jump calibration initialized. Calibration completion in [JUMP_CHARGEUP_TIME/600] minutes.", sender_override="[current_ship.name] Bluespace Pylon", zlevel=get_virtual_z_level())
- calibrating = TRUE
- return TRUE
-
-/obj/machinery/computer/helm/proc/cancel_jump()
- priority_announce("Bluespace Pylon spooling down. Jump calibration aborted.", sender_override="[current_ship.name] Bluespace Pylon", zlevel=get_virtual_z_level())
- calibrating = FALSE
- deltimer(jump_timer)
-
-/obj/machinery/computer/helm/proc/jump_sequence()
- switch(jump_state)
- if(JUMP_STATE_OFF)
- jump_state = JUMP_STATE_CHARGING
- SStgui.close_uis(src)
- if(JUMP_STATE_CHARGING)
- jump_state = JUMP_STATE_IONIZING
- priority_announce("Bluespace Jump Calibration completed. Ionizing Bluespace Pylon.", sender_override="[current_ship.name] Bluespace Pylon", zlevel=get_virtual_z_level())
- if(JUMP_STATE_IONIZING)
- jump_state = JUMP_STATE_FIRING
- priority_announce("Bluespace Ionization finalized; preparing to fire Bluespace Pylon.", sender_override="[current_ship.name] Bluespace Pylon", zlevel=get_virtual_z_level())
- if(JUMP_STATE_FIRING)
- jump_state = JUMP_STATE_FINALIZED
- priority_announce("Bluespace Pylon launched.", sender_override="[current_ship.name] Bluespace Pylon", sound='sound/magic/lightning_chargeup.ogg', zlevel=get_virtual_z_level())
- addtimer(CALLBACK(src, .proc/do_jump), 10 SECONDS)
- return
- addtimer(CALLBACK(src, .proc/jump_sequence, TRUE), JUMP_CHARGE_DELAY)
-
-/obj/machinery/computer/helm/proc/do_jump()
- priority_announce("Bluespace Jump Initiated.", sender_override="[current_ship.name] Bluespace Pylon", sound='sound/magic/lightningbolt.ogg', zlevel=get_virtual_z_level())
- current_ship.shuttle.intoTheSunset()
-
-/obj/machinery/computer/helm/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
- current_ship = port.current_ship
-
-/**
- * This proc manually rechecks that the helm computer is connected to a proper ship
- */
-/obj/machinery/computer/helm/proc/reload_ship()
- var/obj/docking_port/mobile/port = SSshuttle.get_containing_shuttle(src)
- if(port?.current_ship)
- current_ship = port.current_ship
- return TRUE
-
-/obj/machinery/computer/helm/ui_interact(mob/user, datum/tgui/ui)
- if(jump_state != JUMP_STATE_OFF)
- say("Bluespace Jump in progress. Controls suspended.")
- return
- // Update UI
- if(!current_ship && !reload_ship())
- return
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- var/user_ref = REF(user)
- var/is_living = isliving(user)
- // Ghosts shouldn't count towards concurrent users, which produces
- // an audible terminal_on click.
- if(is_living)
- concurrent_users += user_ref
- // Turn on the console
- if(length(concurrent_users) == 1 && is_living)
- playsound(src, 'sound/machines/terminal_on.ogg', 25, FALSE)
- use_power(active_power_usage)
- // Register map objects
- if(current_ship)
- user.client.register_map_obj(current_ship.cam_screen)
- user.client.register_map_obj(current_ship.cam_plane_master)
- user.client.register_map_obj(current_ship.cam_background)
- current_ship.update_screen()
-
- // Open UI
- ui = new(user, src, "HelmConsole", name)
- ui.open()
-
-/obj/machinery/computer/helm/ui_data(mob/user)
- . = list()
- .["integrity"] = current_ship.integrity
- .["calibrating"] = calibrating
- .["otherInfo"] = list()
- for (var/object in current_ship.close_overmap_objects)
- var/obj/structure/overmap/O = object
- var/list/other_data = list(
- name = O.name,
- integrity = O.integrity,
- ref = REF(O)
- )
- .["otherInfo"] += list(other_data)
-
- var/turf/T = get_turf(current_ship)
- .["x"] = T.x
- .["y"] = T.y
-
- if(!istype(current_ship, /obj/structure/overmap/ship/simulated))
- return
-
- var/obj/structure/overmap/ship/simulated/S = current_ship
-
- .["state"] = S.state
- .["docked"] = isturf(S.loc) ? FALSE : TRUE
- .["heading"] = dir2text(S.get_heading()) || "None"
- .["speed"] = S.get_speed()
- .["eta"] = S.get_eta()
- .["est_thrust"] = S.est_thrust
- .["engineInfo"] = list()
- for(var/obj/machinery/power/shuttle/engine/E in S.shuttle.engine_list)
- var/list/engine_data
- if(!E.thruster_active)
- engine_data = list(
- name = E.name,
- fuel = 0,
- maxFuel = 100,
- enabled = E.enabled,
- ref = REF(E)
- )
- else
- engine_data = list(
- name = E.name,
- fuel = E.return_fuel(),
- maxFuel = E.return_fuel_cap(),
- enabled = E.enabled,
- ref = REF(E)
- )
- .["engineInfo"] += list(engine_data)
-
-/obj/machinery/computer/helm/ui_static_data(mob/user)
- . = list()
- .["isViewer"] = viewer
- .["mapRef"] = current_ship.map_name
-
- var/class_name = istype(current_ship, /obj/structure/overmap/ship) ? "Ship" : "Planetoid"
- .["shipInfo"] = list(
- name = current_ship.name,
- can_rename = class_name == "Ship",
- class = class_name,
- mass = current_ship.mass,
- sensor_range = current_ship.sensor_range,
- ref = REF(current_ship)
- )
- if(class_name == "Ship")
- .["canFly"] = TRUE
-
-
-/obj/machinery/computer/helm/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
- . = ..()
- if(.)
- return
- if(viewer)
- return
- if(!istype(current_ship, /obj/structure/overmap/ship/simulated))
- return
-
- var/obj/structure/overmap/ship/simulated/S = current_ship
-
- switch(action) // Universal topics
- if("rename_ship")
- if(!("newName" in params) || params["newName"] == S.name)
- return
- if(!S.set_ship_name(params["newName"]))
- say("Error: [COOLDOWN_TIMELEFT(S, rename_cooldown)/10] seconds until ship designation can be changed..")
- update_static_data(usr, ui)
- return
- if("reload_ship")
- reload_ship()
- update_static_data(usr, ui)
- return
- if("reload_engines")
- S.refresh_engines()
- return
-
- switch(S.state) // Ship state-llimited topics
- if(OVERMAP_SHIP_FLYING)
- switch(action)
- if("act_overmap")
- if(SSshuttle.jump_mode == BS_JUMP_INITIATED)
- to_chat(usr, "You've already escaped. Never going back to that place again!")
- return
- var/obj/structure/overmap/to_act = locate(params["ship_to_act"])
- say(S.overmap_object_act(usr, to_act))
- return
- if("toggle_engine")
- var/obj/machinery/power/shuttle/engine/E = locate(params["engine"])
- E.enabled = !E.enabled
- S.refresh_engines()
- return
- if("change_heading")
- S.current_autopilot_target = null
- S.burn_engines(text2num(params["dir"]))
- return
- if("stop")
- S.current_autopilot_target = null
- S.burn_engines()
- return
- if("bluespace_jump")
- if(calibrating)
- cancel_jump()
- return
- else
- if(tgui_alert(usr, "Do you want to bluespace jump? Your ship and everything on it will be removed from the round.", "Jump Confirmation", list("Yes", "No")) != "Yes")
- return
- calibrate_jump()
- return
- if("dock_empty")
- say(S.dock_in_empty_space(usr))
- return
- if(OVERMAP_SHIP_IDLE)
- if(action == "undock")
- S.calculate_avg_fuel()
- if(S.avg_fuel_amnt < 25 && tgui_alert(usr, "Ship only has ~[round(S.avg_fuel_amnt)]% fuel remaining! Are you sure you want to undock?", name, list("Yes", "No")) != "Yes")
- return
- say(S.undock())
- return
-
-/obj/machinery/computer/helm/ui_close(mob/user)
- var/user_ref = REF(user)
- var/is_living = isliving(user)
- // Living creature or not, we remove you anyway.
- concurrent_users -= user_ref
- // Unregister map objects
- if(current_ship)
- user.client?.clear_map(current_ship.map_name)
- // Turn off the console
- if(length(concurrent_users) == 0 && is_living)
- playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
- use_power(0)
-
-/obj/machinery/computer/helm/viewscreen
- name = "ship viewscreen"
- icon = 'icons/obj/stationobjs.dmi'
- icon_state = "telescreen"
- layer = SIGN_LAYER
- density = FALSE
- viewer = TRUE
-
-#undef JUMP_STATE_OFF
-#undef JUMP_STATE_CHARGING
-#undef JUMP_STATE_IONIZING
-#undef JUMP_STATE_FIRING
-#undef JUMP_STATE_FINALIZED
-#undef JUMP_CHARGE_DELAY
-#undef JUMP_CHARGEUP_TIME
diff --git a/code/modules/overmap/ships/ship_module/_module_structure.dm b/code/modules/overmap/ships/ship_module/_module_structure.dm
new file mode 100644
index 000000000000..2d742ba5b0a2
--- /dev/null
+++ b/code/modules/overmap/ships/ship_module/_module_structure.dm
@@ -0,0 +1,54 @@
+/obj/structure/ship_module
+ name = "Broken Ship Module"
+ icon = 'icons/obj/ship_modules/ship_modules.dmi'
+ icon_state = "default"
+ /// The ship we are located on
+ var/obj/structure/overmap/ship/simulated/parent = null
+ /// The instance of the module managing us
+ var/datum/ship_module/module_instance
+ /// Should this structure process
+ var/structure_process = FALSE
+ /// Structure ID of this module
+ var/structure_id
+ /// Static var containing the next structure id
+ var/static/structure_id_next = 0
+
+/obj/structure/ship_module/Initialize(datum/ship_module/module_instance, obj/structure/overmap/ship/simulated/parent, mob/user)
+ . = ..()
+ src.module_instance = module_instance
+ src.parent = parent
+ structure_id = ++structure_id_next
+
+/obj/structure/ship_module/Destroy()
+ . = ..()
+ structure_process = FALSE
+ parent = null
+ module_instance.uninstall(parent)
+ module_instance = null
+
+/**
+ * Process an action for the module. Note that this is seperate from ui_act because this handles other modules interacting with us
+ * If you are trying to use inter-module communication call this proc ONLY
+ */
+/obj/structure/ship_module/proc/module_act(mob/user, action, list/params)
+ return
+
+/obj/structure/ship_module/proc/ship_damage(ship, damage, damage_type, originator)
+ SIGNAL_HANDLER
+ return SHIP_ALLOW
+
+/obj/structure/ship_module/proc/ship_thrust(ship, thrust, direction)
+ SIGNAL_HANDLER
+ return SHIP_ALLOW
+
+/obj/structure/ship_module/proc/ship_move(ship, new_loc, old_loc)
+ SIGNAL_HANDLER
+ return SHIP_ALLOW
+
+/obj/structure/ship_module/proc/ship_dock(ship, dock)
+ SIGNAL_HANDLER
+ return SHIP_ALLOW
+
+/obj/structure/ship_module/proc/ship_undock(ship, dock)
+ SIGNAL_HANDLER
+ return SHIP_ALLOW
diff --git a/code/modules/overmap/ships/ship_module/_ship_module.dm b/code/modules/overmap/ships/ship_module/_ship_module.dm
new file mode 100644
index 000000000000..09a6addb3c75
--- /dev/null
+++ b/code/modules/overmap/ships/ship_module/_ship_module.dm
@@ -0,0 +1,169 @@
+/datum/ship_module
+ /// Internal use
+ var/abstract = /datum/ship_module
+ /// Name of the module
+ var/name = "Defunct Ship Module"
+ /// Description of the module; try to be at least semi-helpful
+ var/description = "Broken Module, ahelp"
+ /// Actual modularity cost of the module, this can be NEGATIVE!!
+ var/modularity_cost = 0
+ /// Cost of this module in material resources.
+ var/list/material_cost = list(
+ /datum/material/iron = 10000,
+ /datum/material/plasma = 10000
+ )
+ /// The slot this module is installed into. See ship_module_defines.dm
+ var/slot = SHIP_SLOT_NONE
+ /// The flags for this module. See ship_module_defines.dm
+ var/flags = SHIP_MODULE_UNIQUE
+ /// Should this module start processing after being Install'd
+ var/should_process = FALSE
+ /// Installed ships, for tracking and helper proc purposes
+ var/list/installed_on = list()
+ /// The strucutre to spawn when a module is installed onto a ship
+ var/structure_path = /obj/structure/ship_module
+ /// Stored data indexed by parent ship
+ var/static/list/ship_data = new
+
+/datum/ship_module/New()
+ . = ..()
+ if(should_process)
+ START_PROCESSING(SSovermap, src)
+
+/datum/ship_module/Destroy(force)
+ if(type == abstract && !force)
+ stack_trace("QDEL CALLED ON SHIP MODULE")
+ return QDEL_HINT_LETMELIVE
+ else if(force)
+ stack_trace("FORCED QDEL ON SHIP MODULE")
+ STOP_PROCESSING(SSovermap, src)
+ for(var/ship in installed_on)
+ uninstall(ship)
+ if(length(ship_data))
+ stack_trace("After uninstalling module from all ships there is leftover ship data?")
+ ship_data.Cut()
+ ship_data = null
+ return ..()
+
+/datum/ship_module/proc/can_install(obj/structure/overmap/ship/simulated/ship)
+ SHOULD_CALL_PARENT(TRUE)
+ if(slot == SHIP_SLOT_NONE)
+ return MODULE_INSTALL_BAD_GENERIC
+ if(!istype(ship))
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Invalid ship reference for manual module installation, it needs to be the overmap ship object.")
+ return MODULE_INSTALL_BAD_GENERIC
+ var/list/ship_modules = ship.modules
+ var/list/slot_modules = ship_modules[slot]
+ if((flags & SHIP_MODULE_UNIQUE) && length(slot_modules))
+ return MODULE_INSTALL_BAD_UNIQUE
+ if(is_installed(ship))
+ return MODULE_INSTALL_BAD_SINGULAR
+ if(ship.calculate_modularity_left() < modularity_cost)
+ return MODULE_INSTALL_BAD_COST
+ for(var/slot in ship.modules)
+ for(var/datum/ship_module/module as anything in ship.modules[slot])
+ if(module.install_conflicts(ship, src))
+ return MODULE_INSTALL_BAD_CONFLICT
+ return MODULE_INSTALL_GOOD
+
+/datum/ship_module/process()
+ for(var/ship in installed_on)
+ var/obj/structure/ship_module/structure = installed_on[ship]
+ if(structure.structure_process)
+ structure.process()
+
+/datum/ship_module/proc/install_conflicts(obj/structure/overmap/ship/simulated/ship, datum/ship_module/other)
+ return FALSE
+
+/datum/ship_module/proc/is_installed(obj/structure/overmap/ship/simulated/ship)
+ return ship in installed_on
+
+/datum/ship_module/proc/install(obj/structure/overmap/ship/simulated/ship, mob/user, turf/location)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!can_install(ship, user))
+ return FALSE
+ if(!installed_on[ship])
+ ship.modules[slot] |= src
+ installed_on[ship] = list()
+ RegisterSignal(ship, COMSIG_PARENT_QDELETING, .proc/handle_ship_qdel)
+ RegisterSignal(ship, COMSIG_SHIP_DAMAGE, .proc/handle_ship_damage)
+ RegisterSignal(ship, COMSIG_SHIP_THRUST, .proc/handle_ship_thrust)
+ RegisterSignal(ship, COMSIG_SHIP_MOVE, .proc/handle_ship_move)
+ RegisterSignal(ship, COMSIG_SHIP_DOCK, .proc/handle_ship_dock)
+ RegisterSignal(ship, COMSIG_SHIP_UNDOCK, .proc/handle_ship_undock)
+ installed_on[ship] += new structure_path(location, src, ship, user)
+ notify_modules(ship, user, "install", list("module" = src))
+ return TRUE
+
+/datum/ship_module/proc/notify_modules(obj/structure/overmap/ship/simulated/ship, mob/user, action, list/params, structure_id = 0)
+ var/static/list/alerted = new
+ if(length(alerted))
+ alerted.Cut()
+ for(var/slot in ship.modules)
+ for(var/datum/ship_module/module as anything in ship.modules[slot])
+ for(var/obj/structure/ship_module/module_structure as anything in module.installed_on[ship])
+ if(structure_id && structure_id != module_structure.structure_id)
+ continue
+ if(module_structure in alerted)
+ continue
+ alerted |= module_structure
+ module_structure.module_act(user, action, params)
+
+/datum/ship_module/proc/uninstall(obj/structure/overmap/ship/simulated/ship, obj/structure/ship_module/module_structure)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!(module_structure in installed_on[ship]))
+ CRASH("uninstall called on illegal module structure? [module_structure] not in module map")
+ notify_modules(ship, null, "uninstall", list("module" = src, "structure" = module_structure))
+ installed_on[ship] -= module_structure
+ qdel(module_structure)
+ if(!length(installed_on[ship]))
+ ship.modules[slot] -= src
+ ship_data -= ship
+ UnregisterSignal(ship, list(COMSIG_PARENT_QDELETING, COMSIG_SHIP_DAMAGE, COMSIG_SHIP_THRUST, COMSIG_SHIP_MOVE, COMSIG_SHIP_DOCK, COMSIG_SHIP_UNDOCK))
+ return TRUE
+
+/datum/ship_module/proc/get_module_structure_by_id(ship, id)
+ for(var/obj/structure/ship_module/struc as anything in installed_on[ship])
+ if(struc.structure_id == id)
+ return struc
+
+/datum/ship_module/proc/handle_ship_qdel(ship)
+ SIGNAL_HANDLER
+ UnregisterSignal(ship, COMSIG_PARENT_QDELETING)
+ installed_on -= ship
+
+/datum/ship_module/proc/handle_ship_damage(ship, damage, damage_type, originator)
+ SIGNAL_HANDLER
+ var/obj/structure/ship_module/structure = installed_on[ship]
+ if(!structure)
+ CRASH("Calling ship event handler on ship without module structure?")
+ return structure.ship_damage(ship, damage, damage_type, originator)
+
+/datum/ship_module/proc/handle_ship_thrust(ship, thrust, direction)
+ SIGNAL_HANDLER
+ var/obj/structure/ship_module/structure = installed_on[ship]
+ if(!structure)
+ CRASH("Calling ship event handler on ship without module structure?")
+ return structure.ship_thrust(ship, thrust, direction)
+
+/datum/ship_module/proc/handle_ship_move(ship, new_loc, old_loc)
+ SIGNAL_HANDLER
+ var/obj/structure/ship_module/structure = installed_on[ship]
+ if(!structure)
+ CRASH("Calling ship event handler on ship without module structure?")
+ return structure.ship_move(ship, new_loc, old_loc)
+
+/datum/ship_module/proc/handle_ship_dock(ship, dock)
+ SIGNAL_HANDLER
+ var/obj/structure/ship_module/structure = installed_on[ship]
+ if(!structure)
+ CRASH("Calling ship event handler on ship without module structure?")
+ return structure.ship_dock(ship, dock)
+
+/datum/ship_module/proc/handle_ship_undock(ship, dock)
+ SIGNAL_HANDLER
+ var/obj/structure/ship_module/structure = installed_on[ship]
+ if(!structure)
+ CRASH("Calling ship event handler on ship without module structure?")
+ return structure.ship_undock(ship, dock)
diff --git a/code/modules/overmap/ships/ship_module/energy_shield.dm b/code/modules/overmap/ships/ship_module/energy_shield.dm
new file mode 100644
index 000000000000..d2c0b8695cc6
--- /dev/null
+++ b/code/modules/overmap/ships/ship_module/energy_shield.dm
@@ -0,0 +1,147 @@
+/datum/ship_module/shield
+ name = "Energy Shield"
+ slot = SHIP_SLOT_SHIELD
+ modularity_cost = 10
+ material_cost = list(
+ /datum/material/glass = 10000,
+ /datum/material/iron = 10000
+ )
+ should_process = TRUE
+ structure_path = /obj/structure/ship_module/shield
+
+/obj/structure/overmap/ship/simulated/proc/has_active_shield()
+ var/datum/ship_module/shield/shield_instance = GLOB.ship_modules[/datum/ship_module/shield]
+ var/obj/structure/ship_module/shield/shield_struc = shield_instance.installed_on[src]
+ return(shield_struc?.charge)
+
+/obj/structure/ship_module/shield
+ name = "Energy Shield Core"
+ icon_state = "shield_base"
+ structure_process = TRUE
+ /// The current charge of the shield
+ var/charge = 0
+ /// The charge that the shield begins with.
+ var/charge_base = 20
+ /// The amount of charge the shield is allowed to spool to
+ var/charge_limit
+ /// The rate at which this shield can charge
+ var/charge_rate = 20
+ /// The maximum charge allowed for the shield
+ var/charge_max = 100
+ /// The power required to spool the shield. THIS IS PER POINT OF CHARGE
+ var/charge_cost = 4
+ /// The static number to add to the charge limit for each pylon
+ var/pylon_add = 2
+ /// The static number to multiply for each concentrator, multiplicative
+ var/concentrator_mult = 1.2
+ /// How long does the shield take to recover from a zero sum charge
+ var/spool_windup = 2 SECONDS
+ /// Are we actively spooling
+ var/spool_winding = FALSE
+ /// Whether or not this shield has finished spooling
+ var/spool_active = FALSE
+ /// The world.time we will begin attempting to spool
+ var/spool_time
+
+ /// Internal list for calculating the charge limit
+ var/list/prop_cache = new
+
+/obj/structure/ship_module/shield/process()
+ update_icon()
+ var/area/local_area = get_area(src)
+ var/obj/machinery/power/apc/local_apc = local_area.get_apc()
+ if(!spool_active)
+ if(!spool_winding && spool_time < world.time)
+ INVOKE_ASYNC(src, .proc/spool)
+ return
+ if(local_apc.operating && local_apc.equipment)
+ var/wanted = min(charge_limit - charge, charge_rate)
+ var/avail = local_apc?.avail()
+ wanted = min(avail, wanted)
+ if(wanted)
+ local_apc.use_power(wanted * charge_cost)
+ charge += wanted
+ if(!charge)
+ visible_message("[src] blares a warning as the flywheel suddenly grinds to a halt, the magnetic field scattering and failing.")
+ playsound(src, 'sound/machines/defib_failed.ogg', 100)
+ for(var/i in 1 to 2)
+ tesla_zap(src, 2, 5, ZAP_MOB_STUN)
+ spool_active = FALSE
+ spool_winding = FALSE
+ spool_time = world.time + spool_windup
+ return
+
+/obj/structure/ship_module/shield/ship_damage(ship, damage, damage_type, originator)
+ . = SHIP_ALLOW
+ var/effective_damage = damage
+ if(IS_DAMAGE_TYPE(damage_type, DAMAGE_EMP)) // EMPs deal double damage to shields, always
+ effective_damage *= 2
+ . = SHIP_FORCE_BLOCK // If we are hit with an EMP we always force block it, unless the shield is down
+ charge -= effective_damage
+ // After we take a hit force a process cycle
+ process()
+ if(charge) // If we have remaining charge it means that we blocked the shot
+ return SHIP_FORCE_BLOCK
+
+/obj/structure/ship_module/shield/update_overlays()
+ . = ..()
+ if(charge)
+ . += mutable_appearance(icon, "shield_online")
+ else
+ if(spool_winding)
+ . += mutable_appearance(icon, "shield_spooling")
+ else
+ . += mutable_appearance(icon, "shield_offline")
+
+/obj/structure/ship_module/shield/proc/spool()
+ if(spool_winding)
+ return
+ spool_winding = TRUE
+ update_icon()
+ visible_message("[src] begins to emit a loud whine as the tungsten-phoron flywheel begins to spool.")
+ sleep(5 SECONDS)
+ visible_message("[src] blares an alarm as the flywheel approaches terminal velocity. It might be a good idea to back away.")
+ playsound(src, 'sound/machines/defib_failed.ogg', 100)
+ sleep(5 SECONDS)
+ visible_message("[src] suddenly lurches as the flywheel engages with the ionic resonators; creating an intense localized magnetic field.")
+ for(var/mob/living/onlooker in viewers(3, src))
+ onlooker.emp_act(EMP_HEAVY)
+ sleep(5 SECONDS)
+ visible_message("[src] chimes as the morphic reflector concentrates and ionizes the magnetic field; the shield projector is now active.")
+ playsound(src, 'sound/machines/chime.ogg', 50)
+ calculate_max_shield()
+ charge = round(charge_limit / 10)
+ spool_active = TRUE
+ update_icon()
+
+/obj/structure/ship_module/shield/proc/calculate_max_shield()
+ for(var/prop in prop_cache)
+ UnregisterSignal(prop, COMSIG_PARENT_QDELETING)
+ prop_cache.Cut()
+ propagate(prop_cache)
+ var/running_total = charge_base
+ for(var/obj/structure/ship_module/shield/pylon/py in prop_cache)
+ running_total += pylon_add
+ for(var/obj/structure/ship_module/shield/concentrator/cc in prop_cache)
+ running_total *= concentrator_mult
+ charge_limit = min(running_total, charge_max)
+
+/obj/structure/ship_module/shield/proc/propagate(list/cache)
+ for(var/dir in list(NORTH, SOUTH, EAST, WEST))
+ var/turf/neighbor = get_step(src, dir)
+ var/obj/structure/ship_module/shield/prop = locate() in neighbor
+ if(!prop || (prop in cache))
+ continue
+ cache |= prop
+ RegisterSignal(prop, COMSIG_PARENT_QDELETING, .proc/calculate_max_shield)
+ prop.propagate(cache)
+
+// These inherit the above procs, but they really shouldnt, except for propagate, but ohwell
+
+/obj/structure/ship_module/shield/pylon
+ name = "Energy Shield Injection Pylon"
+ icon_state = "shield_pylon"
+
+/obj/structure/ship_module/shield/concentrator
+ name = "Energy Shield Tesla Reflector"
+ icon_state = "shield_conc"
diff --git a/code/modules/overmap/ships/ship_module/module_builder.dm b/code/modules/overmap/ships/ship_module/module_builder.dm
new file mode 100644
index 000000000000..1175c7b2c363
--- /dev/null
+++ b/code/modules/overmap/ships/ship_module/module_builder.dm
@@ -0,0 +1,140 @@
+/obj/item/ship_module_handheld
+ name = "Prototype F-24R Contruction Assistant"
+ desc = "Using the modern wonders of nanobots and streamlined material liquefaction technology this handheld has the ability to use a linked silo to create specialized structures for your vessel."
+ var/obj/machinery/computer/ship/helm/linked_helm
+ var/datum/component/remote_materials/materials
+ var/datum/ship_module/selected_module
+ var/turf/target_turf
+
+/obj/item/ship_module_handheld/Initialize(mapload)
+ . = ..()
+ materials = AddComponent(/datum/component/remote_materials, "module_handheld", mapload, FALSE)
+
+/obj/item/ship_module_handheld/Destroy()
+ . = ..()
+ QDEL_NULL(materials)
+ linked_helm = null
+ selected_module = null
+
+/obj/item/ship_module_handheld/proc/can_afford_selected()
+ if(!materials.silo)
+ return FALSE
+ var/datum/component/material_container/silo_materials = materials.silo.GetComponent(/datum/component/material_container)
+ var/list/needed = selected_module.material_cost.Copy()
+ for(var/datum/material/mat as anything in needed)
+ var/left = needed[mat] -= silo_materials.materials[mat]
+ if(left <= 0)
+ needed -= mat
+ if(length(needed))
+ return FALSE
+ return TRUE
+
+/obj/item/ship_module_handheld/proc/material_list_readable(list/mat_list)
+ . = list()
+ for(var/datum/material/mat as anything in mat_list)
+ .[mat.name] = mat_list[mat]
+
+/obj/item/ship_module_handheld/ui_static_data(mob/user)
+ . = ..()
+ var/list/module_lookup = new
+ for(var/m_path in GLOB.ship_modules)
+ var/datum/ship_module/module = GLOB.ship_modules[m_path]
+ var/list/m_data = new
+ m_data["name"] = module.name
+ m_data["modularity_cost"] = module.modularity_cost
+ m_data["material_cost"] = material_list_readable(module.material_cost)
+ m_data["slot"] = module.slot
+ m_data["install_allow"] = module.can_install(linked_helm.current_ship) == MODULE_INSTALL_GOOD
+ m_data["install_reason"] = module_install_reason(m_data["can_install"])
+ module_lookup["[m_path]"] = m_data
+ .["module_lookup"] = module_lookup
+
+/obj/item/ship_module_handheld/attack_obj(obj/machinery/computer/ship/helm/attacked, mob/living/user)
+ if(!istype(attacked))
+ return ..()
+ linked_helm = attacked
+ say("Linked to Ship Database.")
+ return TRUE
+
+/obj/item/ship_module_handheld/attack_self(mob/user)
+ if(!linked_helm || (linked_helm.get_virtual_z_level() != get_virtual_z_level()))
+ linked_helm = null
+ say("Linking failure with Ship Database.")
+ return TRUE
+ ui_interact(user)
+
+/obj/item/ship_module_handheld/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ShipModuleHandheld")
+ ui.open()
+ ui.set_autoupdate(TRUE)
+
+/obj/item/ship_module_handheld/ui_data(mob/user)
+ . = ..()
+ var/obj/structure/overmap/ship/simulated/current_ship = linked_helm.current_ship
+ var/list/ship_data = list(
+ "name" = current_ship.name,
+ "position" = list(
+ "x" = current_ship.x,
+ "y" = current_ship.y,
+ "z" = current_ship.get_virtual_z_level()
+ ),
+ "valid_target" = !!target_turf,
+ "target" = list(
+ "name" = target_turf?.name || "no target",
+ "x" = target_turf?.x || -1,
+ "y" = target_turf?.y || -1,
+ "z" = target_turf?.get_virtual_z_level() || -1
+ )
+ )
+ .["ship_data"] = ship_data
+ var/list/selected_data = list(
+ "name" = selected_module.name,
+ "desc" = selected_module.description,
+ "slot" = selected_module.slot,
+ "modularity_cost" = selected_module.modularity_cost,
+ "material_cost" = material_list_readable(selected_module.material_cost)
+ "can_afford" = can_afford_selected()
+ )
+ .["selected_data"] = selected_data
+ .["silo_link"] = !!materials.silo
+
+/obj/item/ship_module_handheld/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("set_target")
+ target_turf = get_turf(src)
+ say("Updated target turf to: [target_turf].")
+ return TRUE
+ if("build")
+ if(!target_turf)
+ say("Invalid target for construction.")
+ return TRUE
+ var/datum/ship_module/module = GLOB.ship_modules[params["module"]]
+ if(!istype(module))
+ say("Error synchronizing with module database.")
+ stack_trace("Failed to retrieve module instance for given path '[params["module"]]'")
+ return TRUE
+ if(module.can_install(linked_helm.current_ship) != MODULE_INSTALL_GOOD)
+ say("Failed to begin installation of module.")
+ return TRUE
+ module.install(linked_helm.current_ship, usr, target_turf)
+ return TRUE
+ if("uninstall")
+ if(!target_turf)
+ say("Invalid target for construction.")
+ return TRUE
+ var/datum/ship_module/module = GLOB.ship_modules[params["module"]]
+ if(!istype(module))
+ say("Error synchronizing with module database.")
+ stack_trace("Failed to retrieve module instance for given path '[params["module"]]'")
+ return TRUE
+ var/obj/structure/ship_module/target = locate(module.structure_path) in target_turf
+ if(!istype(target))
+ say("Invalid target location.")
+ return TRUE
+ module.uninstall(linked_helm.current_ship, target)
diff --git a/code/modules/overmap/ships/ship_module/weapons/_weapon_module.dm b/code/modules/overmap/ships/ship_module/weapons/_weapon_module.dm
new file mode 100644
index 000000000000..37db732e1475
--- /dev/null
+++ b/code/modules/overmap/ships/ship_module/weapons/_weapon_module.dm
@@ -0,0 +1,88 @@
+/datum/ship_module/weapon
+ abstract = /datum/ship_module/weapon
+ slot = SHIP_SLOT_WEAPON
+ flags = 0 // Weapons aren't unique unless sub types want them to be
+ /// A list of all the damage types this weapon inflicts
+ var/list/damage_types = list(DAMAGE_PHYSICAL)
+ /// The actual damage the weapon deals
+ var/damage = 0
+ /// The amount of damage at random that is added/subtracted
+ var/damage_variance = 0
+
+/datum/ship_module/weapon/proc/weapon_fire(obj/structure/overmap/ship/simulated/parent, obj/structure/ship_module/weapon/struc, obj/structure/overmap/ship/simulated/target)
+ if(QDELETED(struc.loaded))
+ return WEAPON_EMPTY
+ if(!struc.obj_integrity)
+ return WEAPON_BROKEN
+ if(struc.reload_timer_id)
+ return WEAPON_RELOADING
+ if(!struc.weapon_fire(parent, struc, target))
+ return WEAPON_FAIL
+ if(damage_variance)
+ var/variance = rand(-damage_variance, damage_variance)
+ damage += variance
+ if(damage <= 0)
+ return WEAPON_RICHOCHET
+ return weapon_hit(parent, struc, target, damage)
+
+/datum/ship_module/weapon/proc/weapon_hit(obj/structure/overmap/ship/simulated/parent, obj/structure/ship_module/weapon/struc, obj/structure/overmap/ship/simulated/target, damage)
+ if(target.recieve_damage(damage, parent, damage_types))
+ return WEAPON_HIT
+ return WEAPON_MISS
+
+/obj/structure/ship_module/weapon
+ name = "Default Weapon Module"
+ /// Currently loaded shell
+ var/atom/movable/loaded
+ /// Maximum ammo that can be chambered into the magazine
+ var/mag_size = 1
+ /// The expected world.time of reload finish
+ var/reload_eta = FALSE
+ /// How long does a reload take
+ var/reload_time = 2 SECONDS
+ /// timer ID for our reload
+ var/reload_timer_id
+ /// The actual ammo this weapon uses to fire
+ var/ammo_type = /obj/item/ammo_casing/magic/fireball
+
+/obj/structure/ship_module/weapon/attackby(obj/item/hitby, mob/living/user, params)
+ if(!istype(hitby, ammo_type))
+ return ..()
+ . = TRUE
+ if(length(contents) >= mag_size)
+ to_chat(user, "You cannot fit [hitby] into [src].")
+ else
+ to_chat(user, "You insert [hitby] into [src].")
+ hitby.forceMove(src)
+
+/obj/structure/ship_module/weapon/proc/check_ammo()
+ return istype(loaded, ammo_type)
+
+/obj/structure/ship_module/weapon/proc/check_loaded()
+ return !QDELETED(loaded)
+
+/obj/structure/ship_module/weapon/proc/weapon_fire(obj/structure/overmap/ship/simulated/parent, obj/structure/ship_module/weapon/struc, obj/structure/overmap/ship/simulated/target)
+ explosion(src, 0, 0, 0, 3)
+ . = check_ammo()
+ QDEL_NULL(loaded)
+
+/obj/structure/ship_module/weapon/proc/try_reload()
+ if(reload_timer_id)
+ return
+ if(!reload_start())
+ return FALSE
+ reload_timer_id = addtimer(CALLBACK(src, .proc/reload_finish), reload_time, TIMER_STOPPABLE|TIMER_OVERRIDE|TIMER_UNIQUE)
+ reload_eta = world.time + timeleft(reload_timer_id)
+ return TRUE
+
+/obj/structure/ship_module/weapon/proc/reload_start()
+ return TRUE
+
+/obj/structure/ship_module/weapon/proc/reload_finish()
+ SHOULD_CALL_PARENT(TRUE)
+ reload_timer_id = null
+ if(!QDELETED(loaded))
+ loaded.forceMove(loc)
+ for(var/ammo in contents)
+ loaded = ammo
+ return
diff --git a/code/modules/overmap/ships/ship_module/weapons/emp_cannon.dm b/code/modules/overmap/ships/ship_module/weapons/emp_cannon.dm
new file mode 100644
index 000000000000..63f4e1accc03
--- /dev/null
+++ b/code/modules/overmap/ships/ship_module/weapons/emp_cannon.dm
@@ -0,0 +1,9 @@
+/datum/ship_module/weapon/emp_cannon
+ name = "EMP Cannon"
+ damage = 30
+ damage_types = list(DAMAGE_EMP)
+
+/obj/structure/ship_module/weapon/emp_cannon
+ name = "EMP Cannon"
+ reload_time = 20 SECONDS
+ ammo_type = /obj/item/ammo_casing/emp_charge
diff --git a/code/modules/overmap/ships/ship_module/weapons/flak_cannon.dm b/code/modules/overmap/ships/ship_module/weapons/flak_cannon.dm
new file mode 100644
index 000000000000..c0a6db77e47e
--- /dev/null
+++ b/code/modules/overmap/ships/ship_module/weapons/flak_cannon.dm
@@ -0,0 +1,9 @@
+/datum/ship_module/weapon/flak_cannon
+ name = "Flak Cannon"
+ damage = 0
+ damage_variance = 20
+
+/obj/structure/ship_module/weapon/flak_cannon
+ name = "Flak Cannon"
+ reload_time = 5 SECONDS
+ ammo_type = /obj/item/ammo_casing/flak_shell
diff --git a/code/modules/overmap/ships/ship_module/weapons/missile_launcher.dm b/code/modules/overmap/ships/ship_module/weapons/missile_launcher.dm
new file mode 100644
index 000000000000..e94a57023667
--- /dev/null
+++ b/code/modules/overmap/ships/ship_module/weapons/missile_launcher.dm
@@ -0,0 +1,3 @@
+/datum/ship_module/weapon/missile_launcher
+ name = "Missile Launcher"
+ damage_types = list(DAMAGE_PHYSICAL, DAMAGE_EXPLOSIVE)
diff --git a/code/modules/overmap/ships/ship_module/weapons/weapon_shells.dm b/code/modules/overmap/ships/ship_module/weapons/weapon_shells.dm
new file mode 100644
index 000000000000..9750edbb83ca
--- /dev/null
+++ b/code/modules/overmap/ships/ship_module/weapons/weapon_shells.dm
@@ -0,0 +1,39 @@
+/obj/item/ammo_casing/flak_shell
+ name = "Flak Shell"
+ icon = 'icons/obj/ship_ammo.dmi'
+ icon_state = "flak_shell"
+
+/obj/item/ammo_casing/emp_charge
+ name = "EMP Charge"
+ icon = 'icons/obj/ship_ammo.dmi'
+ icon_state = "emp_shell"
+
+/obj/item/ammo_casing/missile
+ name = "Missile"
+ icon = 'icons/obj/ship_ammo.dmi'
+ icon_state = "rocket_shell"
+
+/obj/item/ammo_casing/laser_charge // TODO
+ name = "Laser Charge"
+ icon = 'icons/obj/ship_ammo.dmi'
+ icon_state = "laser_shell"
+
+/obj/item/ammo_casing/plasma_charge // TODO
+ name = "Plasma Charge"
+ icon = 'icons/obj/ship_ammo.dmi'
+ icon_state = "plasma_shell"
+
+/obj/item/ammo_casing/tesla_charge // TODO
+ name = "Tesla Charge"
+ icon = 'icons/obj/ship_ammo.dmi'
+ icon_state = "tesla_shell"
+
+/obj/item/ammo_casing/railgun_rod // TODO
+ name = "Tungsten-Carbide Rod"
+ icon = 'icons/obj/ship_ammo.dmi'
+ icon_state = "railgun_shell"
+
+/obj/item/ammo_casing/torpedo_shell // TODO
+ name = "Photon-Torpedo"
+ icon = 'icons/obj/ship_ammo.dmi'
+ icon_state = "photon_shell"
diff --git a/code/modules/overmap/ships/simulated.dm b/code/modules/overmap/ships/simulated.dm
index 0d8fe8b5ad1c..b161f4631423 100644
--- a/code/modules/overmap/ships/simulated.dm
+++ b/code/modules/overmap/ships/simulated.dm
@@ -32,6 +32,15 @@
var/obj/structure/overmap/docked
///The docking port of the linked shuttle
var/obj/docking_port/mobile/shuttle
+ /// The helm console we are linked to.
+ var/obj/machinery/computer/ship/helm/helm
+ /// All ship consoles we are linked to. Yes the helm gets its own special var, get over it.
+ var/list/obj/machinery/computer/ship/ship_computers = list()
+
+ /// The modules the ship has installed. This is an assosciative list of lists, indexed by module slot.
+ var/list/modules = new
+ /// Total modularity of this ship. aka how many modules they get to install.
+ var/modularity = -1
/obj/structure/overmap/ship/simulated/Initialize(mapload, obj/docking_port/mobile/_shuttle)
. = ..()
@@ -42,6 +51,9 @@
CRASH("Simulated overmap ship created without associated shuttle!")
name = shuttle.name
calculate_mass()
+ if(!sync_with_helms_console() && mapload)
+ stack_trace("mapload ship '[src]' spawned without a helms console on the ship itself")
+ message_admins("mapload ship [ADMIN_JMP(src)] spawned without a helms console, this may require intervention!")
initial_name()
refresh_engines()
check_loc()
@@ -49,6 +61,11 @@
/obj/structure/overmap/ship/simulated/Destroy()
. = ..()
SSovermap.simulated_ships -= src
+ modules = null
+ docked = null
+ shuttle = null
+ helm = null
+ ship_computers = null
/obj/structure/overmap/ship/simulated/attack_ghost(mob/user)
if(shuttle)
@@ -57,6 +74,22 @@
else
return
+/obj/structure/overmap/ship/simulated/proc/is_jumping()
+ for(var/obj/machinery/computer/ship/bluespace_jump/jump_console in ship_computers)
+ if(jump_console.jump_state != JUMP_STATE_OFF)
+ return TRUE
+ return FALSE
+
+/obj/structure/overmap/ship/simulated/proc/calculate_modularity_left()
+ . = modularity
+ for(var/slot in modules)
+ for(var/datum/ship_module/module in modules[slot])
+ if(module.modularity_cost <= 0)
+ continue
+ . -= module.modularity_cost
+ // If we are less than zero, return 0; WHY THE FUCK IS A NEGATIVE NUMBER TRUTHY
+ return . <= 0 ? 0 : .
+
/obj/structure/overmap/ship/simulated/proc/initial_name()
if(mass < SHIP_SIZE_THRESHOLD)
return //You don't DESERVE a name >:(
@@ -67,7 +100,7 @@
set_ship_name(chosen_name)
///Destroy if integrity <= 0 and no concious mobs on shuttle
-/obj/structure/overmap/ship/simulated/recieve_damage(amount)
+/obj/structure/overmap/ship/simulated/recieve_damage(amount, atom/source, damage_type = DAMAGE_PHYSICAL)
. = ..()
update_icon_state()
if(integrity > 0)
@@ -102,6 +135,13 @@
* * dock_to_use - The [/obj/docking_port/mobile] to dock to.
*/
/obj/structure/overmap/ship/simulated/proc/dock(obj/structure/overmap/to_dock, obj/docking_port/stationary/dock_to_use)
+ var/signal_resp = SEND_SIGNAL(src, COMSIG_SHIP_DOCK, to_dock)
+ if((signal_resp & SHIP_FORCE_BLOCK) || ((signal_resp & SHIP_BLOCK) && !(signal_resp & SHIP_FORCE_ALLOW)))
+ return "Failed to commence docking procedures."
+ signal_resp = SEND_SIGNAL(to_dock, COMSIG_SHIP_UNDOCK, to_dock) // verify the dock accepts our docking
+ if((signal_resp & SHIP_FORCE_BLOCK) || ((signal_resp & SHIP_BLOCK) && !(signal_resp & SHIP_FORCE_ALLOW)))
+ return "Failed to commence undocking procedures."
+
shuttle.request(dock_to_use)
priority_announce("Beginning docking procedures. Completion in [(shuttle.callTime + 1 SECONDS)/10] seconds.", "Docking Announcement", sender_override = name, zlevel = shuttle.get_virtual_z_level())
@@ -121,6 +161,14 @@
return "Ship not docked!"
if(!shuttle)
return "Shuttle not found!"
+ var/obj/structure/overmap/dynamic/dock
+ dock = locate() in get_turf(src)
+ var/signal_resp = SEND_SIGNAL(src, COMSIG_SHIP_UNDOCK, dock) // verify the ship is allowed to dock
+ if((signal_resp & SHIP_FORCE_BLOCK) || ((signal_resp & SHIP_BLOCK) && !(signal_resp & SHIP_FORCE_ALLOW)))
+ return "Failed to commence undocking procedures."
+ signal_resp = SEND_SIGNAL(dock, COMSIG_SHIP_UNDOCK, dock) // verify the dock allows us to leave
+ if((signal_resp & SHIP_FORCE_BLOCK) || ((signal_resp & SHIP_BLOCK) && !(signal_resp & SHIP_FORCE_ALLOW)))
+ return "Failed to commence undocking procedures."
shuttle.destination = null
shuttle.mode = SHUTTLE_IGNITING
shuttle.setTimer(shuttle.ignitionTime)
@@ -129,6 +177,15 @@
state = OVERMAP_SHIP_UNDOCKING
return "Beginning undocking procedures..."
+/obj/structure/overmap/ship/simulated/Move(atom/newloc, direct) // TODO: Consider also calling the signal on the newloc so that certain overmap objects can mark a turf as unenterable
+ var/signal_resp = SEND_SIGNAL(src, COMSIG_SHIP_MOVE, newloc, loc)
+ if((signal_resp & SHIP_FORCE_BLOCK) || ((signal_resp & SHIP_BLOCK) && !(signal_resp & SHIP_FORCE_ALLOW)))
+ SEND_SIGNAL(src, COMSIG_SHIP_BLOCKED_MOVE, newloc)
+
+ get_speed()
+ decelerate(max_speed)
+ return ..()
+
/**
* Docks to an empty dynamic encounter. Used for intership interaction, structural modifications, and such
* * user - The user that initiated the action
@@ -141,7 +198,14 @@
if(E)
return overmap_object_act(user, E)
-/obj/structure/overmap/ship/simulated/burn_engines(n_dir = null, percentage = 100)
+/obj/structure/overmap/ship/simulated/proc/thrust(dir)
+ var/thrust_amt = burn_engines(dir, virtual=TRUE)
+ var/signal_resp = SEND_SIGNAL(src, COMSIG_SHIP_THRUST, thrust_amt, dir)
+ if((signal_resp & SHIP_FORCE_BLOCK) || ((signal_resp & SHIP_BLOCK) && !(signal_resp & SHIP_FORCE_ALLOW)))
+ return
+ return burn_engines(dir)
+
+/obj/structure/overmap/ship/simulated/burn_engines(n_dir = null, percentage = 100, virtual = FALSE)
if(state != OVERMAP_SHIP_FLYING)
return
@@ -153,13 +217,16 @@
for(var/obj/machinery/power/shuttle/engine/E in shuttle.engine_list)
if(!E.enabled)
continue
- thrust_used += E.burn_engine(percentage)
+ thrust_used += E.burn_engine(percentage, virtual)
est_thrust = thrust_used //cheeky way of rechecking the thrust, check it every time it's used
thrust_used = thrust_used / max(mass * 100, 1) //do not know why this minimum check is here, but I clearly ran into an issue here before
+ if(virtual)
+ return thrust_used
if(n_dir)
accelerate(n_dir, thrust_used)
else
decelerate(thrust_used)
+ return thrust_used
/**
* Just double checks all the engines on the shuttle
@@ -183,6 +250,15 @@
mass = .
update_icon_state()
+/// Updates this ship with a new helms console. Depending on the size of the ship this can be a very laggy process!
+/obj/structure/overmap/ship/simulated/proc/sync_with_helms_console()
+ for(var/area/area as anything in shuttle.shuttle_areas)
+ for(var/obj/machinery/computer/ship/helm/helm in area)
+ src.helm = helm
+ helm.current_ship = src
+ return TRUE
+ return FALSE
+
/**
* Calculates the average fuel fullness of all engines.
*/
diff --git a/icons/obj/ship_ammo.dmi b/icons/obj/ship_ammo.dmi
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/icons/obj/ship_modules/ship_modules.dmi b/icons/obj/ship_modules/ship_modules.dmi
new file mode 100644
index 000000000000..7c1d5c51b597
Binary files /dev/null and b/icons/obj/ship_modules/ship_modules.dmi differ
diff --git a/shiptest.dme b/shiptest.dme
index fba590a14000..eddc4762ebbb 100644
--- a/shiptest.dme
+++ b/shiptest.dme
@@ -278,6 +278,7 @@ g// DM Environment file for shiptest.dme.
#include "code\controllers\configuration\entries\game_options.dm"
#include "code\controllers\configuration\entries\general.dm"
#include "code\controllers\configuration\entries\interview.dm"
+#include "code\controllers\configuration\entries\overmap.dm"
#include "code\controllers\configuration\entries\resources.dm"
#include "code\controllers\subsystem\achievements.dm"
#include "code\controllers\subsystem\acid.dm"
@@ -2625,10 +2626,20 @@ g// DM Environment file for shiptest.dme.
#include "code\modules\overmap\_overmap.dm"
#include "code\modules\overmap\dynamic_events.dm"
#include "code\modules\overmap\events.dm"
-#include "code\modules\overmap\helm.dm"
+#include "code\modules\overmap\consoles\_ship_console.dm"
+#include "code\modules\overmap\consoles\bluespace_jump.dm"
+#include "code\modules\overmap\consoles\helm.dm"
+#include "code\modules\overmap\consoles\weapons.dm"
#include "code\modules\overmap\ships\_ships.dm"
#include "code\modules\overmap\ships\simulated.dm"
#include "code\modules\overmap\ships\simulated_ship_data.dm"
+#include "code\modules\overmap\ships\ship_module\_module_structure.dm"
+#include "code\modules\overmap\ships\ship_module\_ship_module.dm"
+#include "code\modules\overmap\ships\ship_module\energy_shield.dm"
+#include "code\modules\overmap\ships\ship_module\module_builder.dm"
+#include "code\modules\overmap\ships\ship_module\weapons\_weapon_module.dm"
+#include "code\modules\overmap\ships\ship_module\weapons\flak_cannon.dm"
+#include "code\modules\overmap\ships\ship_module\weapons\weapon_shells.dm"
#include "code\modules\paperwork\carbonpaper.dm"
#include "code\modules\paperwork\clipboard.dm"
#include "code\modules\paperwork\contract.dm"
diff --git a/tgui/packages/tgui/interfaces/BluespaceJumpConsole.js b/tgui/packages/tgui/interfaces/BluespaceJumpConsole.js
new file mode 100644
index 000000000000..6c16074af094
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/BluespaceJumpConsole.js
@@ -0,0 +1,7 @@
+export const BluespaceJumpConsole = (props, context) => {
+ return(
+
+ TODO
{/** TODO **/}
+
+ )
+}
diff --git a/tgui/packages/tgui/interfaces/HelmConsole.js b/tgui/packages/tgui/interfaces/HelmConsole.js
index 6af47a4c8a84..3067506c7f3e 100644
--- a/tgui/packages/tgui/interfaces/HelmConsole.js
+++ b/tgui/packages/tgui/interfaces/HelmConsole.js
@@ -1,12 +1,10 @@
-import { Fragment } from 'inferno';
-import { useBackend, useLocalState } from '../backend';
-import { Button, ByondUi, LabeledList, Knob, Input, Section, Grid, Box, ProgressBar, Slider, AnimatedNumber, Tooltip } from '../components';
-import { refocusLayout, Window } from '../layouts';
+import { useBackend } from '../backend';
+import { Button, ByondUi, LabeledList, Section, ProgressBar, AnimatedNumber } from '../components';
+import { Window } from '../layouts';
import { Table } from '../components/Table';
-import { ButtonInput } from '../components/Button';
export const HelmConsole = (props, context) => {
- const { act, data, config } = useBackend(context);
+ const { data } = useBackend(context);
const { canFly, mapRef, isViewer } = data;
return (
{
// Arrow directional controls
const ShipControlContent = (props, context) => {
const { act, data } = useBackend(context);
- const { calibrating } = data;
let flyable = (data.state === 'flying');
// DIRECTIONS const idea from Lyra as part of their Haven-Urist project
const DIRECTIONS = {
@@ -295,13 +292,6 @@ const ShipControlContent = (props, context) => {
icon="sign-in-alt"
disabled={data.state !== 'flying'}
onClick={() => act('dock_empty')} />
-