diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 600fc8eb2669..1fcc2a4744e5 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -210,6 +210,8 @@ GLOBAL_LIST_INIT(heavyfootmob, typecacheof(list(
#define ismecha(A) (istype(A, /obj/mecha))
+#define isspacepod(A) (istype(A, /obj/spacepod)) // yogs
+
#define is_cleanable(A) (istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/rune)) //if something is cleanable
#define isorgan(A) (istype(A, /obj/item/organ))
@@ -247,4 +249,4 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list(
#define isblobmonster(O) (istype(O, /mob/living/simple_animal/hostile/blob))
-#define isshuttleturf(T) (length(T.baseturfs) && (/turf/baseturf_skipover/shuttle in T.baseturfs))
\ No newline at end of file
+#define isshuttleturf(T) (length(T.baseturfs) && (/turf/baseturf_skipover/shuttle in T.baseturfs))
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 08ffee272c0f..510eccca1cf6 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -55,6 +55,7 @@
#define BELOW_MOB_LAYER 3.7
#define LYING_MOB_LAYER 3.8
+#define SPACEPOD_LAYER 3.9 // yogs
//#define MOB_LAYER 4 //For easy recordkeeping; this is a byond define
#define ABOVE_MOB_LAYER 4.1
#define WALL_OBJ_LAYER 4.25
diff --git a/code/__DEFINES/~yogs_defines/spacepods.dm b/code/__DEFINES/~yogs_defines/spacepods.dm
new file mode 100644
index 000000000000..a4bfbd5abf88
--- /dev/null
+++ b/code/__DEFINES/~yogs_defines/spacepods.dm
@@ -0,0 +1,18 @@
+#define SPACEPOD_EMPTY 1
+#define SPACEPOD_WIRES_LOOSE 2
+#define SPACEPOD_WIRES_SECURED 3
+#define SPACEPOD_CIRCUIT_LOOSE 4
+#define SPACEPOD_CIRCUIT_SECURED 5
+#define SPACEPOD_CORE_LOOSE 6
+#define SPACEPOD_CORE_SECURED 7
+#define SPACEPOD_BULKHEAD_LOOSE 8
+#define SPACEPOD_BULKHEAD_SECURED 9
+#define SPACEPOD_BULKHEAD_WELDED 10
+#define SPACEPOD_ARMOR_LOOSE 11
+#define SPACEPOD_ARMOR_SECURED 12
+#define SPACEPOD_ARMOR_WELDED 13
+
+#define SPACEPOD_SLOT_CARGO "cargo"
+#define SPACEPOD_SLOT_MISC "misc"
+#define SPACEPOD_SLOT_WEAPON "weapon"
+#define SPACEPOD_SLOT_LOCK "lock"
diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm
index a06e03324685..bd746261c182 100644
--- a/code/_globalvars/lists/maintenance_loot.dm
+++ b/code/_globalvars/lists/maintenance_loot.dm
@@ -106,5 +106,10 @@ GLOBAL_LIST_INIT(maintenance_loot, list(
/obj/item/storage/toolbox/artistic = 2,
/obj/item/toy/eightball = 1,
/obj/item/reagent_containers/pill/floorpill = 1,
+ // yogs start
+ /obj/item/pod_parts/core = 1,
+ /obj/item/pod_parts/armor = 1,
+ /obj/item/circuitboard/mecha/pod = 1,
+ // yogs end
"" = 3
))
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 8fae785ca3ed..cfb674550fc2 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -209,11 +209,28 @@
direct = get_dir(src, newloc)
setDir(direct)
- if(!loc.Exit(src, newloc))
- return
+ // yogs start - multi tile object handling
+ if(bound_width != world.icon_size || bound_height != world.icon_size)
+ var/list/newlocs = isturf(newloc) ? block(locate(newloc.x+(-bound_x)/world.icon_size,newloc.y+(-bound_y)/world.icon_size,newloc.z),locate(newloc.x+(-bound_x+bound_width)/world.icon_size-1,newloc.y+(-bound_y+bound_height)/world.icon_size-1,newloc.z)) : list(newloc)
+ if(!newlocs)
+ return // we're trying to cross into the edge of space
+ var/bothturfs = isturf(newloc) && isturf(loc)
+ var/dx = bothturfs ? newloc.x - loc.x : 0
+ var/dy = bothturfs ? newloc.y - loc.y : 0
+ var/dz = bothturfs ? newloc.z - loc.z : 0
+ for(var/atom/A in (locs - newlocs))
+ if(!A.Exit(src, bothturfs ? locate(A.x+dx,A.y+dy,A.z+dz) : newloc))
+ return
+ for(var/atom/A in (newlocs - locs))
+ if(!A.Enter(src, bothturfs ? locate(A.x-dx,A.y-dy,A.z+dz) : loc))
+ return
+ else
+ if(!loc.Exit(src, newloc))
+ return
- if(!newloc.Enter(src, src.loc))
- return
+ if(!newloc.Enter(src, src.loc))
+ return
+ // yogs end
// Past this is the point of no return
var/atom/oldloc = loc
diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm
index 2a54764024d6..4f3121801709 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -2,6 +2,13 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
new/datum/stack_recipe("grille", /obj/structure/grille, 2, time = 10, one_per_turf = TRUE, on_floor = FALSE), \
new/datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("scooter frame", /obj/item/scooter_frame, 10, time = 25, one_per_turf = 0), \
+ // yogs start
+ null, \
+ new/datum/stack_recipe("fore port spacepod frame", /obj/item/pod_parts/pod_frame/fore_port, 15, time = 30, one_per_turf = 0), \
+ new/datum/stack_recipe("fore starboard spacepod frame", /obj/item/pod_parts/pod_frame/fore_starboard, 15, time = 30, one_per_turf = 0), \
+ new/datum/stack_recipe("aft port spacepod frame", /obj/item/pod_parts/pod_frame/aft_port, 15, time = 30, one_per_turf = 0), \
+ new/datum/stack_recipe("aft starboard spacepod frame", /obj/item/pod_parts/pod_frame/aft_starboard, 15, time = 30, one_per_turf = 0), \
+ // yogs end
))
/obj/item/stack/rods
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index f2c9a6ef96ea..4925aa660d7e 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -100,7 +100,7 @@
if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD)
return
var/target_dir = get_dir(src,target)
-
+
var/static/list/cardinal_sidestep_directions = list(-90,-45,0,45,90)
var/static/list/diagonal_sidestep_directions = list(-45,0,45)
var/chosen_dir = 0
@@ -131,7 +131,7 @@
if(!search_objects)
. = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide
- var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/structure/destructible/clockwork/ocular_warden))
+ var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/structure/destructible/clockwork/ocular_warden, /obj/spacepod)) // yogs - add spacepod
for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines))
if(can_see(targets_from, HM, vision_range))
@@ -296,7 +296,7 @@
if(target)
if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack
MeleeAction()
- else
+ else
if(rapid_melee > 1 && target_distance <= melee_queue_distance)
MeleeAction(FALSE)
in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode
@@ -563,7 +563,7 @@ mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with mega
toggle_ai(AI_ON)
/mob/living/simple_animal/hostile/proc/ListTargetsLazy(var/_Z)//Step 1, find out what we can see
- var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/structure/destructible/clockwork/ocular_warden))
+ var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/structure/destructible/clockwork/ocular_warden, /obj/spacepod)) // yogs - add spacepod
. = list()
for (var/I in SSmobs.clients_by_zlevel[_Z])
var/mob/M = I
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm
index 57df489d4ef3..2e8b1fa4179b 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm
@@ -12,6 +12,12 @@
var/obj/mecha/M = A
if(M.occupant)
return A
+ // yogs start
+ else if(istype(A, /obj/spacepod))
+ var/obj/spacepod/M = A
+ if(M.pilot || M.passengers.len)
+ return A
+ // yogs end
/mob/living/simple_animal/hostile/retaliate/ListTargets()
if(!enemies.len)
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 151aad5cb5ee..84ec44985f79 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -342,6 +342,12 @@
var/obj/mecha/M = the_target
if (M.occupant)
return FALSE
+ // yogs start
+ if(isspacepod(the_target))
+ var/obj/spacepod/SP = the_target
+ if(SP.pilot || SP.passengers.len)
+ return FALSE
+ // yogs end
return TRUE
/mob/living/simple_animal/handle_fire()
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 9e14864c21a5..ec100babd082 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -247,9 +247,16 @@
return TRUE
if(firer && !ignore_source_check)
var/mob/checking = firer
- if((A == firer) || (((A in firer.buckled_mobs) || (istype(checking) && (A == checking.buckled))) && (A != original)) || (A == firer.loc && ismecha(A))) //cannot shoot yourself or your mech
+ if((A == firer) || (((A in firer.buckled_mobs) || (istype(checking) && (A == checking.buckled))) && (A != original)) || (A == firer.loc && (ismecha(A) || isspacepod(A)))) //cannot shoot yourself or your mech // yogs - or your spacepod
trajectory_ignore_forcemove = TRUE
- forceMove(get_turf(A))
+ // yogs start - multitile objects
+ var/turf/T = trajectory.return_turf()
+ if(!istype(T))
+ qdel(src)
+ return
+ if(T != loc)
+ forceMove(get_step_towards(src, T))
+ // yogs end
trajectory_ignore_forcemove = FALSE
return FALSE
diff --git a/code/modules/research/machinery/protolathe.dm b/code/modules/research/machinery/protolathe.dm
index e04261c961dd..ee9a1960fa5d 100644
--- a/code/modules/research/machinery/protolathe.dm
+++ b/code/modules/research/machinery/protolathe.dm
@@ -15,7 +15,8 @@
"Weapons",
"Ammo",
"Firing Pins",
- "Computer Parts"
+ "Computer Parts",
+ "Spacepod Designs" // yogs
)
production_animation = "protolathe_n"
allowed_buildtypes = PROTOLATHE
diff --git a/code/modules/research/machinery/techfab.dm b/code/modules/research/machinery/techfab.dm
index 5525066f2ab6..447ab66fed65 100644
--- a/code/modules/research/machinery/techfab.dm
+++ b/code/modules/research/machinery/techfab.dm
@@ -26,7 +26,8 @@
"Subspace Telecomms",
"Research Machinery",
"Misc. Machinery",
- "Computer Parts"
+ "Computer Parts",
+ "Spacepod Designs" // yogs
)
console_link = FALSE
production_animation = "protolathe_n"
diff --git a/yogstation.dme b/yogstation.dme
index 3398fed23c79..a2a5d6407485 100644
--- a/yogstation.dme
+++ b/yogstation.dme
@@ -118,6 +118,7 @@
#include "code\__DEFINES\~yogs_defines\mentor.dm"
#include "code\__DEFINES\~yogs_defines\preferences.dm"
#include "code\__DEFINES\~yogs_defines\shuttles.dm"
+#include "code\__DEFINES\~yogs_defines\spacepods.dm"
#include "code\__DEFINES\~yogs_defines\wires.dm"
#include "code\__HELPERS\_lists.dm"
#include "code\__HELPERS\_logging.dm"
@@ -2750,6 +2751,7 @@
#include "yogstation\code\_globalvars\configuration.dm"
#include "yogstation\code\_globalvars\logging.dm"
#include "yogstation\code\_globalvars\lists\mobs.dm"
+#include "yogstation\code\_onclick\adjacent.dm"
#include "yogstation\code\_onclick\click.dm"
#include "yogstation\code\_onclick\hud\vampire.dm"
#include "yogstation\code\controllers\configuration\entries\general.dm"
@@ -2803,6 +2805,8 @@
#include "yogstation\code\game\machinery\computer\medical.dm"
#include "yogstation\code\game\machinery\computer\Operating.dm"
#include "yogstation\code\game\machinery\doors\airlock.dm"
+#include "yogstation\code\game\machinery\doors\poddoor.dm"
+#include "yogstation\code\game\machinery\doors\spacepod.dm"
#include "yogstation\code\game\machinery\pipe\construction.dm"
#include "yogstation\code\game\machinery\telecomms\computers\logbrowser.dm"
#include "yogstation\code\game\machinery\telecomms\computers\telemonitor.dm"
@@ -2985,9 +2989,17 @@
#include "yogstation\code\modules\research\rdconsole.dm"
#include "yogstation\code\modules\research\designs\bluespace_designs.dm"
#include "yogstation\code\modules\research\designs\comp_board_designs.dm"
+#include "yogstation\code\modules\research\designs\spacepod_designs.dm"
#include "yogstation\code\modules\research\designs\stock_parts_designs.dm"
#include "yogstation\code\modules\research\designs\tool_designs.dm"
+#include "yogstation\code\modules\research\techweb\all_nodes.dm"
#include "yogstation\code\modules\shuttle\emergency.dm"
+#include "yogstation\code\modules\spacepods\construction.dm"
+#include "yogstation\code\modules\spacepods\equipment.dm"
+#include "yogstation\code\modules\spacepods\parts.dm"
+#include "yogstation\code\modules\spacepods\physics.dm"
+#include "yogstation\code\modules\spacepods\prebuilt.dm"
+#include "yogstation\code\modules\spacepods\spacepod.dm"
#include "yogstation\code\modules\spells\cluwnecurse.dm"
#include "yogstation\code\modules\spells\spells.dm"
#include "yogstation\code\modules\spells\spell_types\mime.dm"
diff --git a/yogstation/code/_onclick/adjacent.dm b/yogstation/code/_onclick/adjacent.dm
new file mode 100644
index 000000000000..3664701f94f1
--- /dev/null
+++ b/yogstation/code/_onclick/adjacent.dm
@@ -0,0 +1,70 @@
+/*
+ Adjacency (to turf):
+ * If you are in the same turf, always true
+ * If you are vertically/horizontally adjacent, ensure there are no border objects
+ * If you are diagonally adjacent, ensure you can pass through at least one of the mutually adjacent square.
+ * Passing through in this case ignores anything with the LETPASSTHROW pass flag, such as tables, racks, and morgue trays.
+*/
+/turf/Adjacent(atom/neighbor, atom/target = null, atom/movable/mover = null)
+ if(neighbor == src)
+ return TRUE //don't be retarded!!
+
+ if(istype(neighbor, /atom/movable)) //fml
+ var/atom/movable/AM = neighbor
+ if((AM.bound_width != world.icon_size || AM.bound_height != world.icon_size) && (islist(AM.locs) && AM.locs.len > 1))
+ for(var/turf/T in AM.locs)
+ if(Adjacent(T, target, mover))
+ return TRUE
+ return FALSE
+
+ var/turf/T0 = get_turf(neighbor)
+
+ if(T0 == src) //same turf
+ return TRUE
+
+ if(get_dist(src, T0) > 1 || z != T0.z) //too far
+ return FALSE
+
+ // Non diagonal case
+ if(T0.x == x || T0.y == y)
+ // Check for border blockages
+ return T0.ClickCross(get_dir(T0,src), border_only = 1, target_atom = target, mover = mover) && src.ClickCross(get_dir(src,T0), border_only = 1, target_atom = target, mover = mover)
+
+ // Diagonal case
+ var/in_dir = get_dir(T0,src) // eg. northwest (1+8) = 9 (00001001)
+ var/d1 = in_dir&3 // eg. north (1+8)&3 (0000 0011) = 1 (0000 0001)
+ var/d2 = in_dir&12 // eg. west (1+8)&12 (0000 1100) = 8 (0000 1000)
+
+ for(var/d in list(d1,d2))
+ if(!T0.ClickCross(d, border_only = 1, target_atom = target, mover = mover))
+ continue // could not leave T0 in that direction
+
+ var/turf/T1 = get_step(T0,d)
+ if(!T1 || T1.density)
+ continue
+ if(!T1.ClickCross(get_dir(T1,src), border_only = 0, target_atom = target, mover = mover) || !T1.ClickCross(get_dir(T1,T0), border_only = 0, target_atom = target, mover = mover))
+ continue // couldn't enter or couldn't leave T1
+
+ if(!src.ClickCross(get_dir(src,T1), border_only = 1, target_atom = target, mover = mover))
+ continue // could not enter src
+
+ return TRUE // we don't care about our own density
+
+ return FALSE
+
+/*
+ Adjacency (to anything else):
+ * Must be on a turf
+*/
+/atom/movable/Adjacent(var/atom/neighbor)
+ if(neighbor == loc)
+ return TRUE
+ if(!isturf(loc))
+ return FALSE
+ if((islist(locs) && locs.len > 1) && (bound_width != world.icon_size || bound_height != world.icon_size))
+ for(var/turf/T in locs) //this is to handle multi tile objects
+ if(T.Adjacent(neighbor, src, src))
+ return TRUE
+ else if(loc.Adjacent(neighbor,target = neighbor, mover = src))
+ return TRUE
+ return FALSE
diff --git a/yogstation/code/game/machinery/doors/poddoor.dm b/yogstation/code/game/machinery/doors/poddoor.dm
new file mode 100644
index 000000000000..323356b1f7ad
--- /dev/null
+++ b/yogstation/code/game/machinery/doors/poddoor.dm
@@ -0,0 +1,60 @@
+/obj/machinery/door/poddoor/multi_tile
+ name = "large pod door"
+ layer = CLOSED_DOOR_LAYER
+ closingLayer = CLOSED_DOOR_LAYER
+
+/obj/machinery/door/poddoor/multi_tile/New()
+ . = ..()
+ apply_opacity_to_my_turfs(opacity)
+
+/obj/machinery/door/poddoor/multi_tile/open()
+ if(..())
+ apply_opacity_to_my_turfs(opacity)
+
+
+/obj/machinery/door/poddoor/multi_tile/close()
+ if(..())
+ apply_opacity_to_my_turfs(opacity)
+
+/obj/machinery/door/poddoor/multi_tile/Destroy()
+ apply_opacity_to_my_turfs(0)
+ return ..()
+
+//Multi-tile poddoors don't turn invisible automatically, so we change the opacity of the turfs below instead one by one.
+/obj/machinery/door/poddoor/multi_tile/proc/apply_opacity_to_my_turfs(var/new_opacity)
+ for(var/turf/T in locs)
+ T.opacity = new_opacity
+ T.has_opaque_atom = new_opacity
+ T.reconsider_lights()
+ T.air_update_turf(1)
+ update_freelook_sight()
+
+/obj/machinery/door/poddoor/multi_tile/four_tile_ver/
+ icon = 'yogstation/icons/obj/doors/1x4blast_vert.dmi'
+ bound_height = 128
+ dir = NORTH
+
+/obj/machinery/door/poddoor/multi_tile/three_tile_ver/
+ icon = 'yogstation/icons/obj/doors/1x3blast_vert.dmi'
+ bound_height = 96
+ dir = NORTH
+
+/obj/machinery/door/poddoor/multi_tile/two_tile_ver/
+ icon = 'yogstation/icons/obj/doors/1x2blast_vert.dmi'
+ bound_height = 64
+ dir = NORTH
+
+/obj/machinery/door/poddoor/multi_tile/four_tile_hor/
+ icon = 'yogstation/icons/obj/doors/1x4blast_hor.dmi'
+ bound_width = 128
+ dir = EAST
+
+/obj/machinery/door/poddoor/multi_tile/three_tile_hor/
+ icon = 'yogstation/icons/obj/doors/1x3blast_hor.dmi'
+ bound_width = 96
+ dir = EAST
+
+/obj/machinery/door/poddoor/multi_tile/two_tile_hor/
+ icon = 'yogstation/icons/obj/doors/1x2blast_hor.dmi'
+ bound_width = 64
+ dir = EAST
diff --git a/yogstation/code/game/machinery/doors/spacepod.dm b/yogstation/code/game/machinery/doors/spacepod.dm
new file mode 100644
index 000000000000..7b9c689ae38c
--- /dev/null
+++ b/yogstation/code/game/machinery/doors/spacepod.dm
@@ -0,0 +1,22 @@
+/obj/structure/spacepoddoor
+ name = "podlock"
+ desc = "Why it no open!!!"
+ icon = 'yogstation/icons/effects/beam.dmi'
+ icon_state = "n_beam"
+ density = 1
+ anchored = 1
+ var/id = 1.0
+ CanAtmosPass = ATMOS_PASS_NO
+
+/obj/structure/spacepoddoor/Initialize()
+ ..()
+ air_update_turf(1)
+
+/obj/structure/spacepoddoor/Destroy()
+ air_update_turf(1)
+ return ..()
+
+/obj/structure/spacepoddoor/CanPass(atom/movable/A, turf/T)
+ if(istype(A, /obj/spacepod))
+ return TRUE
+ return ..()
diff --git a/yogstation/code/modules/research/designs/spacepod_designs.dm b/yogstation/code/modules/research/designs/spacepod_designs.dm
new file mode 100644
index 000000000000..c831f143912a
--- /dev/null
+++ b/yogstation/code/modules/research/designs/spacepod_designs.dm
@@ -0,0 +1,237 @@
+/datum/design/board/spacepod_main
+ name = "Circuit Design (Space Pod Mainboard)"
+ desc = "Allows for the construction of a Space Pod mainboard."
+ id = "spacepod_main"
+ build_path = /obj/item/circuitboard/mecha/pod
+ category = list("Exosuit Modules")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+/datum/design/pod_core
+ name = "Spacepod Core"
+ desc = "Allows for the construction of a spacepod core system, made up of the engine and life support systems."
+ id = "podcore"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=5000, MAT_URANIUM=1000, MAT_PLASMA=5000)
+ build_path = /obj/item/pod_parts/core
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+/datum/design/pod_armor_civ
+ name = "Spacepod Armor (civilian)"
+ desc = "Allows for the construction of spcaepod armor. This is the civilian version."
+ id = "podarmor_civ"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=15000,MAT_GLASS=5000,MAT_PLASMA=10000)
+ build_path = /obj/item/pod_parts/armor
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+/datum/design/pod_armor_black
+ name = "Spacepod Armor (dark)"
+ desc = "Allows for the construction of spacepod armor. This is the dark civillian version."
+ id = "podarmor_dark"
+ build_type = PROTOLATHE
+ build_path = /obj/item/pod_parts/armor/black
+ category = list("Spacepod Designs")
+ materials = list(MAT_METAL=15000,MAT_GLASS=5000,MAT_PLASMA=10000)
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+/datum/design/pod_armor_industrial
+ name = "Spacepod Armor (industrial)"
+ desc = "Allows for the construction of spacepod armor. This is the industrial grade version."
+ id = "podarmor_industiral"
+ build_type = PROTOLATHE
+ build_path = /obj/item/pod_parts/armor/industrial
+ category = list("Spacepod Designs")
+ materials = list(MAT_METAL=15000,MAT_GLASS=5000,MAT_PLASMA=10000,MAT_DIAMOND=5000,MAT_SILVER=7500)
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_ENGINEERING
+
+/datum/design/pod_armor_sec
+ name = "Spacepod Armor (security)"
+ desc = "Allows for the construction of spacepod armor. This is the security version."
+ id = "podarmor_sec"
+ build_type = PROTOLATHE
+ build_path = /obj/item/pod_parts/armor/security
+ category = list("Spacepod Designs")
+ materials = list(MAT_METAL=15000,MAT_GLASS=5000,MAT_PLASMA=10000,MAT_DIAMOND=5000,MAT_SILVER=7500)
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+
+/datum/design/pod_armor_gold
+ name = "Spacepod Armor (golden)"
+ desc = "Allows for the construction of spacepod armor. This is the golden version."
+ id = "podarmor_gold"
+ build_type = PROTOLATHE
+ build_path = /obj/item/pod_parts/armor/gold
+ category = list("Spacepod Designs")
+ materials = list(MAT_METAL=5000,MAT_GLASS=2500,MAT_PLASMA=7500,MAT_GOLD=10000)
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+//////////////////////////////////////////
+//////SPACEPOD GUNS///////////////////////
+//////////////////////////////////////////
+
+/datum/design/pod_gun_disabler
+ name = "Spacepod Equipment (Disabler)"
+ desc = "Allows for the construction of a spacepod mounted disabler."
+ id = "podgun_disabler"
+ build_type = PROTOLATHE
+ build_path = /obj/item/spacepod_equipment/weaponry/disabler
+ category = list("Spacepod Designs")
+ materials = list(MAT_METAL = 15000)
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+
+/datum/design/pod_gun_bdisabler
+ name = "Spacepod Equipment (Burst Disabler)"
+ desc = "Allows for the construction of a spacepod mounted disabler. This is the burst-fire model."
+ id = "podgun_bdisabler"
+ build_type = PROTOLATHE
+ build_path = /obj/item/spacepod_equipment/weaponry/burst_disabler
+ category = list("Spacepod Designs")
+ materials = list(MAT_METAL = 15000,MAT_PLASMA=2000)
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+
+/datum/design/pod_gun_laser
+ name = "Spacepod Equipment (Laser)"
+ desc = "Allows for the construction of a spacepod mounted laser."
+ id = "podgun_laser"
+ build_type = PROTOLATHE
+ build_path = /obj/item/spacepod_equipment/weaponry/laser
+ category = list("Spacepod Designs")
+ materials = list(MAT_METAL=10000,MAT_GLASS=5000,MAT_GOLD=1000,MAT_SILVER=2000)
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+
+/datum/design/pod_ka_basic
+ name = "Spacepod Equipment (Basic Kinetic Accelerator)"
+ desc = "Allows for the construction of a weak spacepod Kinetic Accelerator"
+ id = "pod_ka_basic"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 10000, MAT_GLASS = 5000, MAT_SILVER = 2000, MAT_URANIUM = 2000)
+ build_path = /obj/item/spacepod_equipment/weaponry/basic_pod_ka
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
+
+/datum/design/pod_ka
+ name = "Spacepod Equipment (Kinetic Accelerator)"
+ desc = "Allows for the construction of a spacepod Kinetic Accelerator."
+ id = "pod_ka"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 10000, MAT_GLASS = 5000, MAT_SILVER = 2000, MAT_GOLD = 2000, MAT_DIAMOND = 2000)
+ build_path = /obj/item/spacepod_equipment/weaponry/pod_ka
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
+
+
+/datum/design/pod_plasma_cutter
+ name = "Spacepod Equipment (Plasma Cutter)"
+ desc = "Allows for the construction of a plasma cutter."
+ id = "pod_plasma_cutter"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 10000, MAT_GLASS = 5000, MAT_SILVER = 2000, MAT_GOLD = 2000, MAT_DIAMOND = 2000)
+ build_path = /obj/item/spacepod_equipment/weaponry/plasma_cutter
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
+
+/datum/design/pod_adv_plasma_cutter
+ name = "Spacepod Equipment (Advanced Plasma cutter)"
+ desc = "Allows for the construction of an advanced plasma cutter."
+ id = "pod_adv_plasma_cutter"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 10000, MAT_GLASS = 5000, MAT_SILVER = 4000, MAT_GOLD = 4000, MAT_DIAMOND = 4000)
+ build_path = /obj/item/spacepod_equipment/weaponry/plasma_cutter/adv
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
+
+//////////////////////////////////////////
+//////SPACEPOD MISC. ITEMS////////////////
+//////////////////////////////////////////
+
+/datum/design/pod_misc_tracker
+ name = "Spacepod Tracking Module"
+ desc = "Allows for the construction of a Space Pod Tracking Module."
+ id = "podmisc_tracker"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=5000)
+ build_path = /obj/item/spacepod_equipment/tracker
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+//////////////////////////////////////////
+//////SPACEPOD CARGO ITEMS////////////////
+//////////////////////////////////////////
+
+/datum/design/pod_cargo_ore
+ name = "Spacepod Ore Storage Module"
+ desc = "Allows for the construction of a Space Pod Ore Storage Module."
+ id = "podcargo_ore"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=20000, MAT_GLASS=2000)
+ build_path = /obj/item/spacepod_equipment/cargo/large/ore
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
+
+/datum/design/pod_cargo_crate
+ name = "Spacepod Crate Storage Module"
+ desc = "Allows the construction of a Space Pod Crate Storage Module."
+ id = "podcargo_crate"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=25000)
+ build_path = /obj/item/spacepod_equipment/cargo/large
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+//////////////////////////////////////////
+//////SPACEPOD SEC CARGO ITEMS////////////
+//////////////////////////////////////////
+
+/datum/design/passenger_seat
+ name = "Spacepod Passenger Seat"
+ desc = "Allows the construction of a Space Pod Passenger Seat Module."
+ id = "podcargo_seat"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=7500, MAT_GLASS=2500)
+ build_path = /obj/item/spacepod_equipment/cargo/chair
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+/*/datum/design/loot_box
+ name = "Spacepod Loot Storage Module"
+ desc = "Allows the construction of a Space Pod Auxillary Cargo Module."
+ id = "podcargo_lootbox"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=7500, MAT_GLASS=2500)
+ build_path = /obj/item/spacepod_equipment/cargo/loot_box
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL*/
+
+//////////////////////////////////////////
+//////SPACEPOD LOCK ITEMS////////////////
+//////////////////////////////////////////
+/datum/design/pod_lock_keyed
+ name = "Spacepod Tumbler Lock"
+ desc = "Allows for the construction of a tumbler style podlock."
+ id = "podlock_keyed"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=4500)
+ build_path = /obj/item/spacepod_equipment/lock/keyed
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+/datum/design/pod_key
+ name = "Spacepod Tumbler Lock Key"
+ desc = "Allows for the construction of a blank key for a podlock."
+ id = "podkey"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=500)
+ build_path = /obj/item/spacepod_key
+ category = list("Spacepod Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
+
+/datum/design/lockbuster
+ name = "Spacepod Lock Buster"
+ desc = "Allows for the construction of a spacepod lockbuster."
+ id = "pod_lockbuster"
+ build_type = PROTOLATHE
+ build_path = /obj/item/device/lock_buster
+ category = list("Spacepod Designs")
+ materials = list(MAT_METAL = 15000, MAT_DIAMOND=2500) //it IS a drill!
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
diff --git a/yogstation/code/modules/research/techweb/all_nodes.dm b/yogstation/code/modules/research/techweb/all_nodes.dm
new file mode 100644
index 000000000000..92ce59bb8052
--- /dev/null
+++ b/yogstation/code/modules/research/techweb/all_nodes.dm
@@ -0,0 +1,89 @@
+/datum/techweb_node/spacepod_basic
+ id = "spacepod_basic"
+ display_name = "Spacepod Construction"
+ description = "Basic stuff to construct Spacepods. Don't crash your first spacepod into the station, especially while going more than 10 m/s."
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
+ export_price = 2500
+ prereq_ids = list("base")
+ design_ids = list("podcore", "podarmor_civ", "podarmor_dark", "spacepod_main")
+
+/datum/techweb_node/spacepod_disabler
+ id = "spacepod_disabler"
+ display_name = "Spacepod Weaponry"
+ description = "For a bit of pew pew space battles"
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500)
+ export_price = 3500
+ prereq_ids = list("spacepod_basic", "weaponry")
+ design_ids = list("podgun_disabler")
+
+/datum/techweb_node/spacepod_lasers
+ id = "spacepod_lasers"
+ display_name = "Advanced Spacepod Weaponry"
+ description = "For a lot of pew pew space battles. PEW PEW PEW!! Shit, I missed. I need better aim. Whatever."
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5250)
+ export_price = 5250
+ prereq_ids = list("spacepod_disabler", "electronic_weapons")
+ design_ids = list("podgun_laser", "podgun_bdisabler")
+
+/datum/techweb_node/spacepod_ka
+ id = "spacepod_ka"
+ display_name = "Spacepod Mining Tech"
+ description = "Cutting up asteroids using your spacepods"
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500)
+ export_price = 500
+ prereq_ids = list("basic_mining", "spacepod_disabler")
+ design_ids = list("pod_ka_basic")
+
+/datum/techweb_node/spacepod_advmining
+ id = "spacepod_aka"
+ display_name = "Advanced Spacepod Mining Tech"
+ description = "Cutting up asteroids using your spacepods.... faster!"
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500)
+ export_price = 3500
+ prereq_ids = list("spacepod_ka", "adv_mining")
+ design_ids = list("pod_ka", "pod_plasma_cutter")
+
+/datum/techweb_node/spacepod_advplasmacutter
+ id = "spacepod_apc"
+ display_name = "Advanced Spacepod Plasma Cutter"
+ description = "Cutting up asteroids using your spacepods........... FASTERRRRRR!!!!!! Oh shit, that was gibtonite."
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4500)
+ export_price = 4500
+ prereq_ids = list("spacepod_aka", "adv_plasma")
+ design_ids = list("pod_adv_plasma_cutter")
+
+/datum/techweb_node/spacepod_pseat
+ id = "spacepod_pseat"
+ display_name = "Spacepod Passenger Seat"
+ description = "For bringing along victims as you fly off into the far reaches of space"
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3750)
+ export_price = 3750
+ prereq_ids = list("spacepod_basic", "adv_engi")
+ design_ids = list("podcargo_seat")
+
+/datum/techweb_node/spacepod_storage
+ id = "spacepod_storage"
+ display_name = "Spacepod Storage"
+ description = "For storing the stuff you find in the far reaches of space"
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4500)
+ export_price = 4500
+ prereq_ids = list("spacepod_pseat", "high_efficiency")
+ design_ids = list("podcargo_lootbox", "podcargo_crate", "podcargo_ore")
+
+/datum/techweb_node/spacepod_lockbuster
+ id = "spacepod_lockbuster"
+ display_name = "Spacepod Lock Buster"
+ description = "For when someone's being really naughty with a spacepod"
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 8500)
+ export_price = 8500
+ prereq_ids = list("spacepod_lasers", "high_efficiency", "adv_mining")
+ design_ids = list("pod_lockbuster")
+
+/datum/techweb_node/spacepod_iarmor
+ id = "spacepod_iarmor"
+ display_name = "Advanced Spacepod Armor"
+ description = "Better protection for your precious ride. You'll need it if you plan on engaging in spacepod battles."
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2750)
+ export_price = 2750
+ prereq_ids = list("spacepod_storage", "high_efficiency")
+ design_ids = list("podarmor_industiral", "podarmor_sec", "podarmor_gold")
diff --git a/yogstation/code/modules/spacepods/construction.dm b/yogstation/code/modules/spacepods/construction.dm
new file mode 100644
index 000000000000..3467f54db279
--- /dev/null
+++ b/yogstation/code/modules/spacepods/construction.dm
@@ -0,0 +1,202 @@
+/obj/spacepod/examine(mob/user)
+ ..()
+ switch(construction_state) // more construction states than r-walls!
+ if(SPACEPOD_EMPTY)
+ to_chat(user, "The strits holding it together can be cut and i't is missing wires.")
+ if(SPACEPOD_WIRES_LOOSE)
+ to_chat(user, "The wires need to be screwed on.")
+ if(SPACEPOD_WIRES_SECURED)
+ to_chat(user, "The wires are screwed on and needs a circuit board.")
+ if(SPACEPOD_CIRCUIT_LOOSE)
+ to_chat(user, "The circuit board is loosely attached and needs to be screwed on.")
+ if(SPACEPOD_CIRCUIT_SECURED)
+ to_chat(user, "The circuit board is screwed on, and there is space for a core.")
+ if(SPACEPOD_CORE_LOOSE)
+ to_chat(user, "The core is loosely attached and needs to be bolted on.")
+ if(SPACEPOD_CORE_SECURED)
+ to_chat(user, "The core is bolted on and the metal bulkhead can be attached.")
+ if(SPACEPOD_BULKHEAD_LOOSE)
+ to_chat(user, "The bulkhead is loosely attached and can be bolted down.")
+ if(SPACEPOD_BULKHEAD_SECURED)
+ to_chat(user, "The bulkhead is bolted on but not welded on.")
+ if(SPACEPOD_BULKHEAD_WELDED)
+ to_chat(user, "The bulkhead is welded on and armor can be attached.")
+ if(SPACEPOD_ARMOR_LOOSE)
+ to_chat(user, "The armor is loosely attached and can be bolted down.")
+ if(SPACEPOD_ARMOR_SECURED)
+ to_chat(user, "The armor is bolted on but not welded on.")
+ if(SPACEPOD_ARMOR_WELDED)
+ if(hatch_open)
+ if(cell || internal_tank || equipment.len)
+ to_chat(user, "The maintenance hatch is pried and there are parts inside that can be removed.")
+ else
+ to_chat(user, "The maintenance hatch is pried open and the armor is welded on.")
+ else
+ if(locked)
+ to_chat(user, "[src] is locked.")
+ else
+ to_chat(user, "The maintenance hatch is pried closed.")
+
+/obj/spacepod/proc/handle_spacepod_construction(obj/item/W, mob/living/user)
+ // time for a construction/deconstruction process to rival r-walls
+ var/obj/item/stack/ST = W
+ switch(construction_state)
+ if(SPACEPOD_EMPTY)
+ if(W.tool_behaviour == TOOL_WIRECUTTER)
+ . = TRUE
+ user.visible_message("[user] deconstructs [src].", "You deconstruct [src].")
+ deconstruct(TRUE)
+ else if(istype(W, /obj/item/stack/cable_coil))
+ . = TRUE
+ if(ST.use(10))
+ user.visible_message("[user] wires [src].", "You wire [src].")
+ construction_state++
+ else
+ to_chat(user, "You need 10 wires for this!")
+ if(SPACEPOD_WIRES_LOOSE)
+ if(W.tool_behaviour == TOOL_WIRECUTTER)
+ . = TRUE
+ var/obj/item/stack/cable_coil/CC = new
+ CC.amount = 10
+ CC.forceMove(loc)
+ W.play_tool_sound(src)
+ construction_state--
+ user.visible_message("[user] cuts [src]'s wiring.", "You remove [src]'s wiring.")
+ else if(W.tool_behaviour == TOOL_SCREWDRIVER)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state++
+ user.visible_message("[user] adjusts the wiring.", "You adjust [src]'s wiring.")
+ if(SPACEPOD_WIRES_SECURED)
+ if(W.tool_behaviour == TOOL_SCREWDRIVER)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state--
+ user.visible_message("[user] unclips [src]'s wiring harnesses.", "You unclip [src]'s wiring harnesses.")
+ else if(istype(W, /obj/item/circuitboard/mecha/pod))
+ . = TRUE
+ if(user.temporarilyRemoveItemFromInventory(W))
+ qdel(W)
+ construction_state++
+ user.visible_message("[user] inserts the mainboard into [src].", "You insert the mainboard into [src].")
+ else
+ to_chat(user, "[W] is stuck to your hand!")
+ if(SPACEPOD_CIRCUIT_LOOSE)
+ if(W.tool_behaviour == TOOL_CROWBAR)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state--
+ var/obj/item/circuitboard/mecha/pod/B = new
+ B.forceMove(loc)
+ user.visible_message("[user] pries out the mainboard.", "You pry out the mainboard.")
+ else if(W.tool_behaviour == TOOL_SCREWDRIVER)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state++
+ user.visible_message("[user] secures the mainboard.", "You secure the mainboard.")
+ if(SPACEPOD_CIRCUIT_SECURED)
+ if(W.tool_behaviour == TOOL_SCREWDRIVER)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state--
+ user.visible_message("[user] unsecures the mainboard.", "You unscrew the mainboard from [src].")
+ else if(istype(W, /obj/item/pod_parts/core))
+ . = TRUE
+ if(user.temporarilyRemoveItemFromInventory(W))
+ qdel(W)
+ construction_state++
+ user.visible_message("[user] inserts the core into [src].", "You carefully insert the core into [src].")
+ else
+ to_chat(user, "[W] is stuck to your hand!")
+ if(SPACEPOD_CORE_LOOSE)
+ if(W.tool_behaviour == TOOL_CROWBAR)
+ . = TRUE
+ W.play_tool_sound(src)
+ var/obj/item/pod_parts/core/C = new
+ C.forceMove(loc)
+ construction_state--
+ user.visible_message("[user] delicately removes the core from [src] with a crowbar.", "You delicately remove the core from [src] with a crowbar.")
+ else if(W.tool_behaviour == TOOL_WRENCH)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state++
+ user.visible_message("[user] secures the core's bolts.", "You secure the core's bolts.")
+ if(SPACEPOD_CORE_SECURED)
+ if(W.tool_behaviour == TOOL_WRENCH)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state--
+ user.visible_message("[user] unsecures [src]'s core.", "You unsecure [src]'s core.")
+ else if(istype(W, /obj/item/stack/sheet/metal))
+ . = TRUE
+ if(ST.use(5))
+ user.visible_message("[user] fabricates a pressure bulkhead for [src].", "You frabricate a pressure bulkhead for [src].")
+ construction_state++
+ else
+ to_chat(user, "You need 5 metal for this!")
+ if(SPACEPOD_BULKHEAD_LOOSE)
+ if(W.tool_behaviour == TOOL_CROWBAR)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state--
+ var/obj/item/stack/sheet/metal/five/M = new
+ M.forceMove(loc)
+ user.visible_message("[user] pops [src]'s bulkhead panelling loose.", "You pop [src]'s bulkhead panelling loose.")
+ else if(W.tool_behaviour == TOOL_WRENCH)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state++
+ user.visible_message("[user] secures [src]'s bulkhead panelling.", "You secure [src]'s bulkhead panelling.")
+ if(SPACEPOD_BULKHEAD_SECURED)
+ if(W.tool_behaviour == TOOL_WRENCH)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state--
+ user.visible_message("[user] unbolts [src]'s bulkhead panelling.", "You unbolt [src]'s bulkhead panelling.")
+ else if(W.tool_behaviour == TOOL_WELDER)
+ . = TRUE
+ if(W.use_tool(src, user, 20, amount=3, volume = 50))
+ construction_state = SPACEPOD_ARMOR_WELDED
+ user.visible_message("[user] seals [src]'s bulkhead panelling with a weld.", "You seal [src]'s bulkhead panelling with a weld.")
+ if(SPACEPOD_BULKHEAD_WELDED)
+ if(W.tool_behaviour == TOOL_WELDER)
+ . = TRUE
+ if(W.use_tool(src, user, 20, amount=3, volume = 50))
+ construction_state = SPACEPOD_BULKHEAD_SECURED
+ user.visible_message("[user] cuts [src]'s bulkhead panelling loose.", "You cut [src]'s bulkhead panelling loose.")
+ if(istype(W, /obj/item/pod_parts/armor))
+ . = TRUE
+ if(user.transferItemToLoc(W, src))
+ add_armor(W)
+ construction_state++
+ user.visible_message("[user] installs [src]'s armor plating.", "You install [src]'s armor plating.")
+ else
+ to_chat(user, "[W] is stuck to your hand!")
+ if(SPACEPOD_ARMOR_LOOSE)
+ if(W.tool_behaviour == TOOL_CROWBAR)
+ . = TRUE
+ if(pod_armor)
+ pod_armor.forceMove(loc)
+ remove_armor()
+ W.play_tool_sound(src)
+ construction_state--
+ user.visible_message("[user] pries off [src]'s armor.", "You pry off [src]'s armor.")
+ if(W.tool_behaviour == TOOL_WRENCH)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state++
+ user.visible_message("[user] bolts down [src]'s armor.", "You bolt down [src]'s armor.")
+ if(SPACEPOD_ARMOR_SECURED)
+ if(W.tool_behaviour == TOOL_WRENCH)
+ . = TRUE
+ W.play_tool_sound(src)
+ construction_state--
+ user.visible_message("[user] unsecures [src]'s armor.", "You unsecure [src]'s armor.")
+ else if(W.tool_behaviour == TOOL_WELDER)
+ . = TRUE
+ if(W.use_tool(src, user, 50, amount=3, volume = 50))
+ construction_state = SPACEPOD_ARMOR_WELDED
+ user.visible_message("[user] welds [src]'s armor.", "You weld [src]'s armor.")
+ // finally this took too fucking long
+ // somehow this takes up 40 lines less code than the original, code-less version. And it actually works
+ update_icon()
diff --git a/yogstation/code/modules/spacepods/equipment.dm b/yogstation/code/modules/spacepods/equipment.dm
new file mode 100644
index 000000000000..6f84897079e7
--- /dev/null
+++ b/yogstation/code/modules/spacepods/equipment.dm
@@ -0,0 +1,349 @@
+/obj/item/spacepod_equipment
+ var/obj/spacepod/spacepod
+ icon = 'yogstation/icons/obj/spacepods/parts.dmi'
+ var/slot = SPACEPOD_SLOT_MISC
+ var/slot_space = 1
+
+/obj/item/spacepod_equipment/proc/on_install(obj/spacepod/SP)
+ spacepod = SP
+ SP.equipment |= src
+ forceMove(SP)
+
+/obj/item/spacepod_equipment/proc/on_uninstall()
+ spacepod.equipment -= src
+
+/obj/item/spacepod_equipment/proc/can_install(obj/spacepod/SP, mob/user)
+ var/room = SP.equipment_slot_limits[slot] || 0
+ for(var/obj/item/spacepod_equipment/EQ in SP.equipment)
+ if(EQ.slot == slot)
+ room -= EQ.slot_space
+ if(room < slot_space)
+ to_chat(user, "There's no room for another [slot] system!")
+ return FALSE
+ return TRUE
+
+/obj/item/spacepod_equipment/proc/can_uninstall(mob/user)
+ return TRUE
+
+/obj/item/spacepod_equipment/weaponry
+ slot = SPACEPOD_SLOT_WEAPON
+ var/projectile_type
+ var/shot_cost = 0
+ var/shots_per = 1
+ var/fire_sound
+ var/fire_delay = 15
+ var/overlay_icon
+ var/overlay_icon_state
+
+/obj/item/spacepod_equipment/weaponry/on_install(obj/spacepod/SP)
+ . = ..()
+ SP.weapon = src
+ SP.update_icon()
+
+/obj/item/spacepod_equipment/weaponry/on_uninstall()
+ . = ..()
+ if(spacepod.weapon == src)
+ spacepod.weapon = null
+
+/obj/item/spacepod_equipment/weaponry/proc/fire_weapons(target)
+ if(spacepod.next_firetime > world.time)
+ to_chat(usr, "Your weapons are recharging.")
+ playsound(src, 'sound/weapons/gun_dry_fire.ogg', 30, TRUE)
+ return
+ spacepod.next_firetime = world.time + fire_delay
+ if(!spacepod.cell || !spacepod.cell.use(shot_cost))
+ to_chat(usr, "Insufficient charge to fire the weapons")
+ playsound(src, 'sound/weapons/gun_dry_fire.ogg', 30, TRUE)
+ return
+ for(var/I in 1 to shots_per)
+ spacepod.fire_projectiles(projectile_type, target)
+ playsound(src, fire_sound, 50, TRUE)
+ sleep(2)
+
+/*
+///////////////////////////////////////
+/////////Cargo System//////////////////
+///////////////////////////////////////
+*/
+
+/obj/item/spacepod_equipment/cargo // this one holds large crates and shit
+ name = "pod cargo"
+ desc = "You shouldn't be seeing this"
+ icon_state = "cargo_blank"
+ slot = SPACEPOD_SLOT_CARGO
+
+/obj/item/spacepod_equipment/cargo/large
+ name = "spacepod crate storage system"
+ desc = "A heavy duty storage system for spacepods. Holds one crate."
+ icon_state = "cargo_crate"
+ var/obj/storage = null
+ var/storage_type = /obj/structure/closet/crate
+
+/obj/item/spacepod_equipment/cargo/large/on_install(obj/spacepod/SP)
+ ..()
+ // COMSIG - a way to make component signals sound more important than they actually are.
+ // it's not even limited to components. Does this look like a component to you?
+ // Okay here's a better name: It's a fucking *event handler*. Like the ones in javascript.
+ // a much more descriptive and less scary name than fucking "COMSIG". But noooooooooo
+ // the TG coders were too self important to pick a descriptive name and wanted to sound all scientific
+ RegisterSignal(SP, COMSIG_MOUSEDROPPED_ONTO, .proc/spacepod_mousedrop)
+
+/obj/item/spacepod_equipment/cargo/large/on_uninstall()
+ UnregisterSignal(spacepod, COMSIG_MOUSEDROPPED_ONTO)
+ ..()
+
+/obj/item/spacepod_equipment/cargo/large/can_uninstall(mob/user)
+ if(storage)
+ to_chat(user, "Unload the cargo first!")
+ return FALSE
+ return ..()
+
+/obj/item/spacepod_equipment/cargo/large/proc/spacepod_mousedrop(obj/spacepod/SP, obj/A, mob/user)
+ if(user == SP.pilot || user in SP.passengers)
+ return FALSE
+ if(istype(A, storage_type) && SP.Adjacent(A)) // For loading ore boxes
+ if(!storage)
+ to_chat(user, "You begin loading [A] into [SP]'s [src]")
+ if(do_after_mob(user, list(A, SP), 40))
+ storage = A
+ A.forceMove(src)
+ to_chat(user, "You load [A] into [SP]'s [src]!")
+ else
+ to_chat(user, "You fail to load [A] into [SP]'s [src]")
+ else
+ to_chat(user, "[SP] already has \an [storage]")
+ return TRUE
+ return FALSE
+
+/obj/item/spacepod_equipment/cargo/large/ore
+ name = "spacepod ore storage system"
+ desc = "An ore storage system for spacepods. Scoops up any ore you drive over. Needs to be loaded with an ore box to work"
+ icon_state = "cargo_ore"
+ storage_type = /obj/structure/ore_box
+
+/obj/item/spacepod_equipment/cargo/large/ore/on_install(obj/spacepod/SP)
+ ..()
+ RegisterSignal(SP, COMSIG_MOVABLE_MOVED, .proc/spacepod_moved)
+
+/obj/item/spacepod_equipment/cargo/large/ore/on_uninstall()
+ UnregisterSignal(spacepod, COMSIG_MOVABLE_MOVED)
+ ..()
+
+/obj/item/spacepod_equipment/cargo/large/ore/proc/spacepod_moved(obj/spacepod/SP)
+ if(storage)
+ for(var/turf/T in SP.locs)
+ for(var/obj/item/stack/ore in T)
+ ore.forceMove(storage)
+
+/obj/item/spacepod_equipment/cargo/chair
+ name = "passenger seat"
+ desc = "A passenger seat for a spacepod."
+ icon_state = "sec_cargo_chair"
+ var/occupant_mod = 1
+
+/obj/item/spacepod_equipment/cargo/chair/on_install(obj/spacepod/SP)
+ ..()
+ SP.max_passengers += occupant_mod
+
+/obj/item/spacepod_equipment/cargo/chair/on_uninstall()
+ spacepod.max_passengers -= occupant_mod
+ ..()
+
+/obj/item/spacepod_equipment/cargo/chair/can_uninstall(mob/user)
+ if(spacepod.passengers.len > (spacepod.max_passengers - occupant_mod))
+ to_chat(user, "You can't remove an occupied seat! Remove the occupant first.")
+ return FALSE
+ return ..()
+
+/*
+///////////////////////////////////////
+/////////Weapon System///////////////////
+///////////////////////////////////////
+*/
+
+/obj/item/spacepod_equipment/weaponry/disabler
+ name = "disabler system"
+ desc = "A weak disabler system for space pods, fires disabler beams."
+ icon_state = "weapon_taser"
+ projectile_type = /obj/item/projectile/beam/disabler
+ shot_cost = 400
+ fire_sound = 'sound/weapons/taser2.ogg'
+ overlay_icon = 'yogstation/icons/obj/spacepods/2x2.dmi'
+ overlay_icon_state = "pod_weapon_disabler"
+
+/obj/item/spacepod_equipment/weaponry/burst_disabler
+ name = "burst disabler system"
+ desc = "A weak disabler system for space pods, this one fires 3 at a time."
+ icon_state = "weapon_burst_taser"
+ projectile_type = /obj/item/projectile/beam/disabler
+ shot_cost = 1200
+ shots_per = 3
+ fire_sound = 'sound/weapons/taser2.ogg'
+ fire_delay = 30
+ overlay_icon = 'yogstation/icons/obj/spacepods/2x2.dmi'
+ overlay_icon_state = "pod_weapon_disabler"
+
+/obj/item/spacepod_equipment/weaponry/laser
+ name = "laser system"
+ desc = "A weak laser system for space pods, fires concentrated bursts of energy."
+ icon_state = "weapon_laser"
+ projectile_type = /obj/item/projectile/beam
+ shot_cost = 600
+ fire_sound = 'sound/weapons/Laser.ogg'
+ overlay_icon = 'yogstation/icons/obj/spacepods/2x2.dmi'
+ overlay_icon_state = "pod_weapon_laser"
+
+// MINING LASERS
+/obj/item/spacepod_equipment/weaponry/basic_pod_ka
+ name = "weak kinetic accelerator"
+ desc = "A weak kinetic accelerator for space pods, fires bursts of energy that cut through rock."
+ icon = 'yogstation/goon/icons/obj/spacepods/parts.dmi'
+ icon_state = "pod_taser"
+ projectile_type = /obj/item/projectile/kinetic/pod
+ shot_cost = 300
+ fire_delay = 14
+ fire_sound = 'sound/weapons/Kenetic_accel.ogg'
+
+/obj/item/spacepod_equipment/weaponry/pod_ka
+ name = "kinetic accelerator system"
+ desc = "A kinetic accelerator system for space pods, fires bursts of energy that cut through rock."
+ icon = 'yogstation/goon/icons/obj/spacepods/parts.dmi'
+ icon_state = "pod_m_laser"
+ projectile_type = /obj/item/projectile/kinetic/pod/regular
+ shot_cost = 250
+ fire_delay = 10
+ fire_sound = 'sound/weapons/Kenetic_accel.ogg'
+
+/obj/item/projectile/kinetic/pod
+ range = 4
+
+/obj/item/projectile/kinetic/pod/regular
+ damage = 50
+ pressure_decrease = 0.5
+
+/obj/item/spacepod_equipment/weaponry/plasma_cutter
+ name = "plasma cutter system"
+ desc = "A plasma cutter system for space pods. It is capable of expelling concentrated plasma bursts to mine or cut off xeno limbs!"
+ icon = 'yogstation/goon/icons/obj/spacepods/parts.dmi'
+ icon_state = "pod_p_cutter"
+ projectile_type = /obj/item/projectile/plasma
+ shot_cost = 250
+ fire_delay = 10
+ fire_sound = 'sound/weapons/plasma_cutter.ogg'
+ overlay_icon = 'yogstation/icons/obj/spacepods/2x2.dmi'
+ overlay_icon_state = "pod_weapon_plasma"
+
+/obj/item/spacepod_equipment/weaponry/plasma_cutter/adv
+ name = "enhanced plasma cutter system"
+ desc = "An enhanced plasma cutter system for space pods. It is capable of expelling concentrated plasma bursts to mine or cut off xeno faces!"
+ icon_state = "pod_ap_cutter"
+ projectile_type = /obj/item/projectile/plasma/adv
+ shot_cost = 200
+ fire_delay = 8
+
+/*
+///////////////////////////////////////
+/////////Misc. System///////////////////
+///////////////////////////////////////
+*/
+
+/obj/item/spacepod_equipment/tracker
+ name = "spacepod tracking system"
+ desc = "A tracking device for spacepods."
+ icon = 'yogstation/goon/icons/obj/spacepods/parts.dmi'
+ icon_state = "pod_locator"
+
+/*
+///////////////////////////////////////
+/////////Lock System///////////////////
+///////////////////////////////////////
+*/
+
+/obj/item/spacepod_equipment/lock
+ name = "pod lock"
+ desc = "You shouldn't be seeing this"
+ icon_state = "blank"
+ slot = SPACEPOD_SLOT_LOCK
+
+/obj/item/spacepod_equipment/lock/on_install(obj/spacepod/SP)
+ ..()
+ RegisterSignal(SP, COMSIG_PARENT_ATTACKBY, .proc/spacepod_attackby)
+ SP.lock = src
+
+/obj/item/spacepod_equipment/lock/on_uninstall()
+ UnregisterSignal(spacepod, COMSIG_PARENT_ATTACKBY)
+ if(spacepod.lock == src)
+ spacepod.lock = null
+ spacepod.locked = FALSE
+ ..()
+
+/obj/item/spacepod_equipment/lock/proc/spacepod_attackby(obj/spacepod/SP, I, mob/user)
+ return FALSE
+
+// Key and Tumbler System
+/obj/item/spacepod_equipment/lock/keyed
+ name = "spacepod tumbler lock"
+ desc = "A locking system to stop podjacking. This version uses a standalone key."
+ icon_state = "lock_tumbler"
+ var/static/id_source = 0
+ var/id = null
+
+/obj/item/spacepod_equipment/lock/keyed/Initialize()
+ . = ..()
+ if(id == null)
+ id = ++id_source
+
+
+/obj/item/spacepod_equipment/lock/keyed/spacepod_attackby(obj/spacepod/SP, obj/item/I, mob/user)
+ if(istype(I, /obj/item/spacepod_key))
+ var/obj/item/spacepod_key/key = I
+ if(key.id == id)
+ SP.lock_pod()
+ return
+ else
+ to_chat(user, "This is the wrong key!")
+ return TRUE
+ return FALSE
+
+/obj/item/spacepod_equipment/lock/keyed/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/spacepod_key))
+ var/obj/item/spacepod_key/key = I
+ if(key.id == null)
+ key.id = id
+ to_chat(user, "You grind the blank key to fit the lock.")
+ else
+ to_chat(user, "This key is already ground!")
+ else
+ ..()
+
+/obj/item/spacepod_equipment/lock/keyed/sec
+ id = "security spacepod"
+
+// The key
+/obj/item/spacepod_key
+ name = "spacepod key"
+ desc = "A key for a spacepod lock."
+ icon = 'yogstation/icons/obj/spacepods/parts.dmi'
+ icon_state = "podkey"
+ w_class = WEIGHT_CLASS_TINY
+ var/id = null
+
+/obj/item/spacepod_key/sec
+ name = "security spacepod key"
+ desc = "Unlocks the security spacepod. Probably best kept out of assistant hands."
+ id = "security spacepod"
+
+/obj/item/device/lock_buster
+ name = "pod lock buster"
+ desc = "Destroys a podlock in mere seconds once applied. Waranty void if used."
+ icon = 'yogstation/icons/obj/spacepods/parts.dmi'
+ icon_state = "lock_buster_off"
+ var/on = FALSE
+
+/obj/item/device/lock_buster/attack_self(mob/user)
+ on = !on
+ if(on)
+ icon_state = "lock_buster_on"
+ else
+ icon_state = "lock_buster_off"
+ to_chat(user, "You turn the [src] [on ? "on" : "off"].")
diff --git a/yogstation/code/modules/spacepods/parts.dm b/yogstation/code/modules/spacepods/parts.dm
new file mode 100644
index 000000000000..00836229b329
--- /dev/null
+++ b/yogstation/code/modules/spacepods/parts.dm
@@ -0,0 +1,168 @@
+/obj/item/pod_parts
+ icon = 'yogstation/goon/icons/obj/spacepods/parts.dmi'
+ w_class = WEIGHT_CLASS_GIGANTIC
+ flags_1 = CONDUCT_1
+
+/obj/item/pod_parts/core
+ name = "space pod core"
+ icon_state = "core"
+
+/obj/item/pod_parts/pod_frame
+ name = "space pod frame"
+ density = 0
+ anchored = 0
+ var/link_to = null
+ var/link_angle = 0
+
+/obj/item/pod_parts/pod_frame/ComponentInitialize()
+ AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE)
+
+/obj/item/pod_parts/pod_frame/proc/find_square()
+ /*
+ each part, in essence, stores the relative position of another part
+ you can find where this part should be by looking at the current direction of the current part and applying the link_angle
+ the link_angle is the angle between the part's direction and its following part, which is the current part's link_to
+ the code works by going in a loop - each part is capable of starting a loop by checking for the part after it, and that part checking, and so on
+ this 4-part loop, starting from any part of the frame, can determine if all the parts are properly in place and aligned
+ it also checks that each part is unique, and that all the parts are there for the spacepod itself
+ */
+ var/neededparts = list(/obj/item/pod_parts/pod_frame/aft_port, /obj/item/pod_parts/pod_frame/aft_starboard, /obj/item/pod_parts/pod_frame/fore_port, /obj/item/pod_parts/pod_frame/fore_starboard)
+ var/turf/T
+ var/obj/item/pod_parts/pod_frame/linked
+ var/obj/item/pod_parts/pod_frame/pointer
+ var/list/connectedparts = list()
+ neededparts -= src
+ linked = src
+ for(var/i = 1; i <= 4; i++)
+ T = get_turf(get_step(linked, turn(linked.dir, -linked.link_angle))) //get the next place that we want to look at
+ if(locate(linked.link_to) in T)
+ pointer = locate(linked.link_to) in T
+ if(istype(pointer, linked.link_to) && pointer.dir == linked.dir && pointer.anchored)
+ if(!(pointer in connectedparts))
+ connectedparts += pointer
+ linked = pointer
+ pointer = null
+ if(connectedparts.len < 4)
+ return 0
+ for(var/i = 1; i <=4; i++)
+ var/obj/item/pod_parts/pod_frame/F = connectedparts[i]
+ if(F.type in neededparts) //if one of the items can be founded in neededparts
+ neededparts -= F.type
+ else //because neededparts has 4 distinct items, this must be called if theyre not all in place and wrenched
+ return 0
+ return connectedparts
+
+/obj/item/pod_parts/pod_frame/attackby(var/obj/item/O, mob/user)
+ if(istype(O, /obj/item/stack/rods))
+ var/obj/item/stack/rods/R = O
+ var/list/linkedparts = find_square()
+ if(!linkedparts)
+ to_chat(user, "You cannot assemble a pod frame because you do not have the necessary assembly.")
+ return TRUE
+ if(!R.use(10))
+ to_chat(user, "You need 10 rods for this.")
+ return TRUE
+ var/obj/spacepod/pod = new
+ pod.forceMove(loc)
+ switch(dir)
+ if(NORTH)
+ pod.angle = 0
+ if(SOUTH)
+ pod.angle = 180
+ if(WEST)
+ pod.angle = 270
+ if(EAST)
+ pod.angle = 90
+ pod.process(2)
+ to_chat(user, "You strut the pod frame together.")
+ for(var/obj/item/pod_parts/pod_frame/F in linkedparts)
+ if(1 == turn(F.dir, -F.link_angle)) //if the part links north during construction, as the bottom left part always does
+ pod.forceMove(F.loc)
+ qdel(F)
+ return TRUE
+ if(O.tool_behaviour == TOOL_WRENCH)
+ to_chat(user, "You [!anchored ? "secure \the [src] in place." : "remove the securing bolts."]")
+ anchored = !anchored
+ density = anchored
+ O.play_tool_sound(src)
+ return TRUE
+
+/obj/item/pod_parts/pod_frame/fore_port
+ name = "fore port pod frame"
+ icon_state = "pod_fp"
+ desc = "A space pod frame component. This is the fore port component."
+ link_to = /obj/item/pod_parts/pod_frame/fore_starboard
+ link_angle = 90
+
+/obj/item/pod_parts/pod_frame/fore_starboard
+ name = "fore starboard pod frame"
+ icon_state = "pod_fs"
+ desc = "A space pod frame component. This is the fore starboard component."
+ link_to = /obj/item/pod_parts/pod_frame/aft_starboard
+ link_angle = 180
+
+/obj/item/pod_parts/pod_frame/aft_port
+ name = "aft port pod frame"
+ icon_state = "pod_ap"
+ desc = "A space pod frame component. This is the aft port component."
+ link_to = /obj/item/pod_parts/pod_frame/fore_port
+ link_angle = 0
+
+/obj/item/pod_parts/pod_frame/aft_starboard
+ name = "aft starboard pod frame"
+ icon_state = "pod_as"
+ desc = "A space pod frame component. This is the aft starboard component."
+ link_to = /obj/item/pod_parts/pod_frame/aft_port
+ link_angle = 270
+
+/obj/item/pod_parts/armor
+ name = "civilian pod armor"
+ icon_state = "pod_armor_civ"
+ desc = "Spacepod armor. This is the civilian version. It looks rather flimsy."
+ var/pod_icon = 'yogstation/goon/icons/obj/spacepods/2x2.dmi'
+ var/pod_icon_state = "pod_civ"
+ var/pod_desc = "A sleek civilian space pod."
+ var/pod_integrity = 250
+
+/obj/item/pod_parts/armor/syndicate
+ name = "syndicate pod armor"
+ icon_state = "pod_armor_synd"
+ desc = "Tough-looking spacepod armor, with a bold \"FUCK NT\" stenciled directly into it."
+ pod_icon_state = "pod_synd"
+ pod_desc = "A menacing military space pod with \"FUCK NT\" stenciled onto the side"
+ pod_integrity = 400
+
+/obj/item/pod_parts/armor/black
+ name = "black pod armor"
+ icon_state = "pod_armor_black"
+ desc = "Plain black spacepod armor, with no logos or insignias anywhere on it."
+ pod_icon_state = "pod_black"
+ pod_desc = "An all black space pod with no insignias."
+
+/obj/item/pod_parts/armor/gold
+ name = "golden pod armor"
+ icon_state = "pod_armor_gold"
+ desc = "Golden spacepod armor. Looks like what a rich spessmen put on their spacepod."
+ pod_icon_state = "pod_gold"
+ pod_desc = "A civilian space pod with a gold body, must have cost somebody a pretty penny"
+ pod_integrity = 220
+
+/obj/item/pod_parts/armor/industrial
+ name = "industrial pod armor"
+ icon_state = "pod_armor_industrial"
+ desc = "Tough industrial-grade spacepod armor. While meant for construction work, it is commonly used in spacepod battles, too."
+ pod_icon_state = "pod_industrial"
+ pod_desc = "A rough looking space pod meant for industrial work"
+ pod_integrity = 330
+
+/obj/item/pod_parts/armor/security
+ name = "security pod armor"
+ icon_state = "pod_armor_mil"
+ desc = "Tough military-grade pod armor, meant for use by the NanoTrasen military and it's sub-divisons for space combat."
+ pod_icon_state = "pod_mil"
+ pod_desc = "An armed security spacepod with reinforced armor plating brandishing the Nanotrasen Military insignia"
+ pod_integrity = 350
+
+/obj/item/circuitboard/mecha/pod
+ name = "Circuit board (Space Pod Mainboard)"
+ icon_state = "mainboard"
diff --git a/yogstation/code/modules/spacepods/physics.dm b/yogstation/code/modules/spacepods/physics.dm
new file mode 100644
index 000000000000..3bf8824b27c9
--- /dev/null
+++ b/yogstation/code/modules/spacepods/physics.dm
@@ -0,0 +1,295 @@
+/obj/spacepod/process(time)
+ time /= 10 // fuck off with your deciseconds
+
+ if(world.time > last_slowprocess + 15)
+ last_slowprocess = world.time
+ slowprocess()
+
+ var/last_offset_x = offset_x
+ var/last_offset_y = offset_y
+ var/last_angle = angle
+ var/desired_angular_velocity = 0
+ if(isnum(desired_angle))
+ // do some finagling to make sure that our angles end up rotating the short way
+ while(angle > desired_angle + 180)
+ angle -= 360
+ last_angle -= 360
+ while(angle < desired_angle - 180)
+ angle += 360
+ last_angle += 360
+ if(abs(desired_angle - angle) < (max_angular_acceleration * time))
+ desired_angular_velocity = (desired_angle - angle) / time
+ else if(desired_angle > angle)
+ desired_angular_velocity = 2 * sqrt((desired_angle - angle) * max_angular_acceleration * 0.25)
+ else
+ desired_angular_velocity = -2 * sqrt((angle - desired_angle) * max_angular_acceleration * 0.25)
+ var/angular_velocity_adjustment = CLAMP(desired_angular_velocity - angular_velocity, -max_angular_acceleration*time, max_angular_acceleration*time)
+ if(angular_velocity_adjustment && cell && cell.use(abs(angular_velocity_adjustment) * 0.05))
+ last_rotate = angular_velocity_adjustment / time
+ angular_velocity += angular_velocity_adjustment
+ else
+ last_rotate = 0
+ angle += angular_velocity * time
+
+ // calculate drag and shit
+
+ var/velocity_mag = sqrt(velocity_x*velocity_x+velocity_y*velocity_y) // magnitude
+ if(velocity_mag || angular_velocity != 0)
+ var/drag = 0
+ for(var/turf/T in locs)
+ if(isspaceturf(T))
+ continue
+ drag += 0.001
+ var/floating = FALSE
+ if(T.has_gravity() && !brakes && velocity_mag > 0.1 && cell && cell.use((is_mining_level(z) ? 3 : 15) * time))
+ floating = TRUE // want to fly this shit on the station? Have fun draining your battery.
+ if((!floating && T.has_gravity()) || brakes) // brakes are a kind of magboots okay?
+ drag += is_mining_level(z) ? 0.1 : 0.5 // some serious drag. Damn. Except lavaland, it has less gravity or something
+ if(velocity_mag > 5 && prob(velocity_mag * 4) && istype(T, /turf/open/floor))
+ var/turf/open/floor/TF = T
+ TF.make_plating() // pull up some floor tiles. Stop going so fast, ree.
+ var/datum/gas_mixture/env = T.return_air()
+ if(env)
+ var/pressure = env.return_pressure()
+ drag += velocity_mag * pressure * 0.0001 // 1 atmosphere should shave off 1% of velocity per tile
+ if(velocity_mag > 20)
+ drag = max(drag, (velocity_mag - 20) / time)
+ if(drag)
+ if(velocity_mag)
+ var/drag_factor = 1 - CLAMP(drag * time / velocity_mag, 0, 1)
+ velocity_x *= drag_factor
+ velocity_y *= drag_factor
+ if(angular_velocity != 0)
+ var/drag_factor_spin = 1 - CLAMP(drag * 30 * time / abs(angular_velocity), 0, 1)
+ angular_velocity *= drag_factor_spin
+
+ // Alright now calculate the THRUST
+ var/thrust_x
+ var/thrust_y
+ var/fx = cos(90 - angle)
+ var/fy = sin(90 - angle)
+ var/sx = fy
+ var/sy = -fx
+ last_thrust_forward = 0
+ last_thrust_right = 0
+ if(brakes)
+ if(user_thrust_dir)
+ to_chat(pilot, "Your brakes are on!")
+ // basically calculates how much we can brake using the thrust
+ var/forward_thrust = -((fx * velocity_x) + (fy * velocity_y)) / time
+ var/right_thrust = -((sx * velocity_x) + (sy * velocity_y)) / time
+ forward_thrust = CLAMP(forward_thrust, -backward_maxthrust, forward_maxthrust)
+ right_thrust = CLAMP(right_thrust, -side_maxthrust, side_maxthrust)
+ thrust_x += forward_thrust * fx + right_thrust * sx;
+ thrust_y += forward_thrust * fy + right_thrust * sy;
+ last_thrust_forward = forward_thrust
+ last_thrust_right = right_thrust
+ else // want some sort of help piloting the ship? Haha no fuck you do it yourself
+ if(user_thrust_dir & NORTH)
+ thrust_x += fx * forward_maxthrust
+ thrust_y += fy * forward_maxthrust
+ last_thrust_forward = forward_maxthrust
+ if(user_thrust_dir & SOUTH)
+ thrust_x -= fx * backward_maxthrust
+ thrust_y -= fy * backward_maxthrust
+ last_thrust_forward = -backward_maxthrust
+ if(user_thrust_dir & EAST)
+ thrust_x += sx * side_maxthrust
+ thrust_y += sy * side_maxthrust
+ last_thrust_right = side_maxthrust
+ if(user_thrust_dir & WEST)
+ thrust_x -= sx * side_maxthrust
+ thrust_y -= sy * side_maxthrust
+ last_thrust_right = -side_maxthrust
+
+ if(cell && cell.use(10 * sqrt((thrust_x*thrust_x)+(thrust_y*thrust_y)) * time))
+ velocity_x += thrust_x * time
+ velocity_y += thrust_y * time
+ else
+ last_thrust_forward = 0
+ last_thrust_right = 0
+ if(!brakes && user_thrust_dir)
+ to_chat(pilot, "You are out of power!")
+
+ offset_x += velocity_x * time
+ offset_y += velocity_y * time
+ // alright so now we reconcile the offsets with the in-world position.
+ while((offset_x > 0 && velocity_x > 0) || (offset_y > 0 && velocity_y > 0) || (offset_x < 0 && velocity_x < 0) || (offset_y < 0 && velocity_y < 0))
+ var/failed_x = FALSE
+ var/failed_y = FALSE
+ if(offset_x > 0 && velocity_x > 0)
+ dir = EAST
+ if(!Move(get_step(src, EAST)))
+ offset_x = 0
+ failed_x = TRUE
+ velocity_x *= -bounce_factor
+ velocity_y *= lateral_bounce_factor
+ else
+ offset_x--
+ last_offset_x--
+ else if(offset_x < 0 && velocity_x < 0)
+ dir = WEST
+ if(!Move(get_step(src, WEST)))
+ offset_x = 0
+ failed_x = TRUE
+ velocity_x *= -bounce_factor
+ velocity_y *= lateral_bounce_factor
+ else
+ offset_x++
+ last_offset_x++
+ else
+ failed_x = TRUE
+ if(offset_y > 0 && velocity_y > 0)
+ dir = NORTH
+ if(!Move(get_step(src, NORTH)))
+ offset_y = 0
+ failed_y = TRUE
+ velocity_y *= -bounce_factor
+ velocity_x *= lateral_bounce_factor
+ else
+ offset_y--
+ last_offset_y--
+ else if(offset_y < 0 && velocity_y < 0)
+ dir = SOUTH
+ if(!Move(get_step(src, SOUTH)))
+ offset_y = 0
+ failed_y = TRUE
+ velocity_y *= -bounce_factor
+ velocity_x *= lateral_bounce_factor
+ else
+ offset_y++
+ last_offset_y++
+ else
+ failed_y = TRUE
+ if(failed_x && failed_y)
+ break
+ // prevents situations where you go "wtf I'm clearly right next to it" as you enter a stationary spacepod
+ if(velocity_x == 0)
+ if(offset_x > 0.5)
+ if(Move(get_step(src, EAST)))
+ offset_x--
+ last_offset_x--
+ else
+ offset_x = 0
+ if(offset_x < -0.5)
+ if(Move(get_step(src, WEST)))
+ offset_x++
+ last_offset_x++
+ else
+ offset_x = 0
+ if(velocity_y == 0)
+ if(offset_y > 0.5)
+ if(Move(get_step(src, NORTH)))
+ offset_y--
+ last_offset_y--
+ else
+ offset_y = 0
+ if(offset_y < -0.5)
+ if(Move(get_step(src, SOUTH)))
+ offset_y++
+ last_offset_y++
+ else
+ offset_y = 0
+ dir = NORTH
+ var/matrix/mat_from = new()
+ mat_from.Turn(last_angle)
+ var/matrix/mat_to = new()
+ mat_to.Turn(angle)
+ transform = mat_from
+ pixel_x = last_offset_x*32
+ pixel_y = last_offset_y*32
+ animate(src, transform=mat_to, pixel_x = offset_x*32, pixel_y = offset_y*32, time = time*10, flags=ANIMATION_END_NOW)
+ for(var/mob/living/M in contents)
+ var/client/C = M.client
+ if(!C)
+ continue
+ C.pixel_x = last_offset_x*32
+ C.pixel_y = last_offset_y*32
+ animate(C, pixel_x = offset_x*32, pixel_y = offset_y*32, time = time*10, flags=ANIMATION_END_NOW)
+ user_thrust_dir = 0
+ update_icon()
+
+/obj/spacepod/Bumped(atom/movable/A)
+ if(A.dir & NORTH)
+ velocity_y += bump_impulse
+ if(A.dir & SOUTH)
+ velocity_y -= bump_impulse
+ if(A.dir & EAST)
+ velocity_x += bump_impulse
+ if(A.dir & WEST)
+ velocity_x -= bump_impulse
+ return ..()
+
+/obj/spacepod/Bump(atom/A)
+ var/bump_velocity = 0
+ if(dir & (NORTH|SOUTH))
+ bump_velocity = abs(velocity_y) + (abs(velocity_x) / 15)
+ else
+ bump_velocity = abs(velocity_x) + (abs(velocity_y) / 15)
+ if(istype(A, /obj/machinery/door/airlock)) // try to open doors
+ var/obj/machinery/door/D = A
+ if(!D.operating)
+ if(D.allowed(D.requiresID() ? pilot : null))
+ spawn(0)
+ D.open()
+ else
+ D.do_animate("deny")
+ var/atom/movable/AM = A
+ if(istype(AM) && !AM.anchored && bump_velocity > 1)
+ step(AM, dir)
+ // if a bump is that fast then it's not a bump. It's a collision.
+ if(bump_velocity > 10 && !ismob(A))
+ var/strength = bump_velocity / 10
+ strength = strength * strength
+ strength = min(strength, 5) // don't want the explosions *too* big
+ // wew lad, might wanna slow down there
+ explosion(A, -1, round((strength - 1) / 2), round(strength))
+ message_admins("[key_name_admin(pilot)] has impacted a spacepod into a wall with velocity [bump_velocity]")
+ take_damage(strength*10, BRUTE, "melee", TRUE)
+ log_game("[key_name(pilot)] has impacted a spacepod into a wall with velocity [bump_velocity]")
+ visible_message("The force of the impact causes a shockwave")
+ else if(isliving(A) && bump_velocity > 5)
+ var/mob/living/M = A
+ M.apply_damage(bump_velocity * 2)
+ take_damage(bump_velocity, BRUTE, "melee", FALSE)
+ playsound(M.loc, "swing_hit", 1000, 1, -1)
+ M.Knockdown(bump_velocity * 2)
+ M.visible_message("The force of the impact knocks [M] down!","The force of the impact knocks you down!")
+ log_combat(pilot, M, "impacted", src, "with velocity of [bump_velocity]")
+ return ..()
+
+/obj/spacepod/proc/fire_projectiles(proj_type, target) // if spacepods of other sizes are added override this or something
+ var/fx = cos(90 - angle)
+ var/fy = sin(90 - angle)
+ var/sx = fy
+ var/sy = -fx
+ var/ox = (offset_x * 32) + 16
+ var/oy = (offset_y * 32) + 16
+ var/list/origins = list(list(ox + fx*16 - sx*16, oy + fy*16 - sy*16), list(ox + fx*16 + sx*16, oy + fy*16 + sy*16))
+ for(var/list/origin in origins)
+ var/this_x = origin[1]
+ var/this_y = origin[2]
+ var/turf/T = get_turf(src)
+ while(this_x > 16)
+ T = get_step(T, EAST)
+ this_x -= 32
+ while(this_x < -16)
+ T = get_step(T, WEST)
+ this_x += 32
+ while(this_y > 16)
+ T = get_step(T, NORTH)
+ this_y -= 32
+ while(this_y < -16)
+ T = get_step(T, SOUTH)
+ this_y += 32
+ if(!T)
+ continue
+ var/obj/item/projectile/proj = new proj_type(T)
+ proj.starting = T
+ proj.firer = usr
+ proj.def_zone = "chest"
+ proj.original = target
+ proj.pixel_x = round(this_x)
+ proj.pixel_y = round(this_y)
+ spawn()
+ proj.fire(angle)
diff --git a/yogstation/code/modules/spacepods/prebuilt.dm b/yogstation/code/modules/spacepods/prebuilt.dm
new file mode 100644
index 000000000000..5414cded2865
--- /dev/null
+++ b/yogstation/code/modules/spacepods/prebuilt.dm
@@ -0,0 +1,29 @@
+/obj/spacepod/prebuilt
+ icon = 'yogstation/goon/icons/obj/spacepods/2x2.dmi'
+ icon_state = "pod_civ"
+ var/cell_type = /obj/item/stock_parts/cell/high/plus
+ var/armor_type = /obj/item/pod_parts/armor
+ var/internal_tank_type = /obj/machinery/portable_atmospherics/canister/air
+ var/equipment_types = list()
+ construction_state = SPACEPOD_ARMOR_WELDED
+
+/obj/spacepod/prebuilt/Initialize()
+ ..()
+ add_armor(new armor_type(src))
+ if(cell_type)
+ cell = new cell_type(src)
+ if(internal_tank_type)
+ internal_tank = new internal_tank_type(src)
+ for(var/equip in equipment_types)
+ var/obj/item/spacepod_equipment/SE = new equip(src)
+ SE.on_install(src)
+
+/obj/spacepod/prebuilt/sec
+ name = "security space pod"
+ icon_state = "pod_mil"
+ locked = TRUE
+ armor_type = /obj/item/pod_parts/armor/security
+ equipment_types = list(/obj/item/spacepod_equipment/weaponry/disabler,
+ /obj/item/spacepod_equipment/lock/keyed/sec,
+ /obj/item/spacepod_equipment/tracker,
+ /obj/item/spacepod_equipment/cargo/chair)
diff --git a/yogstation/code/modules/spacepods/spacepod.dm b/yogstation/code/modules/spacepods/spacepod.dm
new file mode 100644
index 000000000000..91fcc51a2358
--- /dev/null
+++ b/yogstation/code/modules/spacepods/spacepod.dm
@@ -0,0 +1,683 @@
+// This is like paradise spacepods but with a few differences:
+// - no spacepod fabricator, parts are made in techfabs and frames are made using metal rods.
+// - not tile based, instead has velocity and acceleration. why? so I can put all this math to use.
+// - damages shit if you run into it too fast instead of just stopping. You have to have a huge running start to do that though and damages the spacepod as well.
+// - doesn't explode
+
+GLOBAL_LIST_INIT(spacepods_list, list())
+
+/obj/spacepod
+ name = "space pod"
+ desc = "A frame for a spacepod."
+ icon = 'yogstation/goon/icons/obj/spacepods/construction_2x2.dmi'
+ icon_state = "pod_1"
+ density = 1
+ opacity = 0
+ dir = NORTH // always points north because why not
+ layer = SPACEPOD_LAYER
+ bound_width = 64
+ bound_height = 64
+ animate_movement = NO_STEPS // we do our own gliding here
+
+ anchored = 1
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF // it floats above lava or something, I dunno
+
+ max_integrity = 50
+ integrity_failure = 50
+
+ var/list/equipment = list()
+ var/list/equipment_slot_limits = list(
+ SPACEPOD_SLOT_MISC = 1,
+ SPACEPOD_SLOT_CARGO = 2,
+ SPACEPOD_SLOT_WEAPON = 1,
+ SPACEPOD_SLOT_LOCK = 1)
+ var/obj/item/spacepod_equipment/lock/lock
+ var/obj/item/spacepod_equipment/weaponry/weapon
+ var/next_firetime = 0
+ var/locked = FALSE
+ var/hatch_open = FALSE
+ var/construction_state = SPACEPOD_EMPTY
+ var/obj/item/pod_parts/armor/pod_armor = null
+ var/obj/item/stock_parts/cell/cell = null
+ var/datum/gas_mixture/cabin_air
+ var/obj/machinery/portable_atmospherics/canister/internal_tank
+ var/last_slowprocess = 0
+
+ var/mob/living/pilot
+ var/list/passengers = list()
+ var/max_passengers = 0
+
+ var/velocity_x = 0 // tiles per second.
+ var/velocity_y = 0
+ var/offset_x = 0 // like pixel_x/y but in tiles
+ var/offset_y = 0
+ var/angle = 0 // degrees, clockwise
+ var/desired_angle = null // set by pilot moving his mouse
+ var/angular_velocity = 0 // degrees per second
+ var/max_angular_acceleration = 360 // in degrees per second per second
+ var/last_thrust_forward = 0
+ var/last_thrust_right = 0
+ var/last_rotate = 0
+
+ var/brakes = TRUE
+ var/user_thrust_dir = 0
+ var/forward_maxthrust = 6
+ var/backward_maxthrust = 3
+ var/side_maxthrust = 1
+
+ var/lights = 0
+ var/lights_power = 6
+ var/static/list/icon_light_color = list("pod_civ" = LIGHT_COLOR_WHITE, \
+ "pod_mil" = "#BBF093", \
+ "pod_synd" = LIGHT_COLOR_RED, \
+ "pod_gold" = LIGHT_COLOR_WHITE, \
+ "pod_black" = "#3B8FE5", \
+ "pod_industrial" = "#CCCC00")
+
+ var/bump_impulse = 0.6
+ var/bounce_factor = 0.2 // how much of our velocity to keep on collision
+ var/lateral_bounce_factor = 0.95 // mostly there to slow you down when you drive (pilot?) down a 2x2 corridor
+
+/obj/spacepod/Initialize()
+ . = ..()
+ GLOB.spacepods_list += src
+ START_PROCESSING(SSfastprocess, src)
+ cabin_air = new
+ cabin_air.temperature = T20C
+ cabin_air.volume = 200
+ /*cabin_air.assert_gas(/datum/gas/oxygen)
+ cabin_air.assert_gas(/datum/gas/nitrogen)
+ cabin_air.gases[/datum/gas/oxygen][MOLES] = ONE_ATMOSPHERE*O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
+ cabin_air.gases[/datum/gas/nitrogen][MOLES] = ONE_ATMOSPHERE*N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)*/
+
+/obj/spacepod/Destroy()
+ GLOB.spacepods_list -= src
+ QDEL_NULL(pilot)
+ QDEL_LIST(passengers)
+ QDEL_LIST(equipment)
+ QDEL_NULL(cabin_air)
+ QDEL_NULL(cell)
+ return ..()
+
+/obj/spacepod/attackby(obj/item/W, mob/living/user)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+ else if(construction_state != SPACEPOD_ARMOR_WELDED)
+ . = handle_spacepod_construction(W, user)
+ if(.)
+ return
+ else
+ return ..()
+ // and now for the real stuff
+ else
+ if(W.tool_behaviour == TOOL_CROWBAR)
+ if(hatch_open || !locked)
+ hatch_open = !hatch_open
+ W.play_tool_sound(src)
+ to_chat(user, "You [hatch_open ? "open" : "close"] the maintenance hatch.")
+ else
+ to_chat(user, "The hatch is locked shut!")
+ return TRUE
+ if(istype(W, /obj/item/stock_parts/cell))
+ if(!hatch_open)
+ to_chat(user, "The maintenance hatch is closed!")
+ return TRUE
+ if(cell)
+ to_chat(user, "The pod already has a battery.")
+ return TRUE
+ if(user.transferItemToLoc(W, src))
+ to_chat(user, "You insert [W] into the pod.")
+ cell = W
+ return TRUE
+ if(istype(W, /obj/item/spacepod_equipment))
+ if(!hatch_open)
+ to_chat(user, "The maintenance hatch is closed!")
+ return TRUE
+ var/obj/item/spacepod_equipment/SE = W
+ if(SE.can_install(src, user) && user.temporarilyRemoveItemFromInventory(SE))
+ SE.forceMove(src)
+ SE.on_install(src)
+ return TRUE
+ if(lock && istype(W, /obj/item/device/lock_buster))
+ var/obj/item/device/lock_buster/L = W
+ if(L.on)
+ user.visible_message(user, "[user] is drilling through the [src]'s lock!",
+ "You start drilling through the [src]'s lock!")
+ if(do_after(user, 100 * W.toolspeed, target = src))
+ if(lock)
+ var/obj/O = lock
+ lock.on_uninstall()
+ qdel(O)
+ user.visible_message(user, "[user] has destroyed the [src]'s lock!",
+ "You destroy the [src]'s lock!")
+ else
+ user.visible_message(user, "[user] fails to break through the [src]'s lock!",
+ "You were unable to break through the [src]'s lock!")
+ return TRUE
+ to_chat(user, "Turn the [L] on first.")
+ return TRUE
+ if(W.tool_behaviour == TOOL_WELDER)
+ var/repairing = cell || internal_tank || equipment.len || (obj_integrity < max_integrity) || pilot || passengers.len
+ if(!hatch_open)
+ to_chat(user, "You must open the maintenance hatch before [repairing ? "attempting repairs" : "unwelding the armor"].")
+ return TRUE
+ if(repairing && obj_integrity >= max_integrity)
+ to_chat(user, "[src] is fully repaired!")
+ return TRUE
+ to_chat(user, "You start [repairing ? "repairing [src]" : "slicing off [src]'s armor'"]")
+ if(W.use_tool(src, user, 50, amount=3, volume = 50))
+ if(repairing)
+ obj_integrity = min(max_integrity, obj_integrity + 10)
+ update_icon()
+ to_chat(user, "You mend some [pick("dents","bumps","damage")] with [W]")
+ else if(!cell && !internal_tank && !equipment.len && !pilot && !passengers.len && construction_state == SPACEPOD_ARMOR_WELDED)
+ user.visible_message("[user] slices off [src]'s armor.", "You slice off [src]'s armor.")
+ construction_state = SPACEPOD_ARMOR_SECURED
+ update_icon()
+ return ..()
+
+/obj/spacepod/attack_hand(mob/user as mob)
+ if(user.a_intent == INTENT_GRAB && !locked)
+ var/mob/living/target
+ if(pilot)
+ target = pilot
+ else if(passengers.len > 0)
+ target = passengers[1]
+
+ if(target && istype(target))
+ src.visible_message("[user] is trying to rip the door open and pull [target] out of the [src]!",
+ "You see [user] outside the door trying to rip it open!")
+ if(do_after(user, 50, target = src) && construction_state == SPACEPOD_ARMOR_WELDED)
+ if(remove_rider(target))
+ target.Stun(20)
+ target.visible_message("[user] flings the door open and tears [target] out of the [src]",
+ "The door flies open and you are thrown out of the [src] and to the ground!")
+ return
+ target.visible_message("[user] was unable to get the door open!",
+ "You manage to keep [user] out of the [src]!")
+
+ if(!hatch_open)
+ //if(cargo_hold.storage_slots > 0)
+ // if(!locked)
+ // cargo_hold.open(user)
+ // else
+ // to_chat(user, "The storage compartment is locked")
+ return ..()
+ var/list/items = list(cell, internal_tank)
+ items += equipment
+ var/list/item_map = list()
+ for(var/obj/I in items)
+ item_map[I.name] = I
+ var/selection = input(user, "Remove which equipment?", null, null) as null|anything in item_map
+ var/obj/O = item_map[selection]
+ if(O && istype(O) && O in contents)
+ // alrightey now to figure out what it is
+ if(O == cell)
+ cell = null
+ else if(O == internal_tank)
+ internal_tank = null
+ else if(O in equipment)
+ var/obj/item/spacepod_equipment/SE = O
+ if(!SE.can_uninstall(user))
+ return
+ SE.on_uninstall()
+ else
+ return
+ O.forceMove(loc)
+ if(isitem(O))
+ user.put_in_hands(O)
+
+
+/obj/spacepod/proc/add_armor(obj/item/pod_parts/armor/armor)
+ desc = armor.pod_desc
+ max_integrity = armor.pod_integrity
+ obj_integrity = max_integrity - integrity_failure + obj_integrity
+ pod_armor = armor
+ update_icon()
+
+/obj/spacepod/proc/remove_armor()
+ if(!pod_armor)
+ obj_integrity = min(integrity_failure, obj_integrity)
+ max_integrity = integrity_failure
+ desc = initial(desc)
+ pod_armor = null
+ update_icon()
+
+
+/obj/spacepod/proc/InterceptClickOn(mob/user, params, atom/target)
+ var/list/params_list = params2list(params)
+ if(target == src || istype(target, /obj/screen) || (target && (target in user.GetAllContents())) || user != pilot || params_list["shift"] || params_list["alt"] || params_list["ctrl"])
+ return FALSE
+ if(weapon)
+ weapon.fire_weapons(target)
+ return TRUE
+
+/obj/spacepod/take_damage()
+ ..()
+ update_icon()
+
+/obj/spacepod/return_air()
+ return cabin_air
+/obj/spacepod/remove_air(amount)
+ return cabin_air.remove(amount)
+
+/obj/spacepod/proc/slowprocess()
+ if(cabin_air && cabin_air.volume > 0)
+ var/delta = cabin_air.temperature - T20C
+ cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1)))
+ if(internal_tank && cabin_air)
+ var/datum/gas_mixture/tank_air = internal_tank.return_air()
+
+ var/release_pressure = ONE_ATMOSPHERE
+ var/cabin_pressure = cabin_air.return_pressure()
+ var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2)
+ var/transfer_moles = 0
+ if(pressure_delta > 0) //cabin pressure lower than release pressure
+ if(tank_air.return_temperature() > 0)
+ transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
+ var/datum/gas_mixture/removed = tank_air.remove(transfer_moles)
+ cabin_air.merge(removed)
+ else if(pressure_delta < 0) //cabin pressure higher than release pressure
+ var/turf/T = get_turf(src)
+ var/datum/gas_mixture/t_air = T.return_air()
+ pressure_delta = cabin_pressure - release_pressure
+ if(t_air)
+ pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta)
+ if(pressure_delta > 0) //if location pressure is lower than cabin pressure
+ transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
+ var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles)
+ if(T)
+ T.assume_air(removed)
+ else //just delete the cabin gas, we're in space or some shit
+ qdel(removed)
+
+/mob/Stat()
+ . = ..()
+ if(isspacepod(loc) && statpanel("Status"))
+ var/obj/spacepod/S = loc
+ stat(null)
+ stat(null, "Spacepod Charge: [S.cell ? "[round(S.cell.charge,0.1)]/[S.cell.maxcharge] KJ" : "NONE"]")
+ stat(null, "Spacepod Integrity: [round(S.obj_integrity,0.1)]/[S.max_integrity]")
+ stat(null, "Spacepod Velocity: [round(sqrt(S.velocity_x*S.velocity_x+S.velocity_y*S.velocity_y), 0.1)] m/s")
+ stat(null)
+
+/obj/spacepod/ex_act(severity)
+ switch(severity)
+ if(1)
+ for(var/mob/living/M in contents)
+ M.ex_act(severity+1)
+ deconstruct()
+ if(2)
+ take_damage(100, BRUTE, "bomb", 0)
+ if(3)
+ if(prob(40))
+ take_damage(40, BRUTE, "bomb", 0)
+
+/obj/spacepod/obj_break()
+ if(obj_integrity <= 0)
+ return // nah we'll let the other boy handle it
+ if(construction_state < SPACEPOD_ARMOR_LOOSE)
+ return
+ if(pod_armor)
+ var/obj/A = pod_armor
+ remove_armor()
+ qdel(A)
+ if(prob(40))
+ new /obj/item/stack/sheet/metal/five(loc)
+ if(prob(40))
+ new /obj/item/stack/sheet/metal/five(loc)
+ construction_state = SPACEPOD_CORE_SECURED
+ if(cabin_air)
+ var/datum/gas_mixture/GM = cabin_air.remove_ratio(1)
+ var/turf/T = get_turf(src)
+ if(GM && T)
+ T.assume_air(GM)
+ cell = null
+ internal_tank = null
+ for(var/atom/movable/AM in contents)
+ if(ismob(AM))
+ forceMove(AM, loc)
+ remove_rider(AM)
+ else if(prob(60))
+ AM.forceMove(loc)
+ else if(isitem(AM) || !isobj(AM))
+ qdel(AM)
+ else
+ var/obj/O = AM
+ O.forceMove(loc)
+ O.deconstruct()
+
+
+/obj/spacepod/deconstruct(disassembled = FALSE)
+ if(!get_turf(src))
+ qdel(src)
+ return
+ remove_rider(pilot)
+ while(passengers.len)
+ remove_rider(passengers[1])
+ passengers.Cut()
+ if(disassembled)
+ // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ // alright fine fine you can have the frame pieces back
+ var/clamped_angle = (round(angle, 90) % 360 + 360) % 360
+ var/target_dir = NORTH
+ switch(clamped_angle)
+ if(0)
+ target_dir = NORTH
+ if(90)
+ target_dir = EAST
+ if(180)
+ target_dir = SOUTH
+ if(270)
+ target_dir = WEST
+
+ var/list/frame_piece_types = list(/obj/item/pod_parts/pod_frame/aft_port, /obj/item/pod_parts/pod_frame/aft_starboard, /obj/item/pod_parts/pod_frame/fore_port, /obj/item/pod_parts/pod_frame/fore_starboard)
+ var/obj/item/pod_parts/pod_frame/current_piece = null
+ var/turf/CT = get_turf(src)
+ var/list/frame_pieces = list()
+ for(var/frame_type in frame_piece_types)
+ var/obj/item/pod_parts/pod_frame/F = new frame_type
+ F.dir = target_dir
+ F.anchored = TRUE
+ if(1 == turn(F.dir, -F.link_angle))
+ current_piece = F
+ frame_pieces += F
+ while(current_piece && !current_piece.loc)
+ if(!CT)
+ break
+ current_piece.forceMove(CT)
+ CT = get_step(CT, turn(current_piece.dir, -current_piece.link_angle))
+ current_piece = locate(current_piece.link_to) in frame_pieces
+ // there here's your frame pieces back, happy?
+ qdel(src)
+
+/obj/spacepod/update_icon()
+ cut_overlays()
+ if(construction_state != SPACEPOD_ARMOR_WELDED)
+ icon = 'yogstation/goon/icons/obj/spacepods/construction_2x2.dmi'
+ icon_state = "pod_[construction_state]"
+ if(pod_armor && construction_state >= SPACEPOD_ARMOR_LOOSE)
+ var/mutable_appearance/masked_armor = mutable_appearance(icon = 'yogstation/goon/icons/obj/spacepods/construction_2x2.dmi', icon_state = "armor_mask")
+ var/mutable_appearance/armor = mutable_appearance(pod_armor.pod_icon, pod_armor.pod_icon_state)
+ armor.blend_mode = BLEND_MULTIPLY
+ masked_armor.overlays = list(armor)
+ masked_armor.appearance_flags = KEEP_TOGETHER
+ add_overlay(masked_armor)
+ return
+
+ if(pod_armor)
+ icon = pod_armor.pod_icon
+ icon_state = pod_armor.pod_icon_state
+ else
+ icon = 'yogstation/goon/icons/obj/spacepods/2x2.dmi'
+ icon_state = initial(icon_state)
+
+ if(obj_integrity <= max_integrity / 2)
+ add_overlay(image(icon='yogstation/goon/icons/obj/spacepods/2x2.dmi', icon_state="pod_damage"))
+ if(obj_integrity <= max_integrity / 4)
+ add_overlay(image(icon='yogstation/goon/icons/obj/spacepods/2x2.dmi', icon_state="pod_fire"))
+
+ if(weapon && weapon.overlay_icon_state)
+ add_overlay(image(icon=weapon.overlay_icon,icon_state=weapon.overlay_icon_state))
+
+ light_color = icon_light_color[icon_state] || LIGHT_COLOR_WHITE
+
+ // Thrust!
+ var/list/left_thrusts = list()
+ left_thrusts.len = 8
+ var/list/right_thrusts = list()
+ right_thrusts.len = 8
+ for(var/cdir in GLOB.cardinals)
+ left_thrusts[cdir] = 0
+ right_thrusts[cdir] = 0
+ var/back_thrust = 0
+ if(last_thrust_right != 0)
+ var/tdir = last_thrust_right > 0 ? WEST : EAST
+ left_thrusts[tdir] = abs(last_thrust_right) / side_maxthrust
+ right_thrusts[tdir] = abs(last_thrust_right) / side_maxthrust
+ if(last_thrust_forward > 0)
+ back_thrust = last_thrust_forward / forward_maxthrust
+ if(last_thrust_forward < 0)
+ left_thrusts[NORTH] = -last_thrust_forward / backward_maxthrust
+ right_thrusts[NORTH] = -last_thrust_forward / backward_maxthrust
+ if(last_rotate != 0)
+ var/frac = abs(last_rotate) / max_angular_acceleration
+ for(var/cdir in GLOB.cardinals)
+ if(last_rotate > 0)
+ right_thrusts[cdir] += frac
+ else
+ left_thrusts[cdir] += frac
+ for(var/cdir in GLOB.cardinals)
+ var/left_thrust = left_thrusts[cdir]
+ var/right_thrust = right_thrusts[cdir]
+ if(left_thrust)
+ add_overlay(image(icon = 'yogstation/icons/obj/spacepods/2x2.dmi', icon_state = "rcs_left", dir = cdir))
+ if(right_thrust)
+ add_overlay(image(icon = 'yogstation/icons/obj/spacepods/2x2.dmi', icon_state = "rcs_right", dir = cdir))
+ if(back_thrust)
+ var/image/I = image(icon = 'yogstation/icons/obj/spacepods/2x2.dmi', icon_state = "thrust")
+ I.transform = matrix(1, 0, 0, 0, 1, -32)
+ add_overlay(I)
+
+/obj/spacepod/MouseDrop_T(atom/movable/A, mob/living/user)
+ if(user == pilot || user in passengers || construction_state != SPACEPOD_ARMOR_WELDED)
+ return
+
+ if(istype(A, /obj/machinery/portable_atmospherics/canister))
+ if(internal_tank)
+ to_chat(user, "[src] already has an internal_tank!")
+ return
+ if(!A.Adjacent(src))
+ to_chat(user, "The canister is not close enough!")
+ return
+ if(hatch_open)
+ to_chat(user, "The hatch is shut!")
+ to_chat(user, "You begin inserting the canister into [src]")
+ if(do_after_mob(user, list(A, src), 50) && construction_state == SPACEPOD_ARMOR_WELDED)
+ to_chat(user, "You insert the canister into [src]")
+ A.forceMove(src)
+ internal_tank = A
+ return
+
+ if(isliving(A))
+ var/mob/living/M = A
+ if(M != user && !locked)
+ if(passengers.len >= max_passengers && !pilot)
+ to_chat(user, "That person can't fly the pod!")
+ return
+ if(passengers.len < max_passengers)
+ visible_message("[user] starts loading [M] into [src]!")
+ if(do_after_mob(user, list(M, src), 50) && construction_state == SPACEPOD_ARMOR_WELDED)
+ add_rider(M, FALSE)
+ return
+ if(M == user)
+ enter_pod(user)
+ return
+
+ return ..()
+
+/obj/spacepod/proc/enter_pod(mob/living/user)
+ if(user.stat != CONSCIOUS)
+ return FALSE
+
+ if(locked)
+ to_chat(user, "[src]'s doors are locked!")
+ return FALSE
+
+ if(!istype(user))
+ return FALSE
+
+ if(user.incapacitated())
+ return FALSE
+ if(!ishuman(user))
+ return FALSE
+
+ if(passengers.len <= max_passengers || !pilot)
+ visible_message("[user] starts to climb into [src].")
+ if(do_after(user, 40, target = src) && construction_state == SPACEPOD_ARMOR_WELDED)
+ var/success = add_rider(user)
+ if(!success)
+ to_chat(user, "You were too slow. Try better next time, loser.")
+ return success
+ else
+ to_chat(user, "You stop entering [src].")
+ else
+ to_chat(user, "You can't fit in [src], it's full!")
+ return FALSE
+
+/obj/spacepod/proc/verb_check(require_pilot = TRUE, mob/user = null)
+ if(!user)
+ user = usr
+ if(require_pilot && user != pilot)
+ to_chat(user, "You can't reach the controls from your chair")
+ return FALSE
+ return !user.incapacitated() && isliving(user)
+
+/obj/spacepod/verb/exit_pod()
+ set name = "Exit pod"
+ set category = "Spacepod"
+ set src = usr.loc
+
+ if(!isliving(usr) || usr.stat > CONSCIOUS)
+ return
+
+ if(usr.restrained())
+ to_chat(usr, "You attempt to stumble out of the [src]. This will take two minutes.")
+ if(pilot)
+ to_chat(pilot, "[usr] is trying to escape the [src].")
+ if(!do_after(usr, 1200, target = src))
+ return
+
+ if(remove_rider(usr))
+ to_chat(usr, "You climb out of [src].")
+
+/obj/spacepod/verb/lock_pod()
+ set name = "Lock Doors"
+ set category = "Spacepod"
+ set src = usr.loc
+
+ if(!verb_check(FALSE))
+ return
+
+ if(!lock)
+ to_chat(usr, "[src] has no locking mechanism.")
+ locked = FALSE //Should never be false without a lock, but if it somehow happens, that will force an unlock.
+ else
+ locked = !locked
+ to_chat(usr, "You [locked ? "lock" : "unlock"] the doors.")
+
+/obj/spacepod/verb/toggle_brakes()
+ set name = "Toggle Brakes"
+ set category = "Spacepod"
+ set src = usr.loc
+
+ if(!verb_check())
+ return
+ brakes = !brakes
+ to_chat(usr, "You toggle the brakes [brakes ? "on" : "off"].")
+
+/obj/spacepod/AltClick(user)
+ if(!verb_check(user = user))
+ return
+ brakes = !brakes
+ to_chat(usr, "You toggle the brakes [brakes ? "on" : "off"].")
+
+/obj/spacepod/verb/toggleLights()
+ set name = "Toggle Lights"
+ set category = "Spacepod"
+ set src = usr.loc
+
+ if(!verb_check())
+ return
+
+ lights = !lights
+ if(lights)
+ set_light(lights_power)
+ else
+ set_light(0)
+ to_chat(usr, "Lights toggled [lights ? "on" : "off"].")
+ for(var/mob/M in passengers)
+ to_chat(M, "Lights toggled [lights ? "on" : "off"].")
+
+/obj/spacepod/verb/toggleDoors()
+ set name = "Toggle Nearby Pod Doors"
+ set category = "Spacepod"
+ set src = usr.loc
+
+ if(!verb_check())
+ return
+
+ for(var/obj/machinery/door/poddoor/multi_tile/P in orange(3,src))
+ for(var/mob/living/carbon/human/O in contents)
+ if(P.check_access(O.get_active_held_item()) || P.check_access(O.wear_id))
+ if(P.density)
+ P.open()
+ return TRUE
+ else
+ P.close()
+ return TRUE
+ to_chat(usr, "Access denied.")
+ return
+
+ to_chat(usr, "You are not close to any pod doors.")
+
+/obj/spacepod/proc/add_rider(mob/living/M, allow_pilot = TRUE)
+ if(M == pilot || (M in passengers))
+ return FALSE
+ if(!pilot && allow_pilot)
+ pilot = M
+ LAZYOR(M.mousemove_intercept_objects, src)
+ M.click_intercept = src
+ else if(passengers.len < max_passengers)
+ passengers += M
+ else
+ return FALSE
+ M.stop_pulling()
+ M.forceMove(src)
+ playsound(src, 'sound/machines/windowdoor.ogg', 50, 1)
+ return TRUE
+
+/obj/spacepod/proc/remove_rider(mob/living/M)
+ if(!M)
+ return
+ if(M == pilot)
+ pilot = null
+ LAZYREMOVE(M.mousemove_intercept_objects, src)
+ if(M.click_intercept == src)
+ M.click_intercept = null
+ desired_angle = null // since there's no pilot there's no one aiming it.
+ else if(M in passengers)
+ passengers -= M
+ else
+ return FALSE
+ if(M.loc == src)
+ M.forceMove(loc)
+ if(M.client)
+ M.client.pixel_x = 0
+ M.client.pixel_y = 0
+ return TRUE
+
+/obj/spacepod/onMouseMove(object,location,control,params)
+ if(!pilot || !pilot.client || pilot.incapacitated())
+ return // I don't know what's going on.
+ var/list/params_list = params2list(params)
+ var/sl_list = splittext(params_list["screen-loc"],",")
+ var/sl_x_list = splittext(sl_list[1], ":")
+ var/sl_y_list = splittext(sl_list[2], ":")
+ var/view_list = isnum(pilot.client.view) ? list("[pilot.client.view*2+1]","[pilot.client.view*2+1]") : splittext(pilot.client.view, "x")
+ var/dx = text2num(sl_x_list[1]) + (text2num(sl_x_list[2]) / world.icon_size) - 1 - text2num(view_list[1]) / 2
+ var/dy = text2num(sl_y_list[1]) + (text2num(sl_y_list[2]) / world.icon_size) - 1 - text2num(view_list[2]) / 2
+ if(sqrt(dx*dx+dy*dy) > 1)
+ desired_angle = 90 - ATAN2(dx, dy)
+ else
+ desired_angle = null
+
+/obj/spacepod/relaymove(mob/user, direction)
+ if(user != pilot || pilot.incapacitated())
+ return
+ user_thrust_dir = direction
+
+/obj/spacepod/Entered()
+ . = ..()
+/obj/spacepod/Exited()
+ . = ..()
diff --git a/yogstation/goon/LICENSE.md b/yogstation/goon/LICENSE.md
new file mode 100644
index 000000000000..d227d11c6cdf
--- /dev/null
+++ b/yogstation/goon/LICENSE.md
@@ -0,0 +1,4 @@
+This work is licensed under the Creative Commons
+Attribution-NonCommercial-ShareAlike 3.0 United States License. To view a copy
+of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/us/ or
+send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
\ No newline at end of file
diff --git a/yogstation/goon/README.md b/yogstation/goon/README.md
new file mode 100644
index 000000000000..cae34de90841
--- /dev/null
+++ b/yogstation/goon/README.md
@@ -0,0 +1,8 @@
+# Goon-ported-assets
+
+All files excluding this one you're reading right now have been most likely, taken from [goonstation's 2016 release](https://github.com/goonstation/goonstation-2016), unless stated otherwise.
+It is very likely that there are modifications to be compatible or on par with our code, however. These changes are licensed under the same license as the other goon files.
+
+## License
+
+See LICENSE.md
diff --git a/yogstation/goon/icons/obj/spacepods/2x2.dmi b/yogstation/goon/icons/obj/spacepods/2x2.dmi
new file mode 100644
index 000000000000..02def41cf1bc
Binary files /dev/null and b/yogstation/goon/icons/obj/spacepods/2x2.dmi differ
diff --git a/yogstation/goon/icons/obj/spacepods/construction_2x2.dmi b/yogstation/goon/icons/obj/spacepods/construction_2x2.dmi
new file mode 100644
index 000000000000..7766fa79788e
Binary files /dev/null and b/yogstation/goon/icons/obj/spacepods/construction_2x2.dmi differ
diff --git a/yogstation/goon/icons/obj/spacepods/parts.dmi b/yogstation/goon/icons/obj/spacepods/parts.dmi
new file mode 100644
index 000000000000..6375a6ebba77
Binary files /dev/null and b/yogstation/goon/icons/obj/spacepods/parts.dmi differ
diff --git a/yogstation/icons/effects/beam.dmi b/yogstation/icons/effects/beam.dmi
new file mode 100644
index 000000000000..792622d8fd2c
Binary files /dev/null and b/yogstation/icons/effects/beam.dmi differ
diff --git a/yogstation/icons/obj/doors/1x2blast_hor.dmi b/yogstation/icons/obj/doors/1x2blast_hor.dmi
new file mode 100644
index 000000000000..59827a280793
Binary files /dev/null and b/yogstation/icons/obj/doors/1x2blast_hor.dmi differ
diff --git a/yogstation/icons/obj/doors/1x2blast_vert.dmi b/yogstation/icons/obj/doors/1x2blast_vert.dmi
new file mode 100644
index 000000000000..f3fe9da8bbfe
Binary files /dev/null and b/yogstation/icons/obj/doors/1x2blast_vert.dmi differ
diff --git a/yogstation/icons/obj/doors/1x3blast_hor.dmi b/yogstation/icons/obj/doors/1x3blast_hor.dmi
new file mode 100644
index 000000000000..72d8898ae6c9
Binary files /dev/null and b/yogstation/icons/obj/doors/1x3blast_hor.dmi differ
diff --git a/yogstation/icons/obj/doors/1x3blast_vert.dmi b/yogstation/icons/obj/doors/1x3blast_vert.dmi
new file mode 100644
index 000000000000..6fe220f28ccd
Binary files /dev/null and b/yogstation/icons/obj/doors/1x3blast_vert.dmi differ
diff --git a/yogstation/icons/obj/doors/1x4blast_hor.dmi b/yogstation/icons/obj/doors/1x4blast_hor.dmi
new file mode 100644
index 000000000000..41165639a905
Binary files /dev/null and b/yogstation/icons/obj/doors/1x4blast_hor.dmi differ
diff --git a/yogstation/icons/obj/doors/1x4blast_vert.dmi b/yogstation/icons/obj/doors/1x4blast_vert.dmi
new file mode 100644
index 000000000000..2835732ad540
Binary files /dev/null and b/yogstation/icons/obj/doors/1x4blast_vert.dmi differ
diff --git a/yogstation/icons/obj/spacepods/2x2.dmi b/yogstation/icons/obj/spacepods/2x2.dmi
new file mode 100644
index 000000000000..ecdb69d66c37
Binary files /dev/null and b/yogstation/icons/obj/spacepods/2x2.dmi differ
diff --git a/yogstation/icons/obj/spacepods/parts.dmi b/yogstation/icons/obj/spacepods/parts.dmi
new file mode 100644
index 000000000000..4b51a7ca68b4
Binary files /dev/null and b/yogstation/icons/obj/spacepods/parts.dmi differ