From 7567726c07f89753683dd7c85ab2ecaeb3c71571 Mon Sep 17 00:00:00 2001 From: silveryferret Date: Wed, 19 Jul 2017 01:39:12 -0400 Subject: [PATCH 1/7] Ports R-UST from Baystation12 + small admin debug verb --- code/__defines/atmos.dm | 6 +- code/__defines/misc.dm | 3 + code/defines/gases.dm | 18 + code/game/machinery/atmoalter/canister.dm | 20 + code/game/objects/items/weapons/manuals.dm | 42 ++ code/modules/materials/material_sheets.dm | 11 + code/modules/materials/materials.dm | 29 + code/modules/power/fusion/_setup.dm | 83 +++ code/modules/power/fusion/core/_core.dm | 131 +++++ .../modules/power/fusion/core/core_control.dm | 176 +++++++ code/modules/power/fusion/core/core_field.dm | 494 ++++++++++++++++++ .../fusion/fuel_assembly/fuel_assembly.dm | 67 +++ .../fusion/fuel_assembly/fuel_compressor.dm | 54 ++ .../fusion/fuel_assembly/fuel_control.dm | 102 ++++ .../fusion/fuel_assembly/fuel_injector.dm | 153 ++++++ code/modules/power/fusion/fusion_circuits.dm | 89 ++++ .../power/fusion/fusion_particle_catcher.dm | 41 ++ code/modules/power/fusion/fusion_reactions.dm | 164 ++++++ .../modules/power/fusion/gyrotron/gyrotron.dm | 57 ++ .../power/fusion/gyrotron/gyrotron_control.dm | 97 ++++ code/modules/power/singularity/emitter.dm | 21 +- .../particle_accelerator/particle.dm | 15 + .../Chemistry-Reagents-Toxins.dm | 25 + code/modules/reagents/Chemistry-Recipes.dm | 21 +- code/modules/supermatter/setup_supermatter.dm | 251 +++++++++ icons/obj/machines/power/fusion.dmi | Bin 0 -> 41882 bytes polaris.dme | 14 + 27 files changed, 2172 insertions(+), 12 deletions(-) create mode 100644 code/modules/power/fusion/_setup.dm create mode 100644 code/modules/power/fusion/core/_core.dm create mode 100644 code/modules/power/fusion/core/core_control.dm create mode 100644 code/modules/power/fusion/core/core_field.dm create mode 100644 code/modules/power/fusion/fuel_assembly/fuel_assembly.dm create mode 100644 code/modules/power/fusion/fuel_assembly/fuel_compressor.dm create mode 100644 code/modules/power/fusion/fuel_assembly/fuel_control.dm create mode 100644 code/modules/power/fusion/fuel_assembly/fuel_injector.dm create mode 100644 code/modules/power/fusion/fusion_circuits.dm create mode 100644 code/modules/power/fusion/fusion_particle_catcher.dm create mode 100644 code/modules/power/fusion/fusion_reactions.dm create mode 100644 code/modules/power/fusion/gyrotron/gyrotron.dm create mode 100644 code/modules/power/fusion/gyrotron/gyrotron_control.dm create mode 100644 code/modules/supermatter/setup_supermatter.dm create mode 100644 icons/obj/machines/power/fusion.dmi diff --git a/code/__defines/atmos.dm b/code/__defines/atmos.dm index f67c967157..6b4299247d 100644 --- a/code/__defines/atmos.dm +++ b/code/__defines/atmos.dm @@ -74,6 +74,7 @@ #define XGM_GAS_FUEL 1 #define XGM_GAS_OXIDIZER 2 #define XGM_GAS_CONTAMINANT 4 +#define XGM_GAS_FUSION_FUEL 8 #define TANK_LEAK_PRESSURE (30.*ONE_ATMOSPHERE) // Tank starts leaking. #define TANK_RUPTURE_PRESSURE (40.*ONE_ATMOSPHERE) // Tank spills all contents into atmosphere. @@ -93,4 +94,7 @@ #define ATMOSTANK_OXYGEN 40000 // O2 is also important for airmix, but not as much as N2 as it's only 21% of it. #define ATMOSTANK_CO2 25000 // CO2 and PH are not critically important for station, only for toxins and alternative coolants, no need to store a lot of those. #define ATMOSTANK_PHORON 25000 -#define ATMOSTANK_NITROUSOXIDE 10000 // N2O doesn't have a real useful use, i guess it's on station just to allow refilling of sec's riot control canisters? \ No newline at end of file +#define ATMOSTANK_NITROUSOXIDE 10000 // N2O doesn't have a real useful use, i guess it's on station just to allow refilling of sec's riot control canisters? + +//R-UST port +#define GAS_FUEL "fuel" \ No newline at end of file diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 12deebbfc1..f3b448ee49 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -198,3 +198,6 @@ #define TSC_GIL "Gilthari" #define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day + +#define WORLD_ICON_SIZE 32 //Needed for the R-UST port +#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 //Needed for the R-UST port diff --git a/code/defines/gases.dm b/code/defines/gases.dm index 65e45c31ef..b1c4cced57 100644 --- a/code/defines/gases.dm +++ b/code/defines/gases.dm @@ -18,6 +18,24 @@ specific_heat = 30 // J/(mol*K) molar_mass = 0.044 // kg/mol +//R-UST port +/decl/xgm_gas/fuel + id = GAS_FUEL + name = "Fuel" + + //Note that this has a significant impact on TTV yield. + //Because it is so high, any leftover phoron soaks up a lot of heat and drops the yield pressure. + specific_heat = 200 // J/(mol*K) + + //Hypothetical group 14 (same as carbon), period 8 element. + //Using multiplicity rule, it's atomic number is 162 + //and following a N/Z ratio of 1.5, the molar mass of a monatomic gas is: + molar_mass = 0.405 // kg/mol + + tile_overlay = "gas_dense" + overlay_limit = 0.7 + flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT | XGM_GAS_FUSION_FUEL + /decl/xgm_gas/phoron id = "phoron" name = "Phoron" diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index dcce5fb938..d15c86048e 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -428,3 +428,23 @@ update_flag src.update_icon() return 1 + +//R-UST port +// Special types used for engine setup admin verb, they contain double amount of that of normal canister. +/obj/machinery/portable_atmospherics/canister/nitrogen/engine_setup/New() + ..() + src.air_contents.adjust_gas("nitrogen", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/New() + ..() + src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/phoron/engine_setup/New() + ..() + src.air_contents.adjust_gas(GAS_FUEL, MolesForPressure()) + src.update_icon() + return 1 \ No newline at end of file diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm index 6ea812ad3a..eb38fa5aba 100644 --- a/code/game/objects/items/weapons/manuals.dm +++ b/code/game/objects/items/weapons/manuals.dm @@ -147,6 +147,48 @@ "} +//R-UST port +/obj/item/weapon/book/manual/rust_engine + name = "R-UST Operating Manual" + icon_state = "bookSupermatter" + author = "Cindy Crawfish" + title = "R-UST Operating Manual" + +/obj/item/weapon/book/manual/rust_engine/New() + ..() + dat = {" + + + + + This guide appears to have been scribbled in haste on the back of a WetSkrell magazine... +

+
    +
  1. Put uranium in the portable generator near the gyrotron and turn it to full. This is to provide initial power to the core.
  2. +
  3. Enable and max output on the SMES in the engine room. This is to power the gyrotron.
  4. +
  5. Go into the control room, interact with the fusion core control console. Turn the field on and raise size to 501. Any bigger and it will start EMPing the doors. Any smaller and the fuel pellets might miss.
  6. +
  7. Interact with the gyrotron control computer, set power as high as the SMES can support, usually around 4, and turn it on. This will start increasing the plasma temperature to the point where reactions can occur.
  8. +
  9. Go into the engine room and insert a deuterium fuel assembly and a tritium fuel assembly into two of the fuel injectors. You can make deuterium rods in the fuel compressor if you want to play it safe.
  10. +
  11. Go back to the control room and turn the fuel injectors on. This will start firing pellets into the field.
  12. +
  13. Wait for reactions to start (plasma temperature will spike and fuel amounts will drop). Turn the gyrotron power down until it's keeping up with field instability. This will prevent cumulative instability from the deuterium-tritium reaction fucking up the field. If you're using straight deuterium instability isn't a problem and you can turn the gyrotron off.
  14. +
  15. Configure the SMES, turn the PACMAN off before it explodes.
  16. +
+
+ NOTES FOR NEWBIES +
+ Anything touching the field will mess with its stability and eventually cause it to rupture. Rupturing is bad. Use the gyrotron to keep instability down if you're running the engine on unstable fuel. +

