Files
Aurora.3/code/modules/power/collector.dm
T
afa06b4cd1 Ported radiation collector arrays from baystation12 (#19174)
These are intended to be available in the cargo list, so marking as
draft until that can be confirmed.

This ports radiation collectors from baystation12. These use the
ssradiation system that we have, to generate power from phoron and
radiation. This is contrasted with the prior iteration of these units,
which solely generated power from the singularity. These could be used
to generate power around a supermatter reactor, or a fusion reactor, or
any number of things that the playerbase can think of. The only thing is
that these cost precious phoron; you do not want to use these as the
sole source of power aboard anything.

### Asset Licenses
The following assets that **have not** been created by myself are
included in this PR:

| Path | Original Author | License |
| --- | --- | --- |
| icons/obj/machinery/rad_collector.dmi | Chinsky (Baystation12) |
CC-BY-SA |

---------

Co-authored-by: Cody Brittain <cbrittain10@live.com>
2025-03-17 21:46:50 +00:00

267 lines
8.8 KiB
Plaintext

/// A global list of all radiation collectors.
GLOBAL_LIST_INIT_TYPED(rad_collectors, /obj/machinery/power/rad_collector, list())
/obj/machinery/power/rad_collector
name = "radiation collector array"
desc = "A device which uses radiation and phoron to produce power."
icon = 'icons/obj/machinery/rad_collector.dmi'
icon_state = "ca"
anchored = FALSE
density = TRUE
req_access = list(ACCESS_ENGINE_EQUIP)
/// The tank of phoron currently attached to the radiation collector
var/obj/item/tank/phoron/loaded_tank = null
/// Current health of the collector. TODO: replace with global health
var/health = 100
/// The maximum safe temperature that the radiation collector can handle
var/max_safe_temp = 1000 + T0C
/// A boolean determining whether the collector has melted or not.
var/melted = FALSE
/// Internal variable storing last power generation amount
var/last_power = 0
/// Internal variable storing last power generation amount. Duplicate variable to account for the SM being processed first
var/last_power_new = 0
/// Whether the radiation collector is active or not.
var/active = FALSE
/// Whether the radiation collector's controls are locked or not.
var/locked = FALSE
/// Determines the ratio of default draining of phoron while radiation collector is active
var/drainratio = 1
/// Internal variable storing last radiation amount measured
var/last_rads
/// Radiation collector will reach max power output at this value, and break at twice this value
var/max_rads = 250
/// Maximum power output for this radiation collector.
var/max_power = 5e5
/// Pulse coefficient, for multiplying power output by radiation input
var/pulse_coeff = 20
/// Internal variable storing last time an alert message was outputted
var/end_time = 0
/// How long to wait between alert messages, if radiation input exceeds safe levels
var/alert_delay = 10 SECONDS
/obj/machinery/power/rad_collector/Initialize()
. = ..()
GLOB.rad_collectors += src
/obj/machinery/power/rad_collector/Destroy()
GLOB.rad_collectors -= src
return ..()
/obj/machinery/power/rad_collector/process(seconds_per_tick)
if((stat && BROKEN) || melted)
return
var/turf/T = get_turf(src)
if(T)
var/datum/gas_mixture/our_turfs_air = T.return_air()
if(our_turfs_air.temperature > max_safe_temp)
health -= ((our_turfs_air.temperature - max_safe_temp) / 10) * seconds_per_tick
if(health <= 0)
collector_break()
//so that we don't zero out the meter if the SM is processed first.
last_power = last_power_new
last_power_new = 0
last_rads = SSradiation.get_rads_at_turf(get_turf(src))
if(loaded_tank && active)
if(last_rads > max_rads*2)
collector_break()
if(last_rads)
if(last_rads > max_rads)
if(world.time > end_time)
end_time = world.time + alert_delay
visible_message("[icon2html(src, viewers(get_turf(src)))] \the [src] beeps loudly as the radiation reaches dangerous levels, indicating imminent damage.")
playsound(src, 'sound/effects/screech.ogg', 100, 1, 1)
receive_pulse(12.5*(last_rads/max_rads)/(0.3+(last_rads/max_rads)) * seconds_per_tick)
if(loaded_tank)
if(loaded_tank.air_contents.gas[GAS_PHORON] == 0)
investigate_log("[SPAN_COLOR("red", "out of fuel")].","singulo")
eject()
else
loaded_tank.air_contents.adjust_gas(GAS_PHORON, (-0.01*drainratio*min(last_rads,max_rads)/max_rads) * seconds_per_tick) //fuel cost increases linearly with incoming radiation
/obj/machinery/power/rad_collector/CanUseTopic(mob/user)
if(!anchored)
return STATUS_CLOSE
return ..()
/obj/machinery/power/rad_collector/attack_hand(mob/user)
if(!CanInteract(user, GLOB.physical_state))
return FALSE
. = TRUE
if((stat & BROKEN) || melted)
USE_FEEDBACK_FAILURE("The [src] is completely destroyed!")
if(!src.locked)
toggle_power()
user.visible_message("[user.name] turns the [src.name] [active? "on":"off"].", \
"You turn the [src.name] [active? "on":"off"].")
investigate_log("turned [active ? SPAN_COLOR("green", "on") : SPAN_COLOR("red", "off")] by [user.key]. [loaded_tank ? "Fuel: [round(loaded_tank.air_contents.gas[GAS_PHORON]/0.29)]%" : SPAN_COLOR("red", "It is empty")].","singulo")
else
USE_FEEDBACK_FAILURE("The controls are locked!")
..()
/obj/machinery/power/rad_collector/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/tank/phoron))
if(!anchored)
USE_FEEDBACK_FAILURE("The [src] needs to be secured to the floor first.")
return TRUE
if(loaded_tank)
USE_FEEDBACK_FAILURE("There's already a phoron tank loaded.")
return TRUE
if(!user.unEquip(attacking_item, src))
return TRUE
loaded_tank = attacking_item
attacking_item.forceMove(src)
to_chat(user, SPAN_NOTICE("You slot [attacking_item] into [src] and tighten the connecting valve."))
update_icon()
return TRUE
if(attacking_item.iscrowbar())
if(loaded_tank && !locked)
to_chat(user, SPAN_NOTICE("You detach and remove \the [loaded_tank.name]."))
eject(user)
return TRUE
if(attacking_item.iswrench())
if(loaded_tank)
USE_FEEDBACK_FAILURE("Remove the phoron tank first.")
return TRUE
for(var/obj/machinery/power/rad_collector/R in get_turf(src))
if(R != src)
USE_FEEDBACK_FAILURE("You cannot install more than one collector on the same spot.")
return TRUE
playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
src.anchored = !src.anchored
user.visible_message("[user.name] [anchored? "secures":"unsecures"] the [src.name].", \
"You [anchored? "secure":"undo"] the external bolts.", \
"You hear a ratchet")
if(anchored && !(stat & BROKEN))
connect_to_network()
else
disconnect_from_network()
return TRUE
if(istype(attacking_item, /obj/item/card/id)||istype(attacking_item, /obj/item/modular_computer))
if (allowed(user))
if(active)
locked = !locked
to_chat(user, "The controls are now [locked ? "locked." : "unlocked."]")
else
locked = FALSE //just in case it somehow gets locked while the collector is active
USE_FEEDBACK_FAILURE("The controls can only be locked when the [src] is active.")
else
to_chat(user, SPAN_ALERT("Access denied!"))
return TRUE
return ..()
/// Handles dealing with a breaking radiation collector
/obj/machinery/power/rad_collector/proc/collector_break()
if(loaded_tank && loaded_tank.air_contents)
var/turf/T = get_turf(src)
if(T)
T.assume_air(loaded_tank.air_contents)
audible_message(SPAN_DANGER("\The [loaded_tank.name] detonates, sending shrapnel flying!"))
explosion(T, -1, -1, 0)
QDEL_NULL(loaded_tank)
disconnect_from_network()
stat |= BROKEN
melted = TRUE
anchored = FALSE
active = FALSE
desc += " This one is destroyed beyond repair."
update_icon()
/obj/machinery/power/rad_collector/get_examine_text(user, distance, is_adjacent, infix, suffix)
. = ..()
if (..(user, 3))
. += "The meter indicates that \the [src] is collecting [last_power] W."
/obj/machinery/power/rad_collector/return_air()
if(loaded_tank)
return loaded_tank.return_air()
/obj/machinery/power/rad_collector/ex_act(severity)
switch(severity)
if(2, 3)
eject()
return ..()
/**
* Ejects the stored phoron tank. If user is not defined or user is an invalid target, will eject onto the collector's position.
*
* Arguments:
* - user: Optional. If defined, will eject the tank into the user's hands, if possible.
*/
/obj/machinery/power/rad_collector/proc/eject(mob/user)
locked = 0
var/obj/item/tank/phoron/tank = src.loaded_tank
if(!tank)
return
if(user)
user.put_in_hands(tank, TRUE)
else
tank.dropInto(loc)
tank.reset_plane_and_layer()
src.loaded_tank = null
if(active)
toggle_power()
else
update_icon()
/**
* Handles the generation of power for the radiation collector
*
* Arguments:
* - pulse_strength: The strength of the pulse to calculate into power output
*/
/obj/machinery/power/rad_collector/proc/receive_pulse(pulse_strength)
if(loaded_tank && active)
var/power_produced = 0
power_produced = min(100*loaded_tank.air_contents.gas[GAS_PHORON]*pulse_strength*pulse_coeff,max_power)
add_avail(power_produced)
last_power_new = power_produced
return
return
/obj/machinery/power/rad_collector/update_icon()
if(melted)
icon_state = "ca_melt"
else if(active)
icon_state = "ca_on"
else
icon_state = "ca"
ClearOverlays()
underlays.Cut()
if(loaded_tank)
AddOverlays(image(icon, "ptank"))
AddOverlays(emissive_appearance(icon, "ca_filling"))
underlays += image(icon, "ca_filling")
underlays += image(icon, "ca_inside")
if(!operable())
return
if(active)
var/rad_power = round(min(100 * last_rads / max_rads, 100), 20)
AddOverlays(emissive_appearance(icon, "rads_[rad_power]"))
AddOverlays(image(icon, "rads_[rad_power]"))
AddOverlays(emissive_appearance(icon, "on"))
AddOverlays(image(icon, "on"))
/obj/machinery/power/rad_collector/toggle_power()
active = !active
if(active)
flick("ca_active", src)
else
flick("ca_deactive", src)
update_icon()