diff --git a/BreakingPoint.dme b/BreakingPoint.dme index dc7c1f8616..e8bbefbe7d 100644 --- a/BreakingPoint.dme +++ b/BreakingPoint.dme @@ -2875,4 +2875,10 @@ #include "modular\aetherium\aetherium golems\aetherium_golem.dm" #include "modular\aetherium\aetherium golems\aetherium_runtling.dm" #include "modular\aetherium\aetherium golems\aetherium_shooter.dm" +#include "modular\reactor\_defines.dm" +#include "modular\reactor\console.dm" +#include "modular\reactor\control_rod_part.dm" +#include "modular\reactor\fuel_rod_part.dm" +#include "modular\reactor\misc.dm" +#include "modular\reactor\reactor.dm" // END_INCLUDE diff --git a/BreakingPoint.dmm-pal b/BreakingPoint.dmm-pal new file mode 100644 index 0000000000..b95cd18c24 --- /dev/null +++ b/BreakingPoint.dmm-pal @@ -0,0 +1 @@ +[Palette] diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index 1b095c555d..932bcb6339 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -3,11 +3,12 @@ // Unless you need the overlay/underlay to have a different direction than the base object. Then you have to use an image due to a bug. // Mutable appearances are children of images, just so you know. - +/* /mutable_appearance/New() ..() plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var +*/ // Helper similar to image() /proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE) diff --git a/maps/CEVEris/centcomm.dmm b/maps/CEVEris/centcomm.dmm index 8887bea229..6c745f9213 100644 --- a/maps/CEVEris/centcomm.dmm +++ b/maps/CEVEris/centcomm.dmm @@ -15,6 +15,18 @@ }, /turf/wall/dummy, /area/space) +"ae" = ( +/turf/wall/dummy, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"af" = ( +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) "ag" = ( /obj/structure/window/reinforced/crescent{ dir = 4 @@ -28,18 +40,98 @@ /obj/structure/railing/holorailing, /turf/open/holonofloor, /area/holodeck/source/industrial_arena) +"ai" = ( +/obj/machinery/atmospherics/pipe/tank, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"aj" = ( +/obj/machinery/atmospherics/pipe/simple/visible{ + dir = 6 + }, +/obj/machinery/meter, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"ak" = ( +/obj/machinery/atmospherics/pipe/simple/visible{ + dir = 4 + }, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"al" = ( +/obj/machinery/atmospherics/pipe/simple/visible{ + dir = 10 + }, +/obj/machinery/meter, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"am" = ( +/obj/machinery/multistructure/nuclear_reactor_part/console, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) "an" = ( /obj/structure/railing/holorailing/grey{ dir = 4 }, /turf/open/holonofloor, /area/holodeck/source/wireframe) +"ao" = ( +/turf/space, +/turf/space, +/area/space) "ap" = ( /obj/structure/table/holosteel_reinforced, /obj/structure/catwalk/holo, /obj/machinery/floor_light/prebuilt, /turf/floor/holofloor/plating/under, /area/holodeck/source/industrial) +"aq" = ( +/obj/machinery/atmospherics/pipe/simple/visible{ + dir = 5 + }, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"ar" = ( +/obj/machinery/atmospherics/pipe/manifold/visible{ + dir = 4 + }, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"as" = ( +/obj/machinery/multistructure/nuclear_reactor_part/wall, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) "at" = ( /obj/structure/cryofeed{ dir = 4; @@ -77,6 +169,56 @@ icon_state = "floor6" }, /area/shuttle/pirate) +"ax" = ( +/obj/machinery/atmospherics/pipe/simple/visible, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"ay" = ( +/obj/machinery/atmospherics/pipe/tank/plasma{ + start_pressure = 10000 + }, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"az" = ( +/obj/machinery/multistructure/nuclear_reactor_part/control_rod, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"aA" = ( +/obj/machinery/multistructure/nuclear_reactor_part/fuel_rod, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"aB" = ( +/obj/machinery/multistructure/nuclear_reactor_part/wall_input, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"aC" = ( +/obj/machinery/multistructure/nuclear_reactor_part/wall_output, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) "aD" = ( /turf/floor/tiled/dark/gray_perforated, /area/centcom/evac) @@ -87,9 +229,49 @@ }, /turf/floor/tiled/dark/gray_platform, /area/centcom/evac) +"aF" = ( +/obj/machinery/atmospherics/pipe/simple/visible{ + dir = 9; + icon_state = "intact" + }, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"aG" = ( +/obj/machinery/atmospherics/binary/pump/high_power/on{ + target_pressure = 15000 + }, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) +"aH" = ( +/obj/machinery/atmospherics/pipe/simple/visible{ + dir = 5 + }, +/obj/machinery/meter, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) "aI" = ( /turf/floor/holofloor/tiled/dark, /area/holodeck/source/industrial) +"aJ" = ( +/obj/machinery/atmospherics/pipe/manifold/visible, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) "aR" = ( /obj/structure/table/bar_special, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -1007,9 +1189,6 @@ icon_state = "floor4" }, /area/shuttle/syndicate_elite/mothership) -"gd" = ( -/turf/wall/low/with_glass/smart, -/area/space) "ge" = ( /turf/wall/low/with_glass/smart, /area/centcom/small_base) @@ -1675,7 +1854,6 @@ }, /area/skipjack_station/start) "if" = ( -/turf/wall/low/with_glass/reinforced, /obj/machinery/door/blast/regular{ density = 0; icon_state = "pdoor0"; @@ -1683,6 +1861,7 @@ name = "Outer Airlock"; opacity = 0 }, +/turf/wall/low/with_glass/reinforced, /area/shuttle/pirate) "ik" = ( /obj/effect/shuttle_landmark/escape_pod/out/pod1, @@ -4247,7 +4426,7 @@ /obj/machinery/vending/cigarette{ name = "hacked cigarette machine"; prices = list(); - products = list(/obj/item/storage/fancy/cigarettes=10,/obj/item/storage/box/matches=10,/obj/item/flame/lighter/zippo=4,/obj/item/clothing/mask/smokable/cigarette/cigar/havana=2) + products = list(/obj/item/storage/fancy/cigarettes=10, /obj/item/storage/box/matches=10, /obj/item/flame/lighter/zippo=4, /obj/item/clothing/mask/smokable/cigarette/cigar/havana=2) }, /turf/floor/dummy{ icon_state = "cult"; @@ -5078,7 +5257,7 @@ /obj/machinery/vending/cigarette{ name = "hacked cigarette machine"; prices = list(); - products = list(/obj/item/storage/fancy/cigarettes=10,/obj/item/storage/box/matches=10,/obj/item/flame/lighter/zippo=4,/obj/item/clothing/mask/smokable/cigarette/cigar/havana=2) + products = list(/obj/item/storage/fancy/cigarettes=10, /obj/item/storage/box/matches=10, /obj/item/flame/lighter/zippo=4, /obj/item/clothing/mask/smokable/cigarette/cigar/havana=2) }, /turf/floor/wood, /area/centcom/pirate_base) @@ -5325,7 +5504,6 @@ /turf/floor/tiled/steel/gray_platform, /area/centcom/pirate_base) "BR" = ( -/turf/wall/low/with_glass/reinforced, /obj/machinery/door/blast/regular{ density = 0; icon_state = "pdoor0"; @@ -5333,6 +5511,7 @@ name = "Outer Airlock"; opacity = 0 }, +/turf/wall/low/with_glass/reinforced, /area/shuttle/pirate) "BX" = ( /obj/structure/bed/chair/custom/holochair, @@ -5704,7 +5883,6 @@ /turf/floor/holofloor/tiled/dark, /area/space) "Gt" = ( -/turf/wall/low/with_glass/reinforced, /obj/machinery/door/blast/regular{ density = 0; icon_state = "pdoor0"; @@ -5712,6 +5890,7 @@ name = "Outer Airlock"; opacity = 0 }, +/turf/wall/low/with_glass/reinforced, /area/shuttle/pirate) "Gw" = ( /turf/floor/holofloor/tiled/bar_dance, @@ -5740,7 +5919,7 @@ /obj/machinery/vending/cigarette{ name = "hacked cigarette machine"; prices = list(); - products = list(/obj/item/storage/fancy/cigarettes=10,/obj/item/storage/box/matches=10,/obj/item/flame/lighter/zippo=4,/obj/item/clothing/mask/smokable/cigarette/cigar/havana=2) + products = list(/obj/item/storage/fancy/cigarettes=10, /obj/item/storage/box/matches=10, /obj/item/flame/lighter/zippo=4, /obj/item/clothing/mask/smokable/cigarette/cigar/havana=2) }, /turf/shuttle/floor{ icon_state = "floor6" @@ -6072,8 +6251,8 @@ /turf/floor/holofloor/reinforced, /area/space) "Kq" = ( -/turf/wall/low/with_glass/reinforced, /obj/structure/grille, +/turf/wall/low/with_glass/reinforced, /area/centcom/merc_base) "Kr" = ( /obj/machinery/light, @@ -6450,7 +6629,7 @@ /obj/machinery/vending/assist{ contraband = null; name = "AntagCorpVend"; - products = list(/obj/item/device/assembly/prox_sensor=5,/obj/item/device/assembly/signaler=4,/obj/item/device/assembly/infra=4,/obj/item/device/assembly/prox_sensor=4,/obj/item/handcuffs=8,/obj/item/device/flash=4,/obj/item/clothing/glasses/sunglasses=4) + products = list(/obj/item/device/assembly/prox_sensor=5, /obj/item/device/assembly/signaler=4, /obj/item/device/assembly/infra=4, /obj/item/device/assembly/prox_sensor=4, /obj/item/handcuffs=8, /obj/item/device/flash=4, /obj/item/clothing/glasses/sunglasses=4) }, /obj/machinery/light{ dir = 4 @@ -6475,7 +6654,6 @@ /turf/floor/holofloor/tiled/dark, /area/space) "Om" = ( -/turf/wall/low/with_glass/reinforced, /obj/effect/shuttle_landmark/pirate/start, /obj/machinery/door/blast/regular{ density = 0; @@ -6484,6 +6662,7 @@ name = "Outer Airlock"; opacity = 0 }, +/turf/wall/low/with_glass/reinforced, /area/shuttle/pirate) "Or" = ( /obj/machinery/light, @@ -7410,6 +7589,17 @@ }, /turf/space/transit/north/shuttlespace_ns10, /area/space) +"YL" = ( +/obj/machinery/atmospherics/binary/pump/high_power/on{ + dir = 8; + target_pressure = 15000 + }, +/turf/floor/dummy{ + icon_state = "dark" + }, +/area/wizard_station{ + name = "\improper Test Reactor" + }) "YN" = ( /obj/structure/table/steel_reinforced, /obj/effect/floor_decal/industrial/danger{ @@ -17964,17 +18154,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +ae +ae +ae +ae +ae +ae +ae +ae +ae +ae aa aa aa @@ -18166,17 +18356,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +af +af +af +af +af +af +af +af +af +ae aa aa aa @@ -18368,17 +18558,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +af +am +af +af +af +af +af +af +af +ae aa aa aa @@ -18570,17 +18760,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +af +af +af +af +af +af +af +af +af +ae aa aa aa @@ -18772,17 +18962,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +ai +aq +ay +aH +af +af +af +af +af +ae aa aa aa @@ -18974,17 +19164,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +aj +ar +aG +aJ +af +af +af +af +af +ae aa aa aa @@ -19176,17 +19366,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +ak +as +as +aB +as +as +af +af +af +ae aa aa aa @@ -19378,17 +19568,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +ak +as +az +aA +az +as +af +af +af +ae aa aa aa @@ -19580,19 +19770,19 @@ aa aa aa aa +ae +YL +as +aA +az +aA +as +af +af +af +ae aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ao aa aa aa @@ -19782,17 +19972,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +ak +as +az +aA +az +as +af +af +af +ae aa aa aa @@ -19984,17 +20174,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +ak +as +as +aC +as +as +af +af +af +ae aa aa aa @@ -20186,17 +20376,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +al +ax +ax +aF +af +af +af +af +af +ae aa aa aa @@ -20388,17 +20578,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +af +af +af +af +af +af +af +af +af +ae aa aa aa @@ -20590,17 +20780,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +af +af +af +af +af +af +af +af +af +ae aa aa aa @@ -20792,17 +20982,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +af +af +af +af +af +af +af +af +af +ae aa aa aa @@ -20994,17 +21184,17 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ae +ae +ae +ae +ae +ae +ae +ae +ae +ae +ae aa aa aa @@ -40532,10 +40722,10 @@ aa aa gi ei -gd -gd -gd -gd +gj +gj +gj +gj ei ei gO diff --git a/modular/reactor/_defines.dm b/modular/reactor/_defines.dm new file mode 100644 index 0000000000..b6f0279a1c --- /dev/null +++ b/modular/reactor/_defines.dm @@ -0,0 +1,89 @@ +// This folder was written by R4d6 for 300$. I just wanted to note that down. + +/* Design Document : +Nuclear Reactor that uses Eris' native Turbine + Motor setup to produce power. +Replaces the Supermatter Crystal as the main Power Source. + +TL;DR : Using pipes, it draws gas in via a pipe, heats it up, and push it out via another pipe. + +Has 4 Fuel Rods spot, and 5 Control Rods in an X-Formation. + +locate(console) in local_area + +The interface should have : +A slider/input value that goes from 0 to 100 that controls the height of the rods. It will also show the average % of the rods +A meter that shows the temperature of the core. +A meter that shows the ESTIMATED amount of power produced, based on the pressure of the exiting gas. [Source]() +A SCRAM button, that instantly makes all the rods go down. +A button to toggle the input of gas. +A button to toggle the output of gas. +A readout for the gas in the reactor. + +The height of the control rods will increase as the slider is raised. +Above 25%, it acts like naked plating, having a chance to trip running players. +Above 50%, it acts like a table, requiring players to climb on top. +Above 75%. it acts like a wall, blocking both view and movement. +If a Control Rod is missing, it is assumed to be 100% for the purpose of calculating the average. +If a Control Rod is broken, it is assumed to be at 0% for the purpose of calculating the average. + +How the Reactor will work behind the scene : +The reactor will be a Multistructure (https://github.com/Psalms-of-Orion/Songs-Of-Orion/blob/master/code/game/machinery/multistructure.dm) +Every fuel rod has a heat_production value, and a consumption_rate value. +When the Reactor ticks, it will do the following : +Step 1 - Use pump_gas_passive(src, gas_input, gas_storage) to draw in gas from the input pipe. [Code]() +Step 2 - Increase the temperature of the Reactor Core by the sum of all of the current fuel rods' head_production, multiplied by the average percentage of the control rods. +Step 3 - Consume/Decrease the fuel rods' durability/lifespan by its consumption_rate value, multiplied by the average percentage of the control rods. +Step 4 - Heat up the stored gas using the Reactor Core's temperature value. (Not sure on the math needed for this) +Step 5 - Use pump_gas_passive(src, gas_storage, gas_output) to push out the heated gas through the output pipe. + +If the Reactor Core's temperature, after pushing out the heated gas, is above the Melting Point of the 'shell' surrounding the control rods and fuel rods, then a meltdown will happen. [Code Reference]() +An alarm will sound if the Reactor Core's Temperature is at 90% of the shell's melting point or higher. + + +Meltdown : +Not yet decided, definitively will have lots of radiation though. +Probably would heat up the place too. +Placeholder : Big Boom. + + +Interactions for the Fuel & Control Rods alike (AKA how to change it) : +Step 1 : Use a big wrench to unlock/unscrew the tile's panel. +Step 2 : Use an empty hand to pull the Rod up. Pulled Control Rods will be at 100%, and pulled non-empty used Fuel Rods will emit lots of radiation. +Step 3 : Use a screwdriver to unsecure the Rod from the socket. +Step 4 : Crowbar the Rod out of the socket. +Step 5 : Place a new Rod in the socket. +Step 6 : Screwdriver the Rod in place. +Step 7 : Use an empty hand to push the Rod down. +Step 8 : Secure the panel with a big wrench. + +This is how players will change a broken control rod or empty fuel rod. +Used Fuel Rods will require players to use thick temperature resistant gloves to pick up, based on the [lightbulb's check]() + +*/ + +// Here are the defines used by the nuclear reactor. +// Control Rod Height Treshold +#define HEIGHT_TRIP 25 +#define HEIGHT_CLIMB 50 +#define HEIGHT_WALL 75 + +// Steps for removing a fuel or control rod +#define STEP_INTACT 0 // Not messed with, need to unwrench the bolts +#define STEP_UNWRENCHED 1 // Unwrenched, need to pull up +#define STEP_PULLED 2 // Pulled, need to screwdriver +#define STEP_UNSECURED 3 // Unsecured, need to crowbar +#define STEP_NO_ROD 4 // Rod removed, need a new one. + +#define REACTOR_RADIATION_MULTIPLIER 200 +#define BREACH_RADIATION_MULTIPLIER 1 +#define REACTOR_TEMPERATURE_CUTOFF 10000 +#define REACTOR_RADS_TO_MJ 10000 + +#define ROD_RADIATION_MULTIPLIER 15 +#define ROD_TEMPERATURE_CUTOFF 10000 +#define ROD_EXPOSED_POWER 0.1 + +#define RAD_FALLOFF_ENGINE_FISSION 0.5 + +/// minimum temperature difference before we can stop ticking/just equalize +#define MINIMUM_MEANINGFUL_TEMPERATURE_DELTA 0.5 diff --git a/modular/reactor/astra_reactor.dmi b/modular/reactor/astra_reactor.dmi new file mode 100644 index 0000000000..ae650a9e47 Binary files /dev/null and b/modular/reactor/astra_reactor.dmi differ diff --git a/modular/reactor/console.dm b/modular/reactor/console.dm new file mode 100644 index 0000000000..234cc44bb3 --- /dev/null +++ b/modular/reactor/console.dm @@ -0,0 +1,47 @@ +/obj/machinery/multistructure/nuclear_reactor_part/console + icon_state = "console" + density = TRUE + var/datum/multistructure/nuclear_reactor/Reactor + +/obj/machinery/multistructure/nuclear_reactor_part/console/New() + ..() + update_icon() + +/obj/machinery/multistructure/nuclear_reactor_part/console/update_icon() + add_overlay("power_key") + add_overlay("smmon_2") + +/obj/machinery/multistructure/nuclear_reactor_part/console/attack_hand(mob/user as mob) + interact(user) + +/obj/machinery/multistructure/nuclear_reactor_part/console/interact(mob/user as mob) + var/dat = Reactor?.Get_HTML() + if(dat) + if((get_dist(src, user) > 1) || (stat & (BROKEN))) + if(!isAI(user)) + user.unset_machine() + user << browse(null, "window=NRcontrol") + return + user.set_machine(src) + + user << browse(dat, "window=NRcontrol;size=420x500") + onclose(user, "NRcontrol") + return + +/obj/machinery/multistructure/nuclear_reactor_part/console/Topic(href, href_list) + ..() + //Ignore input if we are broken or guy is not touching us, AI can control from a ways away + if(stat & (BROKEN) || (get_dist(src, usr) > 1 && !isAI(usr))) + usr.unset_machine() + usr << browse(null, "window=NRcontrol") + return + + if(href_list["close"]) + usr << browse(null, "window=NRcontrol") + usr.unset_machine() + return + + Reactor.Topic(href, href_list) + + updateDialog() + return diff --git a/modular/reactor/control_rod_part.dm b/modular/reactor/control_rod_part.dm new file mode 100644 index 0000000000..030d6762af --- /dev/null +++ b/modular/reactor/control_rod_part.dm @@ -0,0 +1,126 @@ +/obj/machinery/multistructure/nuclear_reactor_part/control_rod + name = "control rod section" + desc = "A section designed to hold and use control rods to moderate nuclear reactions." + //icon_state = "control_spot" + var/height = 0 + var/max_height = 100 + var/min_height = 0 + var/current_step = STEP_INTACT + var/obj/item/control_rod/control + +/obj/machinery/multistructure/nuclear_reactor_part/control_rod/Initialize(mapload, ...) + ..() + if(mapload) + control = new() + update_icon() + +/obj/machinery/multistructure/nuclear_reactor_part/control_rod/attackby(obj/item/I, mob/user) + switch(current_step) + if(STEP_INTACT) + if(I.get_tool_type(user, list(QUALITY_BOLT_TURNING), src) == QUALITY_BOLT_TURNING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] loosen the bolts."), SPAN_NOTICE("You loosen the bolts.")) + current_step = STEP_UNWRENCHED + return + + if(STEP_UNWRENCHED) + if(I.get_tool_type(user, list(QUALITY_BOLT_TURNING), src) == QUALITY_BOLT_TURNING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] tighten the bolts."), SPAN_NOTICE("You tighten the bolts.")) + current_step = STEP_INTACT + return + + if(STEP_PULLED) + if(I.get_tool_type(user, list(QUALITY_SCREW_DRIVING), src) == QUALITY_SCREW_DRIVING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_SCREW_DRIVING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] unsecures the control rod."), SPAN_NOTICE("You unsecures the control rod.")) + current_step = STEP_UNSECURED + return + + if(STEP_UNSECURED) + var/tool_type = I.get_tool_type(user, list(QUALITY_SCREW_DRIVING, QUALITY_PRYING), src) + if(!tool_type) + return + + if(tool_type == QUALITY_PRYING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_PRYING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] pries the control rod out."), SPAN_NOTICE("You pry the control rod out.")) + control.loc = loc + control = null + current_step = STEP_NO_ROD + return + + if(tool_type == QUALITY_SCREW_DRIVING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_SCREW_DRIVING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] secures the control rod."), SPAN_NOTICE("You secures the control rod.")) + current_step = STEP_PULLED + return + + if(STEP_NO_ROD) + if(istype(I, /obj/item/control_rod) && insert_item(I, user)) + control = I + current_step = STEP_UNSECURED + return + ..() + +/obj/machinery/multistructure/nuclear_reactor_part/control_rod/attack_hand(mob/user as mob) + if(current_step == STEP_UNWRENCHED) + user.visible_message(SPAN_NOTICE("[user] pulls the rod container up."), SPAN_NOTICE("You pulls the rod container up.")) + current_step = STEP_PULLED + update_icon() + return + + if(current_step == STEP_PULLED) + user.visible_message(SPAN_NOTICE("[user] push the rod container down."), SPAN_NOTICE("You push the rod container down.")) + current_step = STEP_UNWRENCHED + update_icon() + return + + ..() + +/obj/machinery/multistructure/nuclear_reactor_part/control_rod/examine(mob/user) + ..() + var/message + + switch(current_step) + if(STEP_INTACT) + message = "The bolts are tightly secured." + + if(STEP_UNWRENCHED) + message = "The bolts are loose and the assembly is ready to be pulled up." + + if(STEP_PULLED) + message = "The assembly is pulled up, but the control rod is secured by screws." + + if(STEP_UNSECURED) + message = "The screws keeping the control rod in place are loose. Someone could just pry away the control rod!" + + if(STEP_NO_ROD) + message = "There is no control rod!" + + if(message) + to_chat(user, SPAN_NOTICE("[message]")) + +// TODO for when proper sprites are done. +/obj/machinery/multistructure/nuclear_reactor_part/control_rod/update_icon() + cut_overlays() + switch(Get_Rod_Height()) + if(0 to 24) + add_overlay("C0") + if(25 to 49) + add_overlay("C25") + if(50 to 74) + add_overlay("C50") + if(75 to 99) + add_overlay("C75") + if(100) + add_overlay("C100") + +/obj/machinery/multistructure/nuclear_reactor_part/control_rod/proc/Get_Rod_Height() + if(!control) // No control rod? Well it's not there. + return max_height + if(current_step >= STEP_PULLED) // The rod was pulled up? Might as well not be there. + return max_height + if(stat & (BROKEN)) // It's broken? The rod automatically falls down. + return min_height + return height diff --git a/modular/reactor/fuel_rod_part.dm b/modular/reactor/fuel_rod_part.dm new file mode 100644 index 0000000000..470b7e79cb --- /dev/null +++ b/modular/reactor/fuel_rod_part.dm @@ -0,0 +1,107 @@ +/obj/machinery/multistructure/nuclear_reactor_part/fuel_rod + name = "fuel rod section" + desc = "A section designed to hold and use fuel rods to enable nuclear reactions." + //icon_state = "fuel_spot" + var/current_step = STEP_NO_ROD + var/obj/item/fuel_rod/fuel + +/obj/machinery/multistructure/nuclear_reactor_part/fuel_rod/Initialize(mapload, ...) + ..() + if(mapload) + fuel = new /obj/item/fuel_rod/uranium() + current_step = STEP_INTACT + update_icon() + +/obj/machinery/multistructure/nuclear_reactor_part/fuel_rod/attackby(obj/item/I, mob/user) + switch(current_step) + if(STEP_INTACT) + if(I.get_tool_type(user, list(QUALITY_BOLT_TURNING), src) == QUALITY_BOLT_TURNING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] loosen the bolts."), SPAN_NOTICE("You loosen the bolts.")) + current_step = STEP_UNWRENCHED + return + + if(STEP_UNWRENCHED) + if(I.get_tool_type(user, list(QUALITY_BOLT_TURNING), src) == QUALITY_BOLT_TURNING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_BOLT_TURNING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] tighten the bolts."), SPAN_NOTICE("You tighten the bolts.")) + current_step = STEP_INTACT + return + + if(STEP_PULLED) + if(I.get_tool_type(user, list(QUALITY_SCREW_DRIVING), src) == QUALITY_SCREW_DRIVING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_SCREW_DRIVING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] unsecures the fuel rod."), SPAN_NOTICE("You unsecures the fuel rod.")) + current_step = STEP_UNSECURED + return + + if(STEP_UNSECURED) + var/tool_type = I.get_tool_type(user, list(QUALITY_SCREW_DRIVING, QUALITY_PRYING), src) + if(!tool_type) + return + + if(tool_type == QUALITY_PRYING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_PRYING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] pries the fuel rod out."), SPAN_NOTICE("You pry the fuel rod out.")) + fuel.loc = loc + fuel = null + current_step = STEP_NO_ROD + return + + if(tool_type == QUALITY_SCREW_DRIVING) + if(I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_SCREW_DRIVING, FAILCHANCE_EASY, required_stat = STAT_MEC)) + user.visible_message(SPAN_NOTICE("[user] secures the fuel rod."), SPAN_NOTICE("You secures the fuel rod.")) + current_step = STEP_PULLED + return + + if(STEP_NO_ROD) + if(istype(I, /obj/item/fuel_rod) && insert_item(I, user)) + fuel = I + current_step = STEP_UNSECURED + return + ..() + +/obj/machinery/multistructure/nuclear_reactor_part/fuel_rod/attack_hand(mob/user as mob) + if(current_step == STEP_UNWRENCHED) + user.visible_message(SPAN_NOTICE("[user] pulls the rod container up."), SPAN_NOTICE("You pulls the rod container up.")) + current_step = STEP_PULLED + update_icon() + return + + if(current_step == STEP_PULLED) + user.visible_message(SPAN_NOTICE("[user] push the rod container down."), SPAN_NOTICE("You push the rod container down.")) + current_step = STEP_UNWRENCHED + update_icon() + return + + +/obj/machinery/multistructure/nuclear_reactor_part/fuel_rod/examine(mob/user) + ..() + var/message + + switch(current_step) + if(STEP_INTACT) + message = "The bolts are tightly secured." + + if(STEP_UNWRENCHED) + message = "The bolts are loose and the assembly is ready to be pulled up." + + if(STEP_PULLED) + message = "The assembly is pulled up, but the fuel rod is secured by screws." + + if(STEP_UNSECURED) + message = "The screws keeping the fuel rod in place are loose. Someone could just pry away the fuel rod!" + + if(STEP_NO_ROD) + message = "There is no fuel rod!" + + if(message) + to_chat(user, SPAN_NOTICE("[message]")) + +// TODO for when proper sprites are done. +/obj/machinery/multistructure/nuclear_reactor_part/fuel_rod/update_icon() + cut_overlays() + if(current_step == STEP_PULLED) + add_overlay("F100") + else + add_overlay("F0") diff --git a/modular/reactor/misc.dm b/modular/reactor/misc.dm new file mode 100644 index 0000000000..11ae9e17a3 --- /dev/null +++ b/modular/reactor/misc.dm @@ -0,0 +1,193 @@ +/obj/machinery/multistructure/nuclear_reactor_part + icon = './astra_reactor.dmi' + MS_type = /datum/multistructure/nuclear_reactor + anchored = TRUE + +/obj/machinery/multistructure/nuclear_reactor_part/wall + name = "containement wall" + icon_state = "wall" + +/obj/machinery/multistructure/nuclear_reactor_part/wall_input + name = "reactor gas input" + icon_state = "wall_input" + +/obj/machinery/multistructure/nuclear_reactor_part/wall_output + name = "reactor gas output" + icon_state = "wall_output" + +/obj/item/control_rod + name = "control rod" + desc = "A rod made of graphite, designed to moderate nuclear reactions by its presence." + icon = 'astra_reactor.dmi' + icon_state = "control" + var/durability = 100 + +/obj/item/control_rod/update_icon() + if(durability <= 0) + icon_state = "control_spent" + else + icon_state = "control" + +/obj/item/fuel_rod + name = "aetherium fuel rod" + desc = "You shouldn't be seeing this." + icon = 'astra_reactor.dmi' + + var/gasefficiency = 0.05 + var/insertion = 0 + var/integrity = 100 + var/integrity_max = 100 + var/life = 100 + var/lifespan = 3600 + var/reflective = 1 + var/temperature = T20C + var/specific_heat = 1 // J/(mol*K) - Caluclated by: (specific heat) [kJ/kg*K] * (molar mass) [g/mol] (g/mol = kg/mol * 1000, duh.) + var/molar_mass = 1 // kg/mol + var/mass = 1 // kg + var/melting_point = 3000 // Entering the danger zone. + var/decay_heat = 0 // MJ/mol (Yes, using MegaJoules per Mole. Techincally reduces power, but that reflects reduced lifespan.) + +/obj/item/fuel_rod/aetherium + name = "aetherium fuel rod" + desc = "A rod made of aetherium, acting as a suitable substitute for proper nuclear fuel. It is contained within a lead casing." + icon_state = "unobtanium" + //heat_production = 50 + +/obj/item/fuel_rod/plutonium + name = "plutonium fuel rod" + desc = "A rod made of plutonium, acting as a suitable substitute for proper nuclear fuel. It is contained within a lead casing." + icon_state = "plasma" + specific_heat = 36 // J/(mol*K) + molar_mass = 0.244 // kg/mol + mass = 5 // kg + melting_point = 914 + decay_heat = 20342002 // MJ/mol + lifespan = 1800 + +/obj/item/fuel_rod/uranium + name = "uranium fuel rod" + desc = "A rod made of uranium, acting as a suitable substitute for proper nuclear fuel. It is contained within a lead casing." + icon_state = "uranium" + specific_heat = 28 // J/(mol*K) + molar_mass = 0.235 // kg/mol + mass = 20 // kg + melting_point = 1405 + decay_heat = 19536350 // MJ/mol + +/obj/item/fuel_rod/update_icon() + if(life <= 0) + icon_state = "[initial(icon_state)]_spent" + else + icon_state = "[initial(icon_state)]" + +/obj/item/fuel_rod/Initialize(mapload) + . = ..() + START_PROCESSING(SSobj, src) + +/obj/item/fuel_rod/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/fuel_rod/Process() + if(isnull(loc)) + return PROCESS_KILL + + if(!istype(loc, /obj/machinery/multistructure/nuclear_reactor_part/fuel_rod)) + var/turf/T = get_turf(src) + equalize(T.return_air(), gasefficiency) + + if(decay_heat > 0) + var/insertion_multiplier = ROD_EXPOSED_POWER + if(integrity == 0) + insertion_multiplier = 1 + var/power = (tick_life(0, insertion_multiplier) / REACTOR_RADS_TO_MJ) + adjust_thermal_energy(power) + PulseRadiation(src, max(power * ROD_RADIATION_MULTIPLIER, 0), 10) + +/obj/item/fuel_rod/proc/equalize(var/E, var/efficiency) + var/our_heatcap = heat_capacity() + // Ugly code ahead. Thanks for not allowing polymorphism, Byond. + if(istype(E, /datum/multistructure/nuclear_reactor)) + var/datum/multistructure/nuclear_reactor/sharer = E + var/share_heatcap = sharer.heat_capacity() + + if(our_heatcap + share_heatcap) + var/new_temperature = ((temperature * our_heatcap) + (sharer.temperature * share_heatcap)) / (our_heatcap + share_heatcap) + temperature += (new_temperature - temperature) * efficiency // Add efficiency here, since there's no gas.remove for non-gas objects. + temperature = clamp(temperature, 0, ROD_TEMPERATURE_CUTOFF) + sharer.temperature += (new_temperature - sharer.temperature) * efficiency + sharer.temperature = clamp( sharer.temperature, 0, ROD_TEMPERATURE_CUTOFF) + else if(istype(E, /datum/gas_mixture)) + var/datum/gas_mixture/env = E + var/datum/gas_mixture/sharer = env.remove(efficiency * env.total_moles) + var/share_heatcap = sharer.heat_capacity() + + if(our_heatcap + share_heatcap) + var/new_temperature = ((temperature * our_heatcap) + (sharer.temperature * share_heatcap)) / (our_heatcap + share_heatcap) + temperature += (new_temperature - temperature) * efficiency + temperature = clamp(temperature, 0, ROD_TEMPERATURE_CUTOFF) + sharer.temperature += (new_temperature - sharer.temperature) + sharer.temperature = clamp( sharer.temperature, 0, ROD_TEMPERATURE_CUTOFF) + env.merge(sharer) + +/* + var/integrity_lost = integrity + if(temperature > melting_point && melting_point > 0) + integrity = max(0, integrity - (temperature / melting_point)) + else if(temperature > (melting_point * 0.9)) + integrity = max(0, integrity - ((1 / lifespan) * 100)) + if(integrity == 0 && integrity_lost > 0) // Meltdown time. + meltdown()*/ + +/obj/item/fuel_rod/proc/adjust_thermal_energy(var/thermal_energy) + if(mass < 1) + return 0 + + var/heat_capacity = heat_capacity() + if(thermal_energy < 0) + if(temperature < TCMB) + return 0 + var/thermal_energy_limit = -(temperature - TCMB)*heat_capacity //ensure temperature does not go below TCMB + thermal_energy = max(thermal_energy, thermal_energy_limit) //thermal_energy and thermal_energy_limit are negative here. + temperature += thermal_energy/heat_capacity + return thermal_energy + +/obj/item/fuel_rod/proc/heat_capacity() + . = specific_heat * (mass / molar_mass) + +/obj/item/fuel_rod/proc/tick_life(var/apply_heat = 0, var/insertion_override = 0) + var/applied_insertion = get_insertion() + if(insertion_override) + applied_insertion = insertion_override + if(lifespan < 1 && life > 0) + life = 0 + else if(life > 0) + if(decay_heat > 0 || apply_heat) + life = max(0, life - ((1 / lifespan) * applied_insertion * 100)) + if(life <= 0 && integrity > 0) + name = "depleted [name]" + else if(decay_heat > 0) + return ((decay_heat * (mass / molar_mass)) / lifespan) * (min(life, 100) / 100) * applied_insertion + + update_icon() + return 0 + +/obj/item/fuel_rod/proc/get_insertion() + var/applied_insertion = 1 + if(istype(loc, /obj/machinery/multistructure/nuclear_reactor_part/fuel_rod) && icon_state != "rod_melt") + applied_insertion = insertion + return clamp( applied_insertion, 0, 1) + +/obj/item/fuel_rod/proc/is_melted() + return (icon_state == "rod_melt") ? 1 : 0 + +/obj/item/fuel_rod/proc/meltdown() + if(!is_melted()) + if(decay_heat > 0) + life = life * 10 + decay_heat = 0 // Original was decay_heat * 10. Setting to 0 to counter memes (Testing phase. Unsure HOW much this is going to destroy everything) + else + life = 0 + name = "melted [name]" + //icon_state = "rod_melt" // TODO + integrity = 0 diff --git a/modular/reactor/reactor.dm b/modular/reactor/reactor.dm new file mode 100644 index 0000000000..af37904242 --- /dev/null +++ b/modular/reactor/reactor.dm @@ -0,0 +1,329 @@ +// This is the actual reactor. + +/datum/multistructure/nuclear_reactor + structure = list( + list(/obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/wall), + list(/obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/control_rod, /obj/machinery/multistructure/nuclear_reactor_part/fuel_rod, /obj/machinery/multistructure/nuclear_reactor_part/control_rod, /obj/machinery/multistructure/nuclear_reactor_part/wall), + list(/obj/machinery/multistructure/nuclear_reactor_part/wall_input, /obj/machinery/multistructure/nuclear_reactor_part/fuel_rod, /obj/machinery/multistructure/nuclear_reactor_part/control_rod, /obj/machinery/multistructure/nuclear_reactor_part/fuel_rod, /obj/machinery/multistructure/nuclear_reactor_part/wall_output), + list(/obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/control_rod, /obj/machinery/multistructure/nuclear_reactor_part/fuel_rod, /obj/machinery/multistructure/nuclear_reactor_part/control_rod, /obj/machinery/multistructure/nuclear_reactor_part/wall), + list(/obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/wall, /obj/machinery/multistructure/nuclear_reactor_part/wall) + ) + + var/control_average + var/list/walls = list() + var/list/fuel_spots = list() + var/list/control_spots = list() + var/obj/machinery/multistructure/nuclear_reactor_part/wall_input/wall_input + var/obj/machinery/multistructure/nuclear_reactor_part/wall_output/wall_output + var/obj/machinery/multistructure/nuclear_reactor_part/console/Console + + var/datum/gas_mixture/gas_input + var/datum/gas_mixture/gas_storage + var/datum/gas_mixture/gas_output + + var/health = 200 + var/max_health = 200 + + var/announce = 1 + var/decay_archived = 0 + var/exploded = 0 + var/envefficiency = 1 //0.01 + var/gasefficiency = 1 //0.5 + var/warning_delay = 20 + var/meltwarned = 0 + var/lastwarning = 0 + var/cutoff_temp = 1200 + // Material properties from Tungsten Carbide, otherwise core'll be too weak. + // Material properties for the core are just it's heat exchange system, not the entire core. + var/specific_heat = 40 // J/(mol*K) + var/molar_mass = 0.196 // kg/mol + var/mass = 200 // kg + var/max_temp = 0 //3058 + var/temperature = T20C + var/list/obj/item/fuel_rod/rods + var/obj/item/device/radio/radio + +/datum/multistructure/nuclear_reactor/connect_elements() + ..() + gas_storage = new() + + var/part_count = 0 + for(var/obj/machinery/multistructure/nuclear_reactor_part/part in elements) + part.icon_state = "[part_count]" + part_count++ + if(istype(part, /obj/machinery/multistructure/nuclear_reactor_part/wall)) + walls += part + continue + if(istype(part, /obj/machinery/multistructure/nuclear_reactor_part/control_rod)) + control_spots += part + continue + if(istype(part, /obj/machinery/multistructure/nuclear_reactor_part/fuel_rod)) + fuel_spots += part + continue + if(istype(part, /obj/machinery/multistructure/nuclear_reactor_part/wall_input)) + walls += part + wall_input = part + continue + if(istype(part, /obj/machinery/multistructure/nuclear_reactor_part/wall_output)) + walls += part + wall_output = part + continue + + gas_input = Get_Pipe_Input() + gas_output = Get_Pipe_Output() + control_average = Get_Average_Control_Height() + Console = locate() in get_area(wall_input) + Console?.Reactor = src + radio = new() + START_PROCESSING(SSprocessing, src) + +/datum/multistructure/nuclear_reactor/Process() + control_average = Get_Average_Control_Height() + rods = Get_Fuel_Rods(FALSE) + + if(!Console) + Console = locate() in get_area(wall_input) + Console?.Reactor = src + + gas_input = Get_Pipe_Input() + gas_output = Get_Pipe_Output() + + if(gas_input) + pump_gas_passive(src, gas_input, gas_storage) + + var/decay_heat = 0 + var/activerods = 0 + var/disabledrods = 0 + var/meltedrods = 0 + var/meltingrods = 0 + + for(var/obj/item/fuel_rod/rod in rods) + if(rod.is_melted()) + meltedrods++ + else if(rod.temperature >= rod.melting_point) + meltingrods++ + if(cutoff_temp > 0 && rod.reflective && temperature > cutoff_temp && rod.insertion > 0) + rod.insertion = 0 + disabledrods++ + if(rod.life > 0) + decay_heat += rod.tick_life(decay_archived > 0 ? 1 : 0) + if(rod.reflective) + activerods += rod.get_insertion() + else + activerods -= rod.get_insertion() + + if(disabledrods > 0 && !exploded) + radio.autosay("Core exceeded temperature bounds, and has been shut down.", "Nuclear Monitor", "Engineering") + announce_warning(meltedrods, meltingrods, (temperature >= max_temp && !(max_temp <= 0)) ? 1 : 0) + + decay_archived = decay_heat + adjust_thermal_energy(decay_heat * activerods * (control_average/100)) + + for(var/obj/item/fuel_rod/rod in rods) + rod.equalize(src, gasefficiency) + + equalize(gas_storage, envefficiency) + + if(max_temp > 0 && temperature > max_temp && health > 0) // Overheating, reduce structural integrity, emit more rads. + health = max(0, health - (temperature / max_temp)) + health = clamp( health, 0, max_health) + //if(health < 1) + //go_nuclear() + + var/healthmul = ((health / max_health) - 1) / -1 + var/power = (decay_heat / REACTOR_RADS_TO_MJ) * max(healthmul, 0.1) + PulseRadiation(src, max(power * REACTOR_RADIATION_MULTIPLIER, 0), 10) + + if(gas_output) + pump_gas_passive(src, gas_storage, gas_output) + + +/datum/multistructure/nuclear_reactor/Topic(href, href_list) + ..() + + if(href_list["close"]) + usr << browse(null, "window=NRcontrol") + usr.unset_machine() + return + + if(href_list["scram"]) + scram() + Console?.updateDialog() + return + + if(href_list["set_target_height"]) + var/new_height = input(usr, "What height should the control rods be at?", "Control Rods", 0) as null|num + for(var/obj/machinery/multistructure/nuclear_reactor_part/control_rod/CR in control_spots) + CR.height = clamp(new_height, 0, 100) + CR.update_icon() + control_average = Get_Average_Control_Height() + Console?.updateDialog() + return + + Console?.updateDialog() + return + +/datum/multistructure/nuclear_reactor/proc/scram() + for(var/obj/machinery/multistructure/nuclear_reactor_part/control_rod/CR in control_spots) + CR.height = 0 + +/datum/multistructure/nuclear_reactor/proc/Get_Average_Control_Height() + var/sum_rod = 0 + for(var/obj/machinery/multistructure/nuclear_reactor_part/control_rod/CR in control_spots) + sum_rod += CR.Get_Rod_Height() + var/average = sum_rod / control_spots.len + return average + +/datum/multistructure/nuclear_reactor/proc/Get_Fuel_Rods(var/count_empty = TRUE) + . = list() + for(var/obj/machinery/multistructure/nuclear_reactor_part/fuel_rod/FR in fuel_spots) + if(FR.current_step < STEP_PULLED && FR.fuel) + if(count_empty) + . += FR.fuel + else if(FR.fuel.integrity > 0) + . += FR.fuel + +/datum/multistructure/nuclear_reactor/proc/Get_Pipe_Input() + var/obj/machinery/atmospherics/pipe/P = locate() in orange(1, wall_input) + return P?.return_air() + +/datum/multistructure/nuclear_reactor/proc/Get_Pipe_Output() + var/obj/machinery/atmospherics/pipe/P = locate() in orange(1, wall_output) + return P?.return_air() + +/datum/multistructure/nuclear_reactor/proc/Get_HTML() + var/dat = "" + dat += "Nuclear Reactor Control Panel
" + dat += "Close
" + dat += "Refresh
" + dat += "SCRAM
" + dat += "Reactor Integrity : [get_integrity()]%
" + dat += "Control Rods Height: [control_average]% | Set
" + dat += "Reactor Temperature: [temperature - T0C] C
" + + if(!gas_input) + dat += "WARNING! NO INPUT DETECTED!
" + + if(!gas_output) + dat += "WARNING! NO OUTPUT DETECTED!
" + + dat += "Fuel Rods Status:
" + var/counter = 0 + for(var/obj/item/fuel_rod/FR in Get_Fuel_Rods()) + counter++ + dat += "Fuel Rod [counter] - [FR.life]%
" + + return dat + +// ---------------- + +/datum/multistructure/nuclear_reactor/proc/equalize(datum/gas_mixture/env, var/efficiency) + var/datum/gas_mixture/sharer = env.remove(efficiency * env.total_moles) + + if(!sharer) + return + + var/our_heatcap = heat_capacity() + var/share_heatcap = sharer.heat_capacity() + + if((abs(temperature-sharer.temperature)>MINIMUM_MEANINGFUL_TEMPERATURE_DELTA) && our_heatcap + share_heatcap) + var/new_temperature = ((temperature * our_heatcap) + (sharer.temperature * share_heatcap)) / (our_heatcap + share_heatcap) + temperature += (new_temperature - temperature) + temperature = clamp( temperature, 0, REACTOR_TEMPERATURE_CUTOFF) + sharer.temperature += (new_temperature - sharer.temperature) + sharer.temperature = clamp( sharer.temperature, 0, REACTOR_TEMPERATURE_CUTOFF) + + env.merge(sharer) + +/datum/multistructure/nuclear_reactor/proc/adjust_thermal_energy(var/thermal_energy) + if(mass < 1) + return 0 + + var/heat_capacity = heat_capacity() + if(thermal_energy < 0) + if(temperature < TCMB) + return 0 + var/thermal_energy_limit = -(temperature - TCMB)*heat_capacity //ensure temperature does not go below TCMB + thermal_energy = max(thermal_energy, thermal_energy_limit) //thermal_energy and thermal_energy_limit are negative here. + temperature += thermal_energy/heat_capacity + return thermal_energy + +/datum/multistructure/nuclear_reactor/proc/heat_capacity() + . = specific_heat * (mass / molar_mass) + +/datum/multistructure/nuclear_reactor/proc/get_integrity() + var/integrity = round(health / max_health * 100) + integrity = integrity < 0 ? 0 : integrity + return integrity + +/datum/multistructure/nuclear_reactor/proc/announce_warning(var/meltedrods, var/meltingrods, var/core_overheat) + if(!exploded && (meltedrods > 0 || meltingrods > 0 || temperature >= max_temp)) + var/location = sanitize((get_area(wall_input))?.name) + if((world.timeofday - lastwarning) >= warning_delay * 10) + lastwarning = world.timeofday + if(core_overheat) + radio.autosay("Danger! Fission core at [location] is overheating!", "Nuclear Monitor") + else if(meltedrods > 0 && meltingrods > 0) + radio.autosay("Warning! [meltedrods] rods have melted and [meltingrods] are overheating!", "Nuclear Monitor", "Engineering") + else if(meltedrods > 0) + if(meltedrods == 1) + radio.autosay("Warning! A rod has melted!", "Nuclear Monitor", "Engineering") + else + radio.autosay("Warning! [meltedrods] rods have melted!", "Nuclear Monitor", "Engineering") + else if(meltingrods > 0) + if(meltingrods == 1) + radio.autosay("Warning! A rod is overheating!", "Nuclear Monitor", "Engineering") + else + radio.autosay("Warning! [meltingrods] rods are overheating!", "Nuclear Monitor", "Engineering") + +/* +/datum/multistructure/nuclear_reactor/proc/go_nuclear() + if(health < 1 && !exploded) + var/off_station = 0 + if(!(src.z in (LEGACY_MAP_DATUM).station_levels)) + off_station = 1 + var/turf/L = get_turf(wall_input) + if(!istype(L)) + return + message_admins("[name] exploding in 15 seconds at ([L.x],[L.y],[L.z] - JMP)",0,1) + log_game("[name] exploded at ([L.x],[L.y],[L.z])") + exploded = 1 + if(!anchored) + anchor() + var/decaying_rods = 0 + var/decay_heat = 0 + for(var/obj/item/fuel_rod/rod in rods) + if(rod.life > 0 && rod.decay_heat > 0) + decay_heat += rod.tick_life() + decaying_rods++ + rod.meltdown() + var/rad_power = decay_heat / REACTOR_RADS_TO_MJ + if(announce) + var/sound = sound('sound/effects/nuclear_meltdown.ogg') + if(!off_station) + for(var/mob/M in GLOB.player_list) + SEND_SOUND(M,sound) + spawn(1 SECONDS) + radio.autosay("DANGER! FISSION CORE HAS BREACHED!", "Nuclear Monitor") + radio.autosay("FIND SHELTER IMMEDIATELY!", "Nuclear Monitor") + spawn(5 SECONDS) + radio.autosay("CORE BREACH! FIND SHELTER IMMEDIATELY!", "Nuclear Monitor") + spawn(10 SECONDS) + radio.autosay("CORE BREACH! FIND SHELTER IMMEDIATELY!", "Nuclear Monitor") + + // Give the alarm time to play. Then... FLASH! AH-AH! + spawn(15 SECONDS) + z_radiation(get_turf(src), null, rad_power * BREACH_RADIATION_MULTIPLIER / RAD_MOB_ACT_COEFFICIENT, RAD_FALLOFF_ZLEVEL_FISSION_MELTDOWN) + + // Some engines just want to see the world burn. + spawn(17 SECONDS) + for(var/obj/item/fuel_rod/rod in rods) + rod.forceMove(L) + rods.Cut() + pipes.Cut() + empulse(src, decaying_rods * 10, decaying_rods * 100) + var/explosion_power = 4 * decaying_rods + if(explosion_power < 1) // If you remove the rods but it's over heating, it's still gunna go bang, but without going nuclear. + explosion_power = 1 + explosion(L, explosion_power, explosion_power * 2, explosion_power * 3, explosion_power * 4, 1) +*/