Files
Batrachophreno 4ecb0bc21c Repath obj/machinery to obj/structure/machinery [MDB Ignore] (#22500)
Repaths obj/machinery to obj/structure/machinery. **Note for
reviewers:** the only meaningful changed code exists within
**code/game/objects/structures.dm** and
**code/game/objects/structures/_machinery.dm**, largely concerning
damage procs. With the exception of moving airlock defines to their own
file, ALL OTHER CHANGES ARE STRICTLY PATH CHANGES.

Objects, _categorically_, are largely divided between those you can hold
in your hand/inventory and those you can't. Machinery objects are
already subtypes of Structures behaviorally, this PR just makes their
pathing reflect that, and allows for future work (tool actions, more
health/destruction functionality) to be developed without unnecessary
code duplication.

I have tested this PR by loading up the Horizon and dismantling various
machines and structures with tools, shooting guns of various types
throughout the ship, and detonating a bunch of explosions throughout the
ship.
2026-05-26 19:35:48 +00:00

267 lines
9.0 KiB
Plaintext

/// A global list of all radiation collectors.
GLOBAL_LIST_INIT_TYPED(rad_collectors, /obj/structure/machinery/power/rad_collector, list())
/obj/structure/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
maxhealth = OBJECT_HEALTH_LOW
/// 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/structure/machinery/power/rad_collector/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
if (..(user, 3))
var/last_power_kw = round(last_power / 1000, 0.1)
. += "The meter indicates that \the [src] is collecting [last_power_kw] kW."
/obj/structure/machinery/power/rad_collector/Initialize()
. = ..()
GLOB.rad_collectors += src
/obj/structure/machinery/power/rad_collector/Destroy()
GLOB.rad_collectors -= src
return ..()
/obj/structure/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/structure/machinery/power/rad_collector/CanUseTopic(mob/user)
if(!anchored)
return STATUS_CLOSE
return ..()
/obj/structure/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/structure/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.tool_behaviour == TOOL_CROWBAR)
if(loaded_tank && !locked)
to_chat(user, SPAN_NOTICE("You detach and remove \the [loaded_tank.name]."))
eject(user)
return TRUE
if(attacking_item.tool_behaviour == TOOL_WRENCH)
if(loaded_tank)
USE_FEEDBACK_FAILURE("Remove the phoron tank first.")
return TRUE
for(var/obj/structure/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/structure/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/structure/machinery/power/rad_collector/return_air()
if(loaded_tank)
return loaded_tank.return_air()
/obj/structure/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/structure/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/structure/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_TO_POWERNET(src, power_produced)
last_power_new = power_produced
return
return
/obj/structure/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/structure/machinery/power/rad_collector/toggle_power()
active = !active
if(active)
flick("ca_active", src)
else
flick("ca_deactive", src)
update_icon()