Files
Bubberstation/code/datums/components/weatherannouncer.dm
Alexis 21b4095dfd [MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026

fixes https://github.com/Bubberstation/Bubberstation/issues/5549

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com>
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com>
Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com>
Co-authored-by: loganuk <fakeemail123@aol.com>
Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com>
Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
Co-authored-by: Lucy <lucy@absolucy.moe>
Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com>
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com>
Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com>
Co-authored-by: Alexander V. <volas@ya.ru>
Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com>
Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Tim <timothymtorres@gmail.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com>
Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com>
Co-authored-by: Josh <josh.adam.powell@gmail.com>
Co-authored-by: Josh Powell <josh.powell@softwire.com>
Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com>
Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com>
Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com>
Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
2026-05-16 00:56:00 +02:00

263 lines
9.0 KiB
Plaintext

#define WEATHER_ALERT_CLEAR 0
#define WEATHER_ALERT_INCOMING 1
#define WEATHER_ALERT_IMMINENT_OR_ACTIVE 2
#define WEATHER_CLEAR_DELAY 2 MINUTES
#define WEATHER_INCOMING_DELAY 30 SECONDS
/// Component which makes you yell about what the weather is
/datum/component/weather_announcer
/// Currently displayed warning level
var/warning_level = WEATHER_ALERT_CLEAR
/// Whether the incoming weather is actually going to harm you
var/is_weather_dangerous = TRUE
/// Are we actually turned on right now?
var/enabled = TRUE
/// Overlay added when things are alright
var/state_normal
/// Overlay added when you should start looking for shelter
var/state_warning
/// Overlay added when you are in danger
var/state_danger
/// If null, does nothing
/// If set to a ztrait, like ZTRAIT_MINING, we check zlevels with that trait for weather towers
/// Having no active radar towers on those levels will reduce the accuracy of the warnings
var/radar_z_trait
/// Modifier to the accuracy of the weather warning times
/// Changes every unique storm event
VAR_FINAL/accuracy_mod = 0
/datum/component/weather_announcer/Initialize(
state_normal,
state_warning,
state_danger,
radar_z_trait,
)
. = ..()
if (!ismovable(parent))
return COMPONENT_INCOMPATIBLE
START_PROCESSING(SSprocessing, src)
RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays))
RegisterSignal(parent, COMSIG_MACHINERY_POWER_RESTORED, PROC_REF(on_powered))
RegisterSignal(parent, COMSIG_MACHINERY_POWER_LOST, PROC_REF(on_power_lost))
src.state_normal = state_normal
src.state_warning = state_warning
src.state_danger = state_danger
src.radar_z_trait = radar_z_trait
var/atom/speaker = parent
speaker.update_appearance(UPDATE_ICON)
update_light_color()
/datum/component/weather_announcer/Destroy(force)
STOP_PROCESSING(SSprocessing, src)
return ..()
/// Add appropriate overlays
/datum/component/weather_announcer/proc/on_update_overlays(atom/parent_atom, list/overlays)
SIGNAL_HANDLER
if (!enabled || !state_normal || !state_warning || !state_danger)
return
switch (warning_level)
if(WEATHER_ALERT_CLEAR)
overlays += state_normal
if(WEATHER_ALERT_INCOMING)
overlays += state_warning
if(WEATHER_ALERT_IMMINENT_OR_ACTIVE)
overlays += (is_weather_dangerous) ? state_danger : state_warning
/// If powered, receive updates
/datum/component/weather_announcer/proc/on_powered()
SIGNAL_HANDLER
enabled = TRUE
var/atom/speaker = parent
speaker.update_appearance(UPDATE_ICON)
/// If no power, don't receive updates
/datum/component/weather_announcer/proc/on_power_lost()
SIGNAL_HANDLER
enabled = FALSE
var/atom/speaker = parent
speaker.update_appearance(UPDATE_ICON)
/datum/component/weather_announcer/proc/check_accuracy()
for(var/z_level in SSmapping.levels_by_trait(radar_z_trait))
for(var/obj/machinery/power/weather_tower/radar as anything in GLOB.weather_towers["[z_level]"])
if(radar.active)
return TRUE
return FALSE
/datum/component/weather_announcer/process(seconds_per_tick)
if (!enabled)
return
var/previous_level = warning_level
var/previous_danger = is_weather_dangerous
set_current_alert_level()
if(previous_level == warning_level && previous_danger == is_weather_dangerous)
return // No change
var/atom/movable/speaker = parent
var/msg = get_warning_message()
var/obj/machinery/announcement_system/aas = get_announcement_system(/datum/aas_config_entry/weather, speaker, list(RADIO_CHANNEL_SUPPLY))
// Active AAS will override default announcement lines
if (aas)
msg = aas.compile_config_message(/datum/aas_config_entry/weather, list(), is_weather_dangerous ? warning_level + 1 : 4)
// Stop toggling on radios for it, please!
aas.broadcast(msg, list(RADIO_CHANNEL_SUPPLY))
// Still say it, because you can be not on our level
speaker.say(msg)
speaker.update_appearance(UPDATE_ICON)
update_light_color()
/datum/component/weather_announcer/proc/update_light_color()
var/atom/movable/light = parent
switch(warning_level)
if(WEATHER_ALERT_CLEAR)
light.set_light_color(LIGHT_COLOR_GREEN)
if(WEATHER_ALERT_INCOMING)
light.set_light_color(LIGHT_COLOR_DIM_YELLOW)
if(WEATHER_ALERT_IMMINENT_OR_ACTIVE)
if (is_weather_dangerous)
light.set_light_color(LIGHT_COLOR_INTENSE_RED)
else
light.set_light_color(LIGHT_COLOR_DIM_YELLOW)
light.update_light()
/// Returns a string we should display to communicate what you should be doing
/datum/component/weather_announcer/proc/get_warning_message()
switch(warning_level)
if(WEATHER_ALERT_CLEAR)
return "All clear, no weather alerts to report."
if(WEATHER_ALERT_INCOMING)
return "Weather front incoming, begin to seek shelter."
if(WEATHER_ALERT_IMMINENT_OR_ACTIVE)
if (!is_weather_dangerous)
return "No risk expected from incoming weather front."
return "Weather front imminent, find shelter immediately."
return "Error in meteorological calculation. Please report this deviation to a trained programmer."
/datum/component/weather_announcer/proc/time_till_storm()
var/list/mining_z_levels = SSmapping.levels_by_trait(radar_z_trait)
if(!length(mining_z_levels))
return // No problems if there are no mining z levels
for(var/datum/weather/check_weather as anything in SSweather.processing)
if(!(check_weather.weather_flags & WEATHER_BAROMETER) || check_weather.stage == WIND_DOWN_STAGE || check_weather.stage == END_STAGE)
continue
for (var/mining_level in mining_z_levels)
if(mining_level in check_weather.impacted_z_levels)
warning_level = WEATHER_ALERT_IMMINENT_OR_ACTIVE
accuracy_mod = 0 // reset for next storm
return 0
var/time_until_next = INFINITY
for(var/mining_level in mining_z_levels)
// About to start!
if (SSweather.eligible_zlevels["[mining_level]"])
time_until_next = 0
break
var/next_time = timeleft(SSweather.next_hit_by_zlevel["[mining_level]"])
if (!isnull(next_time) && next_time < time_until_next)
time_until_next = next_time
if(check_accuracy())
return time_until_next
if(!accuracy_mod && radar_z_trait)
accuracy_mod = rand(-9, 9) * 5 SECONDS
var/maximum_value = INFINITY
// Don't backtrack on weather warnings every second of process() if we're poorly calibrated
if (warning_level == WEATHER_ALERT_INCOMING)
maximum_value = WEATHER_CLEAR_DELAY
else if (warning_level == WEATHER_ALERT_IMMINENT_OR_ACTIVE)
maximum_value = WEATHER_INCOMING_DELAY
time_until_next = clamp(time_until_next + accuracy_mod, min(time_until_next, 10 SECONDS), max(time_until_next, maximum_value))
return time_until_next
/// Polls existing weather for what kind of warnings we should be displaying.
/datum/component/weather_announcer/proc/set_current_alert_level()
var/time_until_next = time_till_storm()
is_weather_dangerous = FALSE
if(isnull(time_until_next))
warning_level = WEATHER_ALERT_CLEAR
return // No problems if there are no mining z levels
if(time_until_next > WEATHER_CLEAR_DELAY)
warning_level = WEATHER_ALERT_CLEAR
return
if(time_until_next > WEATHER_INCOMING_DELAY)
warning_level = WEATHER_ALERT_INCOMING
return
// Weather is here, now we need to figure out if it is dangerous
warning_level = WEATHER_ALERT_IMMINENT_OR_ACTIVE
for(var/datum/weather/check_weather as anything in SSweather.processing)
if(!(check_weather.weather_flags & WEATHER_BAROMETER) || check_weather.stage == WIND_DOWN_STAGE || check_weather.stage == END_STAGE)
continue
var/list/mining_z_levels = SSmapping.levels_by_trait(radar_z_trait)
for(var/mining_level in mining_z_levels)
if(!(mining_level in check_weather.impacted_z_levels))
continue
if(check_weather.weather_flags & FUNCTIONAL_WEATHER)
is_weather_dangerous = TRUE
return
/datum/component/weather_announcer/proc/on_examine(atom/radio, mob/examiner, list/examine_texts)
var/time_until_next = time_till_storm()
if(isnull(time_until_next))
return
if (time_until_next == 0)
examine_texts += span_warning("A storm is currently active, please seek shelter.")
else
examine_texts += span_notice("The next storm is inbound in [DisplayTimeText(time_until_next)].")
if(!check_accuracy())
examine_texts += span_smallnoticeital("Due to insufficient radar coverage, the timing of this forecast may be inaccurate.")
/datum/component/weather_announcer/RegisterWithParent()
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
/datum/component/weather_announcer/UnregisterFromParent()
.=..()
UnregisterSignal(parent, COMSIG_ATOM_EXAMINE)
/datum/aas_config_entry/weather
name = "Cargo Alert: Weather Forecast"
general_tooltip = "Allows the radio to announce incoming weather."
announcement_lines_map = list(
"Clear" = "All clear, no weather alerts to report.",
"Incoming" = "Weather front incoming, begin to seek shelter.",
"Imminent or Active" = "Weather front imminent, find shelter immediately.",
"Safe" = "No risk expected from incoming weather front.",
)
/datum/aas_config_entry/weather/act_up()
. = ..()
if (.)
return
// 10% chance to turn off weather broadcast entirely
if (prob(10))
enabled = FALSE
#undef WEATHER_ALERT_CLEAR
#undef WEATHER_ALERT_INCOMING
#undef WEATHER_ALERT_IMMINENT_OR_ACTIVE
#undef WEATHER_CLEAR_DELAY
#undef WEATHER_INCOMING_DELAY