mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-31 00:58:26 +01:00
[MANUAL MIRROR] Adds a new event, "Radiation Leak" (#19075)
Adds a new event, "Radiation Leak" (#72655) Adds a new random event, "Radiation Leak". A random machine in a random department will begin leaking radiation. This includes a radiation aura around it, and semi-regular puffs of smoke clouds containing mutagen and polonium. The machine will stop leaking after some time, but can also be stopped by using a random tool on it, indicated in its examine text. I added a new "radiation emitter" component, for simple "emits radiation" behavior. Then I made the radioactive mutation use it. This means that the radioactive gene emits radiation while on stasis and dead, rather than only in life. Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
This commit is contained in:
co-authored by
MrMelbert
parent
9c7600583c
commit
636c142927
@@ -0,0 +1,87 @@
|
||||
/// Minimum duration between pulses for the radioactive emitter component.
|
||||
/// This is chosen arbitrarily. It can theoretically go down to 0.1 SECONDS but god please don't
|
||||
#define MIN_PULSE_COOLDOWN 0.5 SECONDS
|
||||
|
||||
/**
|
||||
* # Radioactive Emitter
|
||||
*
|
||||
* Simple component that you can attach to something to make it emit radiation pulses over time.
|
||||
*/
|
||||
/datum/component/radioactive_emitter
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
|
||||
|
||||
/// The actual cooldown between rad pulses
|
||||
COOLDOWN_DECLARE(rad_pulse_cooldown)
|
||||
|
||||
/// The length of the cooldown between radiation pulses
|
||||
var/cooldown_time = 5 SECONDS
|
||||
/// How far the radiation pulses aggregate with other radiation pulses (see: [proc/radiation_pulse])
|
||||
var/range = 1
|
||||
/// How much radiation protection threshold is passed to the radiation pulse (see: [proc/radiation_pulse])
|
||||
var/threshold = RAD_MEDIUM_INSULATION
|
||||
/// Optional - What is shown on examine of the parent?
|
||||
var/examine_text
|
||||
|
||||
/datum/component/radioactive_emitter/Initialize(
|
||||
cooldown_time = 5 SECONDS,
|
||||
range = 1,
|
||||
threshold = RAD_MEDIUM_INSULATION,
|
||||
examine_text,
|
||||
)
|
||||
|
||||
if(!isturf(parent) && !ismovable(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.cooldown_time = max(cooldown_time, MIN_PULSE_COOLDOWN)
|
||||
src.range = range
|
||||
src.threshold = threshold
|
||||
src.examine_text = examine_text
|
||||
|
||||
// We process on fastprocess even though we're on a cooldown based system.
|
||||
// Easier to handle edits to the cooldown duration, prevents timer spam for short cooldown emitters
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/datum/component/radioactive_emitter/Destroy(force, silent)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
return ..()
|
||||
|
||||
/datum/component/radioactive_emitter/RegisterWithParent()
|
||||
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
|
||||
|
||||
/datum/component/radioactive_emitter/UnregisterFromParent()
|
||||
UnregisterSignal(parent, COMSIG_PARENT_EXAMINE)
|
||||
|
||||
/datum/component/radioactive_emitter/InheritComponent(
|
||||
datum/component/new_comp,
|
||||
i_am_original,
|
||||
cooldown_time = 5 SECONDS,
|
||||
range = 1,
|
||||
threshold = RAD_NO_INSULATION,
|
||||
examine_text,
|
||||
)
|
||||
|
||||
if(!i_am_original)
|
||||
return
|
||||
|
||||
// Only care about modifying our rad wave argument.
|
||||
src.cooldown_time = cooldown_time
|
||||
src.range = range
|
||||
src.threshold = threshold
|
||||
// Don't touch examine text or whatever else.
|
||||
|
||||
/datum/component/radioactive_emitter/process(delta_time)
|
||||
if(!COOLDOWN_FINISHED(src, rad_pulse_cooldown))
|
||||
return
|
||||
|
||||
COOLDOWN_START(src, rad_pulse_cooldown, cooldown_time)
|
||||
radiation_pulse(parent, range, threshold)
|
||||
|
||||
/datum/component/radioactive_emitter/proc/on_examine(datum/source, mob/user, list/examine_list)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!examine_text)
|
||||
return
|
||||
|
||||
examine_list += examine_text
|
||||
|
||||
#undef MIN_PULSE_COOLDOWN
|
||||
@@ -6,24 +6,38 @@
|
||||
instability = 5
|
||||
difficulty = 8
|
||||
power_coeff = 1
|
||||
|
||||
COOLDOWN_DECLARE(last_radioactive_pulse)
|
||||
|
||||
/datum/mutation/human/radioactive/on_life(delta_time, times_fired)
|
||||
if (!COOLDOWN_FINISHED(src, last_radioactive_pulse))
|
||||
return
|
||||
|
||||
COOLDOWN_START(src, last_radioactive_pulse, 5 SECONDS)
|
||||
radiation_pulse(
|
||||
owner,
|
||||
max_range = 1 * (GET_MUTATION_POWER(src) * 2),
|
||||
threshold = RAD_MEDIUM_INSULATION,
|
||||
)
|
||||
/// Weakref to our radiation emitter component
|
||||
var/datum/weakref/radioactivity_source_ref
|
||||
|
||||
/datum/mutation/human/radioactive/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
|
||||
..()
|
||||
. = ..()
|
||||
if(!(type in visual_indicators))
|
||||
visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "radiation", -MUTATIONS_LAYER))
|
||||
|
||||
/datum/mutation/human/radioactive/get_visual_indicator()
|
||||
return visual_indicators[type][1]
|
||||
|
||||
/datum/mutation/human/radioactive/on_acquiring(mob/living/carbon/human/acquirer)
|
||||
. = ..()
|
||||
var/datum/component/radioactive_emitter/radioactivity_source = make_radioactive(acquirer)
|
||||
radioactivity_source_ref = WEAKREF(radioactivity_source)
|
||||
|
||||
/datum/mutation/human/radioactive/modify()
|
||||
. = ..()
|
||||
make_radioactive(owner)
|
||||
|
||||
/**
|
||||
* Makes the passed mob radioactive, or if they're already radioactive,
|
||||
* update their radioactivity to the newly set values
|
||||
*/
|
||||
/datum/mutation/human/radioactive/proc/make_radioactive(mob/living/carbon/human/who)
|
||||
return who.AddComponent(
|
||||
/datum/component/radioactive_emitter, \
|
||||
cooldown_time = 5 SECONDS, \
|
||||
range = 1 * (GET_MUTATION_POWER(src) * 2), \
|
||||
threshold = RAD_MEDIUM_INSULATION, \
|
||||
)
|
||||
|
||||
/datum/mutation/human/radioactive/on_losing(mob/living/carbon/human/owner)
|
||||
QDEL_NULL(radioactivity_source_ref)
|
||||
return ..()
|
||||
|
||||
@@ -170,12 +170,15 @@ Runs the event
|
||||
var/datum/round_event_control/control
|
||||
|
||||
/// When in the lifetime to call start().
|
||||
/// This is in seconds - so 1 = ~2 seconds in.
|
||||
var/start_when = 0
|
||||
/// When in the lifetime to call announce(). If you don't want it to announce use announce_chance, below.
|
||||
/// This is in seconds - so 1 = ~2 seconds in.
|
||||
var/announce_when = 0
|
||||
/// Probability of announcing, used in prob(), 0 to 100, default 100. Called in process, and for a second time in the ion storm event.
|
||||
var/announce_chance = 100
|
||||
/// When in the lifetime the event should end.
|
||||
/// This is in seconds - so 1 = ~2 seconds in.
|
||||
var/end_when = 0
|
||||
|
||||
/// How long the event has existed. You don't need to change this.
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/datum/round_event_control/radiation_leak
|
||||
name = "Radiation Leak"
|
||||
description = "A radiation leak happens somewhere on the station, emanating radiation around a machine in the area. \
|
||||
Engineering can stop the leak by using certain tools on it."
|
||||
typepath = /datum/round_event/radiation_leak
|
||||
weight = 15
|
||||
max_occurrences = 3
|
||||
category = EVENT_CATEGORY_ENGINEERING
|
||||
|
||||
/datum/round_event/radiation_leak
|
||||
start_when = 1 // 2 seconds in
|
||||
announce_when = 10 // 20 seconds in
|
||||
end_when = 150 // 300 seconds / ~5 minutes in
|
||||
|
||||
/// Weakref to the machine spitting out rads
|
||||
var/datum/weakref/picked_machine_ref
|
||||
/// List of signals added to the picked machine, so we can clear them later
|
||||
var/list/signals_to_add
|
||||
|
||||
/datum/round_event/radiation_leak/setup()
|
||||
// Pick a generic event spawn somewhere in the world.
|
||||
// We will try to find a machine within a few turfs of it to start spewing rads from.
|
||||
var/list/possible_locs = GLOB.generic_event_spawns.Copy()
|
||||
while(length(possible_locs))
|
||||
var/turf/chosen_loc = get_turf(pick_n_take(possible_locs))
|
||||
for(var/obj/machinery/sick_device in range(3, chosen_loc))
|
||||
// Look for dense machinery. Basically stops stuff like wall mounts and pipes, silly ones.
|
||||
// But keep in vents and scrubbers. I think it's funny if they start spitting out radiation
|
||||
if(!sick_device.density && !istype(sick_device, /obj/machinery/atmospherics/components/unary))
|
||||
continue
|
||||
// Skip invisible stuff
|
||||
if(sick_device.invisibility || !sick_device.alpha || !sick_device.mouse_opacity)
|
||||
continue
|
||||
// Skip undertiles
|
||||
if(sick_device.IsObscured())
|
||||
continue
|
||||
// Very basic check for atmos passability here
|
||||
// We don't want to put our smoke inside something that can't spread it out, like airlocks
|
||||
if(sick_device.can_atmos_pass() != ATMOS_PASS_YES)
|
||||
continue
|
||||
|
||||
// We found something, we can just return now
|
||||
picked_machine_ref = WEAKREF(sick_device)
|
||||
return
|
||||
|
||||
/datum/round_event/radiation_leak/announce(fake)
|
||||
var/obj/machinery/the_source_of_our_problems = picked_machine_ref?.resolve()
|
||||
var/area/station/location_descriptor
|
||||
|
||||
if(fake)
|
||||
location_descriptor = pick(GLOB.the_station_areas)
|
||||
|
||||
else if(the_source_of_our_problems)
|
||||
location_descriptor = get_area(the_source_of_our_problems)
|
||||
|
||||
priority_announce("A radiation leak has been detected in [location_descriptor || "an unknown area"]. \
|
||||
All crew are to evacuate the affected area. Our [pick("mechanics", "engineers", "scientists", "interns", "sensors", "readings")] \
|
||||
report that a machine within is causing it - repair it quickly to stop the leak.")
|
||||
|
||||
/datum/round_event/radiation_leak/start()
|
||||
var/obj/machinery/the_source_of_our_problems = picked_machine_ref?.resolve()
|
||||
if(!the_source_of_our_problems)
|
||||
return
|
||||
|
||||
// We'll add some tool acts to the thing that allow people to "repair the machine"
|
||||
// The key of this assoc list is the "method" of how they're fixing the thing (just flavor for examine),
|
||||
// and the value is what tool they actually need to use on the thing to fix it
|
||||
var/list/how_do_we_fix_it = list(
|
||||
"wrenching a few valves" = TOOL_WRENCH,
|
||||
"tightening its bolts" = TOOL_WRENCH,
|
||||
"crowbaring its panel [pick("down", "up")]" = TOOL_CROWBAR,
|
||||
"tightening some screws" = TOOL_SCREWDRIVER,
|
||||
"checking its [pick("wires", "circuits")]" = TOOL_MULTITOOL,
|
||||
"welding its panel [pick("open", "shut")]" = TOOL_WELDER,
|
||||
"analyzing its readings" = TOOL_ANALYZER,
|
||||
"cutting some excess wires" = TOOL_WIRECUTTER,
|
||||
)
|
||||
var/list/fix_it_keys = assoc_to_keys(how_do_we_fix_it) // Returns a copy that we can pick and take from, fortunately
|
||||
|
||||
// Select a few methods of how to fix it
|
||||
var/list/methods_to_fix = list()
|
||||
for(var/i in 1 to rand(1, 3))
|
||||
methods_to_fix += pick_n_take(fix_it_keys)
|
||||
|
||||
// Construct the signals
|
||||
signals_to_add = list()
|
||||
for(var/tool_method in methods_to_fix)
|
||||
signals_to_add += COMSIG_ATOM_TOOL_ACT(how_do_we_fix_it[tool_method])
|
||||
|
||||
the_source_of_our_problems.visible_message(span_danger("[the_source_of_our_problems] starts to emanate a horrible green gas!"))
|
||||
// Add the component that makes the thing radioactive
|
||||
the_source_of_our_problems.AddComponent(
|
||||
/datum/component/radioactive_emitter, \
|
||||
cooldown_time = 2 SECONDS, \
|
||||
range = 5, \
|
||||
threshold = RAD_MEDIUM_INSULATION, \
|
||||
examine_text = span_green("<i>It's emanating a green gas... You could probably stop it by [english_list(methods_to_fix, and_text = " or ")].</i>"), \
|
||||
)
|
||||
// Register signals to make it fixable
|
||||
if(length(signals_to_add))
|
||||
RegisterSignals(the_source_of_our_problems, signals_to_add, PROC_REF(on_machine_tooled))
|
||||
|
||||
// And yknow puffs some nasty reagents into the air, just to seal the deal
|
||||
puff_some_smoke(the_source_of_our_problems)
|
||||
// Let ghosts know
|
||||
announce_to_ghosts(the_source_of_our_problems)
|
||||
|
||||
/datum/round_event/radiation_leak/tick()
|
||||
// Puff some smoke into the air around our machine roughly 3 times before we stop
|
||||
if(activeFor % (end_when / 3) != 0)
|
||||
return
|
||||
|
||||
var/obj/machinery/impromptu_smoke_machine = picked_machine_ref?.resolve()
|
||||
if(!impromptu_smoke_machine)
|
||||
return
|
||||
|
||||
puff_some_smoke(impromptu_smoke_machine)
|
||||
|
||||
/datum/round_event/radiation_leak/end()
|
||||
var/obj/machinery/the_end_of_our_problems = picked_machine_ref?.resolve()
|
||||
if(!the_end_of_our_problems)
|
||||
return
|
||||
|
||||
the_end_of_our_problems.visible_message(span_notice("The gas emanating from [the_end_of_our_problems] dissipates."))
|
||||
qdel(the_end_of_our_problems.GetComponent(/datum/component/radioactive_emitter))
|
||||
if(length(signals_to_add))
|
||||
UnregisterSignal(the_end_of_our_problems, signals_to_add)
|
||||
picked_machine_ref = null
|
||||
signals_to_add = null
|
||||
|
||||
/// Helper to shoot some smoke into the air around the passed atom
|
||||
/datum/round_event/radiation_leak/proc/puff_some_smoke(atom/where)
|
||||
var/turf/below_where = get_turf(where)
|
||||
var/datum/effect_system/fluid_spread/smoke/chem/gross_smoke = new()
|
||||
gross_smoke.chemholder.add_reagent(/datum/reagent/toxin/polonium, 10) // Polonium (it causes radiation)
|
||||
gross_smoke.chemholder.add_reagent(/datum/reagent/toxin/mutagen, 10) // Mutagen (it causes mutations. Also it's green... Primarily because it's green.)
|
||||
gross_smoke.attach(below_where)
|
||||
gross_smoke.set_up(2, holder = where, location = below_where, silent = TRUE)
|
||||
gross_smoke.start()
|
||||
playsound(below_where, 'sound/effects/smoke.ogg', 50, vary = TRUE)
|
||||
|
||||
/**
|
||||
* Signal proc for [COMSIG_ATOM_TOOL_ACT], from a variety of signals, registered on the machine spitting radiation
|
||||
*
|
||||
* We allow for someone to stop the event early by using the proper tools, hinted at in examine, on the machine
|
||||
*/
|
||||
/datum/round_event/radiation_leak/proc/on_machine_tooled(obj/machinery/source, mob/living/user, obj/item/tool)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
INVOKE_ASYNC(src, PROC_REF(try_remove_radiation), source, user, tool)
|
||||
return COMPONENT_BLOCK_TOOL_ATTACK
|
||||
|
||||
/// Attempts a do_after, and if successful, stops the event
|
||||
/datum/round_event/radiation_leak/proc/try_remove_radiation(obj/machinery/source, mob/living/user, obj/item/tool)
|
||||
source.balloon_alert(user, "fixing leak...")
|
||||
// Fairly long do after. It shouldn't be SUPER easy to just run in and stop it.
|
||||
// A tider can fix it if they want to soak a bunch of rads and inhale noxious fumes,
|
||||
// but only an equipped engineer should be able to handle it painlessly.
|
||||
if(!tool.use_tool(source, user, 30 SECONDS, amount = (tool.tool_behaviour == TOOL_WELDER ? 2 : 0), volume = 50))
|
||||
source.balloon_alert(user, "interrupted!")
|
||||
return
|
||||
|
||||
source.balloon_alert(user, "leak repaired")
|
||||
// Force end the event
|
||||
processing = FALSE
|
||||
end()
|
||||
kill()
|
||||
@@ -999,6 +999,7 @@
|
||||
#include "code\datums\components\punchcooldown.dm"
|
||||
#include "code\datums\components\puzzgrid.dm"
|
||||
#include "code\datums\components\radiation_countdown.dm"
|
||||
#include "code\datums\components\radioactive_emitter.dm"
|
||||
#include "code\datums\components\reagent_refiller.dm"
|
||||
#include "code\datums\components\regenerator.dm"
|
||||
#include "code\datums\components\religious_tool.dm"
|
||||
@@ -3225,6 +3226,7 @@
|
||||
#include "code\modules\events\mice_migration.dm"
|
||||
#include "code\modules\events\portal_storm.dm"
|
||||
#include "code\modules\events\processor_overload.dm"
|
||||
#include "code\modules\events\radiation_leak.dm"
|
||||
#include "code\modules\events\radiation_storm.dm"
|
||||
#include "code\modules\events\sandstorm.dm"
|
||||
#include "code\modules\events\scrubber_clog.dm"
|
||||
|
||||
Reference in New Issue
Block a user