+ Likewise, no matter how sad the core seems, don't fucking hug it, you'll blow the field out and set the engine room on fire. + + "} + /obj/item/weapon/book/manual/engineering_hacking name = "Hacking" icon_state ="bookHacking" diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index 84bd14d417..6a957b1eb0 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -158,6 +158,17 @@ default_type = "osmium" apply_colour = 1 +//R-UST port +// Fusion fuel. +/obj/item/stack/material/deuterium + name = "deuterium" + icon_state = "sheet-silver" + default_type = "deuterium" + apply_colour = 1 + +/obj/item/stack/material/deuterium/fifty + amount = 50 + /obj/item/stack/material/steel name = DEFAULT_WALL_MATERIAL icon_state = "sheet-metal" diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm index 921b28d733..12d78b366b 100644 --- a/code/modules/materials/materials.dm +++ b/code/modules/materials/materials.dm @@ -69,6 +69,7 @@ var/list/name_to_material var/flags = 0 // Various status modifiers. var/sheet_singular_name = "sheet" var/sheet_plural_name = "sheets" + var/is_fusion_fuel // Shards/tables/structures var/shard_type = SHARD_SHRAPNEL // Path of debris object. @@ -97,6 +98,7 @@ var/list/name_to_material var/conductive = 1 // Objects with this var add CONDUCTS to flags on spawn. var/conductivity = null // How conductive the material is. Iron acts as the baseline, at 10. var/list/composite_material // If set, object matter var will be a list containing these values. + var/luminescence // Placeholder vars for the time being, todo properly integrate windows/light tiles/rods. var/created_window @@ -270,6 +272,22 @@ var/list/name_to_material sheet_singular_name = "ingot" sheet_plural_name = "ingots" +//R-UST port +/material/supermatter + name = "supermatter" + icon_colour = "#FFFF00" + radioactivity = 20 + stack_type = null + luminescence = 3 + ignition_point = PHORON_MINIMUM_BURN_TEMPERATURE + icon_base = "stone" + shard_type = SHARD_SHARD + hardness = 30 + door_icon_base = "stone" + sheet_singular_name = "crystal" + sheet_plural_name = "crystals" + is_fusion_fuel = 1 + /material/phoron name = "phoron" stack_type = /obj/item/stack/material/phoron @@ -567,6 +585,16 @@ var/list/name_to_material stack_origin_tech = list(TECH_MATERIAL = 5) sheet_singular_name = "ingot" sheet_plural_name = "ingots" + is_fusion_fuel = 1 + +/material/deuterium + name = "deuterium" + stack_type = /obj/item/stack/material/deuterium + icon_colour = "#999999" + stack_origin_tech = list(TECH_MATERIAL = 3) + sheet_singular_name = "ingot" + sheet_plural_name = "ingots" + is_fusion_fuel = 1 /material/mhydrogen name = "mhydrogen" @@ -574,6 +602,7 @@ var/list/name_to_material icon_colour = "#E6C5DE" stack_origin_tech = list(TECH_MATERIAL = 6, TECH_POWER = 6, TECH_MAGNET = 5) conductivity = 100 + is_fusion_fuel = 1 /material/platinum name = "platinum" diff --git a/code/modules/power/fusion/_setup.dm b/code/modules/power/fusion/_setup.dm new file mode 100644 index 0000000000..b4aca0c827 --- /dev/null +++ b/code/modules/power/fusion/_setup.dm @@ -0,0 +1,83 @@ +// temperature of the core of the sun +#define FUSION_HEAT_CAP 1.57e7 + +#define SETUP_OK 1 // All good +#define SETUP_WARNING 2 // Something that shouldn't happen happened, but it's not critical so we will continue +#define SETUP_ERROR 3 // Something bad happened, and it's important so we won't continue setup. +#define SETUP_DELAYED 4 // Wait for other things first. + +/datum/admins/proc/setup_fusion() + set category = "Debug" + set name = "Setup Fusion Core" + set desc = "Allows you to start the R-UST engine." + + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + + if(!(locate(/obj/machinery/power/fusion_core/mapped) in world)) + to_chat(usr, "This map is not appropriate for this verb.") + return + + var/response = input(usr, "Are you sure?", "Engine setup") as null|anything in list("No", "Yes") + if(!response || response == "No") + return + + var/errors = 0 + var/warnings = 0 + var/success = 0 + + log_and_message_admins("## FUSION CORE SETUP - Setup initiated by [usr].") + + for(var/obj/machinery/fusion_fuel_injector/mapped/injector in machines) + injector.cur_assembly = new /obj/item/weapon/fuel_assembly/deuterium(injector) + injector.BeginInjecting() + + var/obj/machinery/power/fusion_core/mapped/core = locate() in machines + if(core.jumpstart(15000)) + var/list/delayed_objects = list() + + // SETUP PHASE + for(var/obj/effect/engine_setup/S in world) + var/result = S.activate(0) + switch(result) + if(SETUP_OK) + success++ + continue + if(SETUP_WARNING) + warnings++ + continue + if(SETUP_ERROR) + errors++ + log_and_message_admins("## FUSION CORE SETUP - Error encountered! Aborting.") + break + if(SETUP_DELAYED) + delayed_objects.Add(S) + continue + + if(!errors) + for(var/obj/effect/engine_setup/S in delayed_objects) + var/result = S.activate(1) + switch(result) + if(SETUP_OK) + success++ + continue + if(SETUP_WARNING) + warnings++ + continue + if(SETUP_ERROR) + errors++ + log_and_message_admins("## FUSION CORE SETUP - Error encountered! Aborting.") + break + else + log_and_message_admins("## FUSION CORE SETUP - Error encountered! Aborting.") + errors++ + + log_and_message_admins("## FUSION CORE SETUP - Setup completed with [errors] errors, [warnings] warnings and [success] successful steps.") + +#undef SETUP_OK +#undef SETUP_WARNING +#undef SETUP_ERROR +#undef SETUP_DELAYED \ No newline at end of file diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/power/fusion/core/_core.dm new file mode 100644 index 0000000000..c78cb47620 --- /dev/null +++ b/code/modules/power/fusion/core/_core.dm @@ -0,0 +1,131 @@ +/* + TODO README +*/ + +var/list/fusion_cores = list() + +#define MAX_FIELD_STR 1000 +#define MIN_FIELD_STR 1 + +/obj/machinery/power/fusion_core + name = "\improper R-UST Mk. 8 Tokamak core" + desc = "An enormous solenoid for generating extremely high power electromagnetic fields. It includes a kinetic energy harvester." + icon = 'icons/obj/machines/power/fusion.dmi' + icon_state = "core0" + density = 1 + use_power = 1 + idle_power_usage = 50 + active_power_usage = 500 //multiplied by field strength + anchored = 0 + + var/obj/effect/fusion_em_field/owned_field + var/field_strength = 1//0.01 + var/id_tag + +/obj/machinery/power/fusion_core/mapped + anchored = 1 + +/obj/machinery/power/fusion_core/initialize() + . = ..() + connect_to_network() + fusion_cores += src + +/obj/machinery/power/fusion_core/Destroy() + for(var/obj/machinery/computer/fusion_core_control/FCC in machines) + FCC.connected_devices -= src + if(FCC.cur_viewed_device == src) + FCC.cur_viewed_device = null + fusion_cores -= src + return ..() + +/obj/machinery/power/fusion_core/process() + if((stat & BROKEN) || !powernet || !owned_field) + Shutdown() + +/obj/machinery/power/fusion_core/Topic(href, href_list) + if(..()) + return 1 + if(href_list["str"]) + var/dif = text2num(href_list["str"]) + field_strength = min(max(field_strength + dif, MIN_FIELD_STR), MAX_FIELD_STR) + active_power_usage = 500 * field_strength + if(owned_field) + owned_field.ChangeFieldStrength(field_strength) + +/obj/machinery/power/fusion_core/proc/Startup() + if(owned_field) + return + owned_field = new(loc, src) + owned_field.ChangeFieldStrength(field_strength) + icon_state = "core1" + use_power = 2 + . = 1 + +/obj/machinery/power/fusion_core/proc/Shutdown(var/force_rupture) + if(owned_field) + icon_state = "core0" + if(force_rupture || owned_field.plasma_temperature > 1000) + owned_field.Rupture() + else + owned_field.RadiateAll() + qdel(owned_field) + owned_field = null + use_power = 1 + +/obj/machinery/power/fusion_core/proc/AddParticles(var/name, var/quantity = 1) + if(owned_field) + owned_field.AddParticles(name, quantity) + . = 1 + +/obj/machinery/power/fusion_core/bullet_act(var/obj/item/projectile/Proj) + if(owned_field) + . = owned_field.bullet_act(Proj) + +/obj/machinery/power/fusion_core/proc/set_strength(var/value) + value = Clamp(value, MIN_FIELD_STR, MAX_FIELD_STR) + field_strength = value + active_power_usage = 5 * value + if(owned_field) + owned_field.ChangeFieldStrength(value) + +/obj/machinery/power/fusion_core/attack_hand(var/mob/user) + if(!Adjacent(user)) // As funny as it was for the AI to hug-kill the tokamak field from a distance... + return + visible_message("\The [user] hugs \the [src] to make it feel better!") + if(owned_field) + Shutdown() + +/obj/machinery/power/fusion_core/attackby(var/obj/item/W, var/mob/user) + + if(owned_field) + to_chat(user,"Shut \the [src] off first!") + return + + if(ismultitool(W)) + var/new_ident = input("Enter a new ident tag.", "Fusion Core", id_tag) as null|text + if(new_ident && user.Adjacent(src)) + id_tag = new_ident + return + + else if(iswrench(W)) + anchored = !anchored + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + if(anchored) + user.visible_message("[user.name] secures [src.name] to the floor.", \ + "You secure the [src.name] to the floor.", \ + "You hear a ratchet") + else + user.visible_message("[user.name] unsecures [src.name] from the floor.", \ + "You unsecure the [src.name] from the floor.", \ + "You hear a ratchet") + return + + return ..() + +/obj/machinery/power/fusion_core/proc/jumpstart(var/field_temperature) + field_strength = 501 // Generally a good size. + Startup() + if(!owned_field) + return FALSE + owned_field.plasma_temperature = field_temperature + return TRUE \ No newline at end of file diff --git a/code/modules/power/fusion/core/core_control.dm b/code/modules/power/fusion/core/core_control.dm new file mode 100644 index 0000000000..bb4e04b1e9 --- /dev/null +++ b/code/modules/power/fusion/core/core_control.dm @@ -0,0 +1,176 @@ +/obj/machinery/computer/fusion_core_control + name = "\improper R-UST Mk. 8 core control" + icon = 'icons/obj/machines/power/fusion.dmi' + icon_state = "core_control" + light_color = COLOR_ORANGE + + var/id_tag + var/scan_range = 25 + var/list/connected_devices = list() + var/obj/machinery/power/fusion_core/cur_viewed_device + +/obj/machinery/computer/fusion_core_control/attackby(var/obj/item/thing, var/mob/user) + if(ismultitool(thing)) + var/new_ident = input("Enter a new ident tag.", "Core Control", id_tag) as null|text + if(new_ident && user.Adjacent(src)) + id_tag = new_ident + cur_viewed_device = null + return + else + return ..() + +/obj/machinery/computer/fusion_core_control/attack_ai(mob/user) + attack_hand(user) + +/obj/machinery/computer/fusion_core_control/attack_hand(mob/user) + add_fingerprint(user) + interact(user) + +/obj/machinery/computer/fusion_core_control/interact(mob/user) + + if(!cur_viewed_device || !check_core_status(cur_viewed_device)) + cur_viewed_device = null + + if(!id_tag) + to_chat(user, "This console has not been assigned an ident tag. Please contact your system administrator or conduct a manual update with a standard multitool.") + return + + if(cur_viewed_device && (cur_viewed_device.id_tag != id_tag || get_dist(src, cur_viewed_device) > scan_range)) + cur_viewed_device = null + + var/dat = "Core Control #[id_tag]
" + + if(cur_viewed_device) + dat += {" + Back to overview
+ Device ident '[cur_viewed_device.id_tag]' [cur_viewed_device.owned_field ? "active" : "inactive"].
+ Power status: [cur_viewed_device.avail()]/[cur_viewed_device.active_power_usage] W
+
+ Bring field [cur_viewed_device.owned_field ? "offline" : "online"].
+
+ Field power density (W.m-3):
+ ---- + --- + -- + - + [cur_viewed_device.field_strength] + + + ++ + +++ + ++++
+ "} + + if(cur_viewed_device.owned_field) + dat += {" + Approximate field diameter (m): [cur_viewed_device.owned_field.size]
+ Field instability: [cur_viewed_device.owned_field.percent_unstable * 100]%
+ Plasma temperature: [cur_viewed_device.owned_field.plasma_temperature + 295]K
+ Fuel:
+ + "} + for(var/reagent in cur_viewed_device.owned_field.dormant_reactant_quantities) + dat += "" + dat += "
NameAmount
[reagent][cur_viewed_device.owned_field.dormant_reactant_quantities[reagent]]

