Files
PollardTheDragon 394c3b0108 Stealth and Steel: The Space Ninja (#31497)
* 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
2026-03-25 23:11:10 +00:00

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