diff --git a/code/__DEFINES/shuttles.dm b/code/__DEFINES/shuttles.dm
index a133816f2d..134a4ea496 100644
--- a/code/__DEFINES/shuttles.dm
+++ b/code/__DEFINES/shuttles.dm
@@ -45,4 +45,9 @@
#define HYPERSPACE_LAUNCH 2
#define HYPERSPACE_END 3
-#define CALL_SHUTTLE_REASON_LENGTH 12
\ No newline at end of file
+#define CALL_SHUTTLE_REASON_LENGTH 12
+
+//Engine related
+#define ENGINE_COEFF_MIN 0.5
+#define ENGINE_COEFF_MAX 2
+#define ENGINE_DEFAULT_MAXSPEED_ENGINES 5
\ No newline at end of file
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index 441fe44740..9f28a78615 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -47,7 +47,7 @@ SUBSYSTEM_DEF(shuttle)
var/lockdown = FALSE //disallow transit after nuke goes off
- var/auto_call = 99000 //time before in deciseconds in which the shuttle is auto called. Default is 2½ hours plus 15 for the shuttle. So total is 3.
+ var/auto_call = 99000 //time before in deciseconds in which the shuttle is auto called. Default is 2½ hours plus 15 for the shuttle. So total is 3.
/datum/controller/subsystem/shuttle/Initialize(timeofday)
if(!arrivals)
@@ -544,3 +544,8 @@ SUBSYSTEM_DEF(shuttle)
for(var/obj/docking_port/mobile/M in mobile)
if(M.is_in_shuttle_bounds(A))
return TRUE
+
+/datum/controller/subsystem/shuttle/proc/get_containing_shuttle(atom/A)
+ for(var/obj/docking_port/mobile/M in mobile)
+ if(M.is_in_shuttle_bounds(A))
+ return M
diff --git a/code/controllers/subsystem/shuttle.dm.rej b/code/controllers/subsystem/shuttle.dm.rej
new file mode 100644
index 0000000000..0fc7b77b16
--- /dev/null
+++ b/code/controllers/subsystem/shuttle.dm.rej
@@ -0,0 +1,9 @@
+diff a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm (rejected hunks)
+@@ -536,3 +536,7 @@ SUBSYSTEM_DEF(shuttle)
+ if(M.is_in_shuttle_bounds(A))
+ return TRUE
+
++/datum/controller/subsystem/shuttle/proc/get_containing_shuttle(atom/A)
++ for(var/obj/docking_port/mobile/M in mobile)
++ if(M.is_in_shuttle_bounds(A))
++ return M
diff --git a/code/datums/shuttles.dm b/code/datums/shuttles.dm
index 94226dc8ec..a86574e710 100644
--- a/code/datums/shuttles.dm
+++ b/code/datums/shuttles.dm
@@ -21,6 +21,10 @@
mappath = "[prefix][shuttle_id].dmm"
. = ..()
+//Whatever special stuff you want
+/datum/map_template/shuttle/proc/on_bought()
+ return
+
/datum/map_template/shuttle/emergency
port_id = "emergency"
name = "Base Shuttle Template (Emergency)"
@@ -49,6 +53,12 @@
// first 10 minutes only
return world.time - SSticker.round_start_time < 6000
+/datum/map_template/shuttle/emergency/airless/on_bought()
+ //enable buying engines from cargo
+ var/datum/supply_pack/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shuttle_engine]
+ P.special_enabled = TRUE
+
+
/datum/map_template/shuttle/emergency/asteroid
suffix = "asteroid"
name = "Asteroid Station Emergency Shuttle"
diff --git a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm
index b364cba2b1..a72c2bf8f6 100644
--- a/code/game/shuttle_engines.dm
+++ b/code/game/shuttle_engines.dm
@@ -1,21 +1,95 @@
+#define ENGINE_UNWRENCHED 0
+#define ENGINE_WRENCHED 1
+#define ENGINE_WELDED 2
+#define ENGINE_WELDTIME 200
/obj/structure/shuttle
name = "shuttle"
icon = 'icons/turf/shuttle.dmi'
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ obj_integrity = 500
+ max_integrity = 500
+ armor = list(melee = 100, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) //default + ignores melee
/obj/structure/shuttle/engine
name = "engine"
- density = 1
- anchored = 1
+ density = TRUE
+ anchored = TRUE
+ var/engine_power = 1
+ var/state = ENGINE_WELDED //welding shmelding
+
+//Ugh this is a lot of copypasta from emitters, welding need some boilerplate reduction
+/obj/structure/shuttle/engine/can_be_unfasten_wrench(mob/user, silent)
+ if(state == ENGINE_WELDED)
+ if(!silent)
+ to_chat(user, "[src] is welded to the floor!")
+ return FAILED_UNFASTEN
+ return ..()
+
+/obj/structure/shuttle/engine/default_unfasten_wrench(mob/user, obj/item/weapon/wrench/W, time = 20)
+ . = ..()
+ if(. == SUCCESSFUL_UNFASTEN)
+ if(anchored)
+ state = ENGINE_WRENCHED
+ else
+ state = ENGINE_UNWRENCHED
+
+/obj/structure/shuttle/engine/attackby(obj/item/I, mob/user, params)
+ add_fingerprint(user)
+ if(default_unfasten_wrench(user, I))
+ return
+ else if(istype(I, /obj/item/weapon/weldingtool))
+ var/obj/item/weapon/weldingtool/WT = I
+ switch(state)
+ if(ENGINE_UNWRENCHED)
+ to_chat(user, "The [src.name] needs to be wrenched to the floor!")
+ if(EM_SECURED)
+ if(WT.remove_fuel(0,user))
+ playsound(loc, WT.usesound, 50, 1)
+ user.visible_message("[user.name] starts to weld the [name] to the floor.", \
+ "You start to weld \the [src] to the floor...", \
+ "You hear welding.")
+ if(do_after(user,ENGINE_WELDTIME*WT.toolspeed, target = src) && WT.isOn())
+ state = ENGINE_WELDED
+ to_chat(user, "You weld \the [src] to the floor.")
+ alter_engine_power(engine_power)
+ if(EM_WELDED)
+ if(WT.remove_fuel(0,user))
+ playsound(loc, WT.usesound, 50, 1)
+ user.visible_message("[user.name] starts to cut the [name] free from the floor.", \
+ "You start to cut \the [src] free from the floor...", \
+ "You hear welding.")
+ if(do_after(user,ENGINE_WELDTIME*WT.toolspeed, target = src) && WT.isOn())
+ state = ENGINE_WRENCHED
+ to_chat(user, "You cut \the [src] free from the floor.")
+ alter_engine_power(-engine_power)
+ return
+ else
+ return ..()
+
+/obj/structure/shuttle/engine/Destroy()
+ if(state == ENGINE_WELDED)
+ alter_engine_power(-engine_power)
+ . = ..()
+
+//Propagates the change to the shuttle.
+/obj/structure/shuttle/engine/proc/alter_engine_power(mod)
+ if(mod == 0)
+ return
+ if(SSshuttle.is_in_shuttle_bounds(src))
+ var/obj/docking_port/mobile/M = SSshuttle.get_containing_shuttle(src)
+ if(M)
+ M.alter_engines(mod)
/obj/structure/shuttle/engine/heater
name = "heater"
icon_state = "heater"
+ engine_power = 0 // todo make these into 2x1 parts
/obj/structure/shuttle/engine/platform
name = "platform"
icon_state = "platform"
+ engine_power = 0
/obj/structure/shuttle/engine/propulsion
name = "propulsion"
@@ -25,6 +99,10 @@
/obj/structure/shuttle/engine/propulsion/burst
name = "burst"
+/obj/structure/shuttle/engine/propulsion/burst/cargo
+ state = ENGINE_UNWRENCHED
+ anchored = FALSE
+
/obj/structure/shuttle/engine/propulsion/burst/left
name = "left"
icon_state = "burst_l"
diff --git a/code/game/shuttle_engines.dm.rej b/code/game/shuttle_engines.dm.rej
new file mode 100644
index 0000000000..d04dfc957e
--- /dev/null
+++ b/code/game/shuttle_engines.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm (rejected hunks)
+@@ -46,7 +117,7 @@
+ bound_height = 64
+ appearance_flags = 0
+
+-obj/structure/shuttle/engine/huge
++/obj/structure/shuttle/engine/huge
+ name = "engine"
+ opacity = 1
+ icon = 'icons/obj/3x3.dmi'
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index 8a688d833b..d695e49bce 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -710,6 +710,15 @@
/obj/item/weapon/am_containment)
crate_name = "antimatter jar crate"
+/datum/supply_pack/engineering/shuttle_engine
+ name = "Shuttle Engine Crate"
+ cost = 5000
+ access = GLOB.access_ce
+ contains = list(/obj/structure/shuttle/engine/propulsion/burst/cargo)
+ crate_name = "shuttle engine crate"
+ crate_type = /obj/structure/closet/crate/secure/engineering
+ special = TRUE
+
//////////////////////////////////////////////////////////////////////////////
//////////////////////////// Medical /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index e1ed63793d..5b3ac7f4d1 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -213,7 +213,7 @@
set_coefficient = 1
else
set_coefficient = 0.5
- var/call_time = SSshuttle.emergencyCallTime * set_coefficient
+ var/call_time = SSshuttle.emergencyCallTime * set_coefficient * engine_coeff
switch(mode)
// The shuttle can not normally be called while "recalling", so
// if this proc is called, it's via admin fiat
@@ -359,7 +359,7 @@
enterTransit()
mode = SHUTTLE_ESCAPE
launch_status = ENDGAME_LAUNCHED
- setTimer(SSshuttle.emergencyEscapeTime)
+ setTimer(SSshuttle.emergencyEscapeTime * engine_coeff)
priority_announce("The Emergency Shuttle has left the station. Estimate [timeLeft(600)] minutes until the shuttle docks at Central Command.", null, null, "Priority")
if(SHUTTLE_STRANDED)
diff --git a/code/modules/shuttle/manipulator.dm b/code/modules/shuttle/manipulator.dm
index c2feb1331a..6dcf366ba5 100644
--- a/code/modules/shuttle/manipulator.dm
+++ b/code/modules/shuttle/manipulator.dm
@@ -285,6 +285,9 @@
message_admins(msg)
WARNING(msg)
+ return
+ //Everything fine
+ S.on_bought()
/obj/machinery/shuttle_manipulator/proc/unload_preview()
if(preview_shuttle)
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 96db9d0ac3..b9457f9394 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -12,7 +12,7 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
anchored = 1
-
+//
var/id
// this should point -away- from the dockingport door, ie towards the ship
dir = NORTH
@@ -224,11 +224,9 @@
var/timid = FALSE
var/list/ripples = list()
-
-/obj/docking_port/mobile/Initialize()
- . = ..()
- if(!timid)
- register()
+ var/engine_coeff = 1 //current engine coeff
+ var/current_engines = 0 //current engine power
+ var/initial_engines = 0 //initial engine power
/obj/docking_port/mobile/proc/register()
SSshuttle.mobile += src
@@ -244,6 +242,8 @@
/obj/docking_port/mobile/Initialize(mapload)
. = ..()
+ if(!timid)
+ register()
var/area/A = get_area(src)
if(istype(A, /area/shuttle))
@@ -259,6 +259,9 @@
areaInstance.name = name
areaInstance.contents += return_ordered_turfs()
+ initial_engines = count_engines()
+ current_engines = initial_engines
+
#ifdef DOCKING_PORT_HIGHLIGHT
highlight("#0f0")
#endif
@@ -325,17 +328,17 @@
switch(mode)
if(SHUTTLE_CALL)
if(S == destination)
- if(timeLeft(1) < callTime)
- setTimer(callTime)
+ if(timeLeft(1) < callTime * engine_coeff)
+ setTimer(callTime * engine_coeff)
else
destination = S
- setTimer(callTime)
+ setTimer(callTime * engine_coeff)
if(SHUTTLE_RECALL)
if(S == destination)
- setTimer(callTime - timeLeft(1))
+ setTimer(callTime * engine_coeff - timeLeft(1))
else
destination = S
- setTimer(callTime)
+ setTimer(callTime * engine_coeff)
mode = SHUTTLE_CALL
if(SHUTTLE_IDLE, SHUTTLE_IGNITING)
destination = S
@@ -621,7 +624,7 @@
return
else
mode = SHUTTLE_CALL
- setTimer(callTime)
+ setTimer(callTime * engine_coeff)
enterTransit()
return
@@ -685,7 +688,7 @@
var/ds_remaining
if(!timer)
- ds_remaining = callTime
+ ds_remaining = callTime * engine_coeff
else
ds_remaining = max(0, timer - world.time)
@@ -765,5 +768,62 @@
return TRUE
return FALSE
+// Losing all initial engines should get you 2
+// Adding another set of engines at 0.5 time
+/obj/docking_port/mobile/proc/alter_engines(mod)
+ if(mod == 0)
+ return
+ var/old_coeff = engine_coeff
+ engine_coeff = get_engine_coeff(current_engines,mod)
+ current_engines = max(0,current_engines + mod)
+ if(in_flight())
+ var/delta_coeff = engine_coeff / old_coeff
+ modTimer(delta_coeff)
+
+/obj/docking_port/mobile/proc/count_engines()
+ . = 0
+ for(var/obj/structure/shuttle/engine/E in areaInstance.contents)
+ if(!QDELETED(E))
+ . += E.engine_power
+
+// Double initial engines to get to 0.5 minimum
+// Lose all initial engines to get to 2
+//For 0 engine shuttles like BYOS 5 engines to get to doublespeed
+/obj/docking_port/mobile/proc/get_engine_coeff(current,engine_mod)
+ var/new_value = max(0,current + engine_mod)
+ if(new_value == initial_engines)
+ return 1
+ if(new_value > initial_engines)
+ var/delta = new_value - initial_engines
+ var/change_per_engine = (1 - ENGINE_COEFF_MIN) / ENGINE_DEFAULT_MAXSPEED_ENGINES // 5 by default
+ if(initial_engines > 0)
+ change_per_engine = (1 - ENGINE_COEFF_MIN) / initial_engines // or however many it had
+ return Clamp(1 - delta * change_per_engine,ENGINE_COEFF_MIN,ENGINE_COEFF_MAX)
+ if(new_value < initial_engines)
+ var/delta = initial_engines - new_value
+ var/change_per_engine = 1 //doesn't really matter should not be happening for 0 engine shuttles
+ if(initial_engines > 0)
+ change_per_engine = (ENGINE_COEFF_MAX - 1) / initial_engines //just linear drop to max delay
+ return Clamp(1 + delta * change_per_engine,ENGINE_COEFF_MIN,ENGINE_COEFF_MAX)
+
+
+/obj/docking_port/mobile/proc/in_flight()
+ switch(mode)
+ if(SHUTTLE_CALL,SHUTTLE_RECALL)
+ return TRUE
+ if(SHUTTLE_IDLE,SHUTTLE_IGNITING)
+ return FALSE
+ else
+ return FALSE // hmm
+
+/obj/docking_port/mobile/emergency/in_flight()
+ switch(mode)
+ if(SHUTTLE_ESCAPE)
+ return TRUE
+ if(SHUTTLE_STRANDED,SHUTTLE_ENDGAME)
+ return FALSE
+ else
+ return ..()
+
#undef DOCKING_PORT_HIGHLIGHT