mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-18 02:24:11 +01:00
394c3b0108
* Space Ninja Antag Datum * Small fixes * Ninja outfit, ekatana, actions * Ninja scanner and spans * Ninja uplink implant * Ninja bombs * Ninja bomb flare grants * Ninja modsuit, objective payouts * Fixes objectives * Ninja stealth fix, modsuit sprites and final implementations * Lints * Ninja scanner sprites * Ninja Suit and Energy Shuriken Sprites * Fixes config, Adjusts stim ability, adds shuriken printer and e-shurikens, uplink stuff * Fixes a duplicate icon * Attack chain * Energy katana item sprite * Ninja net gun * Brazil * Spawning ninjas, ninja on traitor panel, ninja spawn sound * Linter * Objective stuff * Fixes n grey suits * Objectives * Trim intro sound * Event, spawn point * Ninja Outfits, Vox Check, Bug Fixes, Mirror at ninja spawn * Address code review * Oops * Uncomments an important thingy * Update: Gave space ninjas access to maints and an agent ID card. Updated net description. * Update scanner examine * Removes excess file * Makes ninja scanner fit in belts and ninja suits. Gives them NV goggles * Fixes modsuit sprite issue * Energy shuriken fixes * Scanner fix * Printer fix * Fixes some runtimes * Fixes capture teleport * Clothes rename * Buffs energy katana, adds soft no-drop to ekatana, buffs ninja modsuit, fixes equip bug * Adds research levels to ninja gear * Fixes ninja capture issue * Remaps ninja dojo * Better cuff removal * Forgor * Fixes action availability * Updates walls at dojo * Improves ninja modsuit * Windoors can now be opened with the katana * Adds advanced pinpointer to ninja uplink * Fixed energy nets sticking * Fixes slime people ninjas * Adds reroll to ninja capture if target is DNR. Prevents off Z-level targets * Oop * Adds reactor sabotage objective. * Fixes ninja cuffs * Removes Carp scroll from uplink. Adds Krav Implant to uplink
1112 lines
44 KiB
Plaintext
1112 lines
44 KiB
Plaintext
// The steps for repairing the reactor
|
|
#define REACTOR_NEEDS_DIGGING 1
|
|
#define REACTOR_NEEDS_CROWBAR 2
|
|
#define REACTOR_NEEDS_PLASTITANIUM 3
|
|
#define REACTOR_NEEDS_WRENCH 4
|
|
#define REACTOR_NEEDS_WELDING 5
|
|
#define REACTOR_NEEDS_PLASTEEL 6
|
|
#define REACTOR_NEEDS_SCREWDRIVER 7
|
|
|
|
#define REACTOR_LIGHT_COLOR "#569fff"
|
|
|
|
#define TOTAL_CONTROL_RODS 5 //! The max number of control rods.
|
|
|
|
#define MIN_CHAMBERS_TO_OVERLOAD 20 //! The amount of connected chambers required before the overload is valid
|
|
|
|
#define EVENT_MODIFIER 0.5 //! Multiplies the commonality of dangerous events.
|
|
|
|
#define HEAT_CAP 40000 //! the highest temp before we artificially cap it
|
|
#define AVERAGE_HEAT_THRESHOLD 50 //! The threshold the average heat-per-rod must exceed to generate coefficient.
|
|
#define TOTAL_HEAT_THRESHOLD 600 //! The temp (in K) needed to begin generating coefficient.
|
|
#define HEAT_CONVERSION_RATIO 400 //! How much heat over the threshold = an extra coefficient point.
|
|
#define REACTIVITY_COEFFICIENT_CAP 30 //! The highest that reactivity coefficient can be
|
|
|
|
// If integrity percent remaining is less than these values, the monitor sets off the relevant alarm.
|
|
#define NGCR_MELTDOWN_PERCENT 5
|
|
#define NGCR_EMERGENCY_PERCENT 25
|
|
#define NGCR_DANGER_PERCENT 50
|
|
#define NGCR_WARNING_PERCENT 99
|
|
#define CRITICAL_TEMPERATURE 10000
|
|
#define WARNING_POINT 50 //! Begin sending warning messages
|
|
#define EMERGENCY_POINT 700 //! Begin sending warning messages over common
|
|
#define MELTDOWN_POINT 1000 //! The damage cap where meltdown occurs. higher = longer to meltdown
|
|
|
|
#define NGCR_COUNTDOWN_TIME 30 SECONDS //! How long the meltdown countdown lasts
|
|
#define WARNING_DELAY 60 //! Time in deciseconds between warnings
|
|
|
|
#define HEAT_DAMAGE_RATE 500 //! The rate at which damage increases due to heat
|
|
#define MOL_MINIMUM 30 //! The amount of mols of gas needed before it begins to take damage while operational
|
|
#define PRESSURE_MAXIMUM 20000 //! The highest safe pressure allowed by the reactor
|
|
#define PRESSURE_DAMAGE 0.5 //! The minimum damage caused by overpresurization
|
|
#define DAMAGE_MINIMUM 0.002 //! The minimum amount of damage done when taking any damage
|
|
#define DAMAGE_MAXIMUM 8 //! The highest amount of damage done when taking damage
|
|
#define MOL_DAMAGE_MULTIPLIER 1 //! An adjuster for damage balance from no gas
|
|
#define HEAT_DAMAGE_MULTIPLIER 1 //! An adjuster for damage balance from high heat
|
|
#define EXPLOSION_MODIFIER 1.5 //! Adjusts the size of the engine explosion
|
|
|
|
#define MOLE_BONUS_THRESHOLD 800 //! The minimum number of moles needed to begin accruing multiplier.
|
|
#define MOLE_BONUS_COMPONENT 250 //! How many moles are required for one "unit" of modifier increase. Used in the math calculation.
|
|
#define N2_OVERHEAT_BONUS 600 //! The overheat threshold bonus that N2 coolant provides
|
|
#define N2_EVENT_MODIFIER 20 //! The negative event chance from N2.
|
|
#define N2O_OVERHEAT_BONUS 300 //! The overheat threshold bonus that N2O coolant provides
|
|
#define N2O_EVENT_MODIFIER 20 //! The negative event chance from N2O.
|
|
#define CO2_EVENT_MODIFIER 60 //! The negative event chance from CO2.
|
|
#define O2_EVENT_MODIFIER -50 //! The POSITIVE event chance from O2.
|
|
#define O2_REACTIVITY_BONUS 0.8 //! The highest amount of reactivity that O2 coolant provides
|
|
#define PLASMA_REACTIVITY_BONUS 0.2 //! The highest amount of reactivity that plasma coolant provides
|
|
#define PLASMA_OVERHEAT_BONUS 200 //! The overheat threshold bonus that plasma coolant provides
|
|
|
|
/obj/machinery/atmospherics/fission_reactor
|
|
name = "Nuclear Fission Reactor"
|
|
desc = "An ancient yet reliable form of power generation utilizing fissile materials to generate heat."
|
|
icon = 'icons/goonstation/objects/reactor.dmi'
|
|
icon_state = "reactor_off"
|
|
density = TRUE
|
|
pass_flags_self = PASSTAKE
|
|
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
|
|
pixel_x = -32
|
|
flags_2 = RAD_PROTECT_CONTENTS_2 | RAD_NO_CONTAMINATE_2 | IMMUNE_TO_SHUTTLECRUSH_2 | NO_MALF_EFFECT_2 | CRITICAL_ATOM_2
|
|
|
|
/// Holds the list for the connected reactor chambers to take data from
|
|
var/list/connected_chambers = list()
|
|
/// Prevents the reactor from making power under certain circumstances
|
|
var/can_create_power = TRUE
|
|
/// The total amount of heat gathered in the reactor
|
|
var/final_heat
|
|
/// The total amount of power being generated by the engine (in watts)
|
|
var/final_power
|
|
/// A modifier for general reactivity, based off of heat production. Cant go below 1
|
|
var/reactivity_multiplier = 1
|
|
/// How many functional control rods does the reactor have?
|
|
var/control_rods_remaining = 5
|
|
/// What repair step is the reactor on?
|
|
var/repair_step = 1
|
|
/// The desired percentage the engineers set the reactor control rods
|
|
var/desired_power = 0
|
|
/// What percentage are the reactor control rods running at? Minimum raised for each broken control rod
|
|
var/operating_power = 0
|
|
/// The amount of damage we have currently. Use adjust_damage() to change it
|
|
var/damage = 0
|
|
/// Is this the primary station engine that spawns in round?
|
|
var/primary_engine = FALSE
|
|
/// Has the engine warmed up enough to start
|
|
var/starting_up = TRUE
|
|
/// Is the engine currently fully offline
|
|
var/offline = TRUE
|
|
/// The heat (in K) before the reactor accrues damage
|
|
var/heat_damage_threshold = 1000
|
|
/// The amount of heat created by averaging total heat against all rods
|
|
var/average_heatgen = 0
|
|
/// The alert we send when we've reached warning_point
|
|
var/warning_alert = "Danger! Reactor core chamber meltdown in progress!"
|
|
/// Our "Shit is no longer fucked" message. We send it when temp_damage is 0
|
|
var/safe_alert = "Reactor conditions stabilized within operating parameters. Core meltdown averted."
|
|
/// The alert we send when we've reached emergency_point
|
|
var/emergency_alert = "REACTOR CORE MELTDOWN IMMINENT."
|
|
/// Time in 1/10th of seconds since the last sent warning
|
|
var/lastwarning = 0
|
|
/// A boolean value for if we need to send out an alert. (usually during meltdowns)
|
|
var/send_message = FALSE
|
|
/// Are we giving the final countdown to meltdown
|
|
var/final_countdown = FALSE
|
|
/// Are admins freezing the reactor for whatever reason
|
|
var/admin_intervention = FALSE
|
|
/// An admin-triggered var for enabling the station-ending self destruct
|
|
var/safety_override = FALSE
|
|
/// Disables changing the desired power value
|
|
var/control_lockout = FALSE
|
|
/// Toggles whether the reactor is erupting
|
|
var/venting = FALSE
|
|
/// Is the vent allowed to be closed without manual intervention?
|
|
var/vent_lockout = FALSE
|
|
/// How often do we want to process the vent?
|
|
var/ticks_per_run = 20
|
|
/// How long has it been since we processed the vent?
|
|
var/tick_counter = 0
|
|
/// What is the lowest temperature the reactor wants to be at?
|
|
var/minimum_operating_temp = 0
|
|
/// Holds the bonus to overheat threshold from gasses
|
|
var/gas_overheat_bonus = 0
|
|
/// Holds the bonus to reactivity from gasses
|
|
var/gas_reactivity_bonus = 0
|
|
/// Affects the current negative event chances
|
|
var/gas_event_modifier = 1
|
|
/// Our running soundloop
|
|
var/datum/looping_sound/reactor/soundloop
|
|
/// Our startup soundloop
|
|
var/datum/looping_sound/reactor_startup/startloop
|
|
/// The current air contents of this device
|
|
var/datum/gas_mixture/air_contents
|
|
/// Holds the grill for the reactor.
|
|
var/obj/machinery/cooking/grill/loaded/reactor/grill
|
|
/// Our internal radio
|
|
var/obj/item/radio/radio
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/roundstart
|
|
primary_engine = TRUE
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/examine(mob/user)
|
|
. = ..()
|
|
if(stat & BROKEN)
|
|
. += "A burning hole remains where the NGCR Reactor housed its core. It's inoperable in this state. The acrid smell permeates through even the thickest of suits."
|
|
switch(repair_step)
|
|
if(REACTOR_NEEDS_DIGGING)
|
|
. += SPAN_NOTICE("A shovel will be needed to extract all of the melted corium.")
|
|
if(REACTOR_NEEDS_CROWBAR)
|
|
. += SPAN_NOTICE("The old broken plating needs to be removed with a crowbar.")
|
|
if(REACTOR_NEEDS_PLASTITANIUM)
|
|
. += SPAN_NOTICE("The reactor requires a new plastitanium core.")
|
|
if(REACTOR_NEEDS_WRENCH)
|
|
. += SPAN_NOTICE("The new plastitanium core needs to be wrenched into place.")
|
|
if(REACTOR_NEEDS_WELDING)
|
|
. += SPAN_NOTICE("The new plastitanium core needs to be welded into place.")
|
|
if(REACTOR_NEEDS_PLASTEEL)
|
|
. += SPAN_NOTICE("The new plastitanium core needs a new plasteel housing cover.")
|
|
if(REACTOR_NEEDS_SCREWDRIVER)
|
|
. += SPAN_NOTICE("The plasteel housing cover needs screwed into place.")
|
|
return
|
|
if(venting)
|
|
. += SPAN_NOTICE("A crowbar can be used to close the malfunctioning vent.")
|
|
if(grill)
|
|
. += SPAN_NOTICE("Wirecutters can be used to remove the grill.")
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/examine_more(mob/user)
|
|
. = ..()
|
|
. += "The NGCR-5600 Nuclear Reactor was first actualized as a replacement for older, static nuclear or coal models before the discovery of supermatter harvesting techniques. \
|
|
This reactor became widespread due to the modularity and ease of use of existing station materials, allowing it to be inserted into most stations that possessed basic engineering infrastructure."
|
|
. += ""
|
|
. += "However, despite the popularity of the engine, the need for frequent upkeep and higher energy demands led to innovations in newer, more advanced energy sources. \
|
|
This engine soon became a relic of the past, but still remains a staple in many stations due to what Nanotrasen calls its 'long term reliability'."
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/Initialize(mapload)
|
|
. = ..()
|
|
AddComponent(/datum/component/multitile, list(
|
|
list(1, MACH_CENTER, 1),
|
|
))
|
|
air_contents = new
|
|
air_contents.volume = 1000 // kpa
|
|
air_contents.set_oxygen(MOLES_O2STANDARD * 0.6)
|
|
air_contents.set_nitrogen(MOLES_N2STANDARD * 0.6)
|
|
air_contents.set_temperature(T20C)
|
|
GLOB.poi_list |= src
|
|
radio = new(src)
|
|
radio.listening = FALSE
|
|
radio.follow_target = src
|
|
radio.config(list("Engineering" = 0))
|
|
if(primary_engine)
|
|
GLOB.main_fission_reactor = src
|
|
soundloop = new(list(src), FALSE)
|
|
startloop = new(list(src), FALSE)
|
|
update_icon(UPDATE_OVERLAYS)
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/ex_act(severity)
|
|
if(severity == EXPLODE_DEVASTATE) // Very sturdy.
|
|
set_broken()
|
|
else if(severity == EXPLODE_HEAVY)
|
|
adjust_damage(rand(100, 300))
|
|
else
|
|
adjust_damage(rand(30, 150))
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/blob_act(obj/structure/blob/B)
|
|
adjust_damage(rand(20, 60))
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/zap_act(power, zap_flags)
|
|
. = ..()
|
|
if(zap_flags & ZAP_MACHINE_EXPLOSIVE)
|
|
qdel(src) // Like the singulo, tesla deletes it. stops it from exploding over and over
|
|
|
|
// This shouldnt happen normally
|
|
/obj/machinery/atmospherics/fission_reactor/Destroy()
|
|
investigate_log("was destroyed!", INVESTIGATE_REACTOR)
|
|
clear_reactor_network()
|
|
QDEL_NULL(soundloop)
|
|
QDEL_NULL(startloop)
|
|
QDEL_NULL(air_contents)
|
|
QDEL_NULL(grill)
|
|
QDEL_NULL(radio)
|
|
return ..()
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/update_overlays()
|
|
. = ..()
|
|
if(!(stat & BROKEN))
|
|
var/rod_state = round((100 - operating_power + 24) / 25)
|
|
rod_state = clamp(rod_state, 1, 5)
|
|
. += "rods_[control_rods_remaining]_[rod_state]"
|
|
|
|
/// Links all valid chambers to the reactor itself.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/build_reactor_network()
|
|
for(var/turf/T in RECT_TURFS(1, 2, src))
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/chamber in T)
|
|
if(!chamber.linked_reactor && !chamber.skip_link)
|
|
chamber.find_link(src)
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/proc/clear_reactor_network(restart = FALSE)
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/linked in connected_chambers)
|
|
linked.linked_reactor = null
|
|
connected_chambers -= linked
|
|
if(length(connected_chambers)) // For whatever reason, something that was not a reactor chamber was added to our list here (or is null), and shouldnt. Cut it to be safe.
|
|
log_debug("clear_reactor_network ran successfully, however connected_chambers still contains items!")
|
|
connected_chambers.Cut()
|
|
if(restart)
|
|
build_reactor_network()
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/proc/set_broken(meltdown = TRUE)
|
|
if(stat & BROKEN)
|
|
return
|
|
|
|
stat |= BROKEN
|
|
overlays = null
|
|
|
|
if(safety_override && operating_power >= 100)
|
|
INVOKE_ASYNC(src, PROC_REF(finalize_overload))
|
|
return
|
|
|
|
clear_reactor_network()
|
|
if(meltdown) // Just in case we dont want a violent explosion
|
|
INVOKE_ASYNC(src, PROC_REF(meltdown))
|
|
else
|
|
icon_state = "broken"
|
|
|
|
/// Begin the meltdown process
|
|
/obj/machinery/atmospherics/fission_reactor/proc/meltdown()
|
|
update_appearance(UPDATE_OVERLAYS)
|
|
icon_state = "meltdown"
|
|
sleep(2.5 SECONDS)
|
|
var/explosion_modifier = clamp(reactivity_multiplier * EXPLOSION_MODIFIER, 8, 40)
|
|
explosion(get_turf(src), explosion_modifier / 2, explosion_modifier, explosion_modifier + 3, explosion_modifier + 6, ignorecap = TRUE)
|
|
icon_state = "broken"
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/proc/set_fixed()
|
|
stat &= ~BROKEN
|
|
icon_state = "reactor_off"
|
|
build_reactor_network()
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/item_interaction(mob/living/user, obj/item/used, list/modifiers)
|
|
. = ..()
|
|
if(!iscarbon(user))
|
|
return
|
|
var/mob/living/carbon/creature = user
|
|
if(istype(used, /obj/item/shovel) && repair_step == REACTOR_NEEDS_DIGGING && (stat & BROKEN))
|
|
playsound(src, used.usesound, 50, TRUE)
|
|
if(do_after_once(creature, 3 SECONDS, TRUE, src, allow_moving = FALSE))
|
|
playsound(src, used.usesound, 50, TRUE)
|
|
new /obj/item/slag(loc)
|
|
if(prob(20))
|
|
repair_step++
|
|
to_chat(creature, SPAN_INFORMATION("No more melted slag remains in the chamber."))
|
|
else
|
|
to_chat(creature, SPAN_INFORMATION("There seems to be additional slag clogging the ruined reactor core."))
|
|
return ITEM_INTERACT_COMPLETE
|
|
if(istype(used, /obj/item/stack/sheet/mineral/plastitanium))
|
|
var/obj/item/stack/sheet/plastitanium = used
|
|
if(plastitanium.amount < 5)
|
|
to_chat(creature, SPAN_WARNING("You need at least five sheets of plastitanium to reform the reactor core structure!"))
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
if(repair_step == REACTOR_NEEDS_PLASTITANIUM)
|
|
if(do_after_once(creature, 3 SECONDS, TRUE, src))
|
|
if(plastitanium.amount < 5)
|
|
to_chat(creature, SPAN_WARNING("You need at least five sheets of plastitanium to reform the reactor core structure!"))
|
|
return ITEM_INTERACT_COMPLETE
|
|
plastitanium.use(5)
|
|
to_chat(creature, SPAN_INFORMATION("You reform the control rod housing and slot the structure into place."))
|
|
repair_step++
|
|
icon_state = "reactor_maintenance"
|
|
return
|
|
|
|
if(!offline)
|
|
to_chat(creature, SPAN_WARNING("The reactor must be off to repair it!"))
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
if(damage == 0)
|
|
to_chat(creature, SPAN_WARNING("The reactor has nothing left to repair!"))
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
var/obj/item/item = creature.get_inactive_hand()
|
|
if(!istype(item, /obj/item/weldingtool))
|
|
to_chat(creature, SPAN_WARNING("A functional welder is required to adhere the plastitanium."))
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
if(!item.use_tool(src, creature, 0, amount = 1, volume = item.tool_volume))
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
if(do_after_once(creature, 4 SECONDS, TRUE, src, allow_moving = FALSE))
|
|
plastitanium.use(5)
|
|
adjust_damage((-MELTDOWN_POINT * 0.1))
|
|
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
if(istype(used, /obj/item/stack/sheet/plasteel) && repair_step == REACTOR_NEEDS_PLASTEEL)
|
|
var/obj/item/stack/sheet/plasteel = used
|
|
if(plasteel.amount < 5)
|
|
to_chat(user, SPAN_WARNING("You need at least five sheets of plasteel to reform the reactor core structure!"))
|
|
return ITEM_INTERACT_COMPLETE
|
|
if(do_after_once(user, 3 SECONDS, TRUE, src, allow_moving = FALSE))
|
|
repair_step++
|
|
plasteel.use(5)
|
|
to_chat(user, SPAN_INFORMATION("You attach a layer of radiation shielding around the reactor core."))
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
if(istype(used, /obj/item/reagent_containers/cooking/grill_grate))
|
|
var/obj/item/item = creature.get_inactive_hand()
|
|
if(!istype(item, /obj/item/reagent_containers/cooking/grill_grate))
|
|
to_chat(user, SPAN_WARNING("You need a second grate to set up a proper grill!"))
|
|
return ITEM_INTERACT_COMPLETE
|
|
if(grill)
|
|
to_chat(user, SPAN_WARNING("There are already grill grates adhered to the surface of the reactor!"))
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
qdel(used)
|
|
qdel(item)
|
|
grill = new(loc)
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/wirecutter_act(mob/living/user, obj/item/I)
|
|
if(grill)
|
|
to_chat(user, SPAN_WARNING("You begin cutting the adhered grates from the reactor body..."))
|
|
if(I.use_tool(src, user, 4 SECONDS, volume = I.tool_volume))
|
|
new /obj/item/reagent_containers/cooking/grill_grate(loc)
|
|
new /obj/item/reagent_containers/cooking/grill_grate(loc)
|
|
QDEL_NULL(grill)
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/crowbar_act(mob/living/user, obj/item/I)
|
|
if(repair_step == REACTOR_NEEDS_CROWBAR)
|
|
if(I.use_tool(src, user, 1 SECONDS, volume = I.tool_volume))
|
|
playsound(src, I.usesound, 50, TRUE)
|
|
repair_step++
|
|
to_chat(user, SPAN_INFORMATION("You remove any remaining damaged structure from the housing."))
|
|
new /obj/item/stack/sheet/metal(user.loc, 2)
|
|
return ITEM_INTERACT_COMPLETE
|
|
if(!(stat & BROKEN) && venting)
|
|
if(I.use_tool(src, user, (8 SECONDS * I.toolspeed), volume = I.tool_volume))
|
|
venting = FALSE
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/wrench_act(mob/living/user, obj/item/I)
|
|
if(repair_step == REACTOR_NEEDS_WRENCH)
|
|
if(I.use_tool(src, user, 1 SECONDS, volume = 50))
|
|
playsound(src, I.usesound, 50, TRUE)
|
|
repair_step++
|
|
to_chat(user, SPAN_INFORMATION("You secure the new plastitanium structure in place."))
|
|
new /obj/item/stack/sheet/metal(user.loc, 2)
|
|
return ITEM_INTERACT_COMPLETE
|
|
if(!(stat & BROKEN) && control_rods_remaining < TOTAL_CONTROL_RODS)
|
|
if(I.use_tool(src, user, 0, volume = I.tool_volume))
|
|
if(do_after_once(user, (8 SECONDS * I.toolspeed), allow_moving = FALSE, target = src, progress = TRUE))
|
|
control_rods_remaining++
|
|
update_icon(UPDATE_OVERLAYS)
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/screwdriver_act(mob/living/user, obj/item/I)
|
|
if(repair_step == REACTOR_NEEDS_SCREWDRIVER)
|
|
if(I.use_tool(src, user, 1 SECONDS, volume = 50))
|
|
playsound(src, I.usesound, 50, TRUE)
|
|
to_chat(user, SPAN_INFORMATION("You secure the radiation shielding into place."))
|
|
set_fixed()
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/welder_act(mob/living/user, obj/item/I)
|
|
if(repair_step == REACTOR_NEEDS_WELDING)
|
|
if(I.use_tool(src, user, 1 SECONDS, volume = 50))
|
|
playsound(src, I.usesound, 50, TRUE)
|
|
repair_step++
|
|
to_chat(user, SPAN_INFORMATION("You weld together the framing, ensuring an airtight seal within the core."))
|
|
new /obj/item/stack/sheet/metal(user.loc, 2)
|
|
return ITEM_INTERACT_COMPLETE
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/proc/get_integrity()
|
|
var/integrity = damage / MELTDOWN_POINT
|
|
integrity = round(100 - integrity * 100, 0.01)
|
|
integrity = integrity < 0 ? 0 : integrity
|
|
return integrity
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/multitool_act(mob/living/user, obj/item/I)
|
|
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
|
|
return
|
|
. = TRUE
|
|
var/obj/item/multitool/multi = I
|
|
multi.set_multitool_buffer(user, src)
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/process_atmos()
|
|
if(!venting)
|
|
return
|
|
|
|
if(admin_intervention)
|
|
return
|
|
|
|
tick_counter += SSair.wait
|
|
if(tick_counter >= ticks_per_run)
|
|
var/datum/milla_safe/reactor_process/milla = new()
|
|
milla.invoke_async(src)
|
|
tick_counter -= ticks_per_run
|
|
|
|
/datum/milla_safe/reactor_process
|
|
|
|
/datum/milla_safe/reactor_process/on_run(obj/machinery/atmospherics/fission_reactor/reactor)
|
|
var/turf/T = get_turf(reactor)
|
|
var/datum/gas_mixture/environment = get_turf_air(T)
|
|
|
|
if(isnull(T)) // We have a null turf...something is wrong, stop processing this entity.
|
|
return PROCESS_KILL
|
|
|
|
if(!isturf(reactor.loc))
|
|
return
|
|
|
|
if(T.density)
|
|
var/turf/did_it_melt = T.ChangeTurf(T.baseturf)
|
|
if(!did_it_melt.density) // In case some joker finds way to place these on indestructible walls
|
|
reactor.visible_message(SPAN_INFORMATION("[src] melts through [T]!"))
|
|
return
|
|
|
|
for(var/obj/structure/holosign/barrier/atmos/fan in reactor.loc.contents)
|
|
reactor.visible_message(SPAN_INFORMATION("[src] violently ruptures through [fan]!"))
|
|
fan.Destroy()
|
|
return
|
|
|
|
var/pressure_delta = reactor.air_contents.return_pressure() - environment.return_pressure()
|
|
pressure_delta /= 10
|
|
|
|
// Based on pressure_pump to equalize pressure
|
|
if(abs(pressure_delta) < 0.01) // Already equalized
|
|
return FALSE
|
|
. = TRUE
|
|
if(pressure_delta > 0)
|
|
// Transfer from pipe air to environment
|
|
if((reactor.air_contents.total_moles() > 0) && (reactor.air_contents.temperature() > 0))
|
|
var/transfer_moles = pressure_delta * reactor.air_contents.volume / (reactor.air_contents.temperature() * R_IDEAL_GAS_EQUATION)
|
|
transfer_moles = min(transfer_moles, reactor.air_contents.volume)
|
|
|
|
var/datum/gas_mixture/removed = reactor.air_contents.remove(transfer_moles)
|
|
environment.merge(removed)
|
|
return
|
|
|
|
// Transfer from environment to pipe air
|
|
pressure_delta = -pressure_delta
|
|
if((environment.total_moles() > 0) && (environment.temperature() > 0))
|
|
var/transfer_moles = pressure_delta * reactor.air_contents.volume / (environment.temperature() * R_IDEAL_GAS_EQUATION)
|
|
transfer_moles = min(transfer_moles, reactor.air_contents.volume)
|
|
|
|
var/datum/gas_mixture/removed = environment.remove(transfer_moles)
|
|
reactor.air_contents.merge(removed)
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/process()
|
|
if(stat & BROKEN)
|
|
var/rad_type = pick(GAMMA_RAD, ALPHA_RAD, BETA_RAD)
|
|
radiation_pulse(src, 500, rad_type)
|
|
return
|
|
|
|
if(admin_intervention)
|
|
return
|
|
|
|
if(!offline && !starting_up)
|
|
var/light_range = clamp((final_power / (50 KW)), 2, 30)
|
|
set_light(light_range, max(reactivity_multiplier, 3), REACTOR_LIGHT_COLOR)
|
|
else
|
|
remove_light()
|
|
|
|
if(control_lockout)
|
|
return
|
|
|
|
var/minimum_power = 100 * (1 - (control_rods_remaining / TOTAL_CONTROL_RODS))
|
|
if(operating_power < minimum_power) // Oops, control rods stuck
|
|
operating_power++
|
|
update_appearance(UPDATE_OVERLAYS)
|
|
else
|
|
if(desired_power > operating_power)
|
|
operating_power++
|
|
update_appearance(UPDATE_OVERLAYS)
|
|
else if(desired_power < operating_power)
|
|
operating_power--
|
|
update_appearance(UPDATE_OVERLAYS)
|
|
|
|
if(operating_power == desired_power && desired_power == 0 && offline != TRUE)
|
|
shut_off()
|
|
|
|
if(operating_power > 0 && offline == TRUE)
|
|
boot_up()
|
|
|
|
if(operating_power >= 10 && starting_up == TRUE)
|
|
become_operational()
|
|
|
|
if(offline || starting_up)
|
|
return
|
|
|
|
if(safety_override)
|
|
if(operating_power >= 100)
|
|
send_message = FALSE
|
|
countdown()
|
|
return
|
|
|
|
// Lets fake it a little
|
|
var/heat_capacity = air_contents.heat_capacity()
|
|
var/temp = air_contents.temperature()
|
|
if(heat_capacity && temp < CRITICAL_TEMPERATURE)
|
|
air_contents.set_temperature(temp + rand(20, 200))
|
|
else
|
|
air_contents.set_temperature(CRITICAL_TEMPERATURE)
|
|
if(reactivity_multiplier < 20)
|
|
reactivity_multiplier += (rand(15, 40) / 100)
|
|
else
|
|
reactivity_multiplier = 20
|
|
|
|
damage += max(0.5, rand(1, DAMAGE_MAXIMUM * 20) / 20) // This wont actually blow us up early
|
|
return
|
|
|
|
final_power = 0 // Full resets to be recalculated
|
|
final_heat = 0
|
|
|
|
calculate_gas_effects()
|
|
|
|
// Calculate the durability loss for all rods in use
|
|
// Lower operating power = more durability. Algorithm: 1 / (1 + 2.5^(-0.077 * (x - 65)))
|
|
var/algorithm_decay = 0.077 // Higher = steeper decline
|
|
var/durability_loss = 1
|
|
if(operating_power <= 90) // Full loss at 90% and above
|
|
durability_loss = round(1 / (1 + 2.5 ** (-algorithm_decay * (operating_power - 65))), 0.01)
|
|
var/operating_rate = operating_rate()
|
|
|
|
var/active_chambers
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/chamber in connected_chambers)
|
|
if(chamber.held_rod)
|
|
if(chamber.chamber_state == CHAMBER_DOWN)
|
|
chamber.calculate_stats(operating_rate)
|
|
active_chambers++
|
|
continue
|
|
if(chamber.chamber_state == CHAMBER_UP)
|
|
active_chambers++
|
|
continue
|
|
if(!active_chambers) // for average heat generation calculations not to divide by 0
|
|
active_chambers = 0.5
|
|
|
|
// Gather all our data from the chambers, and enrich if we need to
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/chamber in connected_chambers)
|
|
var/power_total
|
|
var/heat_total
|
|
if(!chamber.held_rod)
|
|
continue
|
|
if(chamber.chamber_state == CHAMBER_OPEN)
|
|
continue
|
|
var/durability_mod = chamber.held_rod.get_durability_mod()
|
|
if(chamber.chamber_state == CHAMBER_DOWN)
|
|
if(chamber.operational) // We generate heat but not power while its down.
|
|
power_total = chamber.power_total * durability_mod // Some things have negative power, so we put this before fuel rod checks
|
|
if(istype(chamber.held_rod, /obj/item/nuclear_rod/fuel))
|
|
var/obj/item/nuclear_rod/fuel/fuel_rod = chamber.held_rod
|
|
if(fuel_rod.enrich(chamber.power_mod_total * operating_rate, chamber.heat_mod_total * operating_rate))
|
|
if(!chamber.enriching) // So we arnt constantly updating our overlay
|
|
chamber.enriching = TRUE
|
|
chamber.update_icon(UPDATE_OVERLAYS)
|
|
else if(chamber.enriching)
|
|
chamber.enriching = FALSE
|
|
chamber.update_icon(UPDATE_OVERLAYS)
|
|
|
|
heat_total = chamber.heat_total * durability_mod
|
|
final_heat += heat_total
|
|
final_power += power_total
|
|
chamber.held_rod.durability -= durability_loss
|
|
chamber.held_rod.do_special_effect()
|
|
|
|
if(final_heat)
|
|
average_heatgen = final_heat / active_chambers
|
|
else
|
|
average_heatgen = 0.01
|
|
|
|
// Time to decide reactivity coefficient.
|
|
var/temp = air_contents.temperature()
|
|
var/total_mols = air_contents.total_moles()
|
|
if(!temp || !total_mols)
|
|
temp = 0
|
|
if(average_heatgen > AVERAGE_HEAT_THRESHOLD) // The base reactivity from average heat gen
|
|
reactivity_multiplier = 1 + gas_reactivity_bonus + ((average_heatgen - AVERAGE_HEAT_THRESHOLD) / AVERAGE_HEAT_THRESHOLD)
|
|
else
|
|
reactivity_multiplier = 1 + gas_reactivity_bonus
|
|
if(temp > TOTAL_HEAT_THRESHOLD)
|
|
// Math equation for here: y = a + b * ln(x)
|
|
var/offset = 1 // The offset for the math calc. Gives a flat number boost. (A) component
|
|
var/curve_intensity = 3.5 // Negatively affects the rate of decay. higher = reactivity builds easier. (B) component
|
|
var/heat_component = (temp - TOTAL_HEAT_THRESHOLD) / HEAT_CONVERSION_RATIO // (X) Component
|
|
reactivity_multiplier += (offset + curve_intensity * log(heat_component))
|
|
|
|
reactivity_multiplier = clamp(reactivity_multiplier, 1, REACTIVITY_COEFFICIENT_CAP)
|
|
|
|
|
|
final_heat *= (reactivity_multiplier * 2) * HEAT_MODIFIER // Proportionally affects heat more
|
|
final_power *= reactivity_multiplier
|
|
|
|
final_power = max(final_power, 0) // No negative numbers
|
|
|
|
var/rad_type = pick(GAMMA_RAD, ALPHA_RAD, BETA_RAD)
|
|
|
|
//Generating the amount of Plasma created
|
|
var/datum/gas_mixture/temp_gas = new()
|
|
// Math equation for here: y = a + b * ln(x)
|
|
var/gas_offset = 1 // The offset for the math calc. Gives a flat number boost. (A) component
|
|
var/gas_curve_intensity = 1.6 // Affects the rate of decay. higher = reactivity builds easier. (B) component
|
|
var/power_component = max((final_power / (5 MW)), 0.01) // (X) Component
|
|
var/h2_amount = clamp(gas_offset + gas_curve_intensity * log(power_component), 0, 30)
|
|
temp_gas.set_hydrogen(clamp(h2_amount * reactivity_multiplier, 0.2, 100))
|
|
temp_gas.set_temperature(air_contents.temperature())
|
|
air_contents.merge(temp_gas)
|
|
|
|
radiation_pulse(src, 100 * reactivity_multiplier, rad_type)
|
|
|
|
// Begin heating the air based off heat produced
|
|
var/heat_capacity = air_contents.heat_capacity()
|
|
if(heat_capacity)
|
|
if(temp < minimum_operating_temp)
|
|
air_contents.set_temperature(max(temp + (final_heat / heat_capacity), temp + 200)) // RAPIDLY reach our minimum temperature or our normal heat gen. Whichever is higher
|
|
else if(temp > HEAT_CAP)
|
|
air_contents.set_temperature(temp + rand(3, 20)) // Cap it but slowly gain until we get it down
|
|
else
|
|
air_contents.set_temperature(max(temp + (final_heat / heat_capacity), temp + 2))
|
|
|
|
temp = air_contents.temperature()
|
|
if(temp > heat_damage_threshold * 0.9)
|
|
icon_state = "reactor_overheat"
|
|
else if(temp > heat_damage_threshold * 0.5)
|
|
icon_state = "reactor_hot"
|
|
else
|
|
icon_state = "reactor_on"
|
|
|
|
// Damage the reactor if conditions are not met, and handle events
|
|
var/new_damage = 0
|
|
if(!total_mols)
|
|
new_damage += DAMAGE_MAXIMUM * MOL_DAMAGE_MULTIPLIER
|
|
else
|
|
if(total_mols <= MOL_MINIMUM)
|
|
new_damage += max(((1 - (total_mols / MOL_MINIMUM)) * DAMAGE_MAXIMUM * MOL_DAMAGE_MULTIPLIER), DAMAGE_MINIMUM)
|
|
if(check_overheating())
|
|
// Breaking the equation up a little for readability. Should look like this: Y = (-AB ^ -X) + A
|
|
var/rate_of_decay = 1.13 // Closer to 1 = slower to reach DAMAGE_MAXIMUM. Do not set at or below 1 it will break
|
|
var/damage_increments = -((temp - heat_damage_threshold) / HEAT_DAMAGE_RATE)
|
|
var/damage_calculation = (-DAMAGE_MAXIMUM * (rate_of_decay ** damage_increments)) + DAMAGE_MAXIMUM
|
|
new_damage += max(damage_calculation * HEAT_DAMAGE_MULTIPLIER, DAMAGE_MINIMUM) // God math sucks. This gives decaying increments of damage for heat generation as it gets closer to DAMAGE_MAXIMUM.
|
|
|
|
var/pressure = air_contents.return_pressure()
|
|
if(pressure > PRESSURE_MAXIMUM)
|
|
new_damage += PRESSURE_DAMAGE
|
|
|
|
new_damage = clamp(new_damage, DAMAGE_MINIMUM, DAMAGE_MAXIMUM)
|
|
var/damage_multiplier = clamp(1 + ((50 - get_integrity()) / 25), 1, 3) // Gives a higher event chance below 50% integrity, up to 3x
|
|
damage_multiplier *= EVENT_MODIFIER * gas_event_modifier
|
|
if(final_countdown)
|
|
damage_multiplier = 10
|
|
|
|
// Eject a rod
|
|
if(prob(new_damage * damage_multiplier * 0.3))
|
|
var/list/coolers = list()
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/chamber in connected_chambers)
|
|
if(istype(chamber.held_rod, /obj/item/nuclear_rod/coolant) && chamber.chamber_state == CHAMBER_DOWN)
|
|
coolers += chamber
|
|
if(length(coolers))
|
|
var/obj/machinery/atmospherics/reactor_chamber/failure = coolers[rand(1, length(coolers))]
|
|
if(prob(60) || !failure.welded) // 60% Chance to break through the weld. you got lucky punk
|
|
failure.eject_rod()
|
|
|
|
// Weld a vent.
|
|
if(prob(new_damage * damage_multiplier * 3))
|
|
var/list/valid_chambers = list()
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/chamber in connected_chambers)
|
|
if(chamber.chamber_state == CHAMBER_DOWN)
|
|
valid_chambers += chamber
|
|
if(length(valid_chambers))
|
|
while(length(valid_chambers))
|
|
var/obj/machinery/atmospherics/reactor_chamber/failure = valid_chambers[rand(1, length(valid_chambers))]
|
|
if(!failure.welded)
|
|
failure.weld_shut()
|
|
break
|
|
else
|
|
valid_chambers -= failure // Just keep cycling through.
|
|
|
|
// Control rod failure.
|
|
if(prob(new_damage * damage_multiplier * 0.5) && control_rods_remaining > 0) // Control rod failure. more probable
|
|
control_rod_failure()
|
|
|
|
// Vent seal failure.
|
|
if(prob(new_damage * damage_multiplier * 0.05)) // Vent control failure. much rarer
|
|
begin_venting()
|
|
|
|
if(damage > WARNING_POINT && (REALTIMEOFDAY - lastwarning) / 10 >= WARNING_DELAY && send_message && !final_countdown)
|
|
try_alarm(new_damage)
|
|
|
|
if(new_damage)
|
|
adjust_damage(new_damage)
|
|
send_message = TRUE
|
|
new_damage = 0
|
|
|
|
if(damage >= MELTDOWN_POINT)
|
|
send_message = FALSE
|
|
countdown()
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/proc/try_alarm(new_damage)
|
|
lastwarning = REALTIMEOFDAY
|
|
if(!new_damage)
|
|
radio.autosay("<b>[safe_alert] Integrity: [get_integrity()]%</b>", name, "Engineering")
|
|
send_message = FALSE // Only stop sending alerts when no damage has been taken
|
|
return
|
|
|
|
switch(get_status())
|
|
if(REACTOR_WARNING)
|
|
radio.autosay("<b>[warning_alert] Integrity: [get_integrity()]%</b>", name, "Engineering")
|
|
if(REACTOR_DANGER)
|
|
radio.autosay("<b>[warning_alert] Integrity: [get_integrity()]%</b>", name, "Engineering")
|
|
if(REACTOR_EMERGENCY)
|
|
radio.autosay(SPAN_BIG("[warning_alert] Integrity: [get_integrity()]%"), name, null)
|
|
if(REACTOR_MELTDOWN)
|
|
radio.autosay(SPAN_BIG("[emergency_alert] Integrity: [get_integrity()]%"), name, null)
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/proc/get_status()
|
|
var/integrity = get_integrity()
|
|
if(integrity < NGCR_MELTDOWN_PERCENT)
|
|
return REACTOR_MELTDOWN
|
|
|
|
if(integrity < NGCR_EMERGENCY_PERCENT)
|
|
return REACTOR_EMERGENCY
|
|
|
|
if(integrity < NGCR_DANGER_PERCENT)
|
|
return REACTOR_DANGER
|
|
|
|
if((integrity < NGCR_WARNING_PERCENT) || (air_contents.temperature() > CRITICAL_TEMPERATURE))
|
|
return REACTOR_WARNING
|
|
|
|
if(air_contents.temperature() > (heat_damage_threshold * 0.9))
|
|
return REACTOR_NOTIFY
|
|
|
|
if(offline)
|
|
return REACTOR_INACTIVE
|
|
|
|
return REACTOR_NORMAL
|
|
|
|
/// Turn the reactor off and stop processing.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/shut_off()
|
|
starting_up = TRUE
|
|
offline = TRUE
|
|
can_create_power = FALSE
|
|
icon_state = "reactor_off"
|
|
final_heat = 0
|
|
final_power = 0
|
|
reactivity_multiplier = 1
|
|
remove_light()
|
|
playsound(src, 'sound/machines/fission/reactor_shutoff.ogg', 80, FALSE, 4, ignore_walls = TRUE, channel = CHANNEL_ENGINE)
|
|
soundloop.stop()
|
|
startloop.stop()
|
|
if(send_message)
|
|
radio.autosay("<b>Reactor SCRAM completed successfully. Integrity: [get_integrity()]%</b>", name, "Engineering")
|
|
send_message = FALSE
|
|
if(grill)
|
|
for(var/datum/cooking_surface/surface in grill.surfaces)
|
|
if(surface.on)
|
|
surface.turn_off()
|
|
var/obj/item/reagent_containers/cooking/container = surface.container
|
|
if(istype(container) && container.tracker)
|
|
SEND_SIGNAL(container, COMSIG_COOK_MACHINE_STEP_INTERRUPTED, surface)
|
|
|
|
/// Begin the startup sequence, but dont turn online yet.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/boot_up()
|
|
offline = FALSE
|
|
icon_state = "reactor_starting"
|
|
startloop.start()
|
|
|
|
/// Make the reactor become fully operational.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/become_operational()
|
|
starting_up = FALSE
|
|
offline = FALSE
|
|
can_create_power = TRUE
|
|
playsound(src, 'sound/machines/fission/reactor_startup.ogg', 80, FALSE, 4, ignore_walls = TRUE)
|
|
startloop.stop()
|
|
soundloop.start()
|
|
if(safety_override)
|
|
icon_state = "reactor_overheat"
|
|
else
|
|
icon_state = "reactor_on"
|
|
set_light(2, 5, REACTOR_LIGHT_COLOR)
|
|
if(grill)
|
|
for(var/datum/cooking_surface/surface in grill.surfaces)
|
|
if(!surface.on)
|
|
surface.turn_on()
|
|
|
|
/// Returns a value from 0 to 1 based off current operating power
|
|
/obj/machinery/atmospherics/fission_reactor/proc/operating_rate()
|
|
return operating_power / 100
|
|
|
|
// Pretty much ripped from the SM
|
|
/obj/machinery/atmospherics/fission_reactor/proc/countdown()
|
|
if(final_countdown)
|
|
return
|
|
final_countdown = TRUE
|
|
var/speaking = SPAN_REALLYBIG("[emergency_alert] Reactor structural stability compromised. ")
|
|
for(var/mob/M in GLOB.player_list) // For ALL players
|
|
var/turf/T = get_turf(M)
|
|
if(istype(T) && atoms_share_level(T, src)) // If the player is on the same zlevel as the SM shared
|
|
SEND_SOUND(M, sound('sound/machines/engine_alert2.ogg')) // Then send them the sound file
|
|
radio.autosay(speaking, name, null)
|
|
for(var/i in NGCR_COUNTDOWN_TIME to 0 step (-1 SECONDS))
|
|
if(admin_intervention) // Stop exploding if you're frozen by an admin, damn you
|
|
final_countdown = FALSE
|
|
adjust_damage(MELTDOWN_POINT - 1, TRUE) // One point below exploding, so it will re-start the countdown once unfrozen
|
|
return
|
|
if(offline) // Engineers managed to fully turn off the reactor in time
|
|
radio.autosay(SPAN_BIG("[safe_alert]"), name, null)
|
|
final_countdown = FALSE
|
|
remove_filter(list("outline", "icon"))
|
|
return
|
|
else if((i % (5 SECONDS)) != 0 && i > (5 SECONDS)) // A message once every 5 seconds until the final 5 seconds which count down individualy
|
|
sleep(1 SECONDS)
|
|
continue
|
|
else if(i > (5 SECONDS))
|
|
speaking = "<b>[DisplayTimeText(i, TRUE)] remain before full reactor core meltdown.</b>"
|
|
else
|
|
speaking = SPAN_REALLYBIG("[i * 0.1]...")
|
|
radio.autosay(speaking, name, null)
|
|
sleep(1 SECONDS)
|
|
|
|
set_broken()
|
|
|
|
/// Begins the process of the centcomm doomsday overload
|
|
/obj/machinery/atmospherics/fission_reactor/proc/prep_overload()
|
|
desired_power = 0
|
|
INVOKE_ASYNC(src, PROC_REF(scram))
|
|
control_lockout = TRUE
|
|
safety_override = TRUE
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/chamber in connected_chambers)
|
|
chamber.set_idle_overload()
|
|
|
|
/// Checks all connected chambers for a fuel rod
|
|
/obj/machinery/atmospherics/fission_reactor/proc/check_overload_ready()
|
|
if(length(connected_chambers) < MIN_CHAMBERS_TO_OVERLOAD)
|
|
return FALSE
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/chamber in connected_chambers)
|
|
if(chamber.chamber_state != CHAMBER_OVERLOAD_IDLE)
|
|
return FALSE
|
|
if(!chamber.held_rod)
|
|
return FALSE
|
|
if(!istype(chamber.held_rod, /obj/item/nuclear_rod/fuel))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Sets all the chambers to active overload position and unlocks the reactor.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/set_overload()
|
|
control_lockout = FALSE
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/chamber in connected_chambers)
|
|
chamber.set_active_overload()
|
|
|
|
/// The proc for actually blowing up the station. It is too late
|
|
/obj/machinery/atmospherics/fission_reactor/proc/finalize_overload()
|
|
icon_state = "meltdown"
|
|
playsound(src, 'sound/machines/alarm.ogg', 100, FALSE, 5)
|
|
if(SSticker && SSticker.mode)
|
|
SSticker.mode.explosion_in_progress = TRUE
|
|
SSticker.record_biohazard_results()
|
|
sleep(10 SECONDS)
|
|
|
|
SSblackbox.record_feedback("tally", "fission_overload", 1, "detonation successful")
|
|
icon_state = "broken"
|
|
GLOB.enter_allowed = 0
|
|
SSticker.station_explosion_cinematic(NUKE_SITE_ON_STATION, null)
|
|
SSticker.mode.station_was_nuked = TRUE
|
|
to_chat(world, SPAN_BOLD("The station was destroyed from a nuclear meltdown!"))
|
|
|
|
if(!SSticker.mode.check_finished()) // If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is
|
|
SSticker.reboot_helper("Station destroyed by nuclear fission meltdown.", "nuke - unhandled ending")
|
|
return
|
|
|
|
/// Stops the reactor in a somewhat fancy way. Purely for anyone watching the monitor.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/scram()
|
|
var/power_fraction
|
|
if(final_power)
|
|
power_fraction = final_power / operating_power
|
|
else
|
|
power_fraction = 0
|
|
reactivity_multiplier = 1
|
|
offline = TRUE
|
|
starting_up = TRUE
|
|
final_heat = 0
|
|
var/temp = air_contents.temperature()
|
|
var/temp_fraction
|
|
|
|
if(temp && temp > 300)
|
|
temp_fraction = (temp - 300) / operating_power
|
|
while(operating_power > 0)
|
|
operating_power--
|
|
sleep(0.25)
|
|
final_power -= power_fraction
|
|
if(temp)
|
|
temp = air_contents.temperature()
|
|
air_contents.set_temperature(temp - temp_fraction)
|
|
|
|
final_power = 0
|
|
icon_state = "reactor_off"
|
|
|
|
/// Check to see if the reactor is overheating or taking damage from lack of pressure.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/check_overheating()
|
|
var/temp = air_contents.temperature()
|
|
var/mols = air_contents.total_moles()
|
|
if(!mols)
|
|
return TRUE
|
|
var/pressure = air_contents.return_pressure()
|
|
if(pressure > PRESSURE_MAXIMUM || temp >= heat_damage_threshold)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/// Negative Event. Lose a control rod from the reactor.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/control_rod_failure()
|
|
if(control_rods_remaining <= 0)
|
|
return
|
|
playsound(src, 'sound/effects/meteorimpact.ogg', 80, FALSE)
|
|
control_rods_remaining--
|
|
radio.autosay("<b>ALERT: Control rod failure! [control_rods_remaining] functional control rods remaining.</b>", name, "Engineering")
|
|
update_icon(UPDATE_OVERLAYS)
|
|
|
|
/// Negative Event. Ensure the emergency vent opens and gets stuck.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/begin_venting()
|
|
if(venting)
|
|
return
|
|
var/datum/effect_system/smoke_spread/bad/smoke = new()
|
|
var/rad_type = pick(ALPHA_RAD, BETA_RAD, GAMMA_RAD)
|
|
for(var/turf/T in view(4, loc))
|
|
T.contaminate_atom(src, 100, rad_type)
|
|
smoke.set_up(3, FALSE, loc)
|
|
smoke.start()
|
|
venting = TRUE
|
|
vent_lockout = TRUE
|
|
|
|
/// Update the minimum running temperature of the reactor.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/update_minimum_temp()
|
|
minimum_operating_temp = 0
|
|
for(var/obj/machinery/atmospherics/reactor_chamber/chamber in connected_chambers)
|
|
if(!chamber.held_rod)
|
|
continue
|
|
if(chamber.chamber_state != CHAMBER_DOWN)
|
|
continue
|
|
if(!chamber.held_rod.minimum_temp_modifier)
|
|
continue
|
|
else if(chamber.held_rod.minimum_temp_modifier > minimum_operating_temp)
|
|
minimum_operating_temp = chamber.held_rod.minimum_temp_modifier
|
|
|
|
/// Update our new overheat threshold.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/update_overheat_threshold(heat_change = 0)
|
|
if(!heat_change)
|
|
return
|
|
heat_damage_threshold += heat_change
|
|
|
|
/// Adjust how much damage the reactor has, and set broken if we exceed the meltdown point.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/adjust_damage(new_damage, set_by_number = FALSE)
|
|
if(set_by_number)
|
|
damage = clamp(new_damage, 0, MELTDOWN_POINT)
|
|
else
|
|
damage += new_damage
|
|
damage = clamp(damage, 0, MELTDOWN_POINT)
|
|
if(damage >= MELTDOWN_POINT && offline)
|
|
set_broken(FALSE)
|
|
|
|
/// Calculate all of the bonuses and detriments of using specific gasses.
|
|
/obj/machinery/atmospherics/fission_reactor/proc/calculate_gas_effects()
|
|
gas_reactivity_bonus = 0
|
|
var/temp_bonus_holder = 0 // Heat bonus container
|
|
var/temp_event_holder = 0 // Event chance modifier in percentages. 0 to 100
|
|
var/combined_gas = air_contents.total_moles()
|
|
|
|
// Math equation for here: y = a + b * ln(x)
|
|
var/offset = 1 // The offset for the math calc. Gives a flat number boost. (A) component
|
|
var/curve_intensity = 0.7 // Negatively affects the rate of decay. higher = reactivity builds easier. (B) component
|
|
var/gas_component = max((combined_gas - MOLE_BONUS_THRESHOLD) / MOLE_BONUS_COMPONENT, 0.01)
|
|
var/mole_multiplier = max((offset + curve_intensity * log(gas_component)), 0)
|
|
|
|
// Raw composition of each gas in the chamber, ranges from 0 to 1
|
|
var/n2comp = max(air_contents.nitrogen() / combined_gas, 0)
|
|
var/plasmacomp = max(air_contents.toxins() / combined_gas, 0)
|
|
var/o2comp = max(air_contents.oxygen() / combined_gas, 0)
|
|
var/co2comp = max(air_contents.carbon_dioxide() / combined_gas, 0)
|
|
var/n2ocomp = max(air_contents.sleeping_agent() / combined_gas, 0)
|
|
|
|
// Dont put a mole multiplier on the event chances
|
|
if(n2comp)
|
|
temp_bonus_holder += n2comp * N2_OVERHEAT_BONUS * mole_multiplier
|
|
temp_event_holder += n2comp * N2_EVENT_MODIFIER
|
|
if(n2ocomp)
|
|
temp_bonus_holder += n2ocomp * N2O_OVERHEAT_BONUS * mole_multiplier
|
|
temp_event_holder += n2ocomp * N2O_EVENT_MODIFIER
|
|
if(co2comp)
|
|
temp_event_holder += co2comp * CO2_EVENT_MODIFIER * mole_multiplier
|
|
if(o2comp)
|
|
gas_reactivity_bonus += o2comp * O2_REACTIVITY_BONUS * mole_multiplier
|
|
temp_event_holder += o2comp * O2_EVENT_MODIFIER // This one actually should make events more likely
|
|
if(plasmacomp)
|
|
gas_reactivity_bonus += plasmacomp * PLASMA_REACTIVITY_BONUS * mole_multiplier
|
|
temp_bonus_holder += plasmacomp * PLASMA_OVERHEAT_BONUS * mole_multiplier
|
|
|
|
temp_event_holder /= 100 // Bring between 0 and 1
|
|
gas_event_modifier = 1 - temp_event_holder
|
|
|
|
// Replace the old bonus with the new one
|
|
update_overheat_threshold(-gas_overheat_bonus)
|
|
update_overheat_threshold(temp_bonus_holder)
|
|
gas_overheat_bonus = temp_bonus_holder
|
|
|
|
/obj/machinery/atmospherics/fission_reactor/proc/overload_reactor()
|
|
if(SSsecurity_level.get_current_level_as_number() != SEC_LEVEL_DELTA_REACTOR)
|
|
SSsecurity_level.set_level(SEC_LEVEL_DELTA_REACTOR)
|
|
else
|
|
log_admin("An admin attempted to override fission reactor safeties, but it was already overriden")
|
|
return
|
|
sleep(5 SECONDS)
|
|
radio.autosay(SPAN_BIG("Response teams are to cease all on-station activities and route towards the nuclear fission reactor for manual detonation unless otherwise instructed by CentComm faculty."), "Automated Announcement", "Special Ops")
|
|
prep_overload()
|
|
|
|
#undef REACTOR_NEEDS_DIGGING
|
|
#undef REACTOR_NEEDS_CROWBAR
|
|
#undef REACTOR_NEEDS_PLASTITANIUM
|
|
#undef REACTOR_NEEDS_SCREWDRIVER
|
|
#undef REACTOR_NEEDS_WELDING
|
|
#undef REACTOR_NEEDS_PLASTEEL
|
|
#undef REACTOR_NEEDS_WRENCH
|
|
|
|
#undef REACTOR_LIGHT_COLOR
|
|
|
|
#undef TOTAL_CONTROL_RODS
|
|
|
|
#undef MIN_CHAMBERS_TO_OVERLOAD
|
|
|
|
#undef EVENT_MODIFIER
|
|
|
|
#undef HEAT_CAP
|
|
#undef AVERAGE_HEAT_THRESHOLD
|
|
#undef TOTAL_HEAT_THRESHOLD
|
|
#undef HEAT_CONVERSION_RATIO
|
|
#undef REACTIVITY_COEFFICIENT_CAP
|
|
|
|
#undef NGCR_MELTDOWN_PERCENT
|
|
#undef NGCR_EMERGENCY_PERCENT
|
|
#undef NGCR_DANGER_PERCENT
|
|
#undef NGCR_WARNING_PERCENT
|
|
#undef CRITICAL_TEMPERATURE
|
|
#undef WARNING_POINT
|
|
#undef EMERGENCY_POINT
|
|
#undef MELTDOWN_POINT
|
|
|
|
#undef NGCR_COUNTDOWN_TIME
|
|
#undef WARNING_DELAY
|
|
|
|
#undef HEAT_DAMAGE_RATE
|
|
#undef MOL_MINIMUM
|
|
#undef PRESSURE_MAXIMUM
|
|
#undef PRESSURE_DAMAGE
|
|
#undef DAMAGE_MINIMUM
|
|
#undef DAMAGE_MAXIMUM
|
|
#undef MOL_DAMAGE_MULTIPLIER
|
|
#undef HEAT_DAMAGE_MULTIPLIER
|
|
#undef EXPLOSION_MODIFIER
|
|
|
|
#undef MOLE_BONUS_THRESHOLD
|
|
#undef MOLE_BONUS_COMPONENT
|
|
#undef N2_OVERHEAT_BONUS
|
|
#undef N2_EVENT_MODIFIER
|
|
#undef N2O_OVERHEAT_BONUS
|
|
#undef N2O_EVENT_MODIFIER
|
|
#undef CO2_EVENT_MODIFIER
|
|
#undef O2_EVENT_MODIFIER
|
|
#undef O2_REACTIVITY_BONUS
|
|
#undef PLASMA_REACTIVITY_BONUS
|
|
#undef PLASMA_OVERHEAT_BONUS
|