INDRA, general nuclear fusion update (#21285)

Fixes https://github.com/Aurorastation/Aurora.3/issues/18951
Fixes https://github.com/Aurorastation/Aurora.3/issues/19447
Fixes https://github.com/Aurorastation/Aurora.3/issues/20283
Fixes https://github.com/Aurorastation/Aurora.3/issues/21395

Overhauls a wide variety of INDRA behaviors and related gas/material
properties.
changes:
- balance: "Fusion reaction modifications. Higher radiation and
temperature output, comparable power output."
- balance: "Increased rad resistance modifier of Borosilicate Windows
from 1 -> 4 and (closed) Blast Doors from 1 -> 6."
- balance: "INDRA Field Strength minima/maxima changed from 1-100 ->
20-120. Power costs increased significantly."
- balance: "INDRA instability increase per tick now limited, similar to
Supermatter (prevents sudden instability spike from instantly nuking
it)."
  - balance: "APC cell charge rate increased 10x."
  - balance: "Rebalanced contents of INDRA hard storage compartment."
  - balance: "SMES coils capacities halved, throughputs doubled."
  - balance: "SMES unit maximum coil capacity increased from 6 -> 8."
- balance: "TEG maximum power output increased from 500000 -> 2500000."
- balance: "Power wasted from exceeding TEG maximum power output
decreased from 50% to 33%."
- balance: "Portable generators of all types have had their power
generation capacity doubled."
- rscadd: "New 'Fusion Codex' app added for all crew with ACCESS_ENGINE
that details all available nuclear fusion chains."
- rscadd: "Boron added as a singleton/reagent. Poisonous to vaurca,
still usable in nuclear fusion as fuel assembly rods."
- rscadd: "INDRA Field Strength now scales plasma temperature entropy,
power output from temperature multiplier, and instability increase per
tick."
- refactor: "'Boron' gas renamed to 'Helium-3'. Boron is not a gas. Deal
with it."
  - refactor: "'Steam' gas renamed to 'Water Vapor'."
  - code_imp: "Descriptions added to gas singletons for future use."
  - qol: "Minor remap of INDRA control room for usability."
  - qol: "Standardized gas canister naming conventions."
- qol: "INDRA and APC TGUI interfaces now use use kW, MW abbreviations
for high wattages."
- bugfix: "INDRA Kinetic Harvester no longer rapidly deselects materials
for harvest when too many are being generated at once."
- bugfix: "INDRA EM Field effect no longer considers the camera within
its reactor chamber a destabilizing influence."
  - bugfix: "INDRA reactor core now produces radiation as intended."
- bugfix: "Deuterium and Tritium canisters no longer contain 50%
standard hydrogen (inheritance issue)."

---------

Signed-off-by: Batrachophreno <Batrochophreno@gmail.com>
Co-authored-by: Matt Atlas <mattiathebest2000@hotmail.it>
Co-authored-by: Geeves <22774890+Geevies@users.noreply.github.com>
This commit is contained in:
Batrachophreno
2025-10-05 20:13:06 +00:00
committed by GitHub
co-authored by Matt Atlas Geeves
parent 93b7dbba83
commit b3b4f4b9c6
66 changed files with 4375 additions and 3042 deletions
@@ -14,11 +14,11 @@
#define ATM_2H 9 //Deuterium
#define ATM_3H 10 //Tritium
#define ATM_HE 11 //Helium
#define ATM_B 12 //Boron
#define ATM_3HE 12 //Helium-3
#define ATM_SO2 13 //Sulfur Dioxide
#define ATM_NO2 14 //Nitrogen Dioxide
#define ATM_CL2 15 //Chlorine
#define ATM_H2O 16 //Steam
#define ATM_H2O 16 //Water vapor
//--------------------------------------------
// Omni port datum
@@ -121,8 +121,8 @@
return GAS_TRITIUM
if(ATM_HE)
return GAS_HELIUM
if(ATM_B)
return GAS_BORON
if(ATM_3HE)
return GAS_HELIUMFUEL
if(ATM_SO2)
return GAS_SULFUR
if(ATM_NO2)
@@ -130,6 +130,6 @@
if(ATM_CL2)
return GAS_CHLORINE
if(ATM_H2O)
return GAS_STEAM
return GAS_WATERVAPOR
else
return null
@@ -166,8 +166,8 @@
return "Tritium"
if(ATM_HE)
return "Helium"
if(ATM_B)
return "Boron"
if(ATM_3HE)
return "Helium-3"
if(ATM_SO2)
return "Sulfur Dioxide"
if(ATM_NO2)
@@ -175,7 +175,7 @@
if(ATM_CL2)
return "Chlorine"
if(ATM_H2O)
return "Steam"
return "Water Vapor"
else
return null
@@ -201,7 +201,7 @@
if("switch_mode")
switch_mode(dir_flag(href_list["dir"]), mode_return_switch(href_list["mode"]))
if("switch_filter")
var/new_filter = input(usr,"Select filter mode:","Change filter",href_list["mode"]) in list("None", "Oxygen", "Nitrogen", "Carbon Dioxide", "Phoron", "Nitrous Oxide", "Hydrogen", "Deuterium", "Tritium", "Helium", "Boron", "Sulfur Dioxide", "Nitrogen Dioxide", "Chlorine", "Steam")
var/new_filter = input(usr,"Select filter mode:","Change filter",href_list["mode"]) in list("None", "Oxygen", "Nitrogen", "Carbon Dioxide", "Phoron", "Nitrous Oxide", "Hydrogen", "Deuterium", "Tritium", "Helium", "Helium-3", "Sulfur Dioxide", "Nitrogen Dioxide", "Chlorine", "Water Vapor")
switch_filter(dir_flag(href_list["dir"]), mode_return_switch(new_filter))
update_icon()
@@ -228,15 +228,15 @@
return ATM_3H
if("Helium")
return ATM_HE
if("Boron")
return ATM_B
if("Helium-3")
return ATM_3HE
if("Sulfur Dioxide")
return ATM_SO2
if("Nitrogen Dioxide")
return ATM_NO2
if("Chlorine")
return ATM_CL2
if("Steam")
if("Water Vapor")
return ATM_H2O
if("in")
return ATM_INPUT
@@ -163,11 +163,11 @@
"filter_2h" = (GAS_DEUTERIUM in scrubbing_gas),
"filter_3h" = (GAS_TRITIUM in scrubbing_gas),
"filter_he" = (GAS_HELIUM in scrubbing_gas),
"filter_b" = (GAS_BORON in scrubbing_gas),
"filter_b" = (GAS_HELIUMFUEL in scrubbing_gas),
"filter_so2" = (GAS_SULFUR in scrubbing_gas),
"filter_no2" = (GAS_NO2 in scrubbing_gas),
"filter_cl" = (GAS_CHLORINE in scrubbing_gas),
"filter_h2o" = (GAS_STEAM in scrubbing_gas),
"filter_h2o" = (GAS_WATERVAPOR in scrubbing_gas),
"sigtype" = "status"
)
@@ -312,10 +312,10 @@
else if(signal.data["toggle_he_scrub"])
toggle += GAS_HELIUM
if(!isnull(signal.data["b_scrub"]) && text2num(signal.data["b_scrub"]) != (GAS_BORON in scrubbing_gas))
toggle += GAS_BORON
if(!isnull(signal.data["b_scrub"]) && text2num(signal.data["b_scrub"]) != (GAS_HELIUMFUEL in scrubbing_gas))
toggle += GAS_HELIUMFUEL
else if(signal.data["toggle_b_scrub"])
toggle += GAS_BORON
toggle += GAS_HELIUMFUEL
if(!isnull(signal.data["so2_scrub"]) && text2num(signal.data["so2_scrub"]) != (GAS_SULFUR in scrubbing_gas))
toggle += GAS_SULFUR
@@ -332,10 +332,10 @@
else if(signal.data["toggle_cl_scrub"])
toggle += GAS_CHLORINE
if(!isnull(signal.data["h2o_scrub"]) && text2num(signal.data["h2o_scrub"]) != (GAS_STEAM in scrubbing_gas))
toggle += GAS_STEAM
if(!isnull(signal.data["h2o_scrub"]) && text2num(signal.data["h2o_scrub"]) != (GAS_WATERVAPOR in scrubbing_gas))
toggle += GAS_WATERVAPOR
else if(signal.data["toggle_h2o_scrub"])
toggle += GAS_STEAM
toggle += GAS_WATERVAPOR
scrubbing_gas ^= toggle
+1 -1
View File
@@ -97,7 +97,7 @@
/datum/export/material/mhydrogen
cost = 10
material_id = "mhydrogen"
material_id = "metallic hydrogen"
message = "metallic hydrogen sheets"
/datum/export/material/aluminium
+7 -7
View File
@@ -72,7 +72,7 @@
category = "atmos"
name = "Canister (Air)"
supplier = "hephaestus"
description = "Holds nitrogen-oxygen breatheable air. Has a built-in valve to allow for filling portable tanks."
description = "Holds a nitrogen-oxygen mixture of breathable air. Has a built-in valve to allow for filling portable tanks."
price = 180
items = list(
/obj/machinery/portable_atmospherics/canister/air
@@ -82,14 +82,14 @@
groupable = FALSE
spawn_amount = 1
/singleton/cargo_item/canister_bo
/singleton/cargo_item/canister_3he
category = "atmos"
name = "Canister (Boron)"
name = "Canister (Helium-3)"
supplier = "hephaestus"
description = "Holds boron gas. Has a built-in valve to allow for filling portable tanks."
price = 380
description = "Holds Helium-3 gas, a valuable fusion fuel. Has a built-in valve to allow for filling portable tanks."
price = 3800
items = list(
/obj/machinery/portable_atmospherics/canister/boron
/obj/machinery/portable_atmospherics/canister/heliumfuel
)
access = ACCESS_ENGINE
container_type = "box"
@@ -100,7 +100,7 @@
category = "atmos"
name = "Canister (CO2)"
supplier = "hephaestus"
description = "Holds heavy CO2 gas. Has a built-in valve to allow for filling portable tanks."
description = "Holds heavy CO2 gas, whatever that is. Has a built-in valve to allow for filling portable tanks."
price = 300
items = list(
/obj/machinery/portable_atmospherics/canister/carbon_dioxide
+1 -1
View File
@@ -292,5 +292,5 @@
origin_tech = list(TECH_MATERIAL = 4,TECH_ENGINEERING = 5,TECH_MAGNET = 4,TECH_POWER = 4)
type_to_take = "mhydrogen"
type_to_take = "metallic hydrogen"
charge_per_sheet = 90
@@ -521,23 +521,23 @@
complexity = 9
inputs = list()
outputs = list(
"pressure" = IC_PINTYPE_NUMBER,
"temperature" = IC_PINTYPE_NUMBER,
GAS_OXYGEN = IC_PINTYPE_NUMBER,
GAS_NITROGEN = IC_PINTYPE_NUMBER,
GAS_CO2 = IC_PINTYPE_NUMBER,
GAS_PHORON = IC_PINTYPE_NUMBER,
GAS_N2O = IC_PINTYPE_NUMBER,
GAS_HYDROGEN = IC_PINTYPE_NUMBER,
GAS_DEUTERIUM = IC_PINTYPE_NUMBER,
GAS_TRITIUM = IC_PINTYPE_NUMBER,
GAS_HELIUM = IC_PINTYPE_NUMBER,
GAS_BORON = IC_PINTYPE_NUMBER,
GAS_SULFUR = IC_PINTYPE_NUMBER,
GAS_NO2 = IC_PINTYPE_NUMBER,
GAS_CHLORINE = IC_PINTYPE_NUMBER,
GAS_STEAM = IC_PINTYPE_NUMBER,
"other" = IC_PINTYPE_NUMBER
"pressure" = IC_PINTYPE_NUMBER,
"temperature" = IC_PINTYPE_NUMBER,
GAS_OXYGEN = IC_PINTYPE_NUMBER,
GAS_NITROGEN = IC_PINTYPE_NUMBER,
GAS_CO2 = IC_PINTYPE_NUMBER,
GAS_PHORON = IC_PINTYPE_NUMBER,
GAS_N2O = IC_PINTYPE_NUMBER,
GAS_HYDROGEN = IC_PINTYPE_NUMBER,
GAS_DEUTERIUM = IC_PINTYPE_NUMBER,
GAS_TRITIUM = IC_PINTYPE_NUMBER,
GAS_HELIUM = IC_PINTYPE_NUMBER,
GAS_HELIUMFUE = IC_PINTYPE_NUMBER,
GAS_SULFUR = IC_PINTYPE_NUMBER,
GAS_NO2 = IC_PINTYPE_NUMBER,
GAS_CHLORINE = IC_PINTYPE_NUMBER,
GAS_WATERVAPOR = IC_PINTYPE_NUMBER,
"other" = IC_PINTYPE_NUMBER
)
activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
@@ -642,11 +642,11 @@
var/deuterium_level = environment.gas[GAS_DEUTERIUM]/total_moles
var/tritium_level = environment.gas[GAS_TRITIUM]/total_moles
var/helium_level = environment.gas[GAS_HELIUM]/total_moles
var/boron_level = environment.gas[GAS_BORON]/total_moles
var/helium3_level = environment.gas[GAS_HELIUMFUEL]/total_moles
var/sulfurdioxide_level = environment.gas[GAS_SULFUR]/total_moles
var/nitrogendioxide_level = environment.gas[GAS_NO2]/total_moles
var/chlorine_level = environment.gas[GAS_CHLORINE]/total_moles
var/steam_level = environment.gas[GAS_STEAM]/total_moles
var/watervapor_level = environment.gas[GAS_WATERVAPOR]/total_moles
var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level)
set_pin_data(IC_OUTPUT, 1, pressure)
set_pin_data(IC_OUTPUT, 2, round(environment.temperature-T0C,0.1))
@@ -659,11 +659,11 @@
set_pin_data(IC_OUTPUT, 8, round(deuterium_level*100,0.01))
set_pin_data(IC_OUTPUT, 9, round(tritium_level*100,0.01))
set_pin_data(IC_OUTPUT, 10, round(helium_level*100,0.01))
set_pin_data(IC_OUTPUT, 11, round(boron_level*100,0.01))
set_pin_data(IC_OUTPUT, 11, round(helium3_level*100,0.01))
set_pin_data(IC_OUTPUT, 12, round(sulfurdioxide_level*100,0.01))
set_pin_data(IC_OUTPUT, 13, round(nitrogendioxide_level*100,0.01))
set_pin_data(IC_OUTPUT, 14, round(chlorine_level*100,0.01))
set_pin_data(IC_OUTPUT, 15, round(steam_level*100,0.01))
set_pin_data(IC_OUTPUT, 15, round(watervapor_level*100,0.01))
set_pin_data(IC_OUTPUT, 16, round(unknown_level, 0.01))
set_pin_data(IC_OUTPUT, 17, round(unknown_level, 0.01))
else
@@ -812,9 +812,9 @@
gas_name = GAS_HELIUM
gas_display_name = GAS_HELIUM
/obj/item/integrated_circuit/input/gas_sensor/boron_level
gas_name = GAS_BORON
gas_display_name = GAS_BORON
/obj/item/integrated_circuit/input/gas_sensor/helium3_level
gas_name = GAS_HELIUMFUEL
gas_display_name = GAS_HELIUMFUEL
/obj/item/integrated_circuit/input/gas_sensor/sulfurdioxide_level
gas_name = GAS_SULFUR
@@ -828,9 +828,9 @@
gas_name = GAS_CHLORINE
gas_display_name = GAS_CHLORINE
/obj/item/integrated_circuit/input/gas_sensor/steam_level
gas_name = GAS_STEAM
gas_display_name = GAS_STEAM
/obj/item/integrated_circuit/input/gas_sensor/watervapor_level
gas_name = GAS_WATERVAPOR
gas_display_name = GAS_WATERVAPOR
/obj/item/integrated_circuit/input/turfpoint
name = "tile pointer"
+11
View File
@@ -616,3 +616,14 @@
. = ..()
amount = max_amount
update_icon()
// Fusion fuel.
/obj/item/stack/material/boron
name = "boron"
icon_state = "puck"
default_type = MATERIAL_BORON
/obj/item/stack/material/boron/full/Initialize()
. = ..()
amount = max_amount
update_icon()
+9
View File
@@ -1222,3 +1222,12 @@
sheet_singular_name = "ingot"
sheet_plural_name = "ingots"
is_fusion_fuel = TRUE
/material/boron
name = MATERIAL_BORON
stack_type = /obj/item/stack/material/boron
icon_colour = "#bbbbbb"
stack_origin_tech = list(TECH_MATERIAL = 3)
sheet_singular_name = "ingot"
sheet_plural_name = "ingots"
is_fusion_fuel = TRUE
@@ -26,7 +26,8 @@
/obj/item/computer_hardware,
/obj/item/pipe,
/obj/item/smallDelivery,
/obj/item/gift
/obj/item/gift,
/obj/item/fuel_assembly
)
var/list/cant_hold
@@ -54,6 +54,7 @@ ABSTRACT_TYPE(/datum/modular_computer_app_presets)
/datum/computer_file/program/rcon_console,\
/datum/computer_file/program/camera_monitor,\
/datum/computer_file/program/lighting_control,\
/datum/computer_file/program/fusion_codex,\
)
#define COMPUTER_APP_PRESET_HORIZON_MEDICAL list(\
@@ -0,0 +1,26 @@
/datum/computer_file/program/fusion_codex
filename = "fusioncodex"
filedesc = "Fusion Codex"
program_icon_state = "medcomp"
program_key_icon_state = "teal_key"
extended_desc = "Useful program to reference reaction chains for nuclear fusion reactors."
size = 14
requires_access_to_run = PROGRAM_ACCESS_LIST_ONE
required_access_run = list(ACCESS_ENGINE)
required_access_download = list(ACCESS_ENGINE)
available_on_ntnet = TRUE
tgui_id = "FusionCodex"
/datum/computer_file/program/fusion_codex/ui_data(mob/user)
var/list/data = list()
// Gather data for computer header
var/headerdata = get_header_data(data["_PC"])
if(headerdata)
data["_PC"] = headerdata
. = data
// Here goes listification
if(!data["reactions"])
data["reactions"] = SScodex.fusion_codex_data
return data
+1 -1
View File
@@ -443,7 +443,7 @@
newgases -= GAS_PHORON
if(prob(50)) //alium gas should be slightly less common than mundane shit
newgases -= GAS_ALIEN
newgases -= GAS_STEAM
newgases -= GAS_WATERVAPOR
var/total_moles = MOLES_CELLSTANDARD * rand(80,120)/100
var/badflag = 0
+6 -6
View File
@@ -85,7 +85,7 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
var/areastring = null
var/obj/item/cell/cell
/// Cap for how fast APC cells charge, as a percentage-per-tick (0.01 means cellcharge is capped to 1% per second)
var/chargelevel = 0.0005
var/chargelevel = 0.005
var/cellused = 0
/// Initial cell charge %
var/start_charge = 90
@@ -936,8 +936,8 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
data["fail_time"] = failure_timer * 2
data["silicon_user"] = isAdmin || issilicon(user)
data["is_AI_or_robot"] = isAI(user) || isrobot(user)
data["total_load"] = round(lastused_total)
data["total_charging"] = round(lastused_charging)
data["total_load"] = power_wattage_readable(lastused_total)
data["total_charging"] = power_wattage_readable(lastused_charging)
data["is_operating"] = operating
data["charge_mode"] = chargemode
data["external_power"] = main_status
@@ -947,9 +947,9 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
data["emergency_mode"] = !emergency_lights
data["time"] = time
data["power_channels"] = list(
list("name" = "Equipment", "power_load" = lastused_equip, "status" = equipment),
list("name" = "Lighting", "power_load" = round(lastused_light), "status" = lighting),
list("name" = "Environment", "power_load" = round(lastused_environ), "status" = environ)
list("name" = "Equipment", "power_load" = power_wattage_readable(lastused_equip), "status" = equipment),
list("name" = "Lighting", "power_load" = power_wattage_readable(lastused_light), "status" = lighting),
list("name" = "Environment", "power_load" = power_wattage_readable(lastused_environ), "status" = environ)
)
return data
@@ -3,8 +3,8 @@
icon_keyboard_emis = "yellow_key_mask"
icon_screen = "solar"
light_color = COLOR_ORANGE
idle_power_usage = 250
active_power_usage = 500
idle_power_usage = 2500
active_power_usage = 10000
manufacturer = "hephaestus"
var/ui_template
var/initial_id_tag
@@ -45,5 +45,5 @@
/obj/machinery/computer/fusion/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
ui = new(user, src, ui_template, name, 400, 500)
ui = new(user, src, ui_template, name)
ui.open()
@@ -53,15 +53,25 @@
for(var/i = 1 to LAZYLEN(fusion_cores))
var/list/core = list()
var/obj/machinery/power/fusion_core/C = fusion_cores[i]
var/power_available = C.avail()
var/power_usage = C.active_power_usage
var/power_generated = C.owned_field?.output_avg
core["id"] = "#[i]"
core["ref"] = "[REF(C)]"
core["field"] = !isnull(C.owned_field)
core["power"] = "[C.field_strength / 10]"
core["field_strength"] = C.field_strength
core["field_strength_max"] = C.field_strength_max
core["entropy_multiplier"] = C.owned_field ? round(C.owned_field.field_strength_entropy_multiplier, 0.01) : 1
core["instability_multiplier"] = C.owned_field ? round(C.owned_field.field_strength_instability_multiplier, 0.01) : 1
core["power_multiplier"] = C.owned_field ? round(C.owned_field.field_strength_power_multiplier, 0.01) : 1
core["size"] = C.owned_field ? C.owned_field.size : 0
core["instability"] = C.owned_field ? C.owned_field.percent_unstable * 100 : -1 //%
core["temperature"] = C.owned_field ? C.owned_field.plasma_temperature + 295 : -1 //K
core["power_status"] = "[C.avail()]/[C.active_power_usage]"
core["power_available"] = "[power_wattage_readable(power_available)]"
core["power_usage"] = "[power_wattage_readable(power_usage)]"
core["power_generated"] = "[power_wattage_readable(power_generated)]"
core["shutdown_safe"] = C.owned_field ? C.owned_field.is_shutdown_safe() : TRUE
var/list/reactants = list()
@@ -34,7 +34,7 @@
switch(action)
if("modifypower")
G.mega_energy = clamp(params["modifypower"], 1, 50)
G.mega_energy = clamp(params["modifypower"], 1, 250)
G.change_power_consumption(G.mega_energy * GYRO_POWER, POWER_USE_ACTIVE)
return TRUE
@@ -62,7 +62,7 @@
gyrotron["active"] = G.active
gyrotron["firedelay"] = G.rate
gyrotron["energy"] = G.mega_energy
gyrotron["power_status"] = power_wattage_readable(G.mega_energy * GYRO_POWER)
gyrotrons += list(gyrotron)
data["gyro_power_constant"] = GYRO_POWER
data["gyrotrons"] = gyrotrons
return data
+20 -6
View File
@@ -9,13 +9,16 @@
layer = ABOVE_HUMAN_LAYER
density = TRUE
use_power = POWER_USE_IDLE
idle_power_usage = 50
idle_power_usage = 10000
/// Gets multiplied by field strength
active_power_usage = 500
active_power_usage = 50000
anchored = FALSE
var/obj/effect/fusion_em_field/owned_field
var/field_strength = 1//0.01
/// The currently configured Field Strength (1 = 100 Tesla).
var/field_strength = 1
/// This is for the INDRA, allowing a maximum 5-radius field. Change this for larger/smaller reactors.
var/field_strength_max = 1.2
var/initial_id_tag
/obj/machinery/power/fusion_core/mapped
@@ -26,6 +29,13 @@
connect_to_network()
AddComponent(/datum/component/local_network_member, initial_id_tag)
/obj/machinery/power/fusion_core/Destroy()
if(owned_field)
qdel(owned_field)
owned_field = null
set_light(0)
return ..()
/**
* If there's no powernet or no owned field, shutdown() in those cases too.
*/
@@ -44,6 +54,7 @@
owned_field.ChangeFieldStrength(field_strength)
icon_state = "core1"
update_use_power(POWER_USE_ACTIVE)
set_light(3, 0.7, COLOR_PALE_BLUE_GRAY)
. = 1
/**
@@ -52,13 +63,16 @@
/obj/machinery/power/fusion_core/proc/Shutdown(force_rupture)
if(owned_field)
icon_state = "core0"
if(force_rupture || owned_field.plasma_temperature > 1000)
// Blow the whole fucking thing up.
if(force_rupture || owned_field.plasma_temperature > 720000)
owned_field.Rupture()
// Just radiate all that temperature into the environment. Unless you're under 1000 K, its still a big 'fuck you,' but admittedly better than EMP/explosion.
else
owned_field.RadiateAll()
qdel(owned_field)
owned_field = null
update_use_power(POWER_USE_IDLE)
set_light(0)
/obj/machinery/power/fusion_core/proc/AddParticles(name, quantity = 1)
if(owned_field)
@@ -76,7 +90,7 @@
/obj/machinery/power/fusion_core/proc/set_strength(value)
value = clamp(value, MIN_FIELD_STR, MAX_FIELD_STR)
field_strength = value
change_power_consumption(5 * value, POWER_USE_ACTIVE)
change_power_consumption(1500 * (value ** 1.5), POWER_USE_ACTIVE)
if(owned_field)
owned_field.ChangeFieldStrength(value)
@@ -115,7 +129,7 @@
return ..()
/obj/machinery/power/fusion_core/proc/jumpstart(field_temperature)
field_strength = 501 // Generally a good size.
field_strength = 120 // Generally a good size.
Startup()
if(!owned_field)
return FALSE
+293 -117
View File
@@ -1,24 +1,53 @@
#define FUSION_ENERGY_PER_K 20
#define FUSION_INSTABILITY_DIVISOR 50000
#define FUSION_RUPTURE_THRESHOLD 10000
#define FUSION_REACTANT_CAP 10000
#define FUSION_WARNING_DELAY 20
#define FUSION_INSTABILITY_DIVISOR 100000
#define FUSION_RUPTURE_THRESHOLD 25000
#define FUSION_REACTANT_CAP 10000
#define FUSION_WARNING_DELAY 20
#define FUSION_BLACKBODY_MULTIPLIER 28
#define FUSION_INTEGRITY_RATE_LIMIT 0.11
#define FUSION_TICK_MAX_TEMP_CHANGE 0.2
/obj/effect/fusion_em_field
name = "electromagnetic field"
desc = "A coruscating, barely visible field of energy. It is shaped like a slightly flattened torus."
alpha = 30
layer = 4
layer = ABOVE_ABOVE_HUMAN_LAYER
light_color = COLOR_RED
mouse_opacity = MOUSE_OPACITY_ICON
/// Temporary pool for energy being added to the field from a Gyrotron.
var/energy = 0
/// Actual core field energy (temperature).
var/plasma_temperature = 0
/// Effective core field energy (temperature) as modified by field magnitude constriction or relaxation.
// var/plasma_temperature_effective
/// Current excess radiation from ongoing reactions.
var/radiation = 0
/// The currently configured Field Strength (0.01 = 1 Tesla).
var/field_strength = 0.01
/// Radiation of the previous three ticks averaged out (if != 0).
var/radiation_avg = 0
/// Archived radiation. Used for averaging out new SSradiation.radiate() source creation.
var/radiation_archive_1 = 0
var/radiation_archive_2 = 0
var/radiation_archive_3 = 0
var/radiation_archive_4 = 0
var/radiation_archive_5 = 0
/**
* The currently configured Field Strength (0.2 = 20 Tesla). Capped by TGUI at 1.2 (120 Tesla).
*
* Field Strength scales plasma temperature entropy. With higher field strength, each tick will lose more temp and produce more radiation.
* Field Strength scales power output per temperature. With higher field strength, each degree Kelvin will produce more electricity.
* Field Strength scales instability increase. With higher field strength, more instability for a given reaction will be produced each tick.
*/
var/field_strength = 20
/// Current field strength multiplier applied to entropy.
var/field_strength_entropy_multiplier = 1.0
/// Current field strength multiplier applied to instability.
var/field_strength_instability_multiplier = 1.0
/// Current field strength multiplier applied to power output.
var/field_strength_power_multiplier = 1.0
/// Radius of the EM field. Scales with Field Strength.
var/size = 1
/// Instability generated on this current tick.
var/tick_instability = 0
/// Ranges from 0-1. At or over 1, boom.
var/percent_unstable = 0
@@ -33,7 +62,8 @@
/obj/effect,
/obj/structure/cable,
/obj/machinery/atmospherics,
/obj/machinery/air_sensor
/obj/machinery/air_sensor,
/obj/machinery/camera
)
var/light_min_range = 2
@@ -58,27 +88,51 @@
var/emergency_alert = "DANGER: INDRA REACTOR MELTDOWN IMMINENT!"
var/lastwarning = 0
/// Power output this tick. We average this like we do radiation for player-presenting data.
var/power_output
/// Power of the previous five ticks averaged out (if != 0).
var/output_avg = 0
/// Archived power. Used for averaging out the power dumped into the powernet for players to see.
var/output_archive_1 = 0
var/output_archive_2 = 0
var/output_archive_3 = 0
var/output_archive_4 = 0
var/output_archive_5 = 0
var/vfx_radius_actual
//var/vfx_radius_visual
var/pause_rupture = TRUE
var/power_log_base = 1.4
var/power_multiplier = 3
var/power_power = 3.2
var/aaa_minimum_energy_level_multiplier = 1.0
/obj/effect/fusion_em_field/proc/UpdateVisuals()
//Take the particle system and edit it
//size
var/radius = ((size-1) / 2) * WORLD_ICON_SIZE
particles.position = generator("circle", radius - size, radius + size, NORMAL_RAND)
vfx_radius_actual = ((size-1) / 2) * WORLD_ICON_SIZE
/*
var/vfx_radius_next = ((size+1) / 2) * WORLD_ICON_SIZE
var/percent_to_next_size = round(((field_strength + (size * 50)) - (size * 50)) / (((size + 1) / 2) * 50),0.01)
var/radius_add = round(percent_to_next_size * WORLD_ICON_SIZE, 1)
vfx_radius_visual = min(max(vfx_radius_actual + radius_add, vfx_radius_actual), vfx_radius_next)
*/
particles.position = generator("circle", vfx_radius_actual - size, vfx_radius_actual, NORMAL_RAND)
//Radiation affects drift
var/radiationfactor = clamp((radiation * 0.001), 0, 0.5)
var/radiationfactor = clamp((radiation_avg * 0.001), 0, 0.75)
particles.drift = generator("circle", (0.2 + radiationfactor), NORMAL_RAND)
particles.spawning = last_reactants * 0.9 + Interpolate(0, 200, clamp(plasma_temperature / 70000, 0, 1))
/obj/effect/fusion_em_field/New(loc, obj/machinery/power/fusion_core/new_owned_core)
..()
filters = list(filter(type = "ripple", size = 4, "radius" = 1, "falloff" = 1)
, filter(type="outline", size = 2, color = COLOR_RED)
, filter(type="bloom", size=3, offset = 0.5, alpha = 235))
, filter(type="bloom", size = 3, offset = 0.5, alpha = 235))
set_light(light_min_power, light_min_range / 10, light_min_range)
last_range = light_min_range
@@ -127,49 +181,6 @@
addtimer(CALLBACK(src, PROC_REF(update_light_colors)), 10 SECONDS, TIMER_LOOP)
radio = new /obj/item/device/radio{channels=list("Engineering")}(src)
/**
* Not blackbody radiation- with these colors, is some sort of space magic?
*/
/obj/effect/fusion_em_field/proc/update_light_colors()
var/use_range
var/use_power = 0
var/temp_mod = ((plasma_temperature-5000)/20000)
use_range = light_min_range + Ceil((light_max_range-light_min_range)*temp_mod)
use_power = light_min_power + Ceil((light_max_power-light_min_power)*temp_mod)
switch (plasma_temperature)
if (-INFINITY to 1000)
light_color = COLOR_RED
alpha = 30
if (1000 to 6000)
light_color = COLOR_ORANGE
alpha = 50
if (6000 to 20000)
light_color = COLOR_YELLOW
alpha = 80
if (20000 to 50000)
light_color = COLOR_GREEN
alpha = 120
if (50000 to 70000)
light_color = COLOR_CYAN
alpha = 160
if (70000 to 100000)
light_color = COLOR_BLUE
alpha = 200
if (100000 to INFINITY)
light_color = COLOR_VIOLET
alpha = 230
if (last_range != use_range || last_power != use_power || color != light_color)
set_light(use_range / 6, use_power ? 6 : 0, light_color)
last_range = use_range
last_power = use_power
//Temperature based color
particles.gradient = list(0, COLOR_WHITE, 0.85, light_color)
UNLINT(var/dm_filter/outline = filters[2])
UNLINT(outline.color = light_color)
UNLINT(var/dm_filter/bloom = filters[3])
UNLINT(bloom.alpha = alpha)
/**
* What are we doing every tick? A lot.
* * Grab some gas from the env and convert it to reactants for the pool.
@@ -203,14 +214,27 @@
// Let the particles inside the field react.
React()
field_strength_power_multiplier = max((owned_core.field_strength ** 1.2) / 100, 1)
// Dump power to our powernet.
owned_core.add_avail(FUSION_ENERGY_PER_K * plasma_temperature)
power_output = ((log(power_log_base, plasma_temperature) * power_multiplier) ** power_power) * field_strength_power_multiplier
output_archive_5 = output_archive_4
output_archive_4 = output_archive_3
output_archive_3 = output_archive_2
output_archive_2 = output_archive_1
output_archive_1 = power_output
output_avg = ((output_archive_1 + output_archive_2 + output_archive_3 + output_archive_4 + output_archive_5 ) / 5)
owned_core.add_avail(power_output)
// Roundstart update
if(field_strength < 20)
field_strength = 20
field_strength_entropy_multiplier = clamp((owned_core.field_strength ** 1.075) / 40, 0.8, 2.0)
// Energy decay (entropy tax).
if(plasma_temperature >= 1)
var/lost = plasma_temperature*0.01
var/lost = plasma_temperature * 0.0045
radiation += lost
plasma_temperature -= lost
var/temp_change = 0 - (lost * field_strength_entropy_multiplier)
adjust_temperature(temp_change, cause = "Containment Entropy")
// Handle some reactants formatting.
for(var/reactant in reactants)
@@ -218,7 +242,7 @@
if(amount < 1)
reactants.Remove(reactant)
else if(amount >= FUSION_REACTANT_CAP)
var/radiate = rand(3 * amount / 4, amount / 4)
var/radiate = rand(amount / 16, 3 * amount / 16)
reactants[reactant] -= radiate
radiation += radiate
@@ -229,8 +253,6 @@
warning()
Radiate()
if(radiation)
SSradiation.radiate(src, round(radiation*0.001))
return 1
/**
@@ -247,9 +269,12 @@
* document details later.
*/
/obj/effect/fusion_em_field/proc/check_instability()
field_strength_instability_multiplier = max((owned_core.field_strength ** 1.1)/20, 1)
if(tick_instability > 0)
percent_unstable_archive = percent_unstable
percent_unstable += (tick_instability*size)/FUSION_INSTABILITY_DIVISOR
// Apply any modifiers to instability imparted by current field strength, but only apply up to FUSION_INTEGRITY_RATE_LIMIT additional instability.
var/new_instability = min((tick_instability * field_strength_instability_multiplier)/FUSION_INSTABILITY_DIVISOR, FUSION_INTEGRITY_RATE_LIMIT)
percent_unstable += new_instability
tick_instability = 0
UpdateVisuals()
else
@@ -259,14 +284,14 @@
if(percent_unstable > 1)
percent_unstable = 1
if(percent_unstable > 0)
percent_unstable = max(0, percent_unstable-rand(0.01,0.03))
percent_unstable = max(0, percent_unstable-rand(0.02,0.08))
UpdateVisuals()
if(percent_unstable >= 1)
owned_core.Shutdown(force_rupture=1)
else
if(percent_unstable > 0.5 && prob(percent_unstable*100))
var/ripple_radius = (((size-1) / 2) * WORLD_ICON_SIZE) + WORLD_ICON_SIZE
var/ripple_radius = ((size-1) / 2) + WORLD_ICON_SIZE
var/wave_size = 4
if(plasma_temperature < FUSION_RUPTURE_THRESHOLD)
visible_message(SPAN_DANGER("\The [src] ripples uneasily, like a disturbed pond."))
@@ -291,7 +316,7 @@
rupture = prob(25)
wave_size += 4
if(rupture)
if(rupture && !pause_rupture)
owned_core.Shutdown(force_rupture=1)
else
var/lost_plasma = (plasma_temperature*percent_unstable)
@@ -299,7 +324,8 @@
if(flare)
radiation += plasma_temperature/2
wave_size += 6
plasma_temperature -= lost_plasma
var/temp_change = 0 - lost_plasma
adjust_temperature(temp_change, cause = "Instability Bleed-off")
if(fuel_loss)
for(var/particle in reactants)
@@ -375,13 +401,17 @@
* EMP, rads, and a big fuckoff explosion.
*/
/obj/effect/fusion_em_field/proc/Rupture()
visible_message(SPAN_DANGER("\The [src] shudders like a dying animal before flaring to eye-searing brightness and rupturing!"))
set_light(1, 0.1, 15, 2, "#ccccff")
empulse(get_turf(src), Ceil(plasma_temperature/1000), Ceil(plasma_temperature/300))
sleep(5)
if(pause_rupture)
return
visible_message(SPAN_DANGER("\The [src] convulses violently as gouts of plasma spill forth!"))
set_light(1, 0.1, "#ccccff", 15, 2)
empulse(get_turf(src), Ceil(plasma_temperature/1000000), Ceil(plasma_temperature/300000))
RadiateAll()
explosion(get_turf(owned_core), 8, 8)
return
addtimer(CALLBACK(src, PROC_REF(RuptureExplosion)), 45 SECONDS)
/obj/effect/fusion_em_field/proc/RuptureExplosion()
visible_message(SPAN_DANGER("\The [src] shudders like a dying animal before flaring to eye-searing brightness and rupturing!"))
explosion(get_turf(owned_core), 6, 8)
/**
* Sets field strength in Tesla, and corresponding field size.
@@ -389,17 +419,18 @@
*/
/obj/effect/fusion_em_field/proc/ChangeFieldStrength(new_strength)
var/calc_size = 1
if(new_strength <= 50)
if(new_strength < 40)
calc_size = 1
else if(new_strength <= 200)
else if(new_strength < 80)
calc_size = 3
else if(new_strength <= 500)
// Right now the max value allowed by the interface is 120. Change this from 121->120 if we want to allow bigger reactors.
else if(new_strength < 121)
calc_size = 5
else if(new_strength <= 1000)
else if(new_strength < 160)
calc_size = 7
else if(new_strength <= 2000)
else if(new_strength < 200)
calc_size = 9
else if(new_strength <= 5000)
else if(new_strength < 240)
calc_size = 11
else
calc_size = 13
@@ -407,8 +438,17 @@
change_size(calc_size)
/obj/effect/fusion_em_field/proc/AddEnergy(a_energy, a_plasma_temperature)
energy += a_energy
// Boost gyro effects at low temperatures for faster startup
if(plasma_temperature <= 75000)
a_energy = a_energy * 32
else if(plasma_temperature <= 250000)
a_energy = a_energy * 8
else if(plasma_temperature <= 1000000)
a_energy = a_energy * 2
energy += a_energy / 2
plasma_temperature += a_plasma_temperature
if(a_energy && percent_unstable > 0)
percent_unstable = max(percent_unstable - (a_energy/10000), 0)
while(energy >= 100)
@@ -450,10 +490,10 @@
for(var/particle in reactants)
radiation += reactants[particle]
reactants.Remove(particle)
radiation += plasma_temperature/2
radiation += plasma_temperature/8
plasma_temperature = 0
SSradiation.radiate(src, round(radiation*0.001))
SSradiation.radiate(src, radiation)
Radiate()
/**
@@ -465,14 +505,8 @@
*
* After that, it'll return the air it stole this tick, just heated up.
* The max temperature is capped so it can't be used for TEGs or shit.
*
* I don't know why radiation is set to 0 after that, because it means
* that the INDRA is COMPLETELY radiation-free short of exploding it.
*
* Probably need to delete radiation = 0 after testing it doesn't generate
* unreasonable amounts of rads with current values.
*/
/obj/effect/fusion_em_field/proc/Radiate()
/obj/effect/fusion_em_field/proc/Radiate(safe = TRUE)
if(istype(loc, /turf))
for(var/atom/movable/AM in range(max(1,FLOOR(size/2, 1)), loc))
@@ -488,7 +522,7 @@
continue
AM.visible_message(SPAN_DANGER("The field buckles visibly around \the [AM]!"))
tick_instability += rand(30,50)
tick_instability += rand(10,25)
AM.emp_act(EMP_LIGHT)
if(owned_core && owned_core.loc)
@@ -496,6 +530,19 @@
// Putting an upper bound on it to stop it being used in a TEG.
if(environment && environment.temperature < (T0C+1000))
environment.add_thermal_energy(plasma_temperature*20000)
// Radiation levels can spike unpredictably based on how many reagents we're throwing out, which reactions ran this cycle, etc.
// And while we like some unpredictability, it gets a little excessive with the INDRA. Use these vars to balance out actual rad output.
radiation_archive_5 = radiation_archive_4
radiation_archive_4 = radiation_archive_3
radiation_archive_3 = radiation_archive_2
radiation_archive_2 = radiation_archive_1
radiation_archive_1 = radiation
if(radiation >= 1000)
radiation_avg = ((radiation_archive_1 + radiation_archive_2 + radiation_archive_3 + radiation_archive_4 + radiation_archive_5 ) / 5)
SSradiation.radiate(src, radiation_avg * 0.01)
radiation = 0
/obj/effect/fusion_em_field/proc/change_size(newsize = 1)
@@ -519,63 +566,66 @@
* *
*/
/obj/effect/fusion_em_field/proc/React()
// Loop through the reactants in random order
// Loop through the reactants in random order.
var/list/react_pool = reactants.Copy()
last_reactants = 0
// Can't have any reactions if there aren't any reactants present
// Can't have any reactions if there aren't any reactants present.
if(length(react_pool))
//determine a random amount to actually react this cycle, and remove it from the standard pool
//this is a hack, and quite nonrealistic :(
// Determine a random amount to actually react this cycle, and remove it from the standard pool.
// This is a hack, and quite nonrealistic :(
for(var/reactant in react_pool)
react_pool[reactant] = rand(FLOOR(react_pool[reactant]/2, 1),react_pool[reactant])
reactants[reactant] -= react_pool[reactant]
if(!react_pool[reactant])
react_pool -= reactant
//loop through all the reacting reagents, picking out random reactions for them
// Loop through all the reacting reagents, picking out random reactions for them.
var/list/produced_reactants = new/list
var/list/p_react_pool = react_pool.Copy()
while(length(p_react_pool))
//pick one of the unprocessed reacting reagents randomly
// Pick one of the unprocessed reacting reagents randomly.
var/cur_p_react = pick(p_react_pool)
p_react_pool.Remove(cur_p_react)
//grab all the possible reactants to have a reaction with
// Grab all the possible reactants to have a reaction with.
var/list/possible_s_reacts = react_pool.Copy()
//if there is only one of a particular reactant, then it can not react with itself so remove it
// If there is only one of a particular reactant, then it can not react with itself so remove it.
possible_s_reacts[cur_p_react] -= 1
if(possible_s_reacts[cur_p_react] < 1)
possible_s_reacts.Remove(cur_p_react)
//loop through and work out all the possible reactions
// Loop through and work out all the possible reactions.
var/list/possible_reactions
for(var/cur_s_react in possible_s_reacts)
if(possible_s_reacts[cur_s_react] < 1)
continue
var/singleton/fusion_reaction/cur_reaction = get_fusion_reaction(cur_p_react, cur_s_react)
if(cur_reaction && plasma_temperature >= cur_reaction.minimum_energy_level)
if(cur_reaction && plasma_temperature >= (cur_reaction.minimum_energy_level * aaa_minimum_energy_level_multiplier)&& possible_s_reacts[cur_p_react] >= cur_reaction.minimum_p_react)
LAZYDISTINCTADD(possible_reactions, cur_reaction)
//if there are no possible reactions here, abandon this primary reactant and move on
// If there are no possible reactions here, abandon this primary reactant and move on.
if(!LAZYLEN(possible_reactions))
continue
/// Sort based on reaction priority to avoid deut-deut eating all the deut before deut-trit can run etc.
// Sort based on reaction priority to avoid deut-deut eating all the deut before deut-trit can run etc.
sortTim(possible_reactions, /proc/cmp_fusion_reaction_des)
//split up the reacting atoms between the possible reactions
// Split up the reacting atoms between the possible reactions.
while(length(possible_reactions))
var/singleton/fusion_reaction/cur_reaction = possible_reactions[1]
possible_reactions.Remove(cur_reaction)
//set the randmax to be the lower of the two involved reactants
// Set the randmax to be the lower of the two involved reactants.
var/max_num_reactants = react_pool[cur_reaction.p_react] > react_pool[cur_reaction.s_react] ? \
react_pool[cur_reaction.s_react] : react_pool[cur_reaction.p_react]
if(max_num_reactants < 1)
continue
//make sure we have enough energy
// Make sure we have enough energy.
// First, if minimum_reaction_temperature not set, make it the same as minimum_energy_level.
if(!cur_reaction.minimum_reaction_temperature)
cur_reaction.minimum_reaction_temperature = (cur_reaction.minimum_energy_level * 0.8 * aaa_minimum_energy_level_multiplier)
if(plasma_temperature < cur_reaction.minimum_reaction_temperature)
continue
@@ -584,17 +634,19 @@
if(max_num_reactants < 1)
continue
//randomly determined amount to react
var/amount_reacting = rand(1, max_num_reactants)
// Randomly determined amount to react. Starts at up to 1/20th, scales to up to 2/3rd at 20x min temp
var/temp_over_min = plasma_temperature / (cur_reaction.minimum_energy_level * 20 * aaa_minimum_energy_level_multiplier)
var/max_react_percent = clamp(temp_over_min, (1/20), (2/3))
var/amount_reacting = rand(1, (max_num_reactants * max_react_percent))
//removing the reacting substances from the list of substances that are primed to react this cycle
//if there aren't enough of that substance (there should be) then modify the reactant amounts accordingly
// Removing the reacting substances from the list of substances that are primed to react this cycle.
// If there aren't enough of that substance (there should be) then modify the reactant amounts accordingly.
if( react_pool[cur_reaction.p_react] - amount_reacting >= 0 )
react_pool[cur_reaction.p_react] -= amount_reacting
else
amount_reacting = react_pool[cur_reaction.p_react]
react_pool[cur_reaction.p_react] = 0
//same again for secondary reactant
// Same again for secondary reactant
if(react_pool[cur_reaction.s_react] - amount_reacting >= 0 )
react_pool[cur_reaction.s_react] -= amount_reacting
else
@@ -602,9 +654,18 @@
amount_reacting = react_pool[cur_reaction.s_react]
react_pool[cur_reaction.s_react] = 0
plasma_temperature -= max_num_reactants * cur_reaction.energy_consumption // Remove the consumed energy.
plasma_temperature += max_num_reactants * cur_reaction.energy_production // Add any produced energy.
radiation += max_num_reactants * cur_reaction.radiation // Add any produced radiation.
// Attempt to run temperature changes in isolation to prevent weird drops
var/plasma_temperature_change
var/current_reaction_energy_production = cur_reaction.energy_production
if(cur_reaction.maximum_effective_temperature && plasma_temperature > cur_reaction.maximum_effective_temperature)
current_reaction_energy_production = current_reaction_energy_production * 0.4
plasma_temperature_change -= max_num_reactants * cur_reaction.energy_consumption
plasma_temperature_change += max_num_reactants * current_reaction_energy_production
adjust_temperature(plasma_temperature_change, cur_reaction.max_temp_change_rate, cur_reaction.name)
// Add any produced radiation.
radiation += max_num_reactants * cur_reaction.radiation
tick_instability += max_num_reactants * cur_reaction.instability
last_reactants += amount_reacting
@@ -658,6 +719,118 @@
/obj/effect/fusion_em_field/add_point_filter()
return
/**
* Handles clamping temperature changes to the core field to prevent excessive second-by-second shifts.
* Lives in its own function to ensure consistent behavior across all potential core field temperature interactions.
*
* * var/temperature : The amount temperature is being changed by. Can be positive or negative.
* * var/max_percentage : Limiting value on how much temp can change per tick. Only overriden by specific fusion reactions.
* * var/cause : For debugging only.
*/
/obj/effect/fusion_em_field/proc/adjust_temperature(var/temp_change, var/max_percentage = FUSION_TICK_MAX_TEMP_CHANGE, var/cause)
var/adjusted_plasma_temperature_change = min(temp_change, temp_change * FUSION_TICK_MAX_TEMP_CHANGE)
plasma_temperature += adjusted_plasma_temperature_change
/**
* Accurate(ish***) black-body radiation colors. Fuck you purple light; save it for a phoronics update!
*/
/obj/effect/fusion_em_field/proc/update_light_colors()
var/use_range
var/use_power = 0
var/temp_mod = ((plasma_temperature-5000)/28000)
// Using real values for black-body radiation means the fusion reactor will almost always zip to top temp color.
// This multiplier scales the below temperatures to better match intended range of temps in gameplay.
var/effective_plasma_temperature = plasma_temperature / FUSION_BLACKBODY_MULTIPLIER
use_range = light_min_range + Ceil((light_max_range-light_min_range)*temp_mod)
use_power = light_min_power + Ceil((light_max_power-light_min_power)*temp_mod)
switch (effective_plasma_temperature)
if (-INFINITY to 1000)
light_color = "#ff5800"
alpha = 30
if (1000 to 1400)
light_color = "#ff6500"
alpha = 40
if (1400 to 1800)
light_color = "#ff7e00"
alpha = 50
if (1800 to 2200)
light_color = "#ff932c"
alpha = 60
if (2200 to 2600)
light_color = "#ffa54f"
alpha = 70
if (2600 to 3000)
light_color = "#ffb46b"
alpha = 80
if (3000 to 4000)
light_color = "#ffd1a3"
alpha = 90
if (4000 to 5400)
light_color = "#ffebdc"
alpha = 100
if (5400 to 6200)
light_color = "#fff5f5"
alpha = 110
if (6200 to 7000)
light_color = "#f5f3ff"
alpha = 120
if (7000 to 8000)
light_color = "#e3e9ff"
alpha = 130
if (8000 to 9000)
light_color = "#d6e1ff"
alpha = 140
if (9000 to 10000)
light_color = "#ccdbff"
alpha = 150
if (1000 to 11000)
light_color = "#c4d7ff"
alpha = 160
if (11000 to 12000)
light_color = "#bfd3ff"
alpha = 170
if (12000 to 13000)
light_color = "#bad0ff"
alpha = 180
if (13000 to 14000)
light_color = "#b6ceff"
alpha = 190
if (14000 to 15000)
light_color = "#b3ccff"
alpha = 200
if (15000 to 16000)
light_color = "#b0caff"
alpha = 210
if (16000 to 17000)
light_color = "#aec8ff"
alpha = 220
if (17000 to 18000)
light_color = "#acc7ff"
alpha = 230
if (18000 to 20000)
light_color = "#a8c5ff"
alpha = 230
if (20000 to 23000)
light_color = "#94c2ff"
alpha = 230
if (23000 to INFINITY)
light_color = "#74a2ff"
alpha = 240
if (last_range != use_range || last_power != use_power || color != light_color)
set_light(use_range / 6, use_power ? 6 : 0, light_color)
last_range = use_range
last_power = use_power
//Temperature based color
particles.gradient = list(0, COLOR_WHITE, 0.85, light_color)
UNLINT(var/dm_filter/outline = filters[2])
UNLINT(outline.color = light_color)
UNLINT(var/dm_filter/bloom = filters[3])
UNLINT(bloom.alpha = alpha)
/particles/fusion
width = 500
height = 500
@@ -673,7 +846,10 @@
color = 0
drift = generator("circle", 0.2, NORMAL_RAND)
#undef FUSION_ENERGY_PER_K
#undef FUSION_INSTABILITY_DIVISOR
#undef FUSION_RUPTURE_THRESHOLD
#undef FUSION_REACTANT_CAP
#undef FUSION_WARNING_DELAY
#undef FUSION_BLACKBODY_MULTIPLIER
#undef FUSION_INTEGRITY_RATE_LIMIT
#undef FUSION_TICK_MAX_TEMP_CHANGE
@@ -77,3 +77,9 @@
/obj/item/fuel_assembly/hydrogen/New(newloc)
..(newloc, MATERIAL_HYDROGEN_METALLIC)
/obj/item/fuel_assembly/iron/New(newloc)
..(newloc, MATERIAL_IRON)
/obj/item/fuel_assembly/boron/New(newloc)
..(newloc, MATERIAL_BORON)
+250 -92
View File
@@ -1,16 +1,30 @@
GLOBAL_LIST(fusion_reactions)
/singleton/fusion_reaction
var/p_react = "" // Primary reactant.
var/s_react = "" // Secondary reactant.
var/minimum_energy_level = 1
/// Used in Fusion Codex. The name will default to "[p_react]-[s_react]" unless set.
var/name
/// Primary reactant.
var/p_react = ""
/// Minimum # of p required to react.
var/minimum_p_react = 0
/// Secondary reactant.
var/s_react = ""
/// This is the minimum energy to initiate a given reaction.
var/minimum_energy_level = 250000
/// This is the minimum energy to continue a given reaction, once started. Used for halting energy-consuming reactions.
/// When not set, it will default to (minimum_energy_level * 0.8) to avoid tanking plasma temp in a single tick.
var/minimum_reaction_temperature
/// Gameplay balancing. If set, this reaction will produce less power when above the given temperature level.
var/maximum_effective_temperature
var/energy_consumption = 0
var/energy_production = 0
var/radiation = 0
var/instability = 0
var/list/products = list()
var/minimum_reaction_temperature = 100
/// In what order will reactions be carried out? Default 100, less important reactions have priority reduced.
var/priority = 100
/// Cap on what % of current plasma temperature the reaction is allowed to increase or reduce in a single tick. Higher values for more volatile reactions.
var/max_temp_change_rate = 0.2
/singleton/fusion_reaction/proc/handle_reaction_special(obj/effect/fusion_em_field/holder)
return 0
@@ -32,100 +46,267 @@ GLOBAL_LIST(fusion_reactions)
if(secondary_reactions.Find(s_react))
return GLOB.fusion_reactions[p_react][s_react]
// Material fuels
// deuterium
// tritium
// phoron
// supermatter
// Gaseous/reagent fuels
// hydrogen
// helium
// lithium
// boron
// Basic power production reactions.
// This is not necessarily realistic, but it makes a basic failure more spectacular.
/singleton/fusion_reaction/hydrogen_hydrogen
p_react = GAS_HYDROGEN
s_react = GAS_HYDROGEN
energy_consumption = 1
energy_production = 2
products = list(GAS_HELIUM = 1)
priority = 10
/singleton/fusion_reaction/deuterium_deuterium
p_react = GAS_DEUTERIUM
s_react = GAS_DEUTERIUM
energy_consumption = 1
energy_production = 2
priority = 0
// Advanced production reactions (todo)
/singleton/fusion_reaction/deuterium_helium
p_react = GAS_DEUTERIUM
s_react = GAS_HELIUM
energy_consumption = 1
energy_production = 5
radiation = 2
/singleton/fusion_reaction/deuterium_tritium
p_react = GAS_DEUTERIUM
s_react = GAS_TRITIUM
energy_consumption = 1
energy_production = 1
energy_production = 32
products = list(GAS_HELIUM = 1)
instability = 0.5
radiation = 3
instability = 3.5
// deut-trit bad!!!
radiation = 280
minimum_energy_level = 500
maximum_effective_temperature = 350000
priority = 1
/singleton/fusion_reaction/deuterium_lithium
/singleton/fusion_reaction/deuterium_deuterium
name = "Deuterium Burning"
p_react = GAS_DEUTERIUM
s_react = "lithium"
energy_consumption = 2
energy_production = 0
radiation = 3
products = list(GAS_TRITIUM= 1)
instability = 1
s_react = GAS_DEUTERIUM
energy_consumption = 6
energy_production = 24
products = list(GAS_HELIUM = 1)
radiation = 8
minimum_energy_level = 200000
priority = 0
// Unideal/material production reactions
/singleton/fusion_reaction/hydrogen_hydrogen
name = "Hydrogen Burning"
p_react = GAS_HYDROGEN
s_react = GAS_HYDROGEN
energy_consumption = 4
energy_production = 19
products = list(GAS_DEUTERIUM = 1)
radiation = 3
minimum_energy_level = 400000
priority = 1
/singleton/fusion_reaction/hydrogen_deuterium
p_react = GAS_HYDROGEN
s_react = GAS_DEUTERIUM
energy_consumption = 7
energy_production = 12
products = list(GAS_HELIUMFUEL = 1)
radiation = 16
minimum_energy_level = 300000
priority = 2
/singleton/fusion_reaction/hydrogen_helium3
p_react = GAS_HYDROGEN
s_react = GAS_HELIUMFUEL
energy_consumption = 2
energy_production = 12
products = list(GAS_HYDROGEN = 2)
radiation = 32
instability = 1.8
minimum_energy_level = 450000
priority = 10
/singleton/fusion_reaction/hydrogen_helium
p_react = GAS_HYDROGEN
s_react = GAS_HELIUM
energy_consumption = 6
energy_production = 18
products = list(GAS_DEUTERIUM = 1)
radiation = 48
instability = 2.8
minimum_energy_level = 200000
priority = 9
/singleton/fusion_reaction/helium_helium
name = "Helium Burning"
p_react = GAS_HELIUM
s_react = GAS_HELIUM
energy_consumption = 3
energy_production = 20
products = list(GAS_DEUTERIUM = 2, GAS_HYDROGEN = 1)
radiation = 6
instability = 0.75
minimum_energy_level = 800000
priority = 12
/singleton/fusion_reaction/helium3_helium
p_react = GAS_HELIUM
s_react = GAS_HELIUMFUEL
energy_consumption = 3
energy_production = 18
products = list("beryllium-7" = 1)
radiation = 6
minimum_energy_level = 850000
priority = 13
/singleton/fusion_reaction/beryllium7_decay
name = "Beryllium-7 Decay"
p_react = "beryllium-7"
s_react = "beryllium-7"
energy_consumption = 2
energy_production = 7
products = list("lithium" = 2)
radiation = 54
instability = 2.25
minimum_energy_level = 1000
priority = 15
/singleton/fusion_reaction/lithium_hydrogen
p_react = "lithium"
s_react = GAS_HYDROGEN
energy_consumption = 3
energy_production = 5
products = list(GAS_DEUTERIUM = 1, GAS_HELIUM = 1)
radiation = 22
instability = 0.35
minimum_energy_level = 1480000
priority = 16
/singleton/fusion_reaction/lithium_deuterium
p_react = "lithium"
minimum_p_react = 2000
s_react = GAS_DEUTERIUM
energy_consumption = 1
energy_production = 40
radiation = 18
instability = 2.8
products = list(GAS_TRITIUM = 2)
minimum_energy_level = 2000000
priority = 30
/singleton/fusion_reaction/beryllium7_hydrogen
p_react = GAS_HYDROGEN
s_react = "beryllium-7"
energy_consumption = 4
energy_production = 12
products = list("boron-8" = 1)
radiation = 3
instability = 0.5
minimum_energy_level = 1720000
priority = 17
/singleton/fusion_reaction/boron8_decay
name = "Boron-8 Decay"
p_react = "boron-8"
s_react = "boron-8"
energy_consumption = 4
energy_production = 6
products = list("beryllium-8" = 2)
radiation = 64
instability = 2.5
minimum_energy_level = 1000
priority = 18
/singleton/fusion_reaction/beryllium8_fission
name = "Beryllium-8 Fission Branch A"
p_react = "beryllium-8"
s_react = "beryllium-8"
energy_consumption = 14
energy_production = 10
products = list(GAS_HELIUM = 2)
radiation = 36
instability = 5
minimum_energy_level = 25000
priority = 19
// This is just to delete lone beryllium-8 reactants that might get left over somehow
/singleton/fusion_reaction/beryllium8_hydrogen
name = "Beryllium-8 Fission Branch B"
p_react = "beryllium-8"
s_react = GAS_HYDROGEN
energy_consumption = 18
energy_production = 10
products = list(GAS_HELIUM = 2)
radiation = 72
minimum_energy_level = 400000
priority = 18
/singleton/fusion_reaction/helium3_helium3
name = "Helium-3 Burning"
p_react = GAS_HELIUMFUEL
s_react = GAS_HELIUMFUEL
energy_consumption = 2
energy_production = 96
products = list(GAS_HELIUM = 1, GAS_HYDROGEN = 2)
radiation = 1
minimum_energy_level = 3200000
priority = 30
// bad!!!
/singleton/fusion_reaction/oxygen_oxygen
name = "Oxygen Burning"
p_react = GAS_OXYGEN
s_react = GAS_OXYGEN
energy_consumption = 10
energy_production = 0
instability = 5
radiation = 5
instability = 8
radiation = 250
products = list("silicon"= 1)
/singleton/fusion_reaction/iron_iron
// Iron poisoning
/singleton/fusion_reaction/iron_poison
name = "Iron Poisoning"
p_react = "iron"
s_react = "iron"
products = list("silver" = 10, "gold" = 10, "platinum" = 10) // Not realistic but w/e
energy_consumption = 10
energy_consumption = 32
energy_production = 0
radiation = 48
minimum_energy_level = 1000
priority = 35
// Gold poisoning
/singleton/fusion_reaction/gold_poison
name = "Gold Poisoning"
p_react = "gold"
s_react = "gold"
energy_consumption = 48
energy_production = 0
instability = 2
minimum_reaction_temperature = 10000
minimum_energy_level = 1000
radiation = 96
/singleton/fusion_reaction/phoron_hydrogen
p_react = GAS_HYDROGEN
s_react = GAS_PHORON
energy_consumption = 10
/singleton/fusion_reaction/iron_iron
name = "Iron Burning"
p_react = "iron"
s_react = "iron"
// Much of the gold is going to be consumed to poison the reactor.
products = list("silver" = 12, "gold" = 32, "platinum" = 12, "lead" = 12, ) // Not realistic but w/e
energy_consumption = 32
energy_production = 0
instability = 5
products = list("mhydrogen" = 1)
minimum_reaction_temperature = 8000
instability = 0.5
radiation = 49
minimum_energy_level = 2800000
priority = 40
/singleton/fusion_reaction/mhydrogen
name = "Metallic Hydrogen Stabilization"
p_react = "metallic hydrogen"
s_react = "metallic hydrogen"
energy_consumption = 0
energy_production = 50
instability = -8
radiation = 10
products = list("deuterium" = 1, "metallic hydrogen" = 1)
minimum_energy_level = 800000
/singleton/fusion_reaction/iron_phoron
name = "Phoron Shot"
p_react = "iron"
s_react = GAS_PHORON
energy_consumption = 64
energy_production = 18
radiation = 64
instability = 6.5
products = list("uranium" = 30, "borosilicate glass" = 80, "osmium" = 20) // Psuedoscience but here we are
minimum_energy_level = 4000000
priority = 40
// VERY UNIDEAL REACTIONS.
/singleton/fusion_reaction/phoron_supermatter
p_react = "supermatter"
s_react = GAS_PHORON
energy_consumption = 0
energy_production = 5
radiation = 40
instability = 20
energy_production = 800
radiation = 4000
instability = 30
/singleton/fusion_reaction/phoron_supermatter/handle_reaction_special(obj/effect/fusion_em_field/holder)
wormhole_event(GetConnectedZlevels(holder))
var/turf/origin = get_turf(holder)
@@ -153,26 +334,3 @@ GLOBAL_LIST(fusion_reactions)
explosion(origin, 8)
return 1
// High end reactions.
/singleton/fusion_reaction/boron_hydrogen
p_react = GAS_BORON
s_react = GAS_HYDROGEN
minimum_energy_level = 15000
energy_consumption = 3
energy_production = 12
radiation = 3
instability = 2.5
products = list(GAS_HELIUM = 1)
// Any now we go even further beyond!!!!
/singleton/fusion_reaction/iron_phoron
p_react = "iron"
s_react = GAS_PHORON
minimum_energy_level = 100000
energy_consumption = 10
energy_production = 4
radiation = 30
instability = 5
products = list("uranium" = 10, "lead" = 10, "borosilicate glass" = 10) // Psuedoscience but here we are
@@ -69,7 +69,7 @@
for(var/mat in stored)
var/material/material = SSmaterials.get_material_by_name(mat)
if(material)
var/sheets = FLOOR(stored[mat]/(SHEET_MATERIAL_AMOUNT * 1.5), 1)
var/sheets = FLOOR(stored[mat]/(SHEET_MATERIAL_AMOUNT * 2), 1)
data["materials"] += list(list("material" = mat, "rawamount" = stored[mat], "amount" = sheets, "harvest" = harvesting[mat]))
return data
@@ -87,7 +87,8 @@
harvesting -= mat
else
var/harvest = min(harvest_from.owned_field.reactants[mat], rand(100,200))
harvest_from.owned_field.reactants[mat] -= harvest
// Leave a few counts of the reactant to avoid deactivating harvest mode
harvest_from.owned_field.reactants[mat] -= (harvest - rand(0,5))
if(harvest_from.owned_field.reactants[mat] <= 0)
harvest_from.owned_field.reactants -= mat
stored[mat] += harvest
@@ -112,7 +113,7 @@
var/material/material = SSmaterials.get_material_by_name(mat)
if(material)
var/sheet_cost = (SHEET_MATERIAL_AMOUNT * 1.5)
var/sheets = FLOOR(stored[mat]/sheet_cost, 1)
var/sheets = min(FLOOR(stored[mat]/sheet_cost, 1), 50)
if(sheets > 0)
var/obj/item/stack/material/M = new material.stack_type(get_turf(src), sheets)
M.update_icon()
+3 -3
View File
@@ -7,9 +7,9 @@
obj_flags = OBJ_FLAG_ROTATABLE
use_power = POWER_USE_OFF
idle_power_usage = 100 //Watts, I hope. Just enough to do the computer and display things.
idle_power_usage = 1000
var/max_power = 500000
var/max_power = 2500000
var/thermal_efficiency = 0.65
var/obj/machinery/atmospherics/binary/circulator/circ1
@@ -146,7 +146,7 @@
//Exceeding maximum power leads to some power loss
if(effective_gen > max_power && prob(5))
spark_system.queue()
stored_energy *= 0.5
stored_energy *= 0.67
//Power
last_circ1_gen = circ1.return_stored_energy()
+4 -4
View File
@@ -120,7 +120,7 @@
temperature_gain and max_temperature are set so that the max safe power level is 4.
Setting to 5 or higher can only be done temporarily before the generator overheats.
*/
power_gen = 25000 //Watts output per power_output level
power_gen = 50000 //Watts output per power_output level
var/max_power_output = 5 //The maximum power setting without emagging.
var/max_safe_output = 4 // For UI use, maximal output that won't cause overheat.
var/time_per_sheet = 576 //fuel efficiency - how long 1 sheet lasts at power level 1
@@ -416,7 +416,7 @@
sheet_path = /obj/item/stack/material/uranium
board_path = "/obj/item/circuitboard/portgen/advanced"
power_gen = 50000 // 200 kW = safe max, 250 kW = unsafe max.
power_gen = 100000 // 400 kW = safe max, 500 kW = unsafe max.
max_temperature = 340
temperature_gain = 60
@@ -448,7 +448,7 @@
sheet_path = /obj/item/stack/material/tritium
board_path = "/obj/item/circuitboard/portgen/super"
power_gen = 80000 // 400 kW = safe max, 640 kW = unsafe max
power_gen = 160000 // 800 kW = safe max, 960 kW = unsafe max
max_power_output = 8
max_safe_output = 5
time_per_sheet = 576
@@ -462,7 +462,7 @@
/obj/machinery/power/portgen/basic/fusion
name = "minature fusion reactor"
desc = "The RT7-0, an industrial all-in-one nuclear fusion power plant created by Hephaestus. It uses tritium as a fuel source and relies on coolant to keep the reactor cool. Rated for 500 kW max safe output."
power_gen = 100000
power_gen = 200000
icon_state = "reactor"
base_icon = "reactor"
portgen_lightcolour = "#458943"
+21
View File
@@ -57,6 +57,27 @@
else
return 0
// Proc: power_wattage_readable()
// Parameters: 1 (amount - Power in Watts to be converted to W, kW or MW)
// Description: Helper proc that converts reading in Watts to kW or MW (returns string version of amount parameter)
/obj/machinery/proc/power_wattage_readable(var/amount = 0)
var/units = ""
// 10kW and less - Watts
if(amount < 10000)
units = "W"
// 10MW and less - KiloWatts
else if(amount < 10000000)
units = "kW"
amount = (round(amount/100) / 10)
// More than 10MW - MegaWatts
else
units = "MW"
amount = (round(amount/10000) / 100)
if (units == "W")
return "[amount] W"
else
return "[amount] [units]"
/obj/machinery/power/proc/disconnect_terminal() // machines without a terminal will just return, no harm no fowl.
return
+2 -2
View File
@@ -15,7 +15,7 @@
buckle_lying = FALSE
buckle_require_restraints = TRUE
var/power_gen = 1000 // Enough to power a single APC. 4000 output with T4 capacitor.
var/power_gen = 4000 // Enough to power a single APC. 16000 output with T4 capacitor.
var/irradiate = TRUE // RTGs irradiate surroundings, but only when panel is open.
@@ -70,7 +70,7 @@
/obj/machinery/power/rtg/advanced
desc = "An advanced RTG capable of moderating isotope decay, increasing power output but reducing lifetime. It uses phoron-fueled radiation collectors to increase output even further."
power_gen = 1250 // 2500 on T1, 10000 on T4.
power_gen = 5000 // 10000 on T1, 20000 on T4.
component_types = list(
/obj/item/stack/cable_coil{amount = 5},
+10 -31
View File
@@ -47,27 +47,6 @@
return 1
return 0
// Proc: reading_to_text()
// Parameters: 1 (amount - Power in Watts to be converted to W, kW or MW)
// Description: Helper proc that converts reading in Watts to kW or MW (returns string version of amount parameter)
/obj/machinery/power/sensor/proc/reading_to_text(var/amount = 0)
var/units = ""
// 10kW and less - Watts
if(amount < 10000)
units = "W"
// 10MW and less - KiloWatts
else if(amount < 10000000)
units = "kW"
amount = (round(amount/100) / 10)
// More than 10MW - MegaWatts
else
units = "MW"
amount = (round(amount/10000) / 100)
if (units == "W")
return "[amount] W"
else
return "~[amount] [units]" //kW and MW are only approximate readings, therefore add "~"
// Proc: find_apcs()
// Parameters: None
// Description: Searches powernet for APCs and returns them in a list.
@@ -119,13 +98,13 @@
out += "<td>NO CELL"
var/load = A.lastused_total // Load.
total_apc_load += load
load = reading_to_text(load)
load = power_wattage_readable(load)
out += "<td>[load]"
out += "<br><b>AREA_USAGE_TOTAL AVAILABLE: [reading_to_text(powernet.avail)]</b>"
out += "<br><b>APC LOAD: [reading_to_text(total_apc_load)]</b>"
out += "<br><b>OTHER LOAD: [reading_to_text(max(powernet.load - total_apc_load, 0))]</b>"
out += "<br><b>AREA_USAGE_TOTAL GRID LOAD: [reading_to_text(powernet.viewload)] ([round((powernet.load / powernet.avail) * 100)]%)</b>"
out += "<br><b>AREA_USAGE_TOTAL AVAILABLE: [power_wattage_readable(powernet.avail)]</b>"
out += "<br><b>APC LOAD: [power_wattage_readable(total_apc_load)]</b>"
out += "<br><b>OTHER LOAD: [power_wattage_readable(max(powernet.load - total_apc_load, 0))]</b>"
out += "<br><b>AREA_USAGE_TOTAL GRID LOAD: [power_wattage_readable(powernet.viewload)] ([round((powernet.load / powernet.avail) * 100)]%)</b>"
if(powernet.problem)
out += "<br><b>WARNING: Abnormal grid activity detected!</b>"
@@ -163,7 +142,7 @@
APC_entry["cell_charge"] = A.cell ? round(A.cell.percent()) : 0
APC_entry["cell_status"] = A.cell ? chg[A.charging+1] : 0
// Other info
APC_entry["total_load"] = reading_to_text(A.lastused_total)
APC_entry["total_load"] = power_wattage_readable(A.lastused_total)
var/area_display_name = get_area_display_name(A.area)
APC_entry["name"] = area_display_name
// Add data into main list of APC data.
@@ -171,10 +150,10 @@
// Add load of this APC to total APC load calculation
total_apc_load += A.lastused_total
data["apc_data"] = APC_data
data["total_avail"] = reading_to_text(max(powernet.avail, 0))
data["total_used_apc"] = reading_to_text(max(total_apc_load, 0))
data["total_used_other"] = reading_to_text(max(powernet.viewload - total_apc_load, 0))
data["total_used_all"] = reading_to_text(max(powernet.viewload, 0))
data["total_avail"] = power_wattage_readable(max(powernet.avail, 0))
data["total_used_apc"] = power_wattage_readable(max(total_apc_load, 0))
data["total_used_other"] = power_wattage_readable(max(powernet.viewload - total_apc_load, 0))
data["total_used_all"] = power_wattage_readable(max(powernet.viewload, 0))
// Prevents runtimes when avail is 0 (division by zero)
if(powernet.avail)
data["load_percentage"] = round((powernet.viewload / powernet.avail) * 100)
@@ -52,14 +52,14 @@
else if(istype(A, /obj/machinery/power/fusion_core))
var/obj/machinery/power/fusion_core/collided_core = A
if(particle_type && particle_type != "neutron")
if(collided_core.AddParticles(particle_type, 1 + additional_particles))
if(collided_core.AddParticles(particle_type, 12 + additional_particles))
collided_core.owned_field.plasma_temperature += mega_energy
collided_core.owned_field.energy += energy
qdel(src)
else if(istype(A, /obj/effect/fusion_particle_catcher))
var/obj/effect/fusion_particle_catcher/PC = A
if(particle_type && particle_type != "neutron")
if(PC.parent.owned_core.AddParticles(particle_type, 1 + additional_particles))
if(PC.parent.owned_core.AddParticles(particle_type, 12 + additional_particles))
PC.parent.plasma_temperature += mega_energy
PC.parent.energy += energy
qdel(src)
+49 -25
View File
@@ -1,5 +1,14 @@
// the SMES
// stores power
/**
* BUILDABLE SMES (Superconducting Magnetic Energy Storage) UNIT
* AKA PSUs- both 'SMES unit' and 'PSU' will be used interchangeably in documentation and in-game.
*
* PSUs are linked by the RCON System, allowing for remote management of all linked
* It also supports RCON System which allows you to operate it remotely, if properly set.
*
* This file contains the main code and definition information for SMES units.
* For instantiation of buildable SMES units (the ones you'll see 99% of the time),
* please refer to 'code/modules/power/smes_construction.dm'
*/
#define SMESRATE 0.05
#define SMESMAXCHARGELEVEL 250000
@@ -28,30 +37,41 @@
clicksound = /singleton/sound_category/switch_sound
var/health = 500
var/busted = FALSE // this it to prevent the damage text from playing repeatedly
var/capacity = 5e6 // maximum charge
var/charge = 1e6 // actual charge
/// this it to prevent the damage text from playing repeatedly
var/busted = FALSE
/// maximum charge
var/capacity = 5e6
var/charge = 1e6 /// actual charge
var/max_coils = 0
var/input_attempt = 0 // 1 = attempting to charge, 0 = not attempting to charge
var/inputting = 0 // 1 = actually inputting, 0 = not inputting
var/input_level = 50000 // amount of power the SMES attempts to charge by
var/input_level_max = 200000 // cap on input_level
var/input_taken = 0 // amount that we received from powernet last tick
/// 1 = attempting to charge, 0 = not attempting to charge
var/input_attempt = 0
/// 1 = actually inputting, 0 = not inputting
var/inputting = 0
/// amount of power the SMES attempts to charge by
var/input_level = 250000
/// cap on input_level
var/input_level_max = 2000000
/// amount that we received from powernet last tick
var/input_taken = 0
var/output_attempt = 0 // 1 = attempting to output, 0 = not attempting to output
var/outputting = 0 // 1 = actually outputting, 0 = not outputting
var/output_level = 50000 // amount of power the SMES attempts to output
var/output_level_max = 200000 // cap on output_level
var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power
/// 1 = attempting to output, 0 = not attempting to output
var/output_attempt = 0
/// 1 = actually outputting, 0 = not outputting
var/outputting = 0
/// amount of power the SMES attempts to output
var/output_level = 250000
/// cap on output_level
var/output_level_max = 2000000
/// amount of power actually outputted. may be less than output_level if the powernet returns excess power
var/output_used = 0
//Holders for powerout event.
//var/last_output_attempt = 0
//var/last_input_attempt = 0
//var/last_charge = 0
///Holders for powerout event.
///var/last_output_attempt = 0
///var/last_input_attempt = 0
///var/last_charge = 0
//For icon overlay updates
///For icon overlay updates
var/last_disp
var/last_chrg
var/last_onln
@@ -60,14 +80,18 @@
var/input_pulsed = 0
var/output_cut = 0
var/output_pulsed = 0
var/is_critical = FALSE // Use by gridcheck event, if set to true we do not disable it
var/failure_timer = 0 // Set by gridcheck event, temporarily disables the SMES.
/// Use by gridcheck event, if set to true we do not disable it
var/is_critical = FALSE
/// Set by gridcheck event, temporarily disables the SMES.
var/failure_timer = 0
var/target_load = 0
var/open_hatch = 0
var/name_tag = null
var/building_terminal = 0 //Suggestions about how to avoid clickspam building several terminals accepted!
///Suggestions about how to avoid clickspam building several terminals accepted!
var/building_terminal = 0
var/obj/machinery/power/terminal/terminal = null
var/should_be_mapped = 0 // If this is set to 0 it will send out warning on New()
/// If this is set to 0 it will send out warning on New()
var/should_be_mapped = 0
var/datum/effect_system/sparks/big_spark
var/datum/effect_system/sparks/small_spark
+71 -48
View File
@@ -1,22 +1,26 @@
// BUILDABLE SMES(Superconducting Magnetic Energy Storage) UNIT
//
// Last Change 1.1.2015 by Atlantis - Happy New Year!
//
// This is subtype of SMES that should be normally used. It can be constructed, deconstructed and hacked.
// It also supports RCON System which allows you to operate it remotely, if properly set.
/**
* BUILDABLE SMES (Superconducting Magnetic Energy Storage) UNIT
* AKA PSUs- both 'SMES unit' and 'PSU' will be used interchangeably in documentation and in-game.
*
* PSUs are linked by the RCON System, allowing for remote management of all linked
* It also supports RCON System which allows you to operate it remotely, if properly set.
*
* This is subtype of SMES that should be normally used. It can be constructed, deconstructed and hacked.
*/
//MAGNETIC COILS - These things actually store and transmit power within the SMES. Different types have different
/// MAGNETIC COILS - These things actually store and transmit power within the SMES. Different types have different properties
/obj/item/smes_coil
name = "superconductive magnetic coil"
desc = "Standard superconductive magnetic coil with balanced capacity and I/O rating."
icon = 'icons/obj/stock_parts.dmi'
icon_state = "smes_coil"
w_class = WEIGHT_CLASS_BULKY // It's LARGE (backpack size)
var/ChargeCapacity = 5000000
var/IOCapacity = 250000
/// It's LARGE (backpack sized)
w_class = WEIGHT_CLASS_BULKY
var/ChargeCapacity = 2500000
var/IOCapacity = 500000
/obj/item/smes_coil/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
. = ..()
/obj/item/smes_coil/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
if(is_adjacent)
. += "The label reads: Only certified professionals are allowed to handle and install this component."
. += "Charge capacity: [ChargeCapacity/1000000] MJ."
@@ -27,25 +31,24 @@
name = "basic superconductive magnetic coil"
desc = "Cheaper model of the standard superconductive magnetic coil. Its capacity and I/O rating are considerably lower."
icon_state = "smes_coil_weak"
ChargeCapacity = 1000000
IOCapacity = 150000
ChargeCapacity = 500000
IOCapacity = 300000
// 1000% Charge Capacity, 20% I/O Capacity
/obj/item/smes_coil/super_capacity
name = "superconductive capacitance coil"
desc = "Specialised version of the standard superconductive magnetic coil. It has significantly stronger containment field, allowing for immense power storage. However its I/O rating is much lower."
icon_state = "smes_coil_capacitance"
ChargeCapacity = 50000000
IOCapacity = 50000
ChargeCapacity = 25000000
IOCapacity = 100000
// 10% Charge Capacity, 400% I/O Capacity. Technically turns SMES into large super capacitor.Ideal for shields.
/obj/item/smes_coil/super_io
name = "superconductive transmission coil"
desc = "Specialised version of the standard superconductive magnetic coil. While it's almost useless for power storage it can rapidly transfer power, making it useful in systems that require large throughput."
icon_state = "smes_coil_transmission"
ChargeCapacity = 500000
IOCapacity = 1000000
ChargeCapacity = 250000
IOCapacity = 2000000
// SMES SUBTYPES - THESE ARE MAPPED IN AND CONTAIN DIFFERENT TYPES OF COILS
@@ -66,63 +69,85 @@
/obj/machinery/power/smes/buildable/main_engine
cur_coils = 4
input_attempt = TRUE
input_level = 500000
input_level = 1000000
output_attempt = TRUE
output_level = 500000
output_level = 1000000
charge =1.5e+7
// For the substation SMES around the Horizon.
/// For the substation SMES around the Horizon.
/obj/machinery/power/smes/buildable/substation
input_level = 150000
output_level = 140000
input_level = 250000
output_level = 240000
// Telecomms substation. Based on shuttle settings; those boxes are power-hungry.
/obj/machinery/power/smes/buildable/telecomms/Initialize()
. = ..()
component_parts += new /obj/item/smes_coil/super_io(src)
input_attempt = TRUE
output_attempt = TRUE
input_level = 2000000
output_level = 2000000
charge = 5.55e+007
// The Horizon's shuttles want something with decent capacity to sustain themselves and enough transmission to meet their energy needs.
/obj/machinery/power/smes/buildable/horizon_shuttle/Initialize()
. = ..()
component_parts += new /obj/item/smes_coil/super_io(src)
component_parts += new /obj/item/smes_coil/super_capacity(src)
input_attempt = TRUE
output_attempt = TRUE
input_level = 1300000
output_level = 1300000
charge = 5.55e+007
/obj/machinery/power/smes/buildable/third_party_shuttle/Initialize() //Identical to the horizon_shuttle for now as we try to work out specifics
. = ..()
component_parts += new /obj/item/smes_coil/super_io(src)
component_parts += new /obj/item/smes_coil/super_capacity(src)
input_attempt = TRUE
output_attempt = TRUE
input_level = 1300000
output_level = 1300000
input_level = 2700000
output_level = 2700000
charge = 5.55e+007
//Identical to the horizon_shuttle for now as we try to work out specifics
/obj/machinery/power/smes/buildable/third_party_shuttle/Initialize()
. = ..()
component_parts += new /obj/item/smes_coil/super_io(src)
component_parts += new /obj/item/smes_coil/super_capacity(src)
component_parts += new /obj/item/smes_coil/super_capacity(src)
input_attempt = TRUE
output_attempt = TRUE
input_level = 2700000
output_level = 2700000
charge = 5.55e+007
/obj/machinery/power/smes/buildable/third_party_shuttle/empty/Initialize()
. = ..()
charge = 0
/obj/machinery/power/smes/buildable/autosolars/Initialize() //for third parties that have their solars autostart, It's slightly upgraded for them
//for third parties that have their solars autostart, It's slightly upgraded for them
/obj/machinery/power/smes/buildable/autosolars/Initialize()
. = ..()
component_parts += new /obj/item/smes_coil/super_capacity(src)
component_parts += new /obj/item/smes_coil/super_io(src)
component_parts += new /obj/item/smes_coil/super_capacity(src)
component_parts += new /obj/item/smes_coil/super_capacity(src)
input_attempt = TRUE
output_attempt = TRUE
input_level = 1000000
output_level = 1000000
input_level = 2700000
output_level = 2700000
charge = 3.02024e+006
// END SMES SUBTYPES
// SMES itself
/obj/machinery/power/smes/buildable
max_coils = 6 // 30M capacity, 1.5MW input/output when fully upgraded /w default coils
var/cur_coils = 1 // Current amount of installed coils
var/safeties_enabled = 1 // If 0 modifications can be done without discharging the SMES, at risk of critical failure.
var/failing = 0 // If 1 critical failure has occured and SMES explosion is imminent.
/// 20 MJ capacity, 8 MW input/output when fully upgraded /w default coils
max_coils = 8
/// Current amount of installed coils
var/cur_coils = 1
/// If 0 modifications can be done without discharging the SMES, at risk of critical failure.
var/safeties_enabled = 1
/// If 1 critical failure has occured and SMES explosion is imminent.
var/failing = 0
var/datum/wires/smes/wires
var/grounding = 1 // Cut to quickly discharge, at cost of "minor" electrical issues in output powernet.
var/RCon = 1 // Cut to disable AI and remote control.
var/RCon_tag = "NO_TAG" // RCON tag, change to show it on SMES Remote control console.
/// Cut to quickly discharge, at cost of "minor" electrical issues in output powernet.
var/grounding = 1
/// Cut to disable AI and remote control.
var/RCon = 1
/// RCON tag, change to show it on SMES Remote control console.
var/RCon_tag = "NO_TAG"
charge = 0
should_be_mapped = 1
component_types = list(
@@ -348,8 +373,6 @@
// Not sure if this is necessary, but just in case the SMES *somehow* survived..
qdel(src)
// Proc: apcs_overload()
// Parameters: 3 (failure_chance - chance to actually break the APC, overload_chance - Chance of breaking lights, reboot_chance - Chance of temporarily disabling the APC)
// Description: Damages output powernet by power surge. Destroys few APCs and lights, depending on parameters.
@@ -78,6 +78,23 @@
to_chat(M, SPAN_WARNING(pick("Your skin burns!", "The chemical is melting your skin!", "Wash it off, wash it off!")))
remove_self(REAGENT_VOLUME(holder, type), holder)
/singleton/reagent/boron
name = "Boron"
description = "A dark, silvery metalloid with a small handful of industrial applications."
reagent_state = SOLID
color = "#888888"
taste_description = "metal"
taste_mult = 1.1
fallback_specific_heat = 0.811
value = 0.02
/**
* Boron has very little affect on mammals, but is moderately toxic to arthopods and commonly used in insecticides. Hi Vaurca.
*/
/singleton/reagent/boron/affect_blood(var/mob/living/carbon/M, var/alien, var/removed, var/datum/reagents/holder)
if (alien == IS_VAURCA)
M.adjustToxLoss(2 * removed)
/singleton/reagent/carbon
name = "Carbon"
description = "A chemical element, the building block of life."
+1 -1
View File
@@ -138,4 +138,4 @@
..()
/obj/machinery/shield_capacitor/multiz
max_charge_rate = 1250000 //1250 kW
max_charge_rate = 1250000
+9 -8
View File
@@ -1,26 +1,27 @@
var/global/datum/xgm_gas_data/gas_data
/datum/xgm_gas_data
//Simple list of all the gas IDs.
/// Simple list of all the gas IDs.
var/list/gases = list()
//The friendly, human-readable name for the gas.
/// The friendly, human-readable name for the gas.
var/list/name = list()
//Specific heat of the gas. Used for calculating heat capacity.
/// Specific heat of the gas. Used for calculating heat capacity.
var/list/specific_heat = list()
//Molar mass of the gas. Used for calculating specific entropy.
/// Molar mass of the gas. Used for calculating specific entropy.
var/list/molar_mass = list()
//Tile overlays. /obj/gas_overlay, created from references to 'icons/effects/tile_effects.dmi'
/// Tile overlays. /obj/gas_overlay, created from references to 'icons/effects/tile_effects.dmi'
var/list/tile_overlay = list()
//Optional color for tile overlay
/// Optional color for tile overlay
var/list/tile_overlay_color = list()
//Overlay limits. There must be at least this many moles for the overlay to appear.
/// Overlay limits. There must be at least this many moles for the overlay to appear.
var/list/overlay_limit = list()
//Flags.
/// Flags.
var/list/flags = list()
/singleton/xgm_gas
var/id = ""
var/name = "Unnamed Gas"
var/desc
var/specific_heat = 20 // J/(mol*K)
var/molar_mass = 0.032 // kg/mol