" + + else + + connected_devices.Cut() + for(var/obj/machinery/power/fusion_core/C in fusion_cores) + if(C.id_tag == id_tag && get_dist(src, C) <= scan_range) + connected_devices += C + for(var/obj/machinery/power/fusion_core/C in gyrotrons) + if(C.id_tag == id_tag && get_dist(src, C) <= scan_range) + connected_devices += C + + if(connected_devices.len) + dat += {" + Connected EM field generators:
+ + + + + + + "} + + for(var/obj/machinery/power/fusion_core/C in connected_devices) + var/status + var/can_access = 1 + if(!check_core_status(C)) + status = "Unresponsive" + can_access = 0 + else if(C.avail() < C.active_power_usage) + status = "Underpowered" + else + status = "Good" + + dat += {" + + + + "} + + if(!can_access) + dat += {" + + "} + else + dat += {" + + "} + dat += {" + + "} + + else + dat += "No electromagnetic field generators connected." + + var/datum/browser/popup = new(user, "fusion_control", name, 500, 400, src) + popup.set_content(dat) + popup.open() + user.set_machine(src) + +/obj/machinery/computer/fusion_core_control/Topic(href, href_list) + . = ..() + if(.) + return + + if(href_list["access_device"]) + var/idx = Clamp(text2num(href_list["toggle_active"]), 1, connected_devices.len) + cur_viewed_device = connected_devices[idx] + updateUsrDialog() + return 1 + + //All HREFs from this point on require a device anyways. + if(!cur_viewed_device || !check_core_status(cur_viewed_device) || cur_viewed_device.id_tag != id_tag || get_dist(src, cur_viewed_device) > scan_range) + return + + if(href_list["goto_scanlist"]) + cur_viewed_device = null + updateUsrDialog() + return 1 + + if(href_list["toggle_active"]) + if(!cur_viewed_device.Startup()) //Startup() whilst the device is active will return null. + cur_viewed_device.Shutdown() + updateUsrDialog() + return 1 + + if(href_list["str"]) + var/val = text2num(href_list["str"]) + if(!val) //Value is 0, which is manual entering. + cur_viewed_device.set_strength(input("Enter the new field power density (W.m^-3)", "Fusion Control", cur_viewed_device.field_strength) as num) + else + cur_viewed_device.set_strength(cur_viewed_device.field_strength + val) + updateUsrDialog() + return 1 + +//Returns 1 if the machine can be interacted with via this console. +/obj/machinery/computer/fusion_core_control/proc/check_core_status(var/obj/machinery/power/fusion_core/C) + if(isnull(C)) + return + if(C.stat & BROKEN) + return + if(C.idle_power_usage > C.avail()) + return + . = 1 diff --git a/code/modules/power/fusion/core/core_field.dm b/code/modules/power/fusion/core/core_field.dm new file mode 100644 index 0000000000..587ec7637e --- /dev/null +++ b/code/modules/power/fusion/core/core_field.dm @@ -0,0 +1,494 @@ +#define FUSION_ENERGY_PER_K 20 + +/obj/effect/fusion_em_field + name = "electromagnetic field" + desc = "A coruscating, barely visible field of energy. It is shaped like a slightly flattened torus." + icon = 'icons/obj/machines/power/fusion.dmi' + icon_state = "emfield_s1" + alpha = 50 + layer = 4 + light_color = COLOR_BLUE + + var/size = 1 + var/energy = 0 + var/plasma_temperature = 0 + var/radiation = 0 + var/field_strength = 0.01 + var/tick_instability = 0 + var/percent_unstable = 0 + + var/obj/machinery/power/fusion_core/owned_core + var/list/dormant_reactant_quantities = list() + var/list/particle_catchers = list() + + var/list/ignore_types = list( + /obj/item/projectile, + /obj/effect, + /obj/fire, + /obj/structure/cable, + /obj/machinery/atmospherics, + /obj/machinery/air_sensor, + /mob/observer/dead + ) + + var/light_min_range = 2 + var/light_min_power = 3 + var/light_max_range = 10 + var/light_max_power = 10 + + var/last_range + var/last_power + +/obj/effect/fusion_em_field/New(loc, var/obj/machinery/power/fusion_core/new_owned_core) + ..() + + set_light(light_min_range,light_min_power) + last_range = light_min_range + last_power = light_min_power + + owned_core = new_owned_core + if(!owned_core) + qdel(src) + + //create the gimmicky things to handle field collisions + var/obj/effect/fusion_particle_catcher/catcher + + catcher = new (locate(src.x,src.y,src.z)) + catcher.parent = src + catcher.SetSize(1) + particle_catchers.Add(catcher) + + catcher = new (locate(src.x-1,src.y,src.z)) + catcher.parent = src + catcher.SetSize(3) + particle_catchers.Add(catcher) + catcher = new (locate(src.x+1,src.y,src.z)) + catcher.parent = src + catcher.SetSize(3) + particle_catchers.Add(catcher) + catcher = new (locate(src.x,src.y+1,src.z)) + catcher.parent = src + catcher.SetSize(3) + particle_catchers.Add(catcher) + catcher = new (locate(src.x,src.y-1,src.z)) + catcher.parent = src + catcher.SetSize(3) + particle_catchers.Add(catcher) + + catcher = new (locate(src.x-2,src.y,src.z)) + catcher.parent = src + catcher.SetSize(5) + particle_catchers.Add(catcher) + catcher = new (locate(src.x+2,src.y,src.z)) + catcher.parent = src + catcher.SetSize(5) + particle_catchers.Add(catcher) + catcher = new (locate(src.x,src.y+2,src.z)) + catcher.parent = src + catcher.SetSize(5) + particle_catchers.Add(catcher) + catcher = new (locate(src.x,src.y-2,src.z)) + catcher.parent = src + catcher.SetSize(5) + particle_catchers.Add(catcher) + + catcher = new (locate(src.x-3,src.y,src.z)) + catcher.parent = src + catcher.SetSize(7) + particle_catchers.Add(catcher) + catcher = new (locate(src.x+3,src.y,src.z)) + catcher.parent = src + catcher.SetSize(7) + particle_catchers.Add(catcher) + catcher = new (locate(src.x,src.y+3,src.z)) + catcher.parent = src + catcher.SetSize(7) + particle_catchers.Add(catcher) + catcher = new (locate(src.x,src.y-3,src.z)) + catcher.parent = src + catcher.SetSize(7) + particle_catchers.Add(catcher) + + processing_objects.Add(src) + +/obj/effect/fusion_em_field/process() + //make sure the field generator is still intact + if(!owned_core || QDELETED(owned_core)) + qdel(src) + return + + // Take some gas up from our environment. + var/added_particles = FALSE + var/datum/gas_mixture/uptake_gas = owned_core.loc.return_air() + if(uptake_gas) + uptake_gas = uptake_gas.remove_by_flag(XGM_GAS_FUSION_FUEL, rand(50,100)) + if(uptake_gas && uptake_gas.total_moles) + for(var/gasname in uptake_gas.gas) + if(uptake_gas.gas[gasname]*10 > dormant_reactant_quantities[gasname]) + AddParticles(gasname, uptake_gas.gas[gasname]*10) + uptake_gas.adjust_gas(gasname, -(uptake_gas.gas[gasname]), update=FALSE) + added_particles = TRUE + if(added_particles) + uptake_gas.update_values() + + //let the particles inside the field react + React() + + // Dump power to our powernet. + owned_core.add_avail(FUSION_ENERGY_PER_K * plasma_temperature) + + // Energy decay. + if(plasma_temperature >= 1) + var/lost = plasma_temperature*0.01 + radiation += lost + plasma_temperature -= lost + + //handle some reactants formatting + for(var/reactant in dormant_reactant_quantities) + var/amount = dormant_reactant_quantities[reactant] + if(amount < 1) + dormant_reactant_quantities.Remove(reactant) + else if(amount >= 1000000) + var/radiate = rand(3 * amount / 4, amount / 4) + dormant_reactant_quantities[reactant] -= radiate + radiation += radiate + + var/use_range + var/use_power + if(plasma_temperature <= 6000) + use_range = light_min_range + use_power = light_min_power + else if(plasma_temperature >= 25000) + use_range = light_max_range + use_power = light_max_power + else + 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) + + if(last_range != use_range || last_power != use_power) + set_light(use_range,use_power) + last_range = use_range + last_power = use_power + + check_instability() + Radiate() + if(radiation) + radiation_repository.radiate(src, radiation) + return 1 + +/obj/effect/fusion_em_field/proc/check_instability() + if(tick_instability > 0) + percent_unstable += (tick_instability*size)/10000 + tick_instability = 0 + else + if(percent_unstable < 0) + percent_unstable = 0 + else + if(percent_unstable > 100) + percent_unstable = 100 + if(percent_unstable > 0) + percent_unstable = max(0, percent_unstable-rand(0.01,0.03)) + + if(percent_unstable >= 1) + owned_core.Shutdown(force_rupture=1) + else + if(percent_unstable > 0.5 && prob(percent_unstable*100)) + if(plasma_temperature < 2000) + visible_message("\The [src] ripples uneasily, like a disturbed pond.") + else + var/flare + var/fuel_loss + var/rupture + if(percent_unstable < 0.7) + visible_message("\The [src] ripples uneasily, like a disturbed pond.") + fuel_loss = prob(5) + else if(percent_unstable < 0.9) + visible_message("\The [src] undulates violently, shedding plumes of plasma!") + flare = prob(50) + fuel_loss = prob(20) + rupture = prob(5) + else + visible_message("\The [src] is wracked by a series of horrendous distortions, buckling and twisting like a living thing!") + flare = 1 + fuel_loss = prob(50) + rupture = prob(25) + + if(rupture) + owned_core.Shutdown(force_rupture=1) + else + var/lost_plasma = (plasma_temperature*percent_unstable) + radiation += lost_plasma + if(flare) + radiation += plasma_temperature/2 + plasma_temperature -= lost_plasma + + if(fuel_loss) + for(var/particle in dormant_reactant_quantities) + var/lost_fuel = dormant_reactant_quantities[particle]*percent_unstable + radiation += lost_fuel + dormant_reactant_quantities[particle] -= lost_fuel + if(dormant_reactant_quantities[particle] <= 0) + dormant_reactant_quantities.Remove(particle) + Radiate() + return + +/obj/effect/fusion_em_field/proc/Rupture() + visible_message("\The [src] shudders like a dying animal before flaring to eye-searing brightness and rupturing!") + set_light(15, 15, "#CCCCFF") + empulse(get_turf(src), ceil(plasma_temperature/1000), ceil(plasma_temperature/300)) + sleep(5) + RadiateAll() + explosion(get_turf(owned_core),-1,-1,8,10) // Blow out all the windows. + return + +/obj/effect/fusion_em_field/proc/ChangeFieldStrength(var/new_strength) + var/calc_size = 1 + if(new_strength <= 50) + calc_size = 1 + else if(new_strength <= 200) + calc_size = 3 + else if(new_strength <= 500) + calc_size = 5 + else + calc_size = 7 + field_strength = new_strength + change_size(calc_size) + +/obj/effect/fusion_em_field/proc/AddEnergy(var/a_energy, var/a_plasma_temperature) + energy += a_energy + plasma_temperature += a_plasma_temperature + if(a_energy && percent_unstable > 0) + percent_unstable -= a_energy/10000 + if(percent_unstable < 0) + percent_unstable = 0 + while(energy >= 100) + energy -= 100 + plasma_temperature += 1 + +/obj/effect/fusion_em_field/proc/AddParticles(var/name, var/quantity = 1) + if(name in dormant_reactant_quantities) + dormant_reactant_quantities[name] += quantity + else if(name != "proton" && name != "electron" && name != "neutron") + dormant_reactant_quantities.Add(name) + dormant_reactant_quantities[name] = quantity + +/obj/effect/fusion_em_field/proc/RadiateAll(var/ratio_lost = 1) + + // Create our plasma field and dump it into our environment. + var/turf/T = get_turf(src) + if(istype(T)) + var/datum/gas_mixture/plasma = new + plasma.adjust_gas("oxygen", (size*100), 0) + plasma.adjust_gas("phoron", (size*100), 0) + plasma.temperature = (plasma_temperature/2) + plasma.update_values() + T.assume_air(plasma) + T.hotspot_expose(plasma_temperature) + plasma = null + + // Radiate all our unspent fuel and energy. + for(var/particle in dormant_reactant_quantities) + radiation += dormant_reactant_quantities[particle] + dormant_reactant_quantities.Remove(particle) + radiation += plasma_temperature/2 + plasma_temperature = 0 + + radiation_repository.radiate(src, radiation) + Radiate() + +/obj/effect/fusion_em_field/proc/Radiate() + if(istype(loc, /turf)) + var/empsev = max(1, min(3, ceil(size/2))) + for(var/atom/movable/AM in range(max(1,Floor(size/2)), loc)) + + if(AM == src || AM == owned_core || !AM.simulated) + continue + + var/skip_obstacle + for(var/ignore_path in ignore_types) + if(istype(AM, ignore_path)) + skip_obstacle = TRUE + break + if(skip_obstacle) + continue + + log_debug("R-UST DEBUG: [AM] is [AM.type]") + AM.visible_message("The field buckles visibly around \the [AM]!") + tick_instability += rand(15,30) + AM.emp_act(empsev) + + if(owned_core && owned_core.loc) + var/datum/gas_mixture/environment = owned_core.loc.return_air() + if(environment && environment.temperature < (T0C+1000)) // Putting an upper bound on it to stop it being used in a TEG. + environment.add_thermal_energy(plasma_temperature*20000) + radiation = 0 + +/obj/effect/fusion_em_field/proc/change_size(var/newsize = 1) + var/changed = 0 + switch(newsize) + if(1) + size = 1 + icon = 'icons/obj/machines/power/fusion.dmi' + icon_state = "emfield_s1" + pixel_x = 0 + pixel_y = 0 + // + changed = 1 + if(3) + size = 3 + icon = 'icons/effects/96x96.dmi' + icon_state = "emfield_s3" + pixel_x = -32 * PIXEL_MULTIPLIER + pixel_y = -32 * PIXEL_MULTIPLIER + // + changed = 3 + if(5) + size = 5 + icon = 'icons/effects/160x160.dmi' + icon_state = "emfield_s5" + pixel_x = -64 * PIXEL_MULTIPLIER + pixel_y = -64 * PIXEL_MULTIPLIER + // + changed = 5 + if(7) + size = 7 + icon = 'icons/effects/224x224.dmi' + icon_state = "emfield_s7" + pixel_x = -96 * PIXEL_MULTIPLIER + pixel_y = -96 * PIXEL_MULTIPLIER + // + changed = 7 + for(var/obj/effect/fusion_particle_catcher/catcher in particle_catchers) + catcher.UpdateSize() + return changed + +//the !!fun!! part +/obj/effect/fusion_em_field/proc/React() + //loop through the reactants in random order + var/list/react_pool = dormant_reactant_quantities.Copy() + + //cant have any reactions if there aren't any reactants present + if(react_pool.len) + //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),react_pool[reactant]) + dormant_reactant_quantities[reactant] -= react_pool[reactant] + if(!react_pool[reactant]) + react_pool -= reactant + + //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(p_react_pool.len) + //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 + 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 + 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 + var/list/possible_reactions = new/list + for(var/cur_s_react in possible_s_reacts) + if(possible_s_reacts[cur_s_react] < 1) + continue + var/decl/fusion_reaction/cur_reaction = get_fusion_reaction(cur_p_react, cur_s_react) + if(cur_reaction && plasma_temperature >= cur_reaction.minimum_energy_level) + possible_reactions.Add(cur_reaction) + + //if there are no possible reactions here, abandon this primary reactant and move on + if(!possible_reactions.len) + continue + + //split up the reacting atoms between the possible reactions + while(possible_reactions.len) + var/decl/fusion_reaction/cur_reaction = pick(possible_reactions) + possible_reactions.Remove(cur_reaction) + + //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 + if(plasma_temperature < cur_reaction.minimum_reaction_temperature) + continue + + if(plasma_temperature < max_num_reactants * cur_reaction.energy_consumption) + max_num_reactants = round(plasma_temperature / cur_reaction.energy_consumption) + if(max_num_reactants < 1) + continue + + //randomly determined amount to react + var/amount_reacting = rand(1, max_num_reactants) + + //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 + if(react_pool[cur_reaction.s_react] - amount_reacting >= 0 ) + react_pool[cur_reaction.s_react] -= amount_reacting + else + react_pool[cur_reaction.p_react] += amount_reacting - react_pool[cur_reaction.p_react] + 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. + tick_instability += max_num_reactants * cur_reaction.instability + + // Create the reaction products. + for(var/reactant in cur_reaction.products) + var/success = 0 + for(var/check_reactant in produced_reactants) + if(check_reactant == reactant) + produced_reactants[reactant] += cur_reaction.products[reactant] * amount_reacting + success = 1 + break + if(!success) + produced_reactants[reactant] = cur_reaction.products[reactant] * amount_reacting + + // Handle anything special. If this proc returns true, abort the current reaction. + if(cur_reaction.handle_reaction_special(src)) + return + + // This reaction is done, and can't be repeated this sub-cycle. + possible_reactions.Remove(cur_reaction.s_react) + + // Loop through the newly produced reactants and add them to the pool. + for(var/reactant in produced_reactants) + AddParticles(reactant, produced_reactants[reactant]) + + // Check whether there are reactants left, and add them back to the pool. + for(var/reactant in react_pool) + AddParticles(reactant, react_pool[reactant]) + +/obj/effect/fusion_em_field/Destroy() + set_light(0) + RadiateAll() + for(var/obj/effect/fusion_particle_catcher/catcher in particle_catchers) + qdel(catcher) + if(owned_core) + owned_core.owned_field = null + owned_core = null + processing_objects.Remove(src) + . = ..() + +/obj/effect/fusion_em_field/bullet_act(var/obj/item/projectile/Proj) + AddEnergy(Proj.damage) + update_icon() + return 0 + +#undef FUSION_HEAT_CAP \ No newline at end of file diff --git a/code/modules/power/fusion/fuel_assembly/fuel_assembly.dm b/code/modules/power/fusion/fuel_assembly/fuel_assembly.dm new file mode 100644 index 0000000000..468f24510a --- /dev/null +++ b/code/modules/power/fusion/fuel_assembly/fuel_assembly.dm @@ -0,0 +1,67 @@ +/obj/item/weapon/fuel_assembly + name = "fuel rod assembly" + icon = 'icons/obj/machines/power/fusion.dmi' + icon_state = "fuel_assembly" + layer = 4 + + var/material_name + + var/percent_depleted = 1 + var/list/rod_quantities = list() + var/fuel_type = "composite" + var/fuel_colour + var/radioactivity = 0 + var/const/initial_amount = 300 + +/obj/item/weapon/fuel_assembly/New(var/newloc, var/_material, var/_colour) + fuel_type = _material + fuel_colour = _colour + ..(newloc) + +/obj/item/weapon/fuel_assembly/initialize() + . = ..() + var/material/material = get_material_by_name(fuel_type) + if(istype(material)) + name = "[material.use_name] fuel rod assembly" + desc = "A fuel rod for a fusion reactor. This one is made from [material.use_name]." + fuel_colour = material.icon_colour + fuel_type = material.use_name + if(material.radioactivity) + radioactivity = material.radioactivity + desc += " It is warm to the touch." + processing_objects += src + if(material.luminescence) + set_light(material.luminescence, material.luminescence, material.icon_colour) + else + name = "[fuel_type] fuel rod assembly" + desc = "A fuel rod for a fusion reactor. This one is made from [fuel_type]." + + icon_state = "blank" + var/image/I = image(icon, "fuel_assembly") + I.color = fuel_colour + overlays += list(I, image(icon, "fuel_assembly_bracket")) + rod_quantities[fuel_type] = initial_amount + +/obj/item/weapon/fuel_assembly/process() + if(!radioactivity) + return PROCESS_KILL + + if(istype(loc, /turf)) + radiation_repository.radiate(src, max(1,ceil(radioactivity/30))) + +/obj/item/weapon/fuel_assembly/Destroy() + processing_objects -= src + return ..() + +// Mapper shorthand. +/obj/item/weapon/fuel_assembly/deuterium/New(var/newloc) + ..(newloc, "deuterium") + +/obj/item/weapon/fuel_assembly/tritium/New(var/newloc) + ..(newloc, "tritium") + +/obj/item/weapon/fuel_assembly/phoron/New(var/newloc) + ..(newloc, "phoron") + +/obj/item/weapon/fuel_assembly/supermatter/New(var/newloc) + ..(newloc, "supermatter") diff --git a/code/modules/power/fusion/fuel_assembly/fuel_compressor.dm b/code/modules/power/fusion/fuel_assembly/fuel_compressor.dm new file mode 100644 index 0000000000..469f32135f --- /dev/null +++ b/code/modules/power/fusion/fuel_assembly/fuel_compressor.dm @@ -0,0 +1,54 @@ +/obj/machinery/fusion_fuel_compressor + name = "fuel compressor" + icon = 'icons/obj/machines/power/fusion.dmi' + icon_state = "fuel_compressor1" + density = 1 + anchored = 1 + layer = 4 + +/obj/machinery/fusion_fuel_compressor/MouseDrop_T(var/atom/movable/target, var/mob/user) + if(user.incapacitated() || !user.Adjacent(src)) + return + return do_special_fuel_compression(target, user) + +/obj/machinery/fusion_fuel_compressor/proc/do_special_fuel_compression(var/obj/item/thing, var/mob/user) + if(istype(thing) && thing.reagents && thing.reagents.total_volume && thing.is_open_container()) + if(thing.reagents.reagent_list.len > 1) + to_chat(user, "The contents of \the [thing] are impure and cannot be used as fuel.") + return 1 + if(thing.reagents.total_volume < 50) + to_chat(user, "You need at least fifty units of material to form a fuel rod.") + return 1 + var/datum/reagent/R = thing.reagents.reagent_list[1] + visible_message("\The [src] compresses the contents of \the [thing] into a new fuel assembly.") + var/obj/item/weapon/fuel_assembly/F = new(get_turf(src), R.id, R.color) + thing.reagents.remove_reagent(R.id, R.volume) + user.put_in_hands(F) + + else if(istype(thing, /obj/machinery/power/supermatter/shard)) + var/obj/item/weapon/fuel_assembly/F = new(get_turf(src), "supermatter") + visible_message("\The [src] compresses the \[thing] into a new fuel assembly.") + qdel(thing) + user.put_in_hands(F) + return 1 + return 0 + +/obj/machinery/fusion_fuel_compressor/attackby(var/obj/item/thing, var/mob/user) + if(istype(thing, /obj/item/stack/material)) + var/obj/item/stack/material/M = thing + var/material/mat = M.get_material() + if(!mat.is_fusion_fuel) + to_chat(user, "It would be pointless to make a fuel rod out of [mat.use_name].") + return + if(M.get_amount() < 25) + to_chat(user, "You need at least 25 [mat.sheet_plural_name] to make a fuel rod.") + return + var/obj/item/weapon/fuel_assembly/F = new(get_turf(src), mat.name) + visible_message("\The [src] compresses the [mat.use_name] into a new fuel assembly.") + M.use(25) + user.put_in_hands(F) + + else if(do_special_fuel_compression(thing, user)) + return + + return ..() \ No newline at end of file diff --git a/code/modules/power/fusion/fuel_assembly/fuel_control.dm b/code/modules/power/fusion/fuel_assembly/fuel_control.dm new file mode 100644 index 0000000000..6d95e358d8 --- /dev/null +++ b/code/modules/power/fusion/fuel_assembly/fuel_control.dm @@ -0,0 +1,102 @@ +/obj/machinery/computer/fusion_fuel_control + name = "fuel injection control computer" + icon = 'icons/obj/machines/power/fusion.dmi' + icon_state = "fuel" + + var/id_tag + var/scan_range = 25 + +/obj/machinery/computer/fusion_fuel_control/attack_ai(mob/user) + attack_hand(user) + +/obj/machinery/computer/fusion_fuel_control/attack_hand(mob/user) + add_fingerprint(user) + interact(user) + +/obj/machinery/computer/fusion_fuel_control/interact(var/mob/user) + + if(stat & (BROKEN|NOPOWER)) + user.unset_machine() + user << browse(null, "window=fuel_control") + return + + if (!istype(user, /mob/living/silicon) && get_dist(src, user) > 1) + user.unset_machine() + user << browse(null, "window=fuel_control") + return + + if(!id_tag) + to_chat(user, "This console has not been assigned an ident tag. Please contact your system administrator or conduct a manual update with a standard multitool.") + return + + var/dat = "Reactor Core Fuel Control #[id_tag]
" + dat += {" +
+
Device tagStatusControls
[C.id_tag][status]ERRORACCESS
+ + + + + "} + + for(var/obj/machinery/fusion_fuel_injector/I in fuel_injectors) + if(!id_tag || !I.id_tag || I.id_tag != id_tag || get_dist(src, I) > scan_range) + continue + + dat += "" + + if(I.stat & (BROKEN|NOPOWER)) + dat += "" + dat += "" + dat += "" + else + dat += "" + if(I.cur_assembly) + dat += "" + else + dat += "" + if(I.cur_assembly) + dat += "" + else + dat += "" + + dat += "" + + dat += {"
ContainsAssemblyRemaining
ERRORERRORERROR[I.cur_assembly ? I.cur_assembly.fuel_type : "NONE"]\[[I.injecting ? "Halt injecting" : "Begin injecting"]\]None[I.cur_assembly.percent_depleted * 100]%NA

+ Refresh + Close
"} + + var/datum/browser/popup = new(user, "fuel_control", "Fusion Fuel Control Console", 800, 400, src) + popup.set_content(dat) + popup.open() + user.set_machine(src) + +/obj/machinery/computer/fusion_fuel_control/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["toggle_injecting"]) + var/obj/machinery/fusion_fuel_injector/I = locate(href_list["toggle_injecting"]) + if(I.id_tag != id_tag || get_dist(src, I) > scan_range) + return + + if(istype(I)) + if(I.injecting) + I.StopInjecting() + else + I.BeginInjecting() + + if( href_list["close"] ) + usr << browse(null, "window=fuel_control") + usr.unset_machine() + + updateDialog() + + +/obj/machinery/computer/fusion_fuel_control/attackby(var/obj/item/W, var/mob/user) + if(ismultitool(W)) + var/new_ident = input("Enter a new ident tag.", "Fuel Control", id_tag) as null|text + if(new_ident && user.Adjacent(src)) + id_tag = new_ident + return + return ..() diff --git a/code/modules/power/fusion/fuel_assembly/fuel_injector.dm b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm new file mode 100644 index 0000000000..f0a0fd0426 --- /dev/null +++ b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm @@ -0,0 +1,153 @@ +var/list/fuel_injectors = list() + +/obj/machinery/fusion_fuel_injector + name = "fuel injector" + icon = 'icons/obj/machines/power/fusion.dmi' + icon_state = "injector0" + density = 1 + anchored = 0 + req_access = list(access_engine) + use_power = 1 + idle_power_usage = 10 + active_power_usage = 500 + + var/fuel_usage = 0.0001 + var/id_tag + var/injecting = 0 + var/obj/item/weapon/fuel_assembly/cur_assembly + +/obj/machinery/fusion_fuel_injector/New() + ..() + fuel_injectors += src + tag = null + +/obj/machinery/fusion_fuel_injector/Destroy() + if(cur_assembly) + cur_assembly.forceMove(get_turf(src)) + cur_assembly = null + fuel_injectors -= src + return ..() + +/obj/machinery/fusion_fuel_injector/mapped + anchored = 1 + +/obj/machinery/fusion_fuel_injector/process() + if(injecting) + if(stat & (BROKEN|NOPOWER)) + StopInjecting() + else + Inject() + +/obj/machinery/fusion_fuel_injector/attackby(obj/item/W, mob/user) + + if(ismultitool(W)) + var/new_ident = input("Enter a new ident tag.", "Fuel Injector", id_tag) as null|text + if(new_ident && user.Adjacent(src)) + id_tag = new_ident + return + + if(istype(W, /obj/item/weapon/fuel_assembly)) + + if(injecting) + to_chat(user, "Shut \the [src] off before playing with the fuel rod!") + return + + if(cur_assembly) + cur_assembly.forceMove(get_turf(src)) + visible_message("\The [user] swaps \the [src]'s [cur_assembly] for \a [W].") + else + visible_message("\The [user] inserts \a [W] into \the [src].") + + user.drop_from_inventory(W) + W.forceMove(src) + if(cur_assembly) + cur_assembly.forceMove(get_turf(src)) + user.put_in_hands(cur_assembly) + cur_assembly = W + return + + if(iswrench(W)) + if(injecting) + to_chat(user, "Shut \the [src] off first!") + return + anchored = !anchored + playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + if(anchored) + user.visible_message("\The [user] secures \the [src] to the floor.") + else + user.visible_message("\The [user] unsecures \the [src] from the floor.") + return + + return ..() + +/obj/machinery/fusion_fuel_injector/attack_hand(mob/user) + + if(injecting) + to_chat(user, "Shut \the [src] off before playing with the fuel rod!") + return + + if(cur_assembly) + cur_assembly.forceMove(get_turf(src)) + user.put_in_hands(cur_assembly) + visible_message("\The [user] removes \the [cur_assembly] from \the [src].") + cur_assembly = null + return + else + to_chat(user, "There is no fuel rod in \the [src].") + return + +/obj/machinery/fusion_fuel_injector/proc/BeginInjecting() + if(!injecting && cur_assembly) + icon_state = "injector1" + injecting = 1 + use_power = 1 + +/obj/machinery/fusion_fuel_injector/proc/StopInjecting() + if(injecting) + injecting = 0 + icon_state = "injector0" + use_power = 0 + +/obj/machinery/fusion_fuel_injector/proc/Inject() + if(!injecting) + return + if(cur_assembly) + var/amount_left = 0 + for(var/reagent in cur_assembly.rod_quantities) + if(cur_assembly.rod_quantities[reagent] > 0) + var/amount = cur_assembly.rod_quantities[reagent] * fuel_usage + var/numparticles = round(amount * 1000) + if(numparticles < 1) + numparticles = 1 + var/obj/effect/accelerated_particle/A = new/obj/effect/accelerated_particle(get_turf(src), dir) + A.particle_type = reagent + A.additional_particles = numparticles - 1 + A.move(1) + if(cur_assembly) + cur_assembly.rod_quantities[reagent] -= amount + amount_left += cur_assembly.rod_quantities[reagent] + if(cur_assembly) + cur_assembly.percent_depleted = amount_left / cur_assembly.initial_amount + flick("injector-emitting",src) + else + StopInjecting() + +/obj/machinery/fusion_fuel_injector/verb/rotate_clock() + set category = "Object" + set name = "Rotate Generator (Clockwise)" + set src in view(1) + + if (usr.incapacitated() || usr.restrained() || anchored) + return + + src.dir = turn(src.dir, -90) + +/obj/machinery/fusion_fuel_injector/verb/rotate_anticlock() + set category = "Object" + set name = "Rotate Generator (Counter-clockwise)" + set src in view(1) + + if (usr.incapacitated() || usr.restrained() || anchored) + return + + src.dir = turn(src.dir, 90) \ No newline at end of file diff --git a/code/modules/power/fusion/fusion_circuits.dm b/code/modules/power/fusion/fusion_circuits.dm new file mode 100644 index 0000000000..d3bdc13fad --- /dev/null +++ b/code/modules/power/fusion/fusion_circuits.dm @@ -0,0 +1,89 @@ +/obj/item/weapon/circuitboard/fusion_core_control + name = "circuit board (fusion core controller)" + build_path = /obj/machinery/computer/fusion_core_control + origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4) + +/obj/item/weapon/circuitboard/fusion_fuel_compressor + name = "circuit board (fusion fuel compressor)" + build_path = /obj/machinery/fusion_fuel_compressor + board_type = "machine" + origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 4, TECH_MATERIAL = 4) + req_components = list( + /obj/item/weapon/stock_parts/manipulator/pico = 2, + /obj/item/weapon/stock_parts/matter_bin/super = 2, + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/stack/cable_coil = 5 + ) + +/obj/item/weapon/circuitboard/fusion_fuel_control + name = "circuit board (fusion fuel controller)" + build_path = /obj/machinery/computer/fusion_fuel_control + origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4) + +/obj/item/weapon/circuitboard/gyrotron_control + name = "circuit board (gyrotron controller)" + build_path = /obj/machinery/computer/gyrotron_control + origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4) + +/obj/item/weapon/circuitboard/fusion_core + name = "internal circuitry (fusion core)" + build_path = /obj/machinery/power/fusion_core + board_type = "machine" + origin_tech = list(TECH_BLUESPACE = 2, TECH_MAGNET = 4, TECH_POWER = 4) + req_components = list( + /obj/item/weapon/stock_parts/manipulator/pico = 2, + /obj/item/weapon/stock_parts/micro_laser/ultra = 1, + /obj/item/weapon/stock_parts/subspace/crystal = 1, + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/stack/cable_coil = 5 + ) + +/obj/item/weapon/circuitboard/fusion_injector + name = "internal circuitry (fusion fuel injector)" + build_path = /obj/machinery/fusion_fuel_injector + board_type = "machine" + origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 4, TECH_MATERIAL = 4) + req_components = list( + /obj/item/weapon/stock_parts/manipulator/pico = 2, + /obj/item/weapon/stock_parts/scanning_module/phasic = 1, + /obj/item/weapon/stock_parts/matter_bin/super = 1, + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/stack/cable_coil = 5 + ) + +/datum/design/circuit/fusion + name = "fusion core control console" + id = "fusion_core_control" + build_path = /obj/item/weapon/circuitboard/fusion_core_control + sort_string = "LAAAD" + req_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3, TECH_MATERIAL = 3) + +/datum/design/circuit/fusion/fuel_compressor + name = "fusion fuel compressor" + id = "fusion_fuel_compressor" + build_path = /obj/item/weapon/circuitboard/fusion_fuel_compressor + sort_string = "LAAAE" + +/datum/design/circuit/fusion/fuel_control + name = "fusion fuel control console" + id = "fusion_fuel_control" + build_path = /obj/item/weapon/circuitboard/fusion_fuel_control + sort_string = "LAAAF" + +/datum/design/circuit/fusion/gyrotron_control + name = "gyrotron control console" + id = "gyrotron_control" + build_path = /obj/item/weapon/circuitboard/gyrotron_control + sort_string = "LAAAG" + +/datum/design/circuit/fusion/core + name = "fusion core" + id = "fusion_core" + build_path = /obj/item/weapon/circuitboard/fusion_core + sort_string = "LAAAH" + +/datum/design/circuit/fusion/injector + name = "fusion fuel injector" + id = "fusion_injector" + build_path = /obj/item/weapon/circuitboard/fusion_injector + sort_string = "LAAAI" diff --git a/code/modules/power/fusion/fusion_particle_catcher.dm b/code/modules/power/fusion/fusion_particle_catcher.dm new file mode 100644 index 0000000000..38f35357d4 --- /dev/null +++ b/code/modules/power/fusion/fusion_particle_catcher.dm @@ -0,0 +1,41 @@ +/obj/effect/fusion_particle_catcher + icon = 'icons/effects/effects.dmi' + density = 1 + anchored = 1 + invisibility = 101 + var/obj/effect/fusion_em_field/parent + var/mysize = 0 + + light_color = COLOR_BLUE + +/obj/effect/fusion_particle_catcher/Destroy() + . =..() + parent.particle_catchers -= src + parent = null + +/obj/effect/fusion_particle_catcher/proc/SetSize(var/newsize) + name = "collector [newsize]" + mysize = newsize + UpdateSize() + +/obj/effect/fusion_particle_catcher/proc/AddParticles(var/name, var/quantity = 1) + if(parent && parent.size >= mysize) + parent.AddParticles(name, quantity) + return 1 + return 0 + +/obj/effect/fusion_particle_catcher/proc/UpdateSize() + if(parent.size >= mysize) + density = 1 + name = "collector [mysize] ON" + else + density = 0 + name = "collector [mysize] OFF" + +/obj/effect/fusion_particle_catcher/bullet_act(var/obj/item/projectile/Proj) + parent.AddEnergy(Proj.damage) + update_icon() + return 0 + +/obj/effect/fusion_particle_catcher/CanPass(var/atom/movable/mover, var/turf/target, var/height=0, var/air_group=0) + return ismob(mover) diff --git a/code/modules/power/fusion/fusion_reactions.dm b/code/modules/power/fusion/fusion_reactions.dm new file mode 100644 index 0000000000..f447164ea4 --- /dev/null +++ b/code/modules/power/fusion/fusion_reactions.dm @@ -0,0 +1,164 @@ +var/list/fusion_reactions + +/decl/fusion_reaction + var/p_react = "" // Primary reactant. + var/s_react = "" // Secondary reactant. + var/minimum_energy_level = 1 + var/energy_consumption = 0 + var/energy_production = 0 + var/radiation = 0 + var/instability = 0 + var/list/products = list() + var/minimum_reaction_temperature = 100 + +/decl/fusion_reaction/proc/handle_reaction_special(var/obj/effect/fusion_em_field/holder) + return 0 + +proc/get_fusion_reaction(var/p_react, var/s_react, var/m_energy) + if(!fusion_reactions) + fusion_reactions = list() + for(var/rtype in typesof(/decl/fusion_reaction) - /decl/fusion_reaction) + var/decl/fusion_reaction/cur_reaction = new rtype() + if(!fusion_reactions[cur_reaction.p_react]) + fusion_reactions[cur_reaction.p_react] = list() + fusion_reactions[cur_reaction.p_react][cur_reaction.s_react] = cur_reaction + if(!fusion_reactions[cur_reaction.s_react]) + fusion_reactions[cur_reaction.s_react] = list() + fusion_reactions[cur_reaction.s_react][cur_reaction.p_react] = cur_reaction + + if(fusion_reactions.Find(p_react)) + var/list/secondary_reactions = fusion_reactions[p_react] + if(secondary_reactions.Find(s_react)) + return fusion_reactions[p_react][s_react] + +// Material fuels +// deuterium +// tritium +// phoron +// supermatter + +// Virtual fuels +// helium-3 +// lithium-6 +// boron-11 + +// Basic power production reactions. +/decl/fusion_reaction/deuterium_deuterium + p_react = "deuterium" + s_react = "deuterium" + energy_consumption = 1 + energy_production = 2 + +// Advanced production reactions (todo) +/decl/fusion_reaction/deuterium_helium + p_react = "deuterium" + s_react = "helium-3" + energy_consumption = 1 + energy_production = 5 + radiation = 2 + +/decl/fusion_reaction/deuterium_tritium + p_react = "deuterium" + s_react = "tritium" + energy_consumption = 1 + energy_production = 1 + products = list("helium-3" = 1) + instability = 0.5 + radiation = 3 + +/decl/fusion_reaction/deuterium_lithium + p_react = "deuterium" + s_react = "lithium" + energy_consumption = 2 + energy_production = 0 + radiation = 3 + products = list("tritium"= 1) + instability = 1 + +// Unideal/material production reactions +/decl/fusion_reaction/oxygen_oxygen + p_react = "oxygen" + s_react = "oxygen" + energy_consumption = 10 + energy_production = 0 + instability = 5 + radiation = 5 + products = list("silicon"= 1) + +/decl/fusion_reaction/iron_iron + p_react = "iron" + s_react = "iron" + products = list("silver" = 1, "gold" = 1, "platinum" = 1) // Not realistic but w/e + energy_consumption = 10 + energy_production = 0 + instability = 2 + minimum_reaction_temperature = 10000 + +/decl/fusion_reaction/phoron_hydrogen + p_react = "hydrogen" + s_react = "phoron" + energy_consumption = 10 + energy_production = 0 + instability = 5 + products = list("mydrogen" = 1) + minimum_reaction_temperature = 8000 + +// VERY UNIDEAL REACTIONS. +/decl/fusion_reaction/phoron_supermatter + p_react = "supermatter" + s_react = "phoron" + energy_consumption = 0 + energy_production = 5 + radiation = 20 + instability = 20 + +/decl/fusion_reaction/phoron_supermatter/handle_reaction_special(var/obj/effect/fusion_em_field/holder) + + wormhole_event() + + var/turf/origin = get_turf(holder) + holder.Rupture() + qdel(holder) + var/radiation_level = rand(100, 200) + + // Copied from the SM for proof of concept. //Not any more --Cirra //Use the whole z proc --Leshana + radiation_repository.z_radiate(locate(1, 1, holder.z), radiation_level, 1) + + for(var/mob/living/mob in living_mob_list) + var/turf/T = get_turf(mob) + if(T && (holder.z == T.z)) + if(istype(mob, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = mob + H.hallucination += rand(100,150) + + for(var/obj/machinery/fusion_fuel_injector/I in range(world.view, origin)) + if(I.cur_assembly && I.cur_assembly.fuel_type == "supermatter") + explosion(get_turf(I), 1, 2, 3) + spawn(5) + if(I && I.loc) + qdel(I) + + sleep(5) + explosion(origin, 1, 2, 5) + + return 1 + + +// High end reactions. +/decl/fusion_reaction/boron_hydrogen + p_react = "boron" + s_react = "hydrogen" + minimum_energy_level = FUSION_HEAT_CAP * 0.5 + energy_consumption = 3 + energy_production = 15 + radiation = 3 + instability = 3 + +/decl/fusion_reaction/hydrogen_hydrogen + p_react = "hydrogen" + s_react = "hydrogen" + minimum_energy_level = FUSION_HEAT_CAP * 0.75 + energy_consumption = 0 + energy_production = 20 + radiation = 5 + instability = 5 diff --git a/code/modules/power/fusion/gyrotron/gyrotron.dm b/code/modules/power/fusion/gyrotron/gyrotron.dm new file mode 100644 index 0000000000..edf905ab06 --- /dev/null +++ b/code/modules/power/fusion/gyrotron/gyrotron.dm @@ -0,0 +1,57 @@ +var/list/gyrotrons = list() + +/obj/machinery/power/emitter/gyrotron + name = "gyrotron" + icon = 'icons/obj/machines/power/fusion.dmi' + desc = "It is a heavy duty industrial gyrotron suited for powering fusion reactors." + icon_state = "emitter-off" + req_access = list(access_engine) + use_power = 1 + active_power_usage = 50000 + + var/id_tag + var/rate = 3 + var/mega_energy = 1 + + +/obj/machinery/power/emitter/gyrotron/anchored + anchored = 1 + state = 2 + +/obj/machinery/power/emitter/gyrotron/initialize() + gyrotrons += src + active_power_usage = mega_energy * 50000 + . = ..() + +/obj/machinery/power/emitter/gyrotron/Destroy() + gyrotrons -= src + return ..() + +/obj/machinery/power/emitter/gyrotron/process() + active_power_usage = mega_energy * 50000 + . = ..() + +/obj/machinery/power/emitter/gyrotron/get_rand_burst_delay() + return rate * 10 + +/obj/machinery/power/emitter/gyrotron/get_burst_delay() + return rate * 10 + +/obj/machinery/power/emitter/gyrotron/get_emitter_beam() + var/obj/item/projectile/beam/emitter/E = ..() + E.damage = mega_energy * 50 + return E + +/obj/machinery/power/emitter/gyrotron/update_icon() + if (active && powernet && avail(active_power_usage)) + icon_state = "emitter-on" + else + icon_state = "emitter-off" + +/obj/machinery/power/emitter/gyrotron/attackby(var/obj/item/W, var/mob/user) + if(ismultitool(W)) + var/new_ident = input("Enter a new ident tag.", "Gyrotron", id_tag) as null|text + if(new_ident && user.Adjacent(src)) + id_tag = new_ident + return + return ..() \ No newline at end of file diff --git a/code/modules/power/fusion/gyrotron/gyrotron_control.dm b/code/modules/power/fusion/gyrotron/gyrotron_control.dm new file mode 100644 index 0000000000..9f6c59c753 --- /dev/null +++ b/code/modules/power/fusion/gyrotron/gyrotron_control.dm @@ -0,0 +1,97 @@ +/obj/machinery/computer/gyrotron_control + name = "gyrotron control console" + icon = 'icons/obj/machines/power/fusion.dmi' + icon_state = "engine" + light_color = COLOR_BLUE + + var/id_tag + var/scan_range = 25 + +/obj/machinery/computer/gyrotron_control/attack_ai(var/mob/user) + attack_hand(user) + +/obj/machinery/computer/gyrotron_control/attack_hand(var/mob/user) + add_fingerprint(user) + interact(user) + +/obj/machinery/computer/gyrotron_control/interact(var/mob/user) + + if(!id_tag) + to_chat(user, "This console has not been assigned an ident tag. Please contact your system administrator or conduct a manual update with a standard multitool.") + return + + var/dat = "Gyrotron controller #[id_tag]" + + dat = "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/obj/machinery/power/emitter/gyrotron/G in gyrotrons) + if(!G || G.id_tag != id_tag || get_dist(src, G) > scan_range) + continue + + dat += "" + if(G.state != 2 || (G.stat & (NOPOWER | BROKEN))) //Error data not found. + dat += "" + dat += "" + dat += "" + else + dat += "" + dat += "" + dat += "" + + dat += "
ModeFire DelayPower
ERRORERRORERROR[G.active ? "Emitting" : "Standing By"][G.rate][G.mega_energy]
" + + var/datum/browser/popup = new(user, "gyrotron_controller_[id_tag]", "Gyrotron Remote Control Console", 500, 400, src) + popup.set_content(dat) + popup.open() + add_fingerprint(user) + user.set_machine(src) + +/obj/machinery/computer/gyrotron_control/Topic(var/href, var/list/href_list) + . = ..() + if(.) + return + + if(stat & (NOPOWER | BROKEN)) + return + + var/obj/machinery/power/emitter/gyrotron/G = locate(href_list["machine"]) + if(!G || G.id_tag != id_tag || get_dist(src, G) > scan_range) + return + + if(href_list["modifypower"]) + var/new_val = input("Enter new emission power level (1 - 50)", "Modifying power level", G.mega_energy) as num + if(!new_val) + to_chat(usr, "That's not a valid number.") + return 1 + G.mega_energy = Clamp(new_val, 1, 50) + G.active_power_usage = G.mega_energy * 1500 + updateUsrDialog() + return 1 + + if(href_list["modifyrate"]) + var/new_val = input("Enter new emission delay between 1 and 10 seconds.", "Modifying emission rate", G.rate) as num + if(!new_val) + to_chat(usr, "That's not a valid number.") + return 1 + G.rate = Clamp(new_val, 1, 10) + updateUsrDialog() + return 1 + + if(href_list["toggle"]) + G.activate(usr) + updateUsrDialog() + return 1 + + return 0 + +/obj/machinery/computer/gyrotron_control/attackby(var/obj/item/W, var/mob/user) + if(ismultitool(W)) + var/new_ident = input("Enter a new ident tag.", "Gyrotron Control", id_tag) as null|text + if(new_ident && user.Adjacent(src)) + id_tag = new_ident + return + return ..() diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index d17b69f93f..c186118427 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -75,7 +75,7 @@ src.active = 1 user << "You turn on [src]." src.shot_number = 0 - src.fire_delay = 100 + src.fire_delay = get_initial_fire_delay() message_admins("Emitter turned on by [key_name(user, user.client)](?) in ([x],[y],[z] - JMP)",0,1) log_game("Emitter turned on by [user.ckey]([user]) in ([x],[y],[z])") investigate_log("turned on by [user.key]","singulo") @@ -119,10 +119,10 @@ src.last_shot = world.time if(src.shot_number < burst_shots) - src.fire_delay = 2 + src.fire_delay = get_burst_delay() //R-UST port src.shot_number ++ else - src.fire_delay = rand(min_burst_delay, max_burst_delay) + src.fire_delay = get_rand_burst_delay() //R-UST port src.shot_number = 0 //need to calculate the power per shot as the emitter doesn't fire continuously. @@ -135,7 +135,7 @@ s.set_up(5, 1, src) s.start() - var/obj/item/projectile/beam/emitter/A = new /obj/item/projectile/beam/emitter( src.loc ) + var/obj/item/projectile/beam/emitter/A = get_emitter_beam() A.damage = round(power_per_shot/EMITTER_DAMAGE_POWER_TRANSFER) A.launch( get_step(src.loc, src.dir) ) @@ -266,3 +266,16 @@ user << "\The [src] is damaged." if(77 to 99) user << "\The [src] is slightly damaged." + +//R-UST port +/obj/machinery/power/emitter/proc/get_initial_fire_delay() + return 100 + +/obj/machinery/power/emitter/proc/get_rand_burst_delay() + return rand(min_burst_delay, max_burst_delay) + +/obj/machinery/power/emitter/proc/get_burst_delay() + return 2 + +/obj/machinery/power/emitter/proc/get_emitter_beam() + return new /obj/item/projectile/beam/emitter(get_turf(src)) \ No newline at end of file diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm index 9a1aead8d9..bd7338f00e 100644 --- a/code/modules/power/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/singularity/particle_accelerator/particle.dm @@ -43,6 +43,21 @@ toxmob(A) if((istype(A,/obj/machinery/the_singularitygen))||(istype(A,/obj/singularity/))) A:energy += energy + //R-UST port + 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)) + collided_core.owned_field.plasma_temperature += mega_energy + collided_core.owned_field.energy += energy + loc = null + 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)) + PC.parent.plasma_temperature += mega_energy + PC.parent.energy += energy + loc = null return diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index c04e564ac8..4ab1519b3f 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -43,6 +43,31 @@ color = "#003333" strength = 10 +//R-UST port +// Produced during deuterium synthesis. Super poisonous, SUPER flammable (doesn't need oxygen to burn). +/datum/reagent/toxin/phoroxygen + name = "Oxyphoron" + id = "oxyphoron" + description = "An exceptionally flammable molecule formed from deuterium synthesis." + strength = 80 + var/fire_mult = 30 + +/datum/reagent/toxin/phoroxygen/touch_mob(var/mob/living/L, var/amount) + if(istype(L)) + L.adjust_fire_stacks(amount / fire_mult) + +/datum/reagent/toxin/phoroxygen/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) + M.take_organ_damage(0, removed * 0.1) //being splashed directly with oxyphoron causes minor chemical burns + if(prob(10 * fire_mult)) + M.pl_effects() + +/datum/reagent/toxin/phoroxygen/touch_turf(var/turf/simulated/T) + if(!istype(T)) + return + T.assume_gas("oxygen", ceil(volume/2), T20C) + T.assume_gas("phoron", ceil(volume/2), T20C) + remove_self(volume) + /datum/reagent/toxin/spidertoxin name = "Spidertoxin" id = "spidertoxin" diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 0f0ba3e9b0..0cdb0cd35b 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -1842,7 +1842,6 @@ required_reagents = list("tea" = 5, "berryjuice" = 1) result_amount = 6 -/datum/chemical_reaction/drinks/sakebomb name = "Sake Bomb" id = "sakebomb" result = "sakebomb" @@ -1863,7 +1862,6 @@ required_reagents = list("sake" = 2, "vodka" = 2, "tomatojuice" = 1) result_amount = 5 -/datum/chemical_reaction/drinks/tokyorose name = "Tokyo Rose" id = "tokyorose" result = "tokyorose" @@ -1906,9 +1904,6 @@ result_amount = 3 /datum/chemical_reaction/drinks/euphoria - name = "Euphoria" - id = "euphoria" - result = "euphoria" required_reagents = list("specialwhiskey" = 1, "cognac" = 2) result_amount = 3 @@ -1916,7 +1911,6 @@ name = "Xanadu Cannon" id = "xanaducannon" result = "xanaducannon" - required_reagents = list("ale" = 1, "dr_gibb" = 1) result_amount = 2 /datum/chemical_reaction/drinks/debugger @@ -1945,4 +1939,17 @@ id = "chrysanthemum" result = "chrysanthemum" required_reagents = list("sake" = 1, "melonliquor" = 1) - result_amount = 2 \ No newline at end of file + result_amount = 2 + +/datum/chemical_reaction/deuterium + name = "Deuterium" + id = "deuterium" + result = null + required_reagents = list("water" = 10) + catalysts = list("oxyphoron" = 5) + result_amount = 1 + +/datum/chemical_reaction/deuterium/on_reaction(var/datum/reagents/holder, var/created_volume) + var/turf/T = get_turf(holder.my_atom) + if(istype(T)) new /obj/item/stack/material/deuterium(T, created_volume) + return \ No newline at end of file diff --git a/code/modules/supermatter/setup_supermatter.dm b/code/modules/supermatter/setup_supermatter.dm new file mode 100644 index 0000000000..01aaeb0a21 --- /dev/null +++ b/code/modules/supermatter/setup_supermatter.dm @@ -0,0 +1,251 @@ +#define SETUP_OK 1 // All good +#define SETUP_WARNING 2 // Something that shouldn't happen happened, but it's not critical so we will continue +#define SETUP_ERROR 3 // Something bad happened, and it's important so we won't continue setup. +#define SETUP_DELAYED 4 // Wait for other things first. + + +#define ENERGY_NITROGEN 115 // Roughly 8 emitter shots. +#define ENERGY_CARBONDIOXIDE 150 // Roughly 10 emitter shots. +#define ENERGY_PHORON 300 // Roughly 20 emitter shots. Phoron can take more but this is enough to max out both SMESs anyway. + + +/datum/admins/proc/setup_supermatter() + set category = "Debug" + set name = "Setup Supermatter" + set desc = "Allows you to start the Supermatter engine." + + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + + var/response = input(usr, "Are you sure? This will start up the engine with selected gas as coolant.", "Engine setup") as null|anything in list("N2", "CO2", "PH", "Abort") + if(!response || response == "Abort") + return + + var/errors = 0 + var/warnings = 0 + var/success = 0 + + log_and_message_admins("## SUPERMATTER SETUP - Setup initiated by [usr] using coolant type [response].") + + // CONFIGURATION PHASE + // Coolant canisters, set types according to response. + for(var/obj/effect/engine_setup/coolant_canister/C in world) + switch(response) + if("N2") + C.canister_type = /obj/machinery/portable_atmospherics/canister/nitrogen/engine_setup/ + continue + if("CO2") + C.canister_type = /obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/ + continue + if("PH") + C.canister_type = /obj/machinery/portable_atmospherics/canister/phoron/engine_setup/ + continue + + for(var/obj/effect/engine_setup/core/C in world) + switch(response) + if("N2") + C.energy_setting = ENERGY_NITROGEN + continue + if("CO2") + C.energy_setting = ENERGY_CARBONDIOXIDE + continue + if("PH") + C.energy_setting = ENERGY_PHORON + continue + + for(var/obj/effect/engine_setup/filter/F in world) + F.coolant = response + + var/list/delayed_objects = list() + // SETUP PHASE + for(var/obj/effect/engine_setup/S in world) + var/result = S.activate(0) + switch(result) + if(SETUP_OK) + success++ + continue + if(SETUP_WARNING) + warnings++ + continue + if(SETUP_ERROR) + errors++ + log_and_message_admins("## SUPERMATTER SETUP - Error encountered! Aborting.") + break + if(SETUP_DELAYED) + delayed_objects.Add(S) + continue + + if(!errors) + for(var/obj/effect/engine_setup/S in delayed_objects) + var/result = S.activate(1) + switch(result) + if(SETUP_OK) + success++ + continue + if(SETUP_WARNING) + warnings++ + continue + if(SETUP_ERROR) + errors++ + log_and_message_admins("## SUPERMATTER SETUP - Error encountered! Aborting.") + break + + log_and_message_admins("## SUPERMATTER SETUP - Setup completed with [errors] errors, [warnings] warnings and [success] successful steps.") + + return + + + +/obj/effect/engine_setup/ + name = "Engine Setup Marker" + desc = "You shouldn't see this." + invisibility = 101 + anchored = 1 + density = 0 + icon = 'icons/mob/screen1.dmi' + icon_state = "x3" + +/obj/effect/engine_setup/proc/activate(var/last = 0) + return 1 + + + +// Tries to locate a pump, enables it, and sets it to MAX. Triggers warning if unable to locate a pump. +/obj/effect/engine_setup/pump_max/ + name = "Pump Setup Marker" + +/obj/effect/engine_setup/pump_max/activate() + ..() + var/obj/machinery/atmospherics/binary/pump/P = locate() in get_turf(src) + if(!P) + log_and_message_admins("## WARNING: Unable to locate pump at [x] [y] [z]!") + return SETUP_WARNING + P.target_pressure = P.max_pressure_setting + P.use_power = 1 + P.update_icon() + return SETUP_OK + + + +// Spawns an empty canister on this turf, if it has a connector port. Triggers warning if unable to find a connector port +/obj/effect/engine_setup/empty_canister/ + name = "Empty Canister Marker" + +/obj/effect/engine_setup/empty_canister/activate() + ..() + var/obj/machinery/atmospherics/portables_connector/P = locate() in get_turf(src) + if(!P) + log_and_message_admins("## WARNING: Unable to locate connector port at [x] [y] [z]!") + return SETUP_WARNING + new/obj/machinery/portable_atmospherics/canister(get_turf(src)) // Canisters automatically connect to connectors in New() + return SETUP_OK + + + + +// Spawns a coolant canister on this turf, if it has a connector port. +// Triggers error when unable to locate connector port or when coolant canister type is unset. +/obj/effect/engine_setup/coolant_canister/ + name = "Coolant Canister Marker" + var/canister_type = null + +/obj/effect/engine_setup/coolant_canister/activate() + ..() + var/obj/machinery/atmospherics/portables_connector/P = locate() in get_turf(src) + if(!P) + log_and_message_admins("## ERROR: Unable to locate coolant connector port at [x] [y] [z]!") + return SETUP_ERROR + if(!canister_type) + log_and_message_admins("## ERROR: Canister type unset at [x] [y] [z]!") + return SETUP_ERROR + new canister_type(get_turf(src)) + return SETUP_OK + + + +// Energises the supermatter. Errors when unable to locate supermatter. +/obj/effect/engine_setup/core/ + name = "Supermatter Core Marker" + var/energy_setting = 0 + +/obj/effect/engine_setup/core/activate(var/last = 0) + if(!last) + return SETUP_DELAYED + ..() + var/obj/machinery/power/supermatter/SM = locate() in get_turf(src) + if(!SM) + log_and_message_admins("## ERROR: Unable to locate supermatter core at [x] [y] [z]!") + return SETUP_ERROR + if(!energy_setting) + log_and_message_admins("## ERROR: Energy setting unset at [x] [y] [z]!") + return SETUP_ERROR + SM.power = energy_setting + return SETUP_OK + + + +// Tries to enable the SMES on max input/output settings. With load balancing it should be fine as long as engine outputs at least ~500kW +/obj/effect/engine_setup/smes/ + name = "SMES Marker" + +/obj/effect/engine_setup/smes/activate() + ..() + var/obj/machinery/power/smes/S = locate() in get_turf(src) + if(!S) + log_and_message_admins("## WARNING: Unable to locate SMES unit at [x] [y] [z]!") + return SETUP_WARNING + S.input_attempt = 1 + S.output_attempt = 1 + S.input_level = S.input_level_max + S.output_level = S.output_level_max + S.update_icon() + return SETUP_OK + + + +// Sets up filters. This assumes filters are set to filter out N2 back to the core loop by default! +/obj/effect/engine_setup/filter/ + name = "Omni Filter Marker" + var/coolant = null + +/obj/effect/engine_setup/filter/activate() + ..() + var/obj/machinery/atmospherics/omni/filter/F = locate() in get_turf(src) + if(!F) + log_and_message_admins("## WARNING: Unable to locate omni filter at [x] [y] [z]!") + return SETUP_WARNING + if(!coolant) + log_and_message_admins("## WARNING: No coolant type set at [x] [y] [z]!") + return SETUP_WARNING + + // Non-nitrogen coolant, adjust the filter's config first. + if(coolant != "N2") + for(var/datum/omni_port/P in F.ports) + if(P.mode != ATM_N2) + continue + if(coolant == "PH") + P.mode = ATM_P + break + else if(coolant == "CO2") + P.mode = ATM_CO2 + break + else + log_and_message_admins("## WARNING: Inapropriate filter coolant type set at [x] [y] [z]!") + return SETUP_WARNING + F.rebuild_filtering_list() + + F.use_power = 1 + F.update_icon() + return SETUP_OK + + +#undef SETUP_OK +#undef SETUP_WARNING +#undef SETUP_ERROR +#undef SETUP_DELAYED +#undef ENERGY_NITROGEN +#undef ENERGY_CARBONDIOXIDE +#undef ENERGY_PHORON diff --git a/icons/obj/machines/power/fusion.dmi b/icons/obj/machines/power/fusion.dmi new file mode 100644 index 0000000000000000000000000000000000000000..be236a145e92ccaeff823cf155eb368bcdad8873 GIT binary patch literal 41882 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 literal 0 HcmV?d00001 diff --git a/polaris.dme b/polaris.dme index 74efedd213..00d6194f6f 100644 --- a/polaris.dme +++ b/polaris.dme @@ -1872,6 +1872,19 @@ #include "code\modules\power\antimatter\containment_jar.dm" #include "code\modules\power\antimatter\control.dm" #include "code\modules\power\antimatter\shielding.dm" +#include "code\modules\power\fusion\_setup.dm" +#include "code\modules\power\fusion\fusion_circuits.dm" +#include "code\modules\power\fusion\fusion_particle_catcher.dm" +#include "code\modules\power\fusion\fusion_reactions.dm" +#include "code\modules\power\fusion\core\_core.dm" +#include "code\modules\power\fusion\core\core_control.dm" +#include "code\modules\power\fusion\core\core_field.dm" +#include "code\modules\power\fusion\fuel_assembly\fuel_assembly.dm" +#include "code\modules\power\fusion\fuel_assembly\fuel_compressor.dm" +#include "code\modules\power\fusion\fuel_assembly\fuel_control.dm" +#include "code\modules\power\fusion\fuel_assembly\fuel_injector.dm" +#include "code\modules\power\fusion\gyrotron\gyrotron.dm" +#include "code\modules\power\fusion\gyrotron\gyrotron_control.dm" #include "code\modules\power\sensors\powernet_sensor.dm" #include "code\modules\power\sensors\sensor_monitoring.dm" #include "code\modules\power\singularity\act.dm" @@ -2087,6 +2100,7 @@ #include "code\modules\spells\targeted\projectile\fireball.dm" #include "code\modules\spells\targeted\projectile\magic_missile.dm" #include "code\modules\spells\targeted\projectile\projectile.dm" +#include "code\modules\supermatter\setup_supermatter.dm" #include "code\modules\supermatter\supermatter.dm" #include "code\modules\surgery\_defines.dm" #include "code\modules\surgery\bones.dm" From 3d3ff303c7a10624eb432039f5a5520fcb1e50fe Mon Sep 17 00:00:00 2001 From: silveryferret Date: Wed, 19 Jul 2017 23:14:15 -0400 Subject: [PATCH 2/7] Adds missed Oxyphoron reaction, moves Deuterium/fifty into obj/fiftyspawner/deuterium --- code/modules/materials/fifty_spawner_mats.dm | 7 ++++++- code/modules/materials/material_sheets.dm | 3 --- code/modules/reagents/Chemistry-Recipes.dm | 7 +++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/code/modules/materials/fifty_spawner_mats.dm b/code/modules/materials/fifty_spawner_mats.dm index 53b65f7ab2..95f10e2012 100644 --- a/code/modules/materials/fifty_spawner_mats.dm +++ b/code/modules/materials/fifty_spawner_mats.dm @@ -94,4 +94,9 @@ /obj/fiftyspawner/phoronrglass name = "stack of reinforced borosilicate glass" - material = "material/glass/phoronrglass" \ No newline at end of file + material = "material/glass/phoronrglass" + +//R-UST port +/obj/fiftyspawner/deuterium + name = "stack of deuterium" + material = "material/deuterium" \ No newline at end of file diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index 6a957b1eb0..2f9949098b 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -166,9 +166,6 @@ default_type = "deuterium" apply_colour = 1 -/obj/item/stack/material/deuterium/fifty - amount = 50 - /obj/item/stack/material/steel name = DEFAULT_WALL_MATERIAL icon_state = "sheet-metal" diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 0cdb0cd35b..231984090c 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -1941,6 +1941,13 @@ required_reagents = list("sake" = 1, "melonliquor" = 1) result_amount = 2 +/datum/chemical_reaction/oxyphoron + name = "Oxyphoron" + id = "oxyphoron" + result = "oxyphoron" + required_reagents = list("water" = 1, "phoron" = 1) + result_amount = 2 + /datum/chemical_reaction/deuterium name = "Deuterium" id = "deuterium" From ad5cfd0087bd077ce2a0b8e6af2d45deac4ebc15 Mon Sep 17 00:00:00 2001 From: silveryferret Date: Wed, 19 Jul 2017 23:22:36 -0400 Subject: [PATCH 3/7] Fixes Chemistry-Recipes.dm. Not 100% sure how this happened, mergetool must've screwed up somehow. --- code/modules/reagents/Chemistry-Recipes.dm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 231984090c..c25654a2e1 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -1842,6 +1842,7 @@ required_reagents = list("tea" = 5, "berryjuice" = 1) result_amount = 6 +/datum/chemical_reaction/drinks/sakebomb name = "Sake Bomb" id = "sakebomb" result = "sakebomb" @@ -1862,6 +1863,7 @@ required_reagents = list("sake" = 2, "vodka" = 2, "tomatojuice" = 1) result_amount = 5 +/datum/chemical_reaction/drinks/tokyorose name = "Tokyo Rose" id = "tokyorose" result = "tokyorose" @@ -1904,6 +1906,9 @@ result_amount = 3 /datum/chemical_reaction/drinks/euphoria + name = "Euphoria" + id = "euphoria" + result = "euphoria" required_reagents = list("specialwhiskey" = 1, "cognac" = 2) result_amount = 3 @@ -1911,6 +1916,7 @@ name = "Xanadu Cannon" id = "xanaducannon" result = "xanaducannon" + required_reagents = list("ale" = 1, "dr_gibb" = 1) result_amount = 2 /datum/chemical_reaction/drinks/debugger @@ -1941,6 +1947,7 @@ required_reagents = list("sake" = 1, "melonliquor" = 1) result_amount = 2 +//R-UST Port /datum/chemical_reaction/oxyphoron name = "Oxyphoron" id = "oxyphoron" From f6af2c7733d84b308bfd36c5f1d7feaa3d78b7b5 Mon Sep 17 00:00:00 2001 From: silveryferret Date: Fri, 21 Jul 2017 23:29:31 -0400 Subject: [PATCH 4/7] Fixes slight oversight during copypasting from Europa's R-UST code. --- code/game/machinery/atmoalter/canister.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index d15c86048e..de0d7975c3 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -445,6 +445,6 @@ update_flag /obj/machinery/portable_atmospherics/canister/phoron/engine_setup/New() ..() - src.air_contents.adjust_gas(GAS_FUEL, MolesForPressure()) + src.air_contents.adjust_gas("phoron", MolesForPressure()) src.update_icon() return 1 \ No newline at end of file From a507d54e28d393e75e37a5412f6798c75a047615 Mon Sep 17 00:00:00 2001 From: silveryferret Date: Fri, 21 Jul 2017 23:32:16 -0400 Subject: [PATCH 5/7] Removes GAS_FUEL define, since it's not needed --- code/__defines/atmos.dm | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/code/__defines/atmos.dm b/code/__defines/atmos.dm index 6b4299247d..bd9175aab1 100644 --- a/code/__defines/atmos.dm +++ b/code/__defines/atmos.dm @@ -94,7 +94,4 @@ #define ATMOSTANK_OXYGEN 40000 // O2 is also important for airmix, but not as much as N2 as it's only 21% of it. #define ATMOSTANK_CO2 25000 // CO2 and PH are not critically important for station, only for toxins and alternative coolants, no need to store a lot of those. #define ATMOSTANK_PHORON 25000 -#define ATMOSTANK_NITROUSOXIDE 10000 // N2O doesn't have a real useful use, i guess it's on station just to allow refilling of sec's riot control canisters? - -//R-UST port -#define GAS_FUEL "fuel" \ No newline at end of file +#define ATMOSTANK_NITROUSOXIDE 10000 // N2O doesn't have a real useful use, i guess it's on station just to allow refilling of sec's riot control canisters? \ No newline at end of file From af7fe828d824e2cede8989ac6080d5503a45cb0e Mon Sep 17 00:00:00 2001 From: silveryferret Date: Fri, 21 Jul 2017 23:47:53 -0400 Subject: [PATCH 6/7] Removes more unneeded gas_fuel defines/references, adds XGM_GAS_FUSION_FUEL flag to phoron --- code/defines/gases.dm | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/code/defines/gases.dm b/code/defines/gases.dm index b1c4cced57..74cb0d9379 100644 --- a/code/defines/gases.dm +++ b/code/defines/gases.dm @@ -18,24 +18,6 @@ specific_heat = 30 // J/(mol*K) molar_mass = 0.044 // kg/mol -//R-UST port -/decl/xgm_gas/fuel - id = GAS_FUEL - name = "Fuel" - - //Note that this has a significant impact on TTV yield. - //Because it is so high, any leftover phoron soaks up a lot of heat and drops the yield pressure. - specific_heat = 200 // J/(mol*K) - - //Hypothetical group 14 (same as carbon), period 8 element. - //Using multiplicity rule, it's atomic number is 162 - //and following a N/Z ratio of 1.5, the molar mass of a monatomic gas is: - molar_mass = 0.405 // kg/mol - - tile_overlay = "gas_dense" - overlay_limit = 0.7 - flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT | XGM_GAS_FUSION_FUEL - /decl/xgm_gas/phoron id = "phoron" name = "Phoron" @@ -51,7 +33,7 @@ tile_overlay = "phoron" overlay_limit = 0.7 - flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT + flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT | XGM_GAS_FUSION_FUEL //R-UST port, adding XGM_GAS_FUSION_FUEL flag. /decl/xgm_gas/volatile_fuel id = "volatile_fuel" From d9526bde2c1f500c7aef52475ca78eb819f9065e Mon Sep 17 00:00:00 2001 From: silveryferret Date: Sat, 22 Jul 2017 17:24:08 -0400 Subject: [PATCH 7/7] Puts initial_fire_delay and burst_delay into variables instead of hardcoded numbers, as requested. --- code/modules/power/singularity/emitter.dm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index c186118427..04ba39f0d4 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -24,6 +24,9 @@ var/state = 0 var/locked = 0 + var/burst_delay = 2 + var/initial_fire_delay = 100 + var/integrity = 80 @@ -269,13 +272,13 @@ //R-UST port /obj/machinery/power/emitter/proc/get_initial_fire_delay() - return 100 + return initial_fire_delay /obj/machinery/power/emitter/proc/get_rand_burst_delay() return rand(min_burst_delay, max_burst_delay) /obj/machinery/power/emitter/proc/get_burst_delay() - return 2 + return burst_delay /obj/machinery/power/emitter/proc/get_emitter_beam() return new /obj/item/projectile/beam/emitter(get_turf(src)) \ No newline at end of file