Files
Bubberstation/code/datums/components/area_sound_manager.dm
MrMelbert a9b0ecb7d0 Weather anomaly (#94509)
## About The Pull Request

A weather anomaly can spawn on the station, causing a sandstorm,
snowstorm, or rainstorm to affect the room (and nearby rooms). These
weathers range from "harmless" to "mildly damaging".

They last for around five minutes.
The weather doesn't start immediately, giving people time to neutralize
it before it causes damage.
Neutralizing the anomaly stops the weather. 
If they detonate, it just throw objects away from itself.

<img width="407" height="225" alt="image"
src="https://github.com/user-attachments/assets/2de1b797-2b17-442c-9023-11085b4849cf"
/>

<img width="991" height="674" alt="image"
src="https://github.com/user-attachments/assets/afe5a21d-9cf8-4e9d-95ec-adef6fd12a93"
/>

A rarer version of the anomaly can also occur, which brings a lightning
storm, which may damage the station itself.

Also, reactive weather armor. When you are hit, it summons a lightning
strike below nearby assailants.

## Why It's Good For The Game

With the added rain mechanics I wanted to use them, but it's kinda hard
to shoe them into the station because, "why is it raining on a space
station that makes no sense"

However, anomalies are weird, so this fits perfectly

## Changelog

🆑 Melbert
add: Weather anomalies, in four variants: Rain, snow, sand, and the rare
lightning storm.
add: Reactive Weather Armor
/🆑
2025-12-20 12:33:10 -05:00

82 lines
2.9 KiB
Plaintext

///Allows you to set a theme for a set of areas without tying them to looping sounds explicitly
/datum/component/area_sound_manager
dupe_mode = COMPONENT_DUPE_ALLOWED
///area -> looping sound type
var/list/area_to_looping_type = list()
///Current sound loop
var/datum/looping_sound/our_loop
///A list of "acceptable" z levels to be on. If you leave this, we're gonna delete ourselves
var/list/accepted_zs
/// World.time when we are allowed to start another sound loop
VAR_PRIVATE/next_loop_time
/datum/component/area_sound_manager/Initialize(area_loop_pairs, change_on, remove_on, acceptable_zs)
if(!ismovable(parent))
return
area_to_looping_type = area_loop_pairs
accepted_zs = acceptable_zs
change_the_track()
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(react_to_move))
RegisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(react_to_z_move))
// change on can be a list of signals
if(islist(change_on))
RegisterSignals(parent, change_on, PROC_REF(handle_change))
else if(!isnull(change_on))
RegisterSignal(parent, change_on, PROC_REF(handle_change))
// remove on can be a list of signals
if(islist(remove_on))
RegisterSignals(parent, remove_on, PROC_REF(handle_removal))
else if(!isnull(remove_on))
RegisterSignal(parent, remove_on, PROC_REF(handle_removal))
/datum/component/area_sound_manager/Destroy(force)
QDEL_NULL(our_loop)
. = ..()
/datum/component/area_sound_manager/proc/react_to_move(datum/source, atom/oldloc, dir, forced)
SIGNAL_HANDLER
var/list/loop_lookup = area_to_looping_type
if(loop_lookup[get_area(oldloc)] == loop_lookup[get_area(parent)])
return
change_the_track(TRUE)
/datum/component/area_sound_manager/proc/react_to_z_move(datum/source, turf/old_turf, turf/new_turf)
SIGNAL_HANDLER
if(!length(accepted_zs) || (new_turf?.z in accepted_zs))
return
qdel(src)
/datum/component/area_sound_manager/proc/handle_removal(datum/source)
SIGNAL_HANDLER
qdel(src)
/datum/component/area_sound_manager/proc/handle_change(datum/source)
SIGNAL_HANDLER
change_the_track()
/datum/component/area_sound_manager/proc/change_the_track(skip_start = FALSE)
var/existing_loop_id = our_loop?.timer_id
if(existing_loop_id)
// Time left will sometimes return negative values, just ignore them and start a new sound loop now
next_loop_time = world.time + max(timeleft(existing_loop_id, SSsound_loops) || 0, 0)
QDEL_NULL(our_loop)
var/area/our_area = get_area(parent)
var/new_loop_type = area_to_looping_type[our_area]
if(!new_loop_type)
return
our_loop = new new_loop_type(parent, FALSE, TRUE, skip_start)
// We're not ready to start another loop, wait before changing the sound so we don't double up
if(next_loop_time > world.time)
addtimer(CALLBACK(src, PROC_REF(start_looping_sound)), next_loop_time - world.time, TIMER_UNIQUE | TIMER_CLIENT_TIME | TIMER_NO_HASH_WAIT | TIMER_DELETE_ME, SSsound_loops)
return
start_looping_sound()
/datum/component/area_sound_manager/proc/start_looping_sound()
our_loop?.start()