diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index 125f5a4b90..9986777445 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -509,6 +509,16 @@ Turf and target are seperate in case you want to teleport some distance from a t
// mob_list.Add(M)
return moblist
+// Format a power value in W, kW, MW, or GW.
+/proc/DisplayPower(powerused)
+ if(powerused < 1000) //Less than a kW
+ return "[powerused] W"
+ else if(powerused < 1000000) //Less than a MW
+ return "[round((powerused * 0.001),0.01)] kW"
+ else if(powerused < 1000000000) //Less than a GW
+ return "[round((powerused * 0.000001),0.001)] MW"
+ return "[round((powerused * 0.000000001),0.0001)] GW"
+
//Forces a variable to be posative
/proc/modulus(var/M)
if(M >= 0)
diff --git a/code/game/objects/items/weapons/circuitboards/computer/computer.dm b/code/game/objects/items/weapons/circuitboards/computer/computer.dm
index 2b3631ddbf..71d6e2d867 100644
--- a/code/game/objects/items/weapons/circuitboards/computer/computer.dm
+++ b/code/game/objects/items/weapons/circuitboards/computer/computer.dm
@@ -100,6 +100,7 @@
/obj/item/weapon/circuitboard/turbine_control
name = T_BOARD("turbine control console")
build_path = /obj/machinery/computer/turbine_computer
+ origin_tech = list(TECH_DATA = 2, TECH_POWER = 2)
/obj/item/weapon/circuitboard/solar_control
name = T_BOARD("solar control console")
diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm
index 08abf452f1..30668993a8 100644
--- a/code/modules/power/turbine.dm
+++ b/code/modules/power/turbine.dm
@@ -1,10 +1,34 @@
+// TURBINE v2 AKA rev4407 Engine reborn!
+
+// How to use it? - Mappers
+//
+// This is a very good power generating mechanism. All you need is a blast furnace with soaring flames and output.
+// Not everything is included yet so the turbine can run out of fuel quiet quickly. The best thing about the turbine is that even
+// though something is on fire that passes through it, it won't be on fire as it passes out of it. So the exhaust fumes can still
+// containt unreacted fuel - plasma and oxygen that needs to be filtered out and re-routed back. This of course requires smart piping
+// For a computer to work with the turbine the compressor requires a comp_id matching with the turbine computer's id. This will be
+// subjected to a change in the near future mind you. Right now this method of generating power is a good backup but don't expect it
+// become a main power source unless some work is done. Have fun. At 50k RPM it generates 60k power. So more than one turbine is needed!
+//
+// - Numbers
+//
+// Example setup S - sparker
+// B - Blast doors into space for venting
+// *BBB****BBB* C - Compressor
+// S CT * T - Turbine
+// * ^ * * V * D - Doors with firedoor
+// **|***D**|** ^ - Fuel feed (Not vent, but a gas outlet)
+// | | V - Suction vent (Like the ones in atmos
+//
+
/obj/machinery/compressor
name = "compressor"
desc = "The compressor stage of a gas turbine generator."
icon = 'icons/obj/pipes.dmi'
icon_state = "compressor"
- anchored = 1
- density = 1
+ anchored = TRUE
+ density = TRUE
+ circuit = /obj/item/weapon/circuitboard/machine/power_compressor
var/obj/machinery/power/turbine/turbine
var/datum/gas_mixture/gas_contained
var/turf/simulated/inturf
@@ -13,69 +37,132 @@
var/rpmtarget = 0
var/capacity = 1e6
var/comp_id = 0
+ var/efficiency
/obj/machinery/power/turbine
name = "gas turbine generator"
desc = "A gas turbine used for backup power generation."
icon = 'icons/obj/pipes.dmi'
icon_state = "turbine"
- anchored = 1
- density = 1
+ anchored = TRUE
+ density = TRUE
+ circuit = /obj/item/weapon/circuitboard/machine/power_turbine
var/obj/machinery/compressor/compressor
var/turf/simulated/outturf
var/lastgen
+ var/productivity = 1
/obj/machinery/computer/turbine_computer
- name = "Gas turbine control computer"
- desc = "A computer to remotely control a gas turbine"
+ name = "gas turbine control computer"
+ desc = "A computer to remotely control a gas turbine."
icon_keyboard = "tech_key"
icon_screen = "turbinecomp"
circuit = /obj/item/weapon/circuitboard/turbine_control
- anchored = 1
- density = 1
var/obj/machinery/compressor/compressor
var/list/obj/machinery/door/blast/doors
var/id = 0
var/door_status = 0
+/obj/item/weapon/circuitboard/machine/power_compressor
+ name = T_BOARD("power compressor")
+ build_path = /obj/machinery/compressor
+ board_type = new /datum/frame/frame_types/machine
+ origin_tech = list(TECH_MATERIAL = 4, TECH_POWER = 2)
+ req_components = list(/obj/item/stack/cable_coil = 5, /obj/item/weapon/stock_parts/manipulator = 6)
+
+/obj/item/weapon/circuitboard/machine/power_turbine
+ name = T_BOARD("power turbine")
+ build_path = /obj/machinery/power/turbine
+ board_type = new /datum/frame/frame_types/machine
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_POWER = 4)
+ req_components = list(/obj/item/stack/cable_coil = 5, /obj/item/weapon/stock_parts/capacitor = 6)
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Compressor
// the inlet stage of the gas turbine electricity generator
-
-/obj/machinery/compressor/New()
- ..()
-
- gas_contained = new
- inturf = get_step(src, dir)
-
- spawn(5)
- turbine = locate() in get_step(src, get_dir(inturf, src))
- if(!turbine)
- stat |= BROKEN
- else
- turbine.stat &= !BROKEN
- turbine.compressor = src
-
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define COMPFRICTION 5e5
#define COMPSTARTERLOAD 2800
+/obj/machinery/compressor/initialize()
+ . = ..()
+ default_apply_parts()
+ gas_contained = new()
+ inturf = get_step(src, dir)
+ locate_machinery()
+ if(!turbine)
+ stat |= BROKEN
+
+// When anchored, don't let air past us.
+/obj/machinery/compressor/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
+ if(!height || air_group)
+ return !anchored
+ return !density
+
+/obj/machinery/compressor/proc/locate_machinery()
+ if(turbine)
+ return
+ turbine = locate() in get_step(src, get_dir(inturf, src))
+ if(turbine)
+ turbine.locate_machinery()
+
+/obj/machinery/compressor/RefreshParts()
+ var/E = 0
+ for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
+ E += M.rating
+ efficiency = E / 6
+
+/obj/machinery/compressor/attackby(obj/item/W, mob/user)
+ src.add_fingerprint(user)
+
+ if(default_deconstruction_screwdriver(user, W))
+ return
+ if(default_part_replacement(user, W))
+ return
+ if(default_unfasten_wrench(user, W))
+ return
+ if(default_deconstruction_crowbar(user, W))
+ return
+ if(ismultitool(W))
+ var/new_ident = input("Enter a new ident tag.", name, comp_id) as null|text
+ if(new_ident && user.Adjacent(src))
+ comp_id = new_ident
+ return
+ return ..()
+
+/obj/machinery/compressor/default_unfasten_wrench(var/mob/user, var/obj/item/weapon/wrench/W, var/time = 20)
+ if((. = ..()))
+ turbine = null
+ if(anchored)
+ inturf = get_step(src, dir)
+ locate_machinery()
+ if(turbine)
+ to_chat(user, "Turbine connected.")
+ stat &= ~BROKEN
+ else
+ to_chat(user, "Turbine not connected.")
+ stat |= BROKEN
+
/obj/machinery/compressor/process()
+ if(!turbine)
+ stat = BROKEN
+ if(stat & BROKEN || panel_open)
+ return
if(!starter)
return
overlays.Cut()
- if(stat & BROKEN)
- return
- if(!turbine)
- stat |= BROKEN
- return
+
rpm = 0.9* rpm + 0.1 * rpmtarget
var/datum/gas_mixture/environment = inturf.return_air()
+
+ // It's a simplified version taking only 1/10 of the moles from the turf nearby. It should be later changed into a better version
var/transfer_moles = environment.total_moles / 10
- //var/transfer_moles = rpm/10000*capacity
var/datum/gas_mixture/removed = inturf.remove_air(transfer_moles)
gas_contained.merge(removed)
- rpm = max(0, rpm - (rpm*rpm)/COMPFRICTION)
-
+ // RPM function to include compression friction - be advised that too low/high of a compfriction value can make things screwy
+ rpm = max(0, rpm - (rpm*rpm)/(COMPFRICTION*efficiency))
if(starter && !(stat & NOPOWER))
use_power(2800)
@@ -85,8 +172,6 @@
if(rpm<1000)
rpmtarget = 0
-
-
if(rpm>50000)
overlays += image('icons/obj/pipes.dmi', "comp-o4", FLY_LAYER)
else if(rpm>10000)
@@ -97,38 +182,84 @@
overlays += image('icons/obj/pipes.dmi', "comp-o1", FLY_LAYER)
//TODO: DEFERRED
-/obj/machinery/power/turbine/New()
- ..()
- outturf = get_step(src, dir)
-
- spawn(5)
-
- compressor = locate() in get_step(src, get_dir(outturf, src))
- if(!compressor)
- stat |= BROKEN
- else
- compressor.stat &= !BROKEN
- compressor.turbine = src
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Turbine
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// These are crucial to working of a turbine - the stats modify the power output. TurbGenQ modifies how much raw energy can you get from
+// rpms, TurbGenG modifies the shape of the curve - the lower the value the less straight the curve is.
#define TURBPRES 9000000
-#define TURBGENQ 20000
+#define TURBGENQ 100000
#define TURBGENG 0.8
+/obj/machinery/power/turbine/initialize()
+ . = ..()
+ default_apply_parts()
+ // The outlet is pointed at the direction of the turbine component
+ outturf = get_step(src, dir)
+ locate_machinery()
+ if(!compressor)
+ stat |= BROKEN
+
+/obj/machinery/power/turbine/RefreshParts()
+ var/P = 0
+ for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts)
+ P += C.rating
+ productivity = P / 6
+
+/obj/machinery/power/turbine/proc/locate_machinery()
+ if(compressor)
+ return
+ compressor = locate() in get_step(src, get_dir(outturf, src))
+ if(compressor)
+ compressor.locate_machinery()
+
+/obj/machinery/power/turbine/attackby(obj/item/W, mob/user)
+ src.add_fingerprint(user)
+
+ if(default_deconstruction_screwdriver(user, W))
+ return
+ if(default_part_replacement(user, W))
+ return
+ if(default_unfasten_wrench(user, W))
+ return
+ if(default_deconstruction_crowbar(user, W))
+ return
+ return ..()
+
+/obj/machinery/power/turbine/default_unfasten_wrench(var/mob/user, var/obj/item/weapon/wrench/W, var/time = 20)
+ if((. = ..()))
+ compressor = null
+ if(anchored)
+ outturf = get_step(src, dir)
+ locate_machinery()
+ if(compressor)
+ to_chat(user, "Compressor connected.")
+ stat &= ~BROKEN
+ else
+ to_chat(user, "Compressor not connected.")
+ stat |= BROKEN
+
/obj/machinery/power/turbine/process()
+ if(!compressor)
+ stat = BROKEN
+ if((stat & BROKEN) || panel_open)
+ return
if(!compressor.starter)
return
overlays.Cut()
- if(stat & BROKEN)
- return
- if(!compressor)
- stat |= BROKEN
- return
- lastgen = ((compressor.rpm / TURBGENQ)**TURBGENG) *TURBGENQ
+
+ // This is the power generation function. If anything is needed it's good to plot it in EXCEL before modifying
+ // the TURBGENQ and TURBGENG values
+ lastgen = ((compressor.rpm / TURBGENQ)**TURBGENG) * TURBGENQ * productivity
add_avail(lastgen)
+
+ // Weird function but it works. Should be something else...
var/newrpm = ((compressor.gas_contained.temperature) * compressor.gas_contained.total_moles)/4
+
newrpm = max(0, newrpm)
if(!compressor.starter || newrpm > 1000)
@@ -139,181 +270,138 @@
var/datum/gas_mixture/removed = compressor.gas_contained.remove(oamount)
outturf.assume_air(removed)
+ // If it works, put an overlay that it works!
if(lastgen > 100)
overlays += image('icons/obj/pipes.dmi', "turb-o", FLY_LAYER)
+ updateDialog()
- for(var/mob/M in viewers(1, src))
- if ((M.client && M.machine == src))
- src.interact(M)
- AutoUpdateAI(src)
+/obj/machinery/power/turbine/attack_hand(var/mob/user as mob)
+ if((. = ..()))
+ return
+ src.interact(user)
/obj/machinery/power/turbine/interact(mob/user)
-
- if ( (get_dist(src, user) > 1 ) || (stat & (NOPOWER|BROKEN)) && (!istype(user, /mob/living/silicon/ai)) )
- user.machine = null
+ if(!Adjacent(user) || (stat & (NOPOWER|BROKEN)) && !issilicon(user))
+ user.unset_machine(src)
user << browse(null, "window=turbine")
return
-
- user.machine = src
+ user.set_machine(src)
var/t = "Gas Turbine Generator"
-
- t += "Generated power : [round(lastgen)] W
"
-
+ t += "Generated power : [DisplayPower(lastgen)]
"
t += "Turbine: [round(compressor.rpm)] RPM
"
-
t += "Starter: [ compressor.starter ? "Off On" : "Off On"]"
-
t += "
Close"
-
t += ""
- user << browse(t, "window=turbine")
- onclose(user, "turbine")
+ var/datum/browser/popup = new(user, "turbine", name, 700, 500, src)
+ popup.set_content(t)
+ popup.open()
return
/obj/machinery/power/turbine/Topic(href, href_list)
- ..()
- if(stat & BROKEN)
+ if(..())
return
- if (usr.stat || usr.restrained() )
- return
- if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
- if(!istype(usr, /mob/living/silicon/ai))
- usr << "You don't have the dexterity to do this!"
- return
- if (( usr.machine==src && ((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon/ai)))
-
-
- if( href_list["close"] )
- usr << browse(null, "window=turbine")
- usr.machine = null
- return
-
- else if( href_list["str"] )
- compressor.starter = !compressor.starter
-
- spawn(0)
- for(var/mob/M in viewers(1, src))
- if ((M.client && M.machine == src))
- src.interact(M)
-
- else
+ if(href_list["close"])
usr << browse(null, "window=turbine")
- usr.machine = null
-
- return
-
-
-
+ usr.unset_machine(src)
+ return
+ else if(href_list["str"])
+ if(compressor)
+ compressor.starter = !compressor.starter
+ updateDialog()
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Turbine Computer
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/obj/machinery/computer/turbine_computer/initialize()
+ . = ..()
+ return INITIALIZE_HINT_LATELOAD
+/obj/machinery/computer/turbine_computer/LateInitialize()
+ locate_machinery()
-/obj/machinery/computer/turbine_computer/New()
- ..()
- spawn(5)
- for(var/obj/machinery/compressor/C in machines)
- if(id == C.comp_id)
- compressor = C
- doors = new /list()
- for(var/obj/machinery/door/blast/P in machines)
- if(P.id == id)
- doors += P
+/obj/machinery/computer/turbine_computer/proc/locate_machinery()
+ if(!id)
+ return
+ for(var/obj/machinery/compressor/C in machines)
+ if(C.comp_id == id)
+ compressor = C
+ LAZYINITLIST(doors)
+ for(var/obj/machinery/door/blast/P in machines)
+ if(P.id == id)
+ doors += P
-/*
-/obj/machinery/computer/turbine_computer/attackby(I as obj, user as mob)
- if(istype(I, /obj/item/weapon/screwdriver))
- playsound(src.loc, I.usesound, 50, 1)
- if(do_after(user, 20))
- if (src.stat & BROKEN)
- user << "The broken glass falls out."
- var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
- new /obj/item/weapon/material/shard( src.loc )
- var/obj/item/weapon/circuitboard/turbine_control/M = new /obj/item/weapon/circuitboard/turbine_control( A )
- for (var/obj/C in src)
- C.loc = src.loc
- M.id = src.id
- A.circuit = M
- A.state = 3
- A.icon_state = "3"
- A.anchored = 1
- qdel(src)
- else
- user << "You disconnect the monitor."
- var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
- var/obj/item/weapon/circuitboard/turbine_control/M = new /obj/item/weapon/circuitboard/turbine_control( A )
- for (var/obj/C in src)
- C.loc = src.loc
- M.id = src.id
- A.circuit = M
- A.state = 4
- A.icon_state = "4"
- A.anchored = 1
- qdel(src)
- else
- src.attack_hand(user)
- return
-*/
+/obj/machinery/computer/turbine_computer/attackby(obj/item/W, mob/user)
+ if(ismultitool(W))
+ var/new_ident = input("Enter a new ident tag.", name, id) as null|text
+ if(new_ident && user.Adjacent(src))
+ id = new_ident
+ return
/obj/machinery/computer/turbine_computer/attack_hand(var/mob/user as mob)
- user.machine = src
- var/dat
- if(src.compressor)
- dat += {"
Gas turbine remote control system