diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index ca73059086c..4a8c0a9c3df 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -25390,8 +25390,8 @@ /area/station/science/xenobiology) "gmK" = ( /obj/structure/cable, -/obj/machinery/newscaster/directional/south, /obj/effect/turf_decal/tile/yellow/half/contrasted, +/obj/machinery/mining_weather_monitor/directional/south, /turf/open/floor/iron, /area/station/construction/mining/aux_base) "gmP" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index ddc65ea3eec..47440827f34 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -9561,6 +9561,7 @@ req_access = list("aux_base") }, /obj/effect/turf_decal/tile/yellow/anticorner/contrasted, +/obj/machinery/mining_weather_monitor/directional/south, /turf/open/floor/iron, /area/station/construction/mining/aux_base) "cRX" = ( @@ -66960,6 +66961,7 @@ dir = 1 }, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/machinery/mining_weather_monitor/directional/north, /turf/open/floor/iron, /area/station/hallway/primary/central) "vtA" = ( diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 82f5a8571ea..d4f1987740e 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -8468,6 +8468,7 @@ /obj/item/stack/rods/fifty, /obj/effect/spawner/random/trash/janitor_supplies, /obj/effect/turf_decal/tile/yellow/anticorner/contrasted, +/obj/machinery/mining_weather_monitor/directional/east, /turf/open/floor/iron, /area/station/construction/mining/aux_base) "dhi" = ( @@ -32330,6 +32331,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 4 }, +/obj/machinery/mining_weather_monitor/directional/east, /turf/open/floor/iron/white/corner, /area/station/hallway/secondary/entry) "lRS" = ( diff --git a/_maps/map_files/Mining/Lavaland.dmm b/_maps/map_files/Mining/Lavaland.dmm index a3097e98682..98136e8fcfb 100644 --- a/_maps/map_files/Mining/Lavaland.dmm +++ b/_maps/map_files/Mining/Lavaland.dmm @@ -259,6 +259,7 @@ /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 1 }, +/obj/machinery/mining_weather_monitor/directional/north, /turf/open/floor/iron/dark/smooth_edge, /area/mine/production) "cb" = ( @@ -1799,6 +1800,7 @@ /obj/effect/turf_decal/tile/bar/opposingcorners{ dir = 1 }, +/obj/machinery/mining_weather_monitor/directional/west, /turf/open/floor/iron/checker, /area/mine/cafeteria) "kV" = ( @@ -2501,6 +2503,7 @@ /obj/effect/turf_decal/trimline/red/filled/line{ dir = 10 }, +/obj/machinery/mining_weather_monitor/directional/south, /turf/open/floor/iron/dark, /area/mine/laborcamp/security) "nA" = ( @@ -3712,6 +3715,7 @@ /obj/effect/turf_decal/trimline/red/filled/line{ dir = 9 }, +/obj/machinery/mining_weather_monitor/directional/north, /turf/open/floor/iron/dark, /area/mine/laborcamp/security) "vp" = ( @@ -7016,6 +7020,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 }, +/obj/machinery/mining_weather_monitor/directional/south, /turf/open/floor/iron/smooth_edge{ dir = 1 }, diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm index 0c56241957a..d32728e3899 100644 --- a/_maps/map_files/NorthStar/north_star.dmm +++ b/_maps/map_files/NorthStar/north_star.dmm @@ -65486,6 +65486,7 @@ /area/station/service/library) "rdZ" = ( /obj/structure/cable, +/obj/machinery/mining_weather_monitor/directional/north, /turf/open/floor/iron/dark/side{ dir = 1 }, diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 33e066a3243..98041c2caa7 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -59214,6 +59214,7 @@ dir = 9 }, /obj/machinery/light/directional/west, +/obj/machinery/mining_weather_monitor/directional/north, /turf/open/floor/iron, /area/station/construction/mining/aux_base) "uhw" = ( diff --git a/code/datums/components/weatherannouncer.dm b/code/datums/components/weatherannouncer.dm new file mode 100644 index 00000000000..fec31ccf9d3 --- /dev/null +++ b/code/datums/components/weatherannouncer.dm @@ -0,0 +1,175 @@ +#define WEATHER_ALERT_CLEAR 0 +#define WEATHER_ALERT_INCOMING 1 +#define WEATHER_ALERT_IMMINENT_OR_ACTIVE 2 + +/// 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 + +/datum/component/weather_announcer/Initialize( + state_normal, + state_warning, + state_danger, +) + . = ..() + 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 + var/atom/speaker = parent + speaker.update_appearance(UPDATE_ICON) + update_light_color() + +/datum/component/weather_announcer/Destroy(force, silent) + 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/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 + speaker.say(get_warning_message()) + 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) + light.set_light_color(LIGHT_COLOR_INTENSE_RED) + light.update_light() + +/// Returns a string we should display to communicate what you should be doing +/datum/component/weather_announcer/proc/get_warning_message() + if (!is_weather_dangerous) + return "No risk expected from incoming weather front." + 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) + 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(ZTRAIT_MINING) + 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.barometer_predictable || 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 + return 0 + + var/time_until_next = INFINITY + for(var/mining_level in mining_z_levels) + var/next_time = timeleft(SSweather.next_hit_by_zlevel["[mining_level ]"]) || INFINITY + if (next_time && next_time < time_until_next) + time_until_next = next_time + 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() + if(isnull(time_until_next)) + return // No problems if there are no mining z levels + if(time_until_next >= 2 MINUTES) + warning_level = WEATHER_ALERT_CLEAR + return + + if(time_until_next >= 30 SECONDS) + 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.barometer_predictable || check_weather.stage == WIND_DOWN_STAGE || check_weather.stage == END_STAGE) + continue + var/list/mining_z_levels = SSmapping.levels_by_trait(ZTRAIT_MINING) + for(var/mining_level in mining_z_levels) + if(mining_level in check_weather.impacted_z_levels) + is_weather_dangerous = !check_weather.aesthetic + 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)].") + +/datum/component/weather_announcer/RegisterWithParent() + RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + +/datum/component/weather_announcer/UnregisterFromParent() + .=..() + UnregisterSignal(parent, COMSIG_ATOM_EXAMINE) + +#undef WEATHER_ALERT_CLEAR +#undef WEATHER_ALERT_INCOMING +#undef WEATHER_ALERT_IMMINENT_OR_ACTIVE diff --git a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm index 55c6ed63a65..09bbeed2bed 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm @@ -102,3 +102,7 @@ /datum/orderable_item/mining/style_meter item_path = /obj/item/style_meter cost_per_order = 1500 + +/datum/orderable_item/mining/weather_radio + item_path = /obj/item/radio/weather_monitor + cost_per_order = 400 diff --git a/code/game/machinery/mining_weather_monitor.dm b/code/game/machinery/mining_weather_monitor.dm new file mode 100644 index 00000000000..d05d8820751 --- /dev/null +++ b/code/game/machinery/mining_weather_monitor.dm @@ -0,0 +1,26 @@ +/// Wall mounted mining weather tracker +/obj/machinery/mining_weather_monitor + name = "barometric monitor" + desc = "A machine monitoring atmospheric data from mining environments. Provides warnings about incoming weather fronts." + icon = 'icons/obj/miningradio.dmi' + icon_state = "wallmount" + luminosity = 1 + light_power = 1 + light_range = 1.6 + +/obj/machinery/mining_weather_monitor/Initialize(mapload, ndir, nbuild) + . = ..() + AddComponent( \ + /datum/component/weather_announcer, \ + state_normal = "wallgreen", \ + state_warning = "wallyellow", \ + state_danger = "wallred", \ + ) + +/obj/machinery/mining_weather_monitor/update_overlays() + . = ..() + if((machine_stat & BROKEN) || !powered()) + return + . += emissive_appearance(icon, "emissive", src) + +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/mining_weather_monitor, 28) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm index dc55e46897d..2287c4f2d51 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm @@ -6,6 +6,8 @@ /obj/structure/closet/secure_closet/quartermaster/PopulateContents() ..() new /obj/item/storage/lockbox/medal/cargo(src) + new /obj/item/radio/weather_monitor (src) + new /obj/item/radio/headset/heads/qm(src) new /obj/item/megaphone/cargo(src) new /obj/item/tank/internals/emergency_oxygen(src) new /obj/item/universal_scanner(src) @@ -13,9 +15,6 @@ new /obj/item/circuitboard/machine/techfab/department/cargo(src) new /obj/item/storage/photo_album/qm(src) new /obj/item/circuitboard/machine/ore_silo(src) - new /obj/item/gun/ballistic/rifle/boltaction/sporterized(src) // SKYRAT EDIT - The QM's 'special' head item. It spawns loaded, but you have to find more ammo if you run out and get ready to manually load rounds in! - new /obj/item/cargo_teleporter(src) // SKYRAT EDIT - Adds a cargo teleporter to QM locker, so they can intice others to research it - new /obj/item/clothing/glasses/hud/gun_permit/sunglasses(src) //SKYRAT EDIT - GUN CARGO new /obj/item/storage/bag/garment/quartermaster(src) /obj/structure/closet/secure_closet/quartermaster/populate_contents_immediate() diff --git a/code/modules/mining/equipment/miningradio.dm b/code/modules/mining/equipment/miningradio.dm new file mode 100644 index 00000000000..559740599db --- /dev/null +++ b/code/modules/mining/equipment/miningradio.dm @@ -0,0 +1,24 @@ +/// Portable mining radio purchasable by miners +/obj/item/radio/weather_monitor + icon = 'icons/obj/miningradio.dmi' + name = "mining weather radio" + icon_state = "miningradio" + desc = "A weather radio designed for use in inhospitable environments. Gives audible warnings when storms approach. Has access to cargo channel." + freqlock = RADIO_FREQENCY_LOCKED + luminosity = 1 + light_power = 1 + light_range = 1.6 + +/obj/item/radio/weather_monitor/update_overlays() + . = ..() + . += emissive_appearance(icon, "small_emissive", src, alpha = src.alpha) + +/obj/item/radio/weather_monitor/Initialize(mapload) + . = ..() + AddComponent( \ + /datum/component/weather_announcer, \ + state_normal = "weatherwarning", \ + state_warning = "urgentwarning", \ + state_danger = "direwarning", \ + ) + set_frequency(FREQ_SUPPLY) diff --git a/icons/obj/miningradio.dmi b/icons/obj/miningradio.dmi new file mode 100644 index 00000000000..e3d10f3b6d6 Binary files /dev/null and b/icons/obj/miningradio.dmi differ diff --git a/modular_skyrat/master_files/code/game/objects/structures/crates_lockers/secure/cargo.dm b/modular_skyrat/master_files/code/game/objects/structures/crates_lockers/secure/cargo.dm new file mode 100644 index 00000000000..359f964f68b --- /dev/null +++ b/modular_skyrat/master_files/code/game/objects/structures/crates_lockers/secure/cargo.dm @@ -0,0 +1,5 @@ +/obj/structure/closet/secure_closet/quartermaster/PopulateContents() + ..() + new /obj/item/gun/ballistic/rifle/boltaction/sporterized(src) // The QM's 'special' head item. It spawns loaded, but you have to find more ammo if you run out and get ready to manually load rounds in! + new /obj/item/cargo_teleporter(src) // Adds a cargo teleporter to QM locker, so they can entice others to research it + new /obj/item/clothing/glasses/hud/gun_permit/sunglasses(src) diff --git a/tgstation.dme b/tgstation.dme index dc2230d6265..f5029530b4e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1176,6 +1176,7 @@ #include "code\datums\components\usb_port.dm" #include "code\datums\components\vacuum.dm" #include "code\datums\components\wearertargeting.dm" +#include "code\datums\components\weatherannouncer.dm" #include "code\datums\components\wet_floor.dm" #include "code\datums\components\container_item\container_item.dm" #include "code\datums\components\container_item\tank_holder.dm" @@ -1767,6 +1768,7 @@ #include "code\game\machinery\mechlaunchpad.dm" #include "code\game\machinery\medical_kiosk.dm" #include "code\game\machinery\medipen_refiller.dm" +#include "code\game\machinery\mining_weather_monitor.dm" #include "code\game\machinery\modular_shield.dm" #include "code\game\machinery\navbeacon.dm" #include "code\game\machinery\nebula_shielding.dm" @@ -4123,6 +4125,7 @@ #include "code\modules\mining\equipment\marker_beacons.dm" #include "code\modules\mining\equipment\mineral_scanner.dm" #include "code\modules\mining\equipment\mining_tools.dm" +#include "code\modules\mining\equipment\miningradio.dm" #include "code\modules\mining\equipment\resonator.dm" #include "code\modules\mining\equipment\survival_pod.dm" #include "code\modules\mining\equipment\wormhole_jaunter.dm" @@ -5689,6 +5692,7 @@ #include "modular_skyrat\master_files\code\game\objects\structures\towel_bins.dm" #include "modular_skyrat\master_files\code\game\objects\structures\trash_pile.dm" #include "modular_skyrat\master_files\code\game\objects\structures\watercloset.dm" +#include "modular_skyrat\master_files\code\game\objects\structures\crates_lockers\secure\cargo.dm" #include "modular_skyrat\master_files\code\game\turfs\closed\_closed.dm" #include "modular_skyrat\master_files\code\game\turfs\open\floor\iron_floor.dm" #include "modular_skyrat\master_files\code\game\turfs\open\space\space.dm"