From 6b60388f62fd9de7c59a129645aa665b2a96add0 Mon Sep 17 00:00:00 2001 From: GenericDM <34109002+GenericDM@users.noreply.github.com> Date: Tue, 5 Dec 2023 19:52:35 -0800 Subject: [PATCH] Weather Radio Port (#2517) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## About The Pull Request Port of https://github.com/tgstation/tgstation/pull/76961, which is itself a port of https://github.com/lizardqueenlexi/orbstation/pull/630. This PR will add one new object, the portable weather radio. ![theradio](https://github.com/shiptest-ss13/Shiptest/assets/34109002/2d9acb59-30fa-4354-944d-f5bcc3b88666) ![image](https://github.com/shiptest-ss13/Shiptest/assets/34109002/76bad17f-b15e-4c50-99de-5cb860b19a89) ![image](https://github.com/shiptest-ss13/Shiptest/assets/34109002/ca0db81b-d8dc-43d9-9e8c-8a85b083d7cb) Wallmount shown in these has been axed. ### Todo - [x] Fix the existing code to work for our cases. - [x] Fix runtime errors. - [x] Stay hydrated. ## Why It's Good For The Game They look neat, they add flavor, and the barometer function on analyzers is used maybe once every 50 rounds. ## Changelog 🆑 CoiledLamb, Jacquerel, GenericDM add: Adds Weather Radios! /🆑 --- code/__DEFINES/colors.dm | 2 + code/datums/components/weatherannouncer.dm | 170 ++++++++++++++++++ code/modules/jobs/job_types/shaft_miner.dm | 3 +- code/modules/mining/equipment/miningradio.dm | 23 +++ .../research/designs/mining_designs.dm | 10 ++ code/modules/research/techweb/all_nodes.dm | 2 +- icons/obj/miningradio.dmi | Bin 0 -> 1668 bytes shiptest.dme | 2 + 8 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 code/datums/components/weatherannouncer.dm create mode 100644 code/modules/mining/equipment/miningradio.dm create mode 100644 icons/obj/miningradio.dmi diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 5814f8f27a1f..c428e1e7dd53 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -164,6 +164,8 @@ #define LIGHT_COLOR_LAVA "#C48A18" /// Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75) #define LIGHT_COLOR_FLARE "#FA644B" +/// Vivid red. Leans a bit darker to accentuate red colors and leave other channels a bit dry. rgb(200, 25, 25) +#define LIGHT_COLOR_INTENSE_RED "#C81919" /// Weird color, between yellow and green, very slimy. rgb(175, 200, 75) #define LIGHT_COLOR_SLIME_LAMP "#AFC84B" /// Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175) diff --git a/code/datums/components/weatherannouncer.dm b/code/datums/components/weatherannouncer.dm new file mode 100644 index 000000000000..3821f9a1b559 --- /dev/null +++ b/code/datums/components/weatherannouncer.dm @@ -0,0 +1,170 @@ +#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_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/datum/weather_controller/local_weather_controller = SSmapping.get_map_zone_weather_controller(parent) + if(!local_weather_controller.next_weather) + return null + for(var/type_index in local_weather_controller.current_weathers) + var/datum/weather/check_weather = local_weather_controller.current_weathers[type_index] + if(!check_weather.barometer_predictable || check_weather.stage == WIND_DOWN_STAGE || check_weather.stage == END_STAGE) + continue + warning_level = WEATHER_ALERT_IMMINENT_OR_ACTIVE + return 0 + + var/time_until_next = INFINITY + var/next_time = local_weather_controller.next_weather - world.time || 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 + + var/datum/weather_controller/local_weather_controller = SSmapping.get_map_zone_weather_controller(parent) + for(var/type_index in local_weather_controller.current_weathers) + var/datum/weather/check_weather = local_weather_controller.current_weathers[type_index] + if(!check_weather.barometer_predictable || check_weather.stage == WIND_DOWN_STAGE || check_weather.stage == END_STAGE) + continue + 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_PARENT_EXAMINE, PROC_REF(on_examine)) + +/datum/component/weather_announcer/UnregisterFromParent() + .=..() + UnregisterSignal(parent, COMSIG_PARENT_EXAMINE) + +#undef WEATHER_ALERT_CLEAR +#undef WEATHER_ALERT_INCOMING +#undef WEATHER_ALERT_IMMINENT_OR_ACTIVE diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm index 6a3f13da7c78..cc5ec142932e 100644 --- a/code/modules/jobs/job_types/shaft_miner.dm +++ b/code/modules/jobs/job_types/shaft_miner.dm @@ -27,7 +27,8 @@ backpack_contents = list( /obj/item/flashlight/seclite=1,\ /obj/item/kitchen/knife/combat/survival=1,\ - /obj/item/stack/marker_beacon/ten=1) + /obj/item/stack/marker_beacon/ten=1,\ + /obj/item/radio/weather_monitor=1) backpack = /obj/item/storage/backpack/explorer satchel = /obj/item/storage/backpack/satchel/explorer diff --git a/code/modules/mining/equipment/miningradio.dm b/code/modules/mining/equipment/miningradio.dm new file mode 100644 index 000000000000..a0bef397d8ca --- /dev/null +++ b/code/modules/mining/equipment/miningradio.dm @@ -0,0 +1,23 @@ +/// 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." + 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_COMMON) diff --git a/code/modules/research/designs/mining_designs.dm b/code/modules/research/designs/mining_designs.dm index cf4ba7b9fa41..2cddc5043c3f 100644 --- a/code/modules/research/designs/mining_designs.dm +++ b/code/modules/research/designs/mining_designs.dm @@ -120,3 +120,13 @@ build_path = /obj/item/borg/upgrade/modkit/aoe/turfs category = list("Mining Designs", "Cyborg Upgrade Modules") departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/weather_monitor + name = "Weather Radio" + desc = "A weather radio designed for use in inhospitable environments. Gives audible warnings when storms approach." + id = "weatherradio" + build_type = PROTOLATHE + materials = list(/datum/material/iron=75, /datum/material/glass=25) + build_path = /obj/item/radio/weather_monitor + category = list("Mining Designs") + departmental_flags = DEPARTMENTAL_FLAG_CARGO diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 4f9d389c5a38..79009ed1fdf6 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -559,7 +559,7 @@ display_name = "Mining Technology" description = "Better than Efficiency V." prereq_ids = list("engineering", "basic_plasma") - design_ids = list("drill", "superresonator", "triggermod", "damagemod", "cooldownmod", "rangemod", "ore_redemption", "mining_equipment_vendor", "cargoexpress", "plasmacutter", "mecha_kineticgun")//e a r l y g a m e) + design_ids = list("drill", "superresonator", "triggermod", "damagemod", "cooldownmod", "rangemod", "ore_redemption", "mining_equipment_vendor", "cargoexpress", "plasmacutter", "mecha_kineticgun", "weatherradio")//e a r l y g a m e) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 diff --git a/icons/obj/miningradio.dmi b/icons/obj/miningradio.dmi new file mode 100644 index 0000000000000000000000000000000000000000..08b6052e91a0c204b5653b5027c4509b53bd86de GIT binary patch literal 1668 zcmaJ>eKga182^nLcVTxo-J$N3UZ_=6Zb}TbLSc^O-K@65#!TcT47qjOCgi0T@ALVb&-Xc>^G$Mgveks?!2kfz zw70{$000CmA%Hp*Ogla487!VR3a3L4~qVhOzp2Du0Xh4UszCp{w(`wL%-6gZ{NwMVZYwOs4k!F|llqLXJA8wDeIUAKXJv5YjHbO^j*4EP?j6>hHom*(I zcgXRVK@S&Z@VYbV*dU|&vPAU>xO)Ap0dw__tsV_~SdU6`eNzgrXwx%Ex|}=5(YKvR zdl(0>76gd~<6#hvktcjz&@CSK?8*t_Q{Lx`6GZCu+d0EE~JhQe*{Ea@eXe$9#~ zE*1E0L?A|UQT^(LxLf@|ymBIo$y{W~T|L?B_E+_~1Jkg1{>iYoPpYGzxlCrMVQ?#5 zxw52hU{D-ARjWfd@V*>@(4RVHB9^HRnVDoEElv3Q8L=_~0&zgr-&q%j)?dOH7(_Ni z8li*@^?2h}Q;396x&nJNn$cGWav$XY18x=dhuo%g+v(o*ffH8z$nj)jrP38bIKYO^ z%F2bL@h3#`_>)sD-N>O&Ld)>B(S(n#)b+E^#LJ`}E-q}4+ui-L>Qb6578@s&iy^!( zxys0(-KZy4jJb(HlK9=zC{l;rRGzuchqj%F%Gh`LVvV_~lN~tm;WnvM(OUp<;J;0I zoRRUqMqZR1%l7OKQ2K)?pXr)>yc)UVZmu~mP>$$nbG(gXIm4xLT#vJ>zWntEo-HVnU&R(*Z9j*Z6a3)^F~?IKbfw{Z#6>QscoawqWIU+KfqW=>^mI?IccFB zw*K0hynrPO!c*vkjj4pUp^MEqQ8Uw3glPRaHLkaSkTJNcsY!og0W|V}`BL=o6cv3vB+{Pxc=nsW=|?0yz>pZ-7dEz@-HLc0-U>O&Z)M z{@k{AB5V?d@r7YDG{;SiFT$5nCgZ1<)91c zwHmKJlG)$1(VE@@MlCSvc}@cKIx0N|6FsT8>De14_iV|v?H0-hT`~V532di(6xEGv z2*t!$jo};|9D;=WI7Xvyr67>!1*TdrPQOSbDpAac;BdJ3rOE<>KhYe=0tYApZ8;f? z={0%G$l&{mg8_hcKh;#Pie}aa!teD^t7)5L6oI@Kkf7bH$Hw8>mW9ai=k7B08%Z^WM`*o-J%pqAm!ql3(sN~ f_lUpuWO^6zHS<_d{-vBVYp=II>4dE~e(uUYZKUs% literal 0 HcmV?d00001 diff --git a/shiptest.dme b/shiptest.dme index 4244191d5dc2..919c61c53c36 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -530,6 +530,7 @@ #include "code\datums\components\udder.dm" #include "code\datums\components\uplink.dm" #include "code\datums\components\wearertargeting.dm" +#include "code\datums\components\weatherannouncer.dm" #include "code\datums\components\wet_floor.dm" #include "code\datums\components\crafting\crafting.dm" #include "code\datums\components\crafting\guncrafting.dm" @@ -2365,6 +2366,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\regenerative_core.dm" #include "code\modules\mining\equipment\resonator.dm" #include "code\modules\mining\equipment\survival_pod.dm"