diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index a1aac6e7f61..7cd6bf8727b 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -143,15 +143,6 @@ /// Max length of a status line in the status display #define MAX_STATUS_LINE_LENGTH 40 -///Define for automated system arrival announcement -#define AUTO_ANNOUNCE_ARRIVAL "ARRIVAL" -///Define for automated system announcement when a head of staff arrives -#define AUTO_ANNOUNCE_NEWHEAD "NEWHEAD" -///Define for automated system announcement for when the arrival shuttle is broken -#define AUTO_ANNOUNCE_ARRIVALS_BROKEN "ARRIVALS_BROKEN" -///Define for automated system announcement for researched nodes -#define AUTO_ANNOUNCE_NODE "NODE" - /// Blank Status Display #define SD_BLANK 0 /// Shows the emergency shuttle timer diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index b9af3204238..4d2dd6da94d 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -235,23 +235,8 @@ return var/area/player_area = get_area(character) deadchat_broadcast(span_game(" has arrived at the station at [span_name(player_area.name)]."), span_game("[span_name(character.real_name)] ([rank])"), follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE) - if(!character.mind) - return - if(!GLOB.announcement_systems.len) - return - if(!(character.mind.assigned_role.job_flags & JOB_ANNOUNCE_ARRIVAL)) - return - - var/obj/machinery/announcement_system/announcer - var/list/available_machines = list() - for(var/obj/machinery/announcement_system/announce as anything in GLOB.announcement_systems) - if(announce.arrival_toggle) - available_machines += announce - break - if(!length(available_machines)) - return - announcer = pick(available_machines) - announcer.announce(AUTO_ANNOUNCE_ARRIVAL, character.real_name, rank, list()) //make the list empty to make it announce it in common + if(character.mind && (character.mind.assigned_role.job_flags & JOB_ANNOUNCE_ARRIVAL)) + aas_config_announce(/datum/aas_config_entry/arrival, list("PERSON" = character.real_name,"RANK" = rank)) ///Check if the turf pressure allows specialized equipment to work /proc/lavaland_equipment_pressure_check(turf/turf_to_check) diff --git a/code/controllers/subsystem/modular_computers.dm b/code/controllers/subsystem/modular_computers.dm index fc89881d45a..eb25e0c97ae 100644 --- a/code/controllers/subsystem/modular_computers.dm +++ b/code/controllers/subsystem/modular_computers.dm @@ -41,9 +41,9 @@ SUBSYSTEM_DEF(modular_computers) ///Generate new coupon codes that can be redeemed with the Coupon Master App /datum/controller/subsystem/modular_computers/proc/announce_coupon() //If there's no way to announce the coupon, we may as well skip it. - if(!length(GLOB.announcement_systems)) + var/obj/machinery/announcement_system/announcement_system = get_announcement_system() + if(!announcement_system) return - var/obj/machinery/announcement_system/announcement_system = pick(GLOB.announcement_systems) var/static/list/discounts = list("0.10" = 7, "0.15" = 16, "0.20" = 20, "0.25" = 16, "0.50" = 8, "0.66" = 1) var/static/list/flash_discounts = list("0.30" = 3, "0.40" = 8, "0.50" = 8, "0.66" = 2, "0.75" = 1) diff --git a/code/datums/components/weatherannouncer.dm b/code/datums/components/weatherannouncer.dm index 0eda6c47a32..4ac103a7758 100644 --- a/code/datums/components/weatherannouncer.dm +++ b/code/datums/components/weatherannouncer.dm @@ -80,7 +80,15 @@ if(previous_level == warning_level && previous_danger == is_weather_dangerous) return // No change var/atom/movable/speaker = parent - speaker.say(get_warning_message()) + var/msg = get_warning_message() + var/obj/machinery/announcement_system/aas = get_announcement_system(/datum/aas_config_entry/weather, speaker) + // Active AAS will override default announcement lines + if (aas) + msg = aas.compile_config_message(/datum/aas_config_entry/weather, list(), !is_weather_dangerous ? 4 : warning_level + 1) + // 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() @@ -170,6 +178,17 @@ .=..() 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.", + ) + + #undef WEATHER_ALERT_CLEAR #undef WEATHER_ALERT_INCOMING #undef WEATHER_ALERT_IMMINENT_OR_ACTIVE diff --git a/code/datums/request_message.dm b/code/datums/request_message.dm index d9cf51bc5be..18feada06cb 100644 --- a/code/datums/request_message.dm +++ b/code/datums/request_message.dm @@ -12,8 +12,8 @@ var/message_stamped_by = "" /// The priority of the message var/priority = "" - /// The radio frequency the message should be broadcasted on - var/radio_freq = null + /// The radio channel the message should be broadcasted on + var/radio_channel = null /// The type of the request var/request_type = "" /// A list to be appended after the message, for example, list of ores @@ -26,7 +26,7 @@ message_verified_by = data["verified"] message_stamped_by = data["stamped"] priority = data["priority"] - radio_freq = data["notify_freq"] + radio_channel = data["notify_channel"] request_type = data["ore_update"] ? ORE_UPDATE_REQUEST : data["request_type"] var/list/data_appended_list = data["appended_list"] if(data_appended_list && data_appended_list.len) diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm index f3de8a749ec..e7bb603ef07 100644 --- a/code/game/machinery/announcement_system.dm +++ b/code/game/machinery/announcement_system.dm @@ -16,194 +16,82 @@ GLOBAL_LIST_EMPTY(announcement_systems) circuit = /obj/item/circuitboard/machine/announcement_system + ///All possible announcements and their local configurations + var/list/datum/aas_config_entry/config_entries = list() + ///The headset that we use for broadcasting var/obj/item/radio/headset/radio - ///The message that we send when someone is joining. - var/arrival = "%PERSON has signed up as %RANK" - ///Whether the arrival message is sent - var/arrival_toggle = TRUE - ///The message that we send when a department head arrives. - var/newhead = "%PERSON, %RANK, is the department head." - ///Whether the newhead message is sent. - var/newhead_toggle = TRUE + ///AIs headset support all stations channels, but it may require an override for away site or syndie AASs. + var/radio_type = /obj/item/radio/headset/silicon/ai var/greenlight = "Light_Green" var/pinklight = "Light_Pink" var/errorlight = "Error_Red" - ///If true, researched nodes will be announced to the appropriate channels - var/announce_research_node = TRUE - /// The text that we send when announcing researched nodes. - var/node_message = "The %NODE techweb node has been researched" - /obj/machinery/announcement_system/Initialize(mapload) . = ..() GLOB.announcement_systems += src - radio = new /obj/item/radio/headset/silicon/ai(src) + radio = new radio_type(src) + config_entries = init_subtypes(/datum/aas_config_entry, list()) update_appearance() /obj/machinery/announcement_system/randomize_language_if_on_station() return /obj/machinery/announcement_system/update_icon_state() - icon_state = "[base_icon_state]_[is_operational ? "On" : "Off"][panel_open ? "_Open" : null]" + icon_state = "[base_icon_state]_[is_operational && !(machine_stat & EMPED) ? "On" : "Off"][panel_open ? "_Open" : null]" return ..() /obj/machinery/announcement_system/update_overlays() . = ..() - if(arrival_toggle) + var/datum/aas_config_entry/entry = locate(/datum/aas_config_entry/arrival) in config_entries + if(entry && entry.enabled) . += greenlight - if(newhead_toggle) + entry = locate(/datum/aas_config_entry/newhead) in config_entries + if(entry && entry.enabled) . += pinklight - if(machine_stat & BROKEN) + if(machine_stat & EMPED) . += errorlight /obj/machinery/announcement_system/Destroy() QDEL_NULL(radio) + QDEL_LAZYLIST(config_entries) GLOB.announcement_systems -= src //"OH GOD WHY ARE THERE 100,000 LISTED ANNOUNCEMENT SYSTEMS?!!" return ..() /obj/machinery/announcement_system/screwdriver_act(mob/living/user, obj/item/tool) - tool.play_tool_sound(src) - toggle_panel_open() - to_chat(user, span_notice("You [panel_open ? "open" : "close"] the maintenance hatch of [src].")) - update_appearance() - return TRUE + var/icon_state_assemble = "[base_icon_state]_[is_operational && !(machine_stat & EMPED) ? "On" : "Off"]" + if(default_deconstruction_screwdriver(user, "[icon_state_assemble]_Open", icon_state_assemble, tool)) + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING /obj/machinery/announcement_system/crowbar_act(mob/living/user, obj/item/tool) + . = ..() if(default_deconstruction_crowbar(tool)) - return TRUE + return ITEM_INTERACT_SUCCESS /obj/machinery/announcement_system/multitool_act(mob/living/user, obj/item/tool) - if(!panel_open || !(machine_stat & BROKEN)) - return FALSE + if(!panel_open || !(machine_stat & EMPED)) + return ITEM_INTERACT_BLOCKING to_chat(user, span_notice("You reset [src]'s firmware.")) - set_machine_stat(machine_stat & ~BROKEN) + set_machine_stat(machine_stat & ~EMPED) update_appearance() + return ITEM_INTERACT_SUCCESS -/obj/machinery/announcement_system/proc/CompileText(str, user, rank) //replaces user-given variables with actual thingies. - str = replacetext(str, "%PERSON", "[user]") - str = replacetext(str, "%RANK", "[rank]") - return str - -/obj/machinery/announcement_system/proc/announce(message_type, target, rank, list/channels) - if(!is_operational) +/// Does funny breakage stuff +/obj/machinery/announcement_system/proc/act_up() + if (machine_stat & EMPED) return - - var/message - - switch(message_type) - if(AUTO_ANNOUNCE_ARRIVAL) - if(!arrival_toggle) - return - message = CompileText(arrival, target, rank) - if(AUTO_ANNOUNCE_NEWHEAD) - if(!newhead_toggle) - return - message = CompileText(newhead, target, rank) - if(AUTO_ANNOUNCE_ARRIVALS_BROKEN) - message = "The arrivals shuttle has been damaged. Docking for repairs..." - if(AUTO_ANNOUNCE_NODE) - message = replacetext(node_message, "%NODE", target) - - broadcast(message, channels) - -/// Announces a new security officer joining over the radio -/obj/machinery/announcement_system/proc/announce_officer(mob/officer, department) - if (!is_operational) - return - - broadcast("Officer [officer.real_name] has been assigned to [department].", list(RADIO_CHANNEL_SECURITY)) - -/// Sends a message to the appropriate channels. -/obj/machinery/announcement_system/proc/broadcast(message, list/channels) - use_energy(active_power_usage) - if(channels.len == 0) - radio.talk_into(src, message, null) - else - for(var/channel in channels) - radio.talk_into(src, message, channel) - -/obj/machinery/announcement_system/ui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AutomatedAnnouncement") - ui.open() - -/obj/machinery/announcement_system/ui_data() - var/list/data = list() - data["arrival"] = arrival - data["arrivalToggle"] = arrival_toggle - data["newhead"] = newhead - data["newheadToggle"] = newhead_toggle - data["node_message"] = node_message - data["node_toggle"] = announce_research_node - return data - -/obj/machinery/announcement_system/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) - . = ..() - if(.) - return - if(!usr.can_perform_action(src, ALLOW_SILICON_REACH)) - return - if(machine_stat & BROKEN) - visible_message(span_warning("[src] buzzes."), span_hear("You hear a faint buzz.")) - playsound(src.loc, 'sound/machines/buzz/buzz-two.ogg', 50, TRUE) - return - switch(action) - if("ArrivalText") - var/new_message = trim(html_encode(params["newText"]), MAX_MESSAGE_LEN) - if(new_message) - arrival = new_message - usr.log_message("updated the arrivals announcement to: [new_message]", LOG_GAME) - if("NewheadText") - var/new_message = trim(html_encode(params["newText"]), MAX_MESSAGE_LEN) - if(new_message) - newhead = new_message - usr.log_message("updated the head announcement to: [new_message]", LOG_GAME) - if("node_message") - var/new_message = trim(html_encode(params["newText"]), MAX_MESSAGE_LEN) - if(new_message) - node_message = new_message - usr.log_message("updated the researched node announcement to: [node_message]", LOG_GAME) - if("newhead_toggle") - newhead_toggle = !newhead_toggle - update_appearance() - if("arrivalToggle") - arrival_toggle = !arrival_toggle - update_appearance() - if("node_toggle") - announce_research_node = !announce_research_node - add_fingerprint(usr) - -/obj/machinery/announcement_system/attack_robot(mob/living/silicon/user) - . = attack_ai(user) - -/obj/machinery/announcement_system/attack_ai(mob/user) - if(!user.can_perform_action(src, ALLOW_SILICON_REACH)) - return - if(machine_stat & BROKEN) - to_chat(user, span_warning("[src]'s firmware appears to be malfunctioning!")) - return - interact(user) - -/obj/machinery/announcement_system/proc/act_up() //does funny breakage stuff - if(!atom_break()) // if badmins flag this unbreakable or its already broken - return - - arrival = pick("#!@%ERR-34%2 CANNOT LOCAT@# JO# F*LE!", "CRITICAL ERROR 99.", "ERR)#: DA#AB@#E NOT F(*ND!") - newhead = pick("OV#RL()D: \[UNKNOWN??\] DET*#CT)D!", "ER)#R - B*@ TEXT F*O(ND!", "AAS.exe is not responding. NanoOS is searching for a solution to the problem.") - node_message = pick(list( - replacetext(/obj/machinery/announcement_system::node_message, "%NODE", /datum/techweb_node/mech_clown::display_name), - "R/NT1M3 A= ANNOUN-*#nt_SY!?EM.dm, LI%£ 86: N=0DE NULL!", - "BEPIS BEPIS BEPIS", - )) + set_machine_stat(machine_stat | EMPED) + update_appearance() + for (var/datum/aas_config_entry/config in config_entries) + config.act_up() /obj/machinery/announcement_system/emp_act(severity) . = ..() - if(!(machine_stat & (NOPOWER|BROKEN)) && !(. & EMP_PROTECT_SELF)) + if(!(machine_stat & (NOPOWER|EMPED|BROKEN)) && !(. & EMP_PROTECT_SELF)) act_up() /obj/machinery/announcement_system/emag_act(mob/user, obj/item/card/emag/emag_card) @@ -213,3 +101,245 @@ GLOBAL_LIST_EMPTY(announcement_systems) act_up() balloon_alert(user, "announcement strings corrupted") return TRUE + +/obj/machinery/announcement_system/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AutomatedAnnouncement") + ui.open() + +/obj/machinery/announcement_system/ui_data() + var/list/configs = list() + for(var/datum/aas_config_entry/config in config_entries) + configs += list(list( + name = config.name, + entryRef = REF(config), + enabled = config.enabled, + modifiable = config.modifiable, + announcementLinesMap = config.announcement_lines_map, + generalTooltip = config.general_tooltip, + varsAndTooltipsMap = config.vars_and_tooltips_map + )) + return list("config_entries" = configs) + +/obj/machinery/announcement_system/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + if(!usr.can_perform_action(src, ALLOW_SILICON_REACH)) + return + if(machine_stat & EMPED) + visible_message(span_warning("[src] buzzes."), span_hear("You hear a faint buzz.")) + playsound(src.loc, 'sound/machines/buzz/buzz-two.ogg', 50, TRUE) + return + + add_fingerprint(usr) + var/datum/aas_config_entry/config = locate(params["entryRef"]) in config_entries + if(!config || !config.modifiable) + return + + switch(action) + if("Toggle") + config.enabled = !config.enabled + if (config.type in list(/datum/aas_config_entry/arrival, /datum/aas_config_entry/newhead)) + update_appearance() + if("Text") + if(!(params["lineKey"] in config.announcement_lines_map)) + message_admins("[ADMIN_LOOKUPFLW(usr)] tried to set announcement line for nonexisting line in the [config.name] for AAS. Probably href injection. Received line: [params["lineKey"]]") + log_game("[key_name(usr)] tried to mess with AAS. For [config.name] he tried to edit nonexistend [params["lineKey"]]") + return + var/new_message = trim(html_encode(params["newText"]), MAX_MESSAGE_LEN) + if(new_message) + config.announcement_lines_map[params["lineKey"]] = new_message + usr.log_message("updated [params["lineKey"]] line in the [config.name] to: [new_message]", LOG_GAME) + +/obj/machinery/announcement_system/can_interact(mob/user) + . = ..() + if (!.) + return + + if (machine_stat & EMPED) + to_chat(user, span_warning("[src]'s firmware appears to be malfunctioning!")) + if (!isAI(user)) // Deus Ex Machina goes without multitool in his default complectation. + to_chat(user, span_warning("However, you can reset it with [EXAMINE_HINT("multitool")], while its [EXAMINE_HINT("panel is open")]!")) + return FALSE + +/// If AAS can't broadcast message, it shouldn't be picked by randomizer. +/obj/machinery/announcement_system/proc/has_supported_channels(list/channels) + if (!LAZYLEN(channels) || (RADIO_CHANNEL_COMMON in channels)) + // Okay, I am not proud of this, but I don't want CentCom or Syndie AASs to broadcast on Common. + return src.type == /obj/machinery/announcement_system + for(var/channel in channels) + if(radio.channels[channel]) + return TRUE + return FALSE + +/// Can AAS receive request for broadcast from you? +/obj/machinery/announcement_system/proc/can_be_reached_from(atom/source) + if(!source || !istype(source)) + return TRUE + var/turf/source_turf = get_turf(source) + if (!source_turf) + return TRUE + // Keep updated with broadcasting.dm (/datum/signal/subspace/vocal/New) + return z in SSmapping.get_connected_levels(source_turf) + +/// Compiles the announcement message with the provided variables. Announcement line is optional. +/obj/machinery/announcement_system/proc/compile_config_message(aas_config_entry_type, list/variables_map, announcement_line, fail_if_disabled=FALSE) + var/datum/aas_config_entry/config = locate(aas_config_entry_type) in config_entries + if (!config || (fail_if_disabled && !config.enabled)) + return + return config.compile_announce(variables_map, announcement_line) + +/// Sends a message to the appropriate channels. +/obj/machinery/announcement_system/proc/broadcast(message, list/channels, command_span = FALSE) + use_energy(active_power_usage) + if(!LAZYLEN(channels)) + radio.talk_into(src, message, null, command_span ? list(speech_span, SPAN_COMMAND) : null) + return + + // For some reasons, radio can't recognize RADIO_CHANNEL_COMMON in channels, so we need to handle it separately. + if (RADIO_CHANNEL_COMMON in channels) + radio.talk_into(src, message, null, command_span ? list(speech_span, SPAN_COMMAND) : null) + channels -= RADIO_CHANNEL_COMMON + for(var/channel in channels) + radio.talk_into(src, message, channel, command_span ? list(speech_span, SPAN_COMMAND) : null) + +/// Announces configs entry message with the provided variables. Channels and announcement_line are optional. +/obj/machinery/announcement_system/proc/announce(aas_config_entry_type, list/variables_map, list/channels, announcement_line, command_span) + var/msg = compile_config_message(aas_config_entry_type, variables_map, announcement_line, TRUE) + if (msg) + broadcast(msg, channels, command_span) + +/// Returns a random announcement system that is operational, has the specified config entry, signal can reach source and radio supports any channel in list. Config entry, source and channels are optional. +/proc/get_announcement_system(aas_config_entry_type, source, list/channels) + if (!GLOB.announcement_systems.len) + return null + var/list/intact_aass = list() + for(var/obj/machinery/announcement_system/announce as anything in GLOB.announcement_systems) + if(!QDELETED(announce) && announce.is_operational && announce.has_supported_channels(channels) && announce.can_be_reached_from(source)) + if(aas_config_entry_type) + var/datum/aas_config_entry/entry = locate(aas_config_entry_type) in announce.config_entries + if(!entry || !entry.enabled) + continue + intact_aass += announce + return intact_aass.len ? pick(intact_aass) : null + +/// Announces the provided message with the provided variables and config entry type. Channels, announcement_line, command_span and source are optional. +/proc/aas_config_announce(aas_config_entry_type, list/variables_map, source, list/channels, announcement_line, command_span) + var/obj/machinery/announcement_system/announcer = get_announcement_system(aas_config_entry_type, source, channels) + if (!announcer) + return + announcer.announce(aas_config_entry_type, variables_map, channels, announcement_line, command_span) + +/datum/aas_config_entry + var/name = "AAS configurable entry" + // Should we broadcast this announcement? + var/enabled = TRUE + // The announcement message. Key will be displayed in the UI. + var/list/announcement_lines_map = list("Message" = "This is a default announcement line.") + // Goes before tooltips for vars, mainly used if announcement has no replacable vars + var/general_tooltip + // Contains all replacable vars and their tooltips + var/list/vars_and_tooltips_map = list() + // Can be changed or disabled by players + var/modifiable = TRUE + +/// Compiles the announcement message with the provided variables. Announcement line is optional. +/datum/aas_config_entry/proc/compile_announce(list/variables_map, announcement_line) + var/announcement_message = LAZYACCESS(announcement_lines_map, announcement_line) + // If index was provided LAZYACCESS will return us a key, not value + if (isnum(announcement_line)) + announcement_message = announcement_lines_map[announcement_message] + // Fallback - first line + if (!announcement_message) + announcement_message = announcement_lines_map[announcement_lines_map[1]] + // Replace variables with their value + for(var/variable in vars_and_tooltips_map) + announcement_message = replacetext_char(announcement_message, "%[variable]", variables_map[variable] || "\[NO DATA\]") + return announcement_message + +/// Called when the announcement system is emagged or EMPed. +/datum/aas_config_entry/proc/act_up() + SHOULD_CALL_PARENT(TRUE) + + // Please do not mess with entries, that players can't fix. + if(!modifiable) + return TRUE + return FALSE + +/* + Global config entries for the announcement system. +*/ + +/datum/aas_config_entry/arrival + name = "Arrival Announcement" + announcement_lines_map = list( + "Message" = "%PERSON has signed up as %RANK") + vars_and_tooltips_map = list( + "PERSON" = "will be replaced with their name.", + "RANK" = "with their job." + ) + +/datum/aas_config_entry/arrival/act_up() + . = ..() + if (.) + return + + announcement_lines_map["Message"] = pick("#!@%ERR-34%2 CANNOT LOCAT@# JO# F*LE!", + "CRITICAL ERROR 99.", + "ERR)#: DA#AB@#E NOT F(*ND!") + +/datum/aas_config_entry/newhead + name = "Departmental Head Announcement" + announcement_lines_map = list( + "Message" = "%PERSON, %RANK, is the department head.") + vars_and_tooltips_map = list( + "PERSON" = "will be replaced with their name.", + "RANK" = "with their job." + ) + +/datum/aas_config_entry/newhead/act_up() + . = ..() + if (.) + return + + announcement_lines_map["Message"] = pick("OV#RL()D: \[UNKNOWN??\] DET*#CT)D!", + "ER)#R - B*@ TEXT F*O(ND!", + "AAS.exe is not responding. NanoOS is searching for a solution to the problem.") + +/datum/aas_config_entry/researched_node + name = "Research Node Announcement" + announcement_lines_map = list( + "Message" = "The %NODE techweb node has been researched") + vars_and_tooltips_map = list( + "NODE" = "will be replaced with the researched node." + ) + +/datum/aas_config_entry/researched_node/act_up() + . = ..() + if (.) + return + + announcement_lines_map["Message"] = pick( + replacetext(/datum/aas_config_entry/researched_node::announcement_lines_map["Message"], "%NODE", /datum/techweb_node/mech_clown::display_name), + "R/NT1M3 A= ANNOUN-*#nt_SY!?EM.dm, LI%£ 86: N=0DE NULL!", + "BEPIS BEPIS BEPIS", + "ERR)#R - B*@ TEXT F*O(ND!") + +/datum/aas_config_entry/arrivals_broken + name = "Arrivals Shuttle Malfunction Announcement" + announcement_lines_map = list( + "Message" = "The arrivals shuttle has been damaged. Docking for repairs...") + general_tooltip = "Broadcasted, when arrivals shuttle docks for repairs. No replacable variables provided." + modifiable = FALSE + +/datum/aas_config_entry/announce_officer + name = "Security Alert: Officer Arrival Announcement" + announcement_lines_map = list( + "Message" = "Officer %OFFICER has been assigned to %DEPARTMENT.") + vars_and_tooltips_map = list( + "OFFICER" = "will be replaced with the officer's name.", + "DEPARTMENT" = "with the department they were assigned to." + ) + modifiable = FALSE diff --git a/code/game/machinery/civilian_bounties.dm b/code/game/machinery/civilian_bounties.dm index dcd967e082e..0d74cdd5d49 100644 --- a/code/game/machinery/civilian_bounties.dm +++ b/code/game/machinery/civilian_bounties.dm @@ -309,25 +309,10 @@ var/datum/bank_account/bounty_holder_account ///Bank account of the person who receives the handling tip. var/datum/bank_account/bounty_handler_account - ///Our internal radio. - var/obj/item/radio/radio - ///The key our internal radio uses. - var/radio_key = /obj/item/encryptionkey/headset_cargo /obj/item/bounty_cube/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_NO_BARCODES, INNATE_TRAIT) // Don't allow anyone to override our pricetag component with a barcode - radio = new(src) - radio.keyslot = new radio_key - radio.set_listening(FALSE) - radio.recalculateChannels() - RegisterSignal(radio, COMSIG_ITEM_PRE_EXPORT, PROC_REF(on_export)) - -/obj/item/bounty_cube/Destroy() - if(radio) - UnregisterSignal(radio, COMSIG_ITEM_PRE_EXPORT) - QDEL_NULL(radio) - return ..() /obj/item/bounty_cube/examine() . = ..() @@ -336,29 +321,20 @@ if(handler_tip && !bounty_handler_account) . += span_notice("Scan this in the cargo shuttle with an export scanner to register your bank account for the [bounty_value * handler_tip] credit handling tip.") -/* - * Signal proc for [COMSIG_ITEM_EXPORTED], registered on the internal radio. - * - * Deletes the internal radio before being exported, - * to stop it from bring counted as an export. - * - * No 4 free credits for you! - */ -/obj/item/bounty_cube/proc/on_export(datum/source) - SIGNAL_HANDLER - - QDEL_NULL(radio) - return COMPONENT_STOP_EXPORT // stops the radio from exporting, not the cube - /obj/item/bounty_cube/process(seconds_per_tick) //if our nag cooldown has finished and we aren't on Centcom or in transit, then nag if(COOLDOWN_FINISHED(src, next_nag_time) && !is_centcom_level(z) && !is_reserved_level(z)) - //set up our nag message + //set up our fallback message, in case of AAS being broken it will be sent to card holders var/nag_message = "[src] is unsent in [get_area(src)]." //nag on Supply channel and reduce the speed bonus multiplier to nothing - var/speed_bonus_lost = "[speed_bonus ? " Speedy delivery bonus of [bounty_value * speed_bonus] credit\s lost." : ""]" - radio.talk_into(src, "[nag_message][speed_bonus_lost]", RADIO_CHANNEL_SUPPLY) + var/obj/machinery/announcement_system/aas = get_announcement_system(/datum/aas_config_entry/bounty_cube_unsent, src) + if (aas) + nag_message = aas.compile_config_message(/datum/aas_config_entry/bounty_cube_unsent, list("LOCATION" = get_area_name(src), "COST" = bounty_value), "Regular Message") + if (speed_bonus) + aas.announce(/datum/aas_config_entry/bounty_cube_unsent, list("LOCATION" = get_area_name(src), "COST" = bounty_value, "BONUSLOST" = bounty_value * speed_bonus), list(RADIO_CHANNEL_SUPPLY), "When Bonus Lost") + else + aas.broadcast("[nag_message]", list(RADIO_CHANNEL_SUPPLY)) speed_bonus = 0 //alert the holder @@ -383,7 +359,13 @@ AddComponent(/datum/component/gps, "[src]") START_PROCESSING(SSobj, src) COOLDOWN_START(src, next_nag_time, nag_cooldown) - radio.talk_into(src,"Created in [get_area(src)] by [bounty_holder] ([bounty_holder_job]). Speedy delivery bonus lost in [time2text(next_nag_time - world.time,"mm:ss")].", RADIO_CHANNEL_SUPPLY) + aas_config_announce(/datum/aas_config_entry/bounty_cube_created, list( + "LOCATION" = get_area_name(src), + "PERSON" = bounty_holder, + "RANK" = bounty_holder_job, + "BONUSTIME" = time2text(next_nag_time - world.time,"mm:ss"), + "COST" = bounty_value + ), src, list(RADIO_CHANNEL_SUPPLY)) //for when you need a REAL bounty cube to test with and don't want to do a bounty each time your code changes /obj/item/bounty_cube/debug_cube @@ -429,4 +411,27 @@ qdel(src) uses-- +/datum/aas_config_entry/bounty_cube_created + name = "Cargo Alert: Bounty Cube Created" + announcement_lines_map = list( + "Message" = "A %COST cr bounty cube has been created in %LOCATION by %PERSON (%RANK). Speedy delivery bonus lost in %BONUSTIME.") + vars_and_tooltips_map = list( + "LOCATION" = "will be replaced with the location of the cube.", + "PERSON" = "with who created the cube.", + "RANK" = "with their job.", + "BONUSTIME" = "with the time left for speedy delivery tip.", + "COST" = "with the cost of the cube.", + ) + +/datum/aas_config_entry/bounty_cube_unsent + name = "Cargo Alert: Bounty Cube Unsent" + announcement_lines_map = list( + "Regular Message" = "The %COST cr bounty cube is unsent in %LOCATION.", + "When Bonus Lost" = "The %COST cr bounty cube is unsent in %LOCATION. Speedy delivery bonus of %BONUSLOST credits lost.") + vars_and_tooltips_map = list( + "LOCATION" = "will be replaced with the location of the cube.", + "COST" = "with the cost of the cube.", + "BONUSLOST" = "with the lost bonus tip, it will be sent just for When Bonus Lost message!", + ) + #undef CIV_BOUNTY_SPLIT diff --git a/code/game/machinery/computer/arcade/orion.dm b/code/game/machinery/computer/arcade/orion.dm index c8236b5e883..5c2ed9b6d46 100644 --- a/code/game/machinery/computer/arcade/orion.dm +++ b/code/game/machinery/computer/arcade/orion.dm @@ -28,7 +28,6 @@ var/spaceport_raided = FALSE var/gameStatus = ORION_STATUS_START - var/obj/item/radio/radio var/list/gamers = list() var/killed_crew = 0 @@ -40,15 +39,12 @@ var/datum/orion_event/new_event = new path(src) events[new_event] = new_event.weight orion_events = events - radio = new /obj/item/radio(src) - radio.set_listening(FALSE) setup_events() /obj/machinery/computer/arcade/orion_trail/proc/setup_events() events = orion_events.Copy() /obj/machinery/computer/arcade/orion_trail/Destroy() - QDEL_NULL(radio) events = null return ..() @@ -123,14 +119,15 @@ gamers[gamer]++ // How many times the player has 'prestiged' (massacred their crew) if(gamers[gamer] > ORION_GAMER_REPORT_THRESHOLD && prob(20 * gamers[gamer])) + aas_config_announce(/datum/aas_config_entry/orion_violent_behavior_alert, list( + "PERSON" = gamer.name, + "LOCATION" = get_area_name(src), + "SOURCE" = name), src, list(RADIO_CHANNEL_SECURITY), RADIO_CHANNEL_SECURITY) - radio.set_frequency(FREQ_SECURITY) - radio.talk_into(src, "SECURITY ALERT: Crewmember [gamer] recorded displaying antisocial tendencies in [get_area(src)]. Please watch for violent behavior.", FREQ_SECURITY) - - radio.set_frequency(FREQ_MEDICAL) - radio.talk_into(src, "PSYCH ALERT: Crewmember [gamer] recorded displaying antisocial tendencies in [get_area(src)]. Please schedule psych evaluation.", FREQ_MEDICAL) - - remove_radio_all(radio)//so we dont keep transmitting sec and medical comms + aas_config_announce(/datum/aas_config_entry/orion_violent_behavior_alert, list( + "PERSON" = gamer.name, + "LOCATION" = get_area_name(src), + "SOURCE" = name), src, list(RADIO_CHANNEL_MEDICAL), RADIO_CHANNEL_MEDICAL) gamers[gamer] = ORION_GAMER_PAMPHLET //next report send a pamph @@ -547,4 +544,17 @@ dialogue_level++ addtimer(CALLBACK(src, PROC_REF(commit_explosion), dialogue_level), time_for_next_level) +/datum/aas_config_entry/orion_violent_behavior_alert + // Well we don't want to show that only Orion Trails reports violent behavior, eh-h? + name = "Violent Behavior Alert" + announcement_lines_map = list( + RADIO_CHANNEL_SECURITY = "SECURITY ALERT: Crewmember %PERSON recorded displaying antisocial tendencies in %LOCATION by %SOURCE. Please watch for violent behavior.", + RADIO_CHANNEL_MEDICAL = "PSYCH ALERT: Crewmember %PERSON recorded displaying antisocial tendencies in %LOCATION by %SOURCE. Please schedule psych evaluation.", + ) + vars_and_tooltips_map = list( + "PERSON" = "will be replaced with the crewmember reported", + "LOCATION" = "with the area of violent behavior", + "SOURCE" = "with the reporter", + ) + #undef ORION_TRAIL_WINTURN diff --git a/code/game/machinery/computer/orders/order_computer/cook_order.dm b/code/game/machinery/computer/orders/order_computer/cook_order.dm index 88266ea63b8..b378c53b17d 100644 --- a/code/game/machinery/computer/orders/order_computer/cook_order.dm +++ b/code/game/machinery/computer/orders/order_computer/cook_order.dm @@ -8,12 +8,13 @@ CATEGORY_SAUCES_REAGENTS, ) blackbox_key = "chef" + announcement_line = "The kitchen has ordered groceries which will arrive on the cargo shuttle! Please make sure it gets to them as soon as possible!" // Discount for items in the chefs category like mining/bitrunning consoles cargo_cost_multiplier = 0.65 /obj/machinery/computer/order_console/cook/order_groceries(mob/living/purchaser, obj/item/card/id/card, list/groceries) say("Thank you for your purchase! It will arrive on the next cargo shuttle!") - radio.talk_into(src, "The kitchen has ordered groceries which will arrive on the cargo shuttle! Please make sure it gets to them as soon as possible!", radio_channel) + aas_config_announce(/datum/aas_config_entry/order_console, list(), src, list(radio_channel), capitalize(blackbox_key)) for(var/datum/orderable_item/ordered_item in groceries) if(!(ordered_item.category_index in order_categories)) groceries.Remove(ordered_item) diff --git a/code/game/machinery/computer/orders/order_computer/mining_order.dm b/code/game/machinery/computer/orders/order_computer/mining_order.dm index 627f778a3ef..7bce69ffc02 100644 --- a/code/game/machinery/computer/orders/order_computer/mining_order.dm +++ b/code/game/machinery/computer/orders/order_computer/mining_order.dm @@ -24,6 +24,7 @@ CATEGORY_PKA, ) blackbox_key = "mining" + announcement_line = "A shaft miner has ordered equipment which will arrive on the cargo shuttle! Please make sure it gets to them as soon as possible!" /obj/machinery/computer/order_console/mining/subtract_points(final_cost, obj/item/card/id/card) if(final_cost <= card.registered_account.mining_points) @@ -56,7 +57,7 @@ can_be_cancelled = FALSE, ) say("Thank you for your purchase! It will arrive on the next cargo shuttle!") - radio.talk_into(src, "A shaft miner has ordered equipment which will arrive on the cargo shuttle! Please make sure it gets to them as soon as possible!", radio_channel) + aas_config_announce(/datum/aas_config_entry/order_console, list(), src, list(radio_channel), capitalize(blackbox_key)) SSshuttle.shopping_list += new_order /obj/machinery/computer/order_console/mining/retrieve_points(obj/item/card/id/id_card) diff --git a/code/game/machinery/computer/orders/order_computer/order_computer.dm b/code/game/machinery/computer/orders/order_computer/order_computer.dm index 10bee9c4aec..b62e8d8a6a2 100644 --- a/code/game/machinery/computer/orders/order_computer/order_computer.dm +++ b/code/game/machinery/computer/orders/order_computer/order_computer.dm @@ -19,10 +19,10 @@ GLOBAL_LIST_EMPTY(order_console_products) COOLDOWN_DECLARE(order_cooldown) ///Cooldown time between uses, express console will have extra time depending on express_cost_multiplier. var/cooldown_time = 60 SECONDS - ///The radio the console can speak into - var/obj/item/radio/radio ///The channel we will attempt to speak into through our radio. var/radio_channel = RADIO_CHANNEL_SUPPLY + ///What line we should announce on ordering_groceries + var/announcement_line ///The kind of cash does the console use. var/credit_type = CREDIT_TYPE_CREDIT @@ -41,11 +41,6 @@ GLOBAL_LIST_EMPTY(order_console_products) /obj/machinery/computer/order_console/Initialize(mapload) . = ..() - radio = new(src) - radio.set_frequency(FREQ_SUPPLY) - radio.subspace_transmission = TRUE - radio.canhear_range = 0 - radio.recalculateChannels() if(GLOB.order_console_products.len) return @@ -54,10 +49,6 @@ GLOBAL_LIST_EMPTY(order_console_products) continue GLOB.order_console_products += new path -/obj/machinery/computer/order_console/Destroy() - QDEL_NULL(radio) - return ..() - /obj/machinery/computer/order_console/proc/get_total_cost() var/cost = 0 for(var/datum/orderable_item/item as anything in grocery_list) @@ -244,4 +235,21 @@ GLOBAL_LIST_EMPTY(order_console_products) /obj/machinery/computer/order_console/proc/order_groceries(mob/living/purchaser, obj/item/card/id/card, list/groceries) return +/datum/aas_config_entry/order_console + name = "Local Ordering Console Announcements" + announcement_lines_map = list() + general_tooltip = "Used to make announces, when consoles listed here placing new order" + +/datum/aas_config_entry/order_console/New() + . = ..() + for(var/obj/machinery/computer/order_console/subconsole as anything in subtypesof(/obj/machinery/computer/order_console)) + if(subconsole.blackbox_key) + announcement_lines_map[capitalize(subconsole.blackbox_key)] = subconsole.announcement_line + +/datum/aas_config_entry/order_console/compile_announce(list/variables_map, announcement_line) + if (!announcement_lines_map.len) + announcement_lines_map["Error"] = "Unknown Error happened, while we tried to procceed an order, please report this to Nanotrasen." + . = ..() + + #undef CREDIT_TYPE_CREDIT diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 3b66f304eb3..90a295abc83 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -35,15 +35,12 @@ var/list/flashers = list() ///List of weakrefs to nearby closets var/list/closets = list() - ///needed to send messages to sec radio - var/obj/item/radio/sec_radio + ///Channel to report prisoneer's release + var/broadcast_channel = RADIO_CHANNEL_SECURITY /obj/machinery/status_display/door_timer/Initialize(mapload) . = ..() - sec_radio = new/obj/item/radio(src) - sec_radio.set_listening(FALSE) - if(id != null) for(var/obj/machinery/door/window/brigdoor/M in urange(20, src)) if (M.id == id) @@ -140,8 +137,7 @@ return 0 if(!forced) - sec_radio.set_frequency(FREQ_SECURITY) - sec_radio.talk_into(src, "Timer has expired. Releasing prisoner.", FREQ_SECURITY) + aas_config_announce(/datum/aas_config_entry/brig_cell_release_announcement, list("CELL" = name), src, list(broadcast_channel)) timing = FALSE activation_time = 0 @@ -284,6 +280,15 @@ continue timer_end(forced = TRUE) +/datum/aas_config_entry/brig_cell_release_announcement + name = "Security Alert: Cell Timer Expired" + announcement_lines_map = list( + "Message" = "Timer for %CELL has expired. Releasing prisoner.", + ) + vars_and_tooltips_map = list( + "CELL" = "will be replaced with the cell name.", + ) + #undef PRESET_SHORT #undef PRESET_MEDIUM #undef PRESET_LONG diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index dea6503858c..15f87b18103 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -43,8 +43,6 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments) var/message_verified_by = "" /// If a message is stamped, this will contain the stamp name var/message_stamped_by = "" - /// Reference to the internal radio - var/obj/item/radio/radio ///If an emergency has been called by this device. Acts as both a cooldown and lets the responder know where it the emergency was triggered from var/emergency /// If ore redemption machines will send an update when it receives new ores. @@ -124,13 +122,9 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments) GLOB.req_console_all += src GLOB.req_console_ckey_departments[ckey(department)] = department // and then we set ourselves a listed name - - radio = new /obj/item/radio(src) - radio.set_listening(FALSE) find_and_hang_on_wall() /obj/machinery/requests_console/Destroy() - QDEL_NULL(radio) QDEL_LIST(messages) GLOB.req_console_all -= src return ..() @@ -168,20 +162,15 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments) if("set_emergency") if(emergency) return - var/radio_freq switch(params["emergency"]) if(REQ_EMERGENCY_SECURITY) //Security - radio_freq = FREQ_SECURITY + aas_config_announce(/datum/aas_config_entry/rc_emergency, list("LOCATION" = department), null, list(RADIO_CHANNEL_SECURITY), REQ_EMERGENCY_SECURITY) if(REQ_EMERGENCY_ENGINEERING) //Engineering - radio_freq = FREQ_ENGINEERING + aas_config_announce(/datum/aas_config_entry/rc_emergency, list("LOCATION" = department), null, list(RADIO_CHANNEL_ENGINEERING), REQ_EMERGENCY_ENGINEERING) if(REQ_EMERGENCY_MEDICAL) //Medical - radio_freq = FREQ_MEDICAL - if(radio_freq) - emergency = params["emergency"] - radio.set_frequency(radio_freq) - radio.talk_into(src,"[emergency] emergency in [department]!!",radio_freq) - update_appearance() - addtimer(CALLBACK(src, PROC_REF(clear_emergency)), 5 MINUTES) + aas_config_announce(/datum/aas_config_entry/rc_emergency, list("LOCATION" = department), null, list(RADIO_CHANNEL_MEDICAL), REQ_EMERGENCY_MEDICAL) + update_appearance() + addtimer(CALLBACK(src, PROC_REF(clear_emergency)), 5 MINUTES) return TRUE if("send_announcement") if(!COOLDOWN_FINISHED(src, announcement_cooldown)) @@ -242,20 +231,22 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments) ///Sends the message from the request console /obj/machinery/requests_console/proc/send_message(recipient, message, priority, request_type) - var/radio_freq + var/radio_channel + // They all naming them wrong, all the time... I'll probably rewrite this later in separate PR. + // Automatically from areas or via mapping helpers. (ther is no "Cargobay Request Console" in any map) switch(ckey(recipient)) if("bridge") - radio_freq = FREQ_COMMAND + radio_channel = RADIO_CHANNEL_COMMAND if("medbay") - radio_freq = FREQ_MEDICAL + radio_channel = RADIO_CHANNEL_MEDICAL if("science") - radio_freq = FREQ_SCIENCE + radio_channel = RADIO_CHANNEL_SCIENCE if("engineering") - radio_freq = FREQ_ENGINEERING + radio_channel = RADIO_CHANNEL_ENGINEERING if("security") - radio_freq = FREQ_SECURITY + radio_channel = RADIO_CHANNEL_SECURITY if("cargobay", "mining") - radio_freq = FREQ_SUPPLY + radio_channel = RADIO_CHANNEL_SUPPLY var/datum/signal/subspace/messaging/rc/signal = new(src, list( "sender_department" = department, @@ -264,7 +255,7 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments) "verified" = message_verified_by, "stamped" = message_stamped_by, "priority" = priority, - "notify_freq" = radio_freq, + "notify_channel" = radio_channel, "request_type" = request_type, )) signal.send_to_receivers() @@ -347,15 +338,26 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments) SStgui.update_uis(src) - var/alert = new_message.get_alert() - if(!silent) playsound(src, 'sound/machines/beep/twobeep_high.ogg', 50, TRUE) - say(alert) + say(new_message.get_alert()) - if(new_message.radio_freq) - radio.set_frequency(new_message.radio_freq) - radio.talk_into(src, "[alert]: [new_message.content]", new_message.radio_freq) + if(new_message.radio_channel) + var/authentication + var/announcement_line = "Unauthenticated" + if (new_message.message_verified_by) + authentication = new_message.message_verified_by + announcement_line = "Verified with ID" + else if (new_message.message_stamped_by) + authentication = new_message.message_stamped_by + announcement_line = "Stamped with stamp" + + aas_config_announce(/datum/aas_config_entry/rc_new_message, list( + "AUTHENTICATION" = authentication, + "SENDER" = new_message.sender_department, + "RECEIVER" = department, + "MESSAGE" = new_message.content + ), null, list(new_message.radio_channel), announcement_line, new_message.priority == REQ_EXTREME_MESSAGE_PRIORITY) /obj/machinery/requests_console/crowbar_act(mob/living/user, obj/item/tool) tool.play_tool_sound(src, 50) @@ -412,6 +414,32 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/requests_console/auto_name, 30) result_path = /obj/machinery/requests_console/auto_name pixel_shift = 30 +/datum/aas_config_entry/rc_emergency + name = "RC Alert: Emergency" + announcement_lines_map = list( + "Security" = "Security emergency in %LOCATION!!!", + "Engineering" = "Engineering emergency in %LOCATION!!!", + "Medical" = "Medical emergency in %LOCATION!!!", + ) + vars_and_tooltips_map = list( + "LOCATION" = "will be replaced with the department name", + ) + +/datum/aas_config_entry/rc_new_message + name = "RC Alert: New Message " + // Yes, players can't use html tags, however they can use speech mods like | or +, but sh-sh-sh, don't tell them! + announcement_lines_map = list( + "Unauthenticated" = "Message from %SENDER to %RECEIVER: %MESSAGE", + "Verified with ID" = "Message from %SENDER to %RECEIVER, Verified by %AUTHENTICATION (Authenticated): %MESSAGE", + "Stamped with stamp" = "Message from %SENDER to %RECEIVER, Stamped by %AUTHENTICATION (Authenticated): %MESSAGE", + ) + vars_and_tooltips_map = list( + "AUTHENTICATION" = "will be replaced with ID or stamp, if present", + "SENDER" = "with the sender department ", + "RECEIVER" = "with the receiver department", + "MESSAGE" = "with the message content", + ) + #undef REQ_EMERGENCY_SECURITY #undef REQ_EMERGENCY_ENGINEERING #undef REQ_EMERGENCY_MEDICAL diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm index 0dba54bf2aa..d6b2df74b62 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm @@ -101,9 +101,9 @@ GLOBAL_LIST_EMPTY(jam_on_wardec) var/datum/techweb/station_techweb = locate(/datum/techweb/science) in SSresearch.techwebs if(station_techweb) - var/obj/machinery/announcement_system/announcement_system = pick(GLOB.announcement_systems) + var/obj/machinery/announcement_system/announcement_system = get_announcement_system() if (!isnull(announcement_system)) - announcement_system.broadcast("Additional research data received from Nanotrasen R&D Division following the emergency protocol.", list(RADIO_CHANNEL_SCIENCE)) + announcement_system.broadcast("Additional research data received from Nanotrasen R&D Division following the emergency protocol.", list(RADIO_CHANNEL_SCIENCE), TRUE) station_techweb.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS * 3)) qdel(src) diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index 114ead12bcd..5a9454fcd56 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -92,8 +92,6 @@ var/conduction_coefficient = 0.3 ///The beaker usually contains cryoxadone that is pumped into the mob var/obj/item/reagent_containers/cup/beaker = null - ///Inform medical about cryo status over this radio - var/obj/item/radio/radio /// Visual content - Occupant var/atom/movable/visual/cryo_occupant/occupant_vis ///Cryo will continue to treat people with 0 damage but existing wounds, but will sound off when damage healing is done in case doctors want to directly treat the wounds instead @@ -104,6 +102,8 @@ var/on = FALSE /// The sound loop that can be heard when the generator is processing. var/datum/looping_sound/cryo_cell/soundloop + /// For away sites, custom or admin events + var/broadcast_channel = RADIO_CHANNEL_MEDICAL /datum/armor/unary_cryo_cell energy = 100 @@ -113,12 +113,6 @@ /obj/machinery/cryo_cell/Initialize(mapload) . = ..() - radio = new(src) - radio.keyslot = new /obj/item/encryptionkey/headset_med - radio.subspace_transmission = TRUE - radio.canhear_range = 0 - radio.recalculateChannels() - occupant_vis = new(mapload, src) vis_contents += occupant_vis internal_connector = new(loc, src, dir, CELL_VOLUME * 0.5) @@ -131,7 +125,6 @@ vis_contents.Cut() QDEL_NULL(occupant_vis) - QDEL_NULL(radio) QDEL_NULL(beaker) QDEL_NULL(internal_connector) QDEL_NULL(soundloop) @@ -416,12 +409,10 @@ mob_occupant.extinguish_mob() if(mob_occupant.stat == DEAD) // Notify doctors and potentially eject if the patient is dead set_on(FALSE) - var/msg = "Patient is deceased." + aas_config_announce(/datum/aas_config_entry/medical_cryo_announcements, list("EJECTING" = autoeject), src, list(broadcast_channel), "Deceased") if(autoeject) // Eject if configured. - msg += " Auto ejecting patient now." open_machine() playsound(src, 'sound/machines/cryo_warning.ogg', 100) - radio.talk_into(src, msg, RADIO_CHANNEL_MEDICAL) return PROCESS_KILL // Don't bother with fully healed people. @@ -432,18 +423,16 @@ if(!treating_wounds) // if we have wounds and haven't already alerted the doctors we're only dealing with the wounds, let them know treating_wounds = TRUE playsound(src, 'sound/machines/cryo_warning.ogg', 100) // Bug the doctors. - radio.talk_into(src, "Patient vitals fully recovered, continuing automated wound treatment.", RADIO_CHANNEL_MEDICAL) + aas_config_announce(/datum/aas_config_entry/medical_cryo_announcements, list(), src, list(broadcast_channel), "Wound Treatment") else // otherwise if we were only treating wounds and now we don't have any, turn off treating_wounds so we can boot 'em out treating_wounds = FALSE if(!treating_wounds) set_on(FALSE) playsound(src, 'sound/machines/cryo_warning.ogg', 100) // Bug the doctors. - var/msg = "Patient fully restored." + aas_config_announce(/datum/aas_config_entry/medical_cryo_announcements, list("EJECTING" = autoeject), src, list(broadcast_channel), "Fully Recovered") if(autoeject) // Eject if configured. - msg += " Auto ejecting patient now." open_machine() - radio.talk_into(src, msg, RADIO_CHANNEL_MEDICAL) return PROCESS_KILL var/datum/gas_mixture/air1 = internal_connector.gas_connector.airs[1] @@ -464,11 +453,9 @@ //check for workable conditions if(!internal_connector.gas_connector.nodes[1] || !air1 || !air1.gases.len || air1.total_moles() < CRYO_MIN_GAS_MOLES) // Turn off if the machine won't work. set_on(FALSE) - var/msg = "Insufficient cryogenic gas, shutting down." + aas_config_announce(/datum/aas_config_entry/medical_cryo_announcements, list("EJECTING" = autoeject), src, list(broadcast_channel), "Insufficient Gas") if(autoeject) // Eject if configured. - msg += " Auto ejecting patient now." open_machine() - radio.talk_into(src, msg, RADIO_CHANNEL_MEDICAL) return PROCESS_KILL //take damage from high temperatures @@ -679,6 +666,26 @@ /obj/machinery/cryo_cell/get_remote_view_fullscreens(mob/user) user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1) +/datum/aas_config_entry/medical_cryo_announcements + name = "Medical Alert: Cryogenics Reports" + announcement_lines_map = list( + "Autoejecting" = "Auto ejecting patient now.", + "Deceased" = "Cryogenics report: Patient is deceased. %AUTOEJECTING", + "Fully Recovered" = "Cryogenics report: Patient fully restored. %AUTOEJECTING", + "Insufficient Gas" = "Cryogenics report: Insufficient cryogenic gas, shutting down. %AUTOEJECTING", + "Wound Treatment" = "Cryogenics report: Patient vitals fully recovered, continuing automated wound treatment." + ) + vars_and_tooltips_map = list( + "AUTOEJECTING" = "will be replaced with Autoejecting line, if system reports it's necessity" + ) + +/datum/aas_config_entry/medical_cryo_announcements/compile_announce(list/variables_map, announcement_line) + variables_map["AUTOEJECTING"] = variables_map["EJECTING"] ? announcement_lines_map["Autoejecting"] : "" + . = ..() + // Why double replacetext_char? Well, to handle cases where variable in the middle of sentence like "also %AUTOEJECTING this", so there will be no double spaces + // Yeah I am bad, at this, sorry (it should be a perfect place for regex usage, but I am weak) + . = trim(replacetext_char(replacetext_char(., "\[NO DATA\] ", ""), "\[NO DATA\]", "")) + #undef MAX_TEMPERATURE #undef CRYO_MULTIPLY_FACTOR #undef CRYO_TX_QTY diff --git a/code/modules/bitrunning/objects/vendor.dm b/code/modules/bitrunning/objects/vendor.dm index f373a8617b4..96c415b9bfe 100644 --- a/code/modules/bitrunning/objects/vendor.dm +++ b/code/modules/bitrunning/objects/vendor.dm @@ -23,6 +23,7 @@ CATEGORY_BEPIS, ) blackbox_key = "bitrunning" + announcement_line = "A bitrunner has ordered equipment which will arrive on the cargo shuttle! Please make sure it gets to them as soon as possible!" /obj/machinery/computer/order_console/bitrunning/subtract_points(final_cost, obj/item/card/id/card) if(final_cost <= card.registered_account.bitrunning_points) @@ -56,7 +57,7 @@ can_be_cancelled = FALSE, ) say("Thank you for your purchase! It will arrive on the next cargo shuttle!") - radio.talk_into(src, "A bitrunner has ordered equipment which will arrive on the cargo shuttle! Please make sure it gets to them as soon as possible!", radio_channel) + aas_config_announce(/datum/aas_config_entry/order_console, list(), src, list(radio_channel), capitalize(blackbox_key)) SSshuttle.shopping_list += new_order /obj/machinery/computer/order_console/bitrunning/retrieve_points(obj/item/card/id/id_card) diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm index 5e29c1729b4..f166a82c817 100644 --- a/code/modules/bitrunning/server/_parent.dm +++ b/code/modules/bitrunning/server/_parent.dm @@ -28,8 +28,6 @@ var/list/datum/weakref/spawned_threat_refs = list() /// Scales loot with extra players var/multiplayer_bonus = 1.1 - ///The radio the console can speak into - var/obj/item/radio/radio /// The amount of points in the system, used to purchase maps var/points = 0 /// Keeps track of the number of times someone has built a hololadder @@ -54,11 +52,6 @@ /obj/machinery/quantum_server/post_machine_initialize() . = ..() - radio = new(src) - radio.keyslot = new /obj/item/encryptionkey/headset_cargo() - radio.set_listening(FALSE) - radio.recalculateChannels() - RegisterSignals(src, list(COMSIG_MACHINERY_BROKEN, COMSIG_MACHINERY_POWER_LOST), PROC_REF(on_broken)) RegisterSignal(src, COMSIG_QDELETING, PROC_REF(on_delete)) @@ -70,7 +63,6 @@ spawned_threat_refs.Cut() QDEL_NULL(exit_turfs) QDEL_NULL(generated_domain) - QDEL_NULL(radio) /obj/machinery/quantum_server/examine(mob/user) . = ..() @@ -178,3 +170,10 @@ servo_bonus = servo_rating return ..() + +/datum/aas_config_entry/bitrunning_QS_ready_announcement + name = "Cargo Alert: Bitrunning QS Ready" + general_tooltip = "Announces when the quantum server is ready to be used. No variables provided" + announcement_lines_map = list( + "Message" = "Quantum Server report: Thermal systems within operational parameters. Proceeding to domain configuration." + ) diff --git a/code/modules/bitrunning/server/threats.dm b/code/modules/bitrunning/server/threats.dm index 77d1c6b6cda..60bf6220839 100644 --- a/code/modules/bitrunning/server/threats.dm +++ b/code/modules/bitrunning/server/threats.dm @@ -132,12 +132,15 @@ /obj/machinery/quantum_server/proc/station_spawn(mob/living/antag, obj/machinery/byteforge/chosen_forge) antag.balloon_alert(antag, "scanning...") chosen_forge.setup_particles(angry = TRUE) - radio.talk_into(src, "SECURITY BREACH: Unauthorized entry sequence detected.", RADIO_CHANNEL_SUPPLY) + var/obj/machinery/announcement_system/aas = get_announcement_system(source = src) + if (aas) + aas.broadcast("QUANTUM SERVER ALERT: Security breach detected. Unauthorized entry sequence in progress...", list(RADIO_CHANNEL_SUPPLY)) SEND_SIGNAL(src, COMSIG_BITRUNNER_STATION_SPAWN) var/timeout = 2 SECONDS if(!ishuman(antag)) - radio.talk_into(src, "Fabrication protocols have crashed unexpectedly. Please evacuate the area.", RADIO_CHANNEL_SUPPLY) + if (aas) + aas.broadcast("QUANTUM SERVER ALERT: Fabrication protocols have crashed unexpectedly. Please evacuate the area.", list(RADIO_CHANNEL_SUPPLY)) timeout = 10 SECONDS if(!do_after(antag, timeout) || QDELETED(chosen_forge) || QDELETED(antag) || QDELETED(src) || !is_ready || !is_operational) @@ -159,8 +162,8 @@ if(ishuman(antag)) reset_equipment(antag) - else - radio.talk_into(src, "CRITICAL ALERT: Unregistered mechanical entity deployed.") + else if (aas) + aas.broadcast("QUANTUM SERVER CRITICAL ALERT: Unregistered mechanical entity deployed.", list()) var/datum/antagonist/antag_datum = antag.mind?.has_antag_datum(/datum/antagonist/bitrunning_glitch) if(istype(antag_datum)) diff --git a/code/modules/bitrunning/server/util.dm b/code/modules/bitrunning/server/util.dm index 4f819c54cdc..13a36fc115a 100644 --- a/code/modules/bitrunning/server/util.dm +++ b/code/modules/bitrunning/server/util.dm @@ -12,7 +12,7 @@ balloon_alert_to_viewers("cooldown has completed") return //BUBBER ADDITION END - radio.talk_into(src, "Thermal systems within operational parameters. Proceeding to domain configuration.", RADIO_CHANNEL_SUPPLY) + aas_config_announce(/datum/aas_config_entry/bitrunning_QS_ready_announcement, list(), src, list(RADIO_CHANNEL_SUPPLY)) /// If there are hosted minds, attempts to get a list of their current virtual bodies w/ vitals diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm index f39f2c5c970..2f62ea76560 100644 --- a/code/modules/cargo/orderconsole.dm +++ b/code/modules/cargo/orderconsole.dm @@ -17,8 +17,6 @@ human remains, classified nuclear weaponry, mail, undelivered departmental order crates, syndicate bombs, \ homing beacons, unstable eigenstates, fax machines, or machinery housing any form of artificial intelligence." var/blockade_warning = "Bluespace instability detected. Shuttle movement impossible." - /// radio used by the console to send messages on supply channel - var/obj/item/radio/headset/radio /// var that tracks message cooldown var/message_cooldown var/list/loaded_coupons @@ -46,14 +44,6 @@ can_approve_requests = FALSE requestonly = TRUE -/obj/machinery/computer/cargo/Initialize(mapload) - . = ..() - radio = new /obj/item/radio/headset/headset_cargo(src) - -/obj/machinery/computer/cargo/Destroy() - QDEL_NULL(radio) - return ..() - /obj/machinery/computer/cargo/attacked_by(obj/item/I, mob/living/user) if(istype(I,/obj/item/trade_chip)) var/obj/item/trade_chip/contract = I @@ -311,8 +301,7 @@ if(self_paid) say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.") if(requestonly && message_cooldown < world.time) - var/message = amount == 1 ? "A new order has been requested." : "[amount] order has been requested." - radio.talk_into(src, message, RADIO_CHANNEL_SUPPLY) + aas_config_announce(/datum/aas_config_entry/cargo_orders_announcement, list("AMOUNT" = amount), src, list(RADIO_CHANNEL_SUPPLY), amount == 1 ? "Single Order" : "Multiple Orders") message_cooldown = world.time + 30 SECONDS . = TRUE @@ -487,3 +476,13 @@ var/datum/signal/status_signal = new(list("command" = command)) frequency.post_signal(src, status_signal) + +/datum/aas_config_entry/cargo_orders_announcement + name = "Cargo Alert: New Orders" + announcement_lines_map = list( + "Single Order" = "A new order has been requested.", + "Multiple Orders" = "%AMOUNT orders have been requested.", + ) + vars_and_tooltips_map = list( + "AMOUNT" = "will be replaced wuth number of orders.", + ) diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index dfff0ac3bbb..f5893108fa6 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -253,20 +253,10 @@ dna.species.pre_equip_species_outfit(equipping, src, visual_only) equip_outfit_and_loadout(equipping.get_outfit(consistent), player_client?.prefs, visual_only, equipping) // SKYRAT EDIT CHANGE - Add equipping param -/datum/job/proc/announce_head(mob/living/carbon/human/human, channels, job_title) //tells the given channel that the given mob is the new department head. See communications.dm for valid channels. // SKYRAT EDIT CHANGE - ALTERNATIVE_JOB_TITLES - if(!human) - return - var/obj/machinery/announcement_system/system - var/list/available_machines = list() - for(var/obj/machinery/announcement_system/announce as anything in GLOB.announcement_systems) - if(announce.newhead_toggle) - available_machines += announce - break - if(!length(available_machines)) - return - system = pick(available_machines) - //timer because these should come after the captain announcement - SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), CALLBACK(system, TYPE_PROC_REF(/obj/machinery/announcement_system, announce), AUTO_ANNOUNCE_NEWHEAD, human.real_name, job_title, channels), 1)) // SKYRAT EDIT CHANGE - ALTERNATIVE_JOB_TITLES +/datum/job/proc/announce_head(mob/living/carbon/human/human, channels) //tells the given channel that the given mob is the new department head. See communications.dm for valid channels. // BUBBER EDIT CHANGE - ALTERNATIVE_JOB_TITLES + if(human) + //timer because these should come after the captain announcement + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(aas_config_announce), /datum/aas_config_entry/newhead, list("PERSON" = human.real_name, "RANK" = job_title), null, channels, null, TRUE), 1)) // BUBBER EDIT CHANGE - ALTERNATIVE_JOB_TITLES //If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 /datum/job/proc/player_old_enough(client/player) diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index b0039e63b7b..0dc68dc337c 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -165,11 +165,14 @@ GLOBAL_LIST_EMPTY(security_officer_distribution) department, distribution, ) - var/obj/machinery/announcement_system/announcement_system = pick(GLOB.announcement_systems) + var/obj/machinery/announcement_system/announcement_system = get_announcement_system(/datum/aas_config_entry/announce_officer) if (isnull(announcement_system)) return - announcement_system.announce_officer(officer, department) + announcement_system.announce(/datum/aas_config_entry/announce_officer, list( + "OFFICER" = officer.real_name, + "DEPARTMENT" = department, + ), list(RADIO_CHANNEL_SECURITY)) var/list/targets = list() @@ -190,10 +193,14 @@ GLOBAL_LIST_EMPTY(security_officer_distribution) if (!targets.len) return + // I thought it would be great, if AAS also modifies PDA messages. Especially because it's AASs message. var/datum/signal/subspace/messaging/tablet_message/signal = new(announcement_system, list( "fakename" = "Security Department Update", "fakejob" = "Automated Announcement System", - "message" = "Officer [officer.real_name] has been assigned to your department, [department].", + "message" = announcement_system.compile_config_message(/datum/aas_config_entry/announce_officer, list( + "OFFICER" = officer.real_name, + "DEPARTMENT" = department, + )), "targets" = targets, "automated" = TRUE, )) diff --git a/code/modules/lost_crew/lost_crew_manager.dm b/code/modules/lost_crew/lost_crew_manager.dm index e8a85ff13cf..ed222e063b6 100644 --- a/code/modules/lost_crew/lost_crew_manager.dm +++ b/code/modules/lost_crew/lost_crew_manager.dm @@ -104,21 +104,16 @@ GLOBAL_DATUM_INIT(lost_crew_manager, /datum/lost_crew_manager, new) /// Give medbay a happy announcement and put some money into their budget /datum/lost_crew_manager/proc/award_succes(datum/mind/revived_mind, list/death_lore) - var/obj/item/radio/headset/radio = new /obj/item/radio/headset/silicon/ai(revived_mind.current) //radio cant be in nullspace or brit shakes - radio.set_frequency(FREQ_MEDICAL) - radio.name = "Medical Announcer" - - // i am incredibly disappointed in you + // I am incredibly disappointed in you if(revived_mind.current.stat == DEAD) - radio.talk_into(radio, "Sensors indicate lifesigns of [revived_mind.name] have seized. Please inform their family of your failure.", RADIO_CHANNEL_MEDICAL) + aas_config_announce(/datum/aas_config_entry/medical_lost_crew_reward, list("PERSON" = revived_mind.name, "AWARD" = 0), null, list(RADIO_CHANNEL_MEDICAL), "Deceased") return // You are a credit to society - radio.talk_into(radio, "Sensors indicate continued survival of [revived_mind.name]. Well done, [credits_on_succes]cr has been transferred to the medical budget.", RADIO_CHANNEL_MEDICAL) + aas_config_announce(/datum/aas_config_entry/medical_lost_crew_reward, list("PERSON" = revived_mind.name, "AWARD" = credits_on_succes), null, list(RADIO_CHANNEL_MEDICAL), "Revived") var/datum/bank_account/medical_budget = SSeconomy.get_dep_account(ACCOUNT_MED) medical_budget.adjust_money(credits_on_succes) - qdel(radio) /// A box for recovered items that can only be opened by the new crewmember /obj/item/storage/lockbox/mind @@ -162,3 +157,15 @@ GLOBAL_DATUM_INIT(lost_crew_manager, /datum/lost_crew_manager, new) return NONE context[SCREENTIP_CONTEXT_LMB] = "Use in-hand to unlock" return CONTEXTUAL_SCREENTIP_SET + +/datum/aas_config_entry/medical_lost_crew_reward + name = "Medical Alert: Lost Crew Revival Program" + announcement_lines_map = list( + "Deceased" = "Sensors indicate lifesigns of %PERSON have seized. Please inform their family of your failure.", + "Revived" = "Sensors indicate continued survival of %PERSON. Well done, %AWARDcr has been transferred to the medical budget." + ) + vars_and_tooltips_map = list( + "PERSON" = "will be replaced with body's name", + "AWARD" = "with money that medical department receive, if any" + ) + modifiable = FALSE diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index 75d532249f4..a347232b83b 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -10,8 +10,6 @@ density = FALSE /// Connected stacking machine var/obj/machinery/mineral/stacking_machine/laborstacker/stacking_machine - /// Needed to send messages to sec radio - var/obj/item/radio/security_radio /// Whether the claim console initiated the launch. var/initiated_launch = FALSE /// Cooldown for console says. @@ -19,8 +17,6 @@ /obj/machinery/mineral/labor_claim_console/Initialize(mapload) . = ..() - security_radio = new /obj/item/radio(src) - security_radio.set_listening(FALSE) locate_stacking_machine() if(!SSshuttle.initialized) RegisterSignal(SSshuttle, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(register_shuttle_signal)) @@ -37,7 +33,6 @@ UnregisterSignal(SSshuttle, COMSIG_SUBSYSTEM_POST_INITIALIZE) /obj/machinery/mineral/labor_claim_console/Destroy() - QDEL_NULL(security_radio) if(stacking_machine) stacking_machine.labor_console = null stacking_machine = null @@ -130,11 +125,10 @@ COOLDOWN_START(src, say_cooldown, 2 SECONDS) else if(!(obj_flags & EMAGGED)) - security_radio.set_frequency(FREQ_SECURITY) var/datum/record/crew/target = find_record(user_mob.real_name) target?.wanted_status = WANTED_PAROLE - security_radio.talk_into(src, "[user_mob.name] returned to the station. Minerals and Prisoner ID card ready for retrieval.", FREQ_SECURITY) + aas_config_announce(/datum/aas_config_entry/security_labor_stacker, list("PERSON" = user_mob.real_name), src, list(RADIO_CHANNEL_SECURITY)) user_mob.log_message("has completed their labor points goal and is now sending the gulag shuttle back to the station.", LOG_GAME) say("Labor sentence finished, shuttle returning.") initiated_launch = TRUE @@ -227,4 +221,13 @@ say("Points Collected: [prisoner_id.points] / [prisoner_id.goal].") say("Collect points by bringing smelted minerals to the Labor Shuttle stacking machine. Reach your quota to earn your release.") +/datum/aas_config_entry/security_labor_stacker + name = "Security Alert: Labor Camp Release" + announcement_lines_map = list( + "Message" = "%PERSON returned to the station. Minerals and Prisoner ID card ready for retrieval." + ) + vars_and_tooltips_map = list( + "PERSON" = "will be replaced with the name of the prisoner." + ) + #undef SHEET_POINT_VALUE diff --git a/code/modules/modular_computers/file_system/programs/dept_order.dm b/code/modules/modular_computers/file_system/programs/dept_order.dm index c24ac025e0e..d8f3377114b 100644 --- a/code/modules/modular_computers/file_system/programs/dept_order.dm +++ b/code/modules/modular_computers/file_system/programs/dept_order.dm @@ -32,18 +32,16 @@ GLOBAL_VAR(department_cd_override) ) /// Reference to the order we've made UNTIL it gets sent on the supply shuttle. this is so heads can cancel it VAR_PRIVATE/datum/supply_order/department_order - /// Our radio object we use to talk to our department. - VAR_PRIVATE/obj/item/radio/radio /// The radio channel we will speak into by default. VAR_PRIVATE/radio_channel - /// Maps what department gets what encryption key + /// Maps what department should report to what radio channel /// I could've put this on the job department datum but it felt unnecessary - VAR_PRIVATE/static/list/dept_to_radio = list( - /datum/job_department/engineering = /obj/item/encryptionkey/headset_eng, - /datum/job_department/medical = /obj/item/encryptionkey/headset_med, - /datum/job_department/science = /obj/item/encryptionkey/headset_sci, - /datum/job_department/security = /obj/item/encryptionkey/headset_sec, - /datum/job_department/service = /obj/item/encryptionkey/headset_service, + VAR_PRIVATE/static/list/dept_to_radio_channel = list( + /datum/job_department/engineering = RADIO_CHANNEL_ENGINEERING, + /datum/job_department/medical = RADIO_CHANNEL_MEDICAL, + /datum/job_department/science = RADIO_CHANNEL_SCIENCE, + /datum/job_department/security = RADIO_CHANNEL_SECURITY, + /datum/job_department/service = RADIO_CHANNEL_SERVICE, ) /// Sets the passed department type as the active department for this computer file. @@ -56,22 +54,10 @@ GLOBAL_VAR(department_cd_override) use_access |= linked_department_real.head_of_staff_access use_access |= linked_department_real.department_access // Also set up the radio - if(dept_to_radio[linked_department]) - if(!isnull(radio)) - QDEL_NULL(radio) - var/picked_key = dept_to_radio[linked_department] || /obj/item/encryptionkey/headset_cargo - radio = new(computer) - radio.keyslot = new picked_key() - radio.subspace_transmission = TRUE - radio.canhear_range = 0 - radio.recalculateChannels() - radio_channel = radio.keyslot.channels[1] + if(dept_to_radio_channel[linked_department]) + radio_channel = dept_to_radio_channel[linked_department] || RADIO_CHANNEL_SUPPLY computer.update_static_data_for_all_viewers() -/datum/computer_file/program/department_order/Destroy() - QDEL_NULL(radio) - return ..() - /datum/computer_file/program/department_order/ui_interact(mob/user, datum/tgui/ui) check_cooldown() @@ -244,6 +230,9 @@ GLOBAL_VAR(department_cd_override) SSshuttle.shopping_list += department_order if(!already_signalled) RegisterSignal(SSshuttle, COMSIG_SUPPLY_SHUTTLE_BUY, PROC_REF(finalize_department_order)) + if(!alert_silenced && alert_able) + aas_config_announce(/datum/aas_config_entry/department_orders, list("ORDER" = pack.name, "PERSON" = name), computer.physical, list(radio_channel), "Order Placed") + aas_config_announce(/datum/aas_config_entry/department_orders_cargo, list("DEPARTMENT" = linked_department.department_name), computer.physical, list(RADIO_CHANNEL_SUPPLY)) computer.physical.say("Order processed. Cargo will deliver the crate when it comes in on their shuttle. NOTICE: Heads of staff may override the order.") calculate_cooldown(pack.cost) @@ -266,7 +255,7 @@ GLOBAL_VAR(department_cd_override) /datum/computer_file/program/department_order/process_tick(seconds_per_tick) if(!check_cooldown() || alert_silenced || !alert_able) return - radio?.talk_into(computer, "Order cooldown has expired! A new order may now be placed!", radio_channel) + aas_config_announce(/datum/aas_config_entry/department_orders, list(), computer.physical, list(radio_channel), "Cooldown Reset") computer.alert_call(src, "Order cooldown expired!", 'sound/machines/ping.ogg') /// Checks if the cooldown is up and resets it if so. @@ -275,3 +264,23 @@ GLOBAL_VAR(department_cd_override) department_cooldowns[linked_department] = 0 return TRUE return FALSE + +/datum/aas_config_entry/department_orders + name = "Departmental Order Announcement" + announcement_lines_map = list( + "Order Placed" = "A department order has been placed by %PERSON for %ORDER.", + "Cooldown Reset" = "Department order cooldown has expired! A new order may now be placed!", + ) + vars_and_tooltips_map = list( + "ORDER" = "will be replaced with the package name", + "PERSON" = "with the orderer's name", + ) + +/datum/aas_config_entry/department_orders_cargo + name = "Cargo Alert: New Departmental Order" + announcement_lines_map = list( + "Message" = "New %DEPARTMENT departmental order has been placed" + ) + vars_and_tooltips_map = list( + "DEPARTMENT" = "will be replaced with orderer's department." + ) diff --git a/code/modules/power/turbine/turbine.dm b/code/modules/power/turbine/turbine.dm index be10a0de311..e2d47010850 100644 --- a/code/modules/power/turbine/turbine.dm +++ b/code/modules/power/turbine/turbine.dm @@ -411,8 +411,6 @@ var/damage = 0 ///Used to calculate the max damage received per tick and if the alarm should be called var/damage_archived = 0 - ///Our internal radio - var/obj/item/radio/radio COOLDOWN_DECLARE(turbine_damage_alert) @@ -420,17 +418,8 @@ //Volume of gas mixture is 3000 . = ..(mapload, gas_theoretical_volume = 3000) - radio = new(src) - radio.keyslot = new /obj/item/encryptionkey/headset_eng - radio.set_listening(FALSE) - radio.recalculateChannels() - new /obj/item/paper/guides/jobs/atmos/turbine(loc) -/obj/machinery/power/turbine/core_rotor/Destroy() - QDEL_NULL(radio) - return ..() - /obj/machinery/power/turbine/core_rotor/add_context(atom/source, list/context, obj/item/held_item, mob/user) . = ..() if(. == NONE) @@ -670,7 +659,7 @@ explosion(src, 2, 5, 7) return PROCESS_KILL - radio.talk_into(src, "Warning, turbine at [get_area_name(src)] taking damage, current integrity at [integrity]%!", RADIO_CHANNEL_ENGINEERING) + aas_config_announce(/datum/aas_config_entry/engineering_turbine_failure, list("INTEGRITY" = integrity, "LOCATION" = get_area_name(src)), src, list(RADIO_CHANNEL_ENGINEERING)) playsound(src, 'sound/machines/engine_alert/engine_alert1.ogg', 100, FALSE, 30, 30, falloff_distance = 10) //================ROTOR WORKING============// @@ -707,5 +696,15 @@ -Each tier increases the efficiency (more power), the max reachable RPM, and the max temperature that the machine can process without taking damage (up to fusion temperatures at the last tier!).
\ -A word of warning, the machine is very inefficient in its gas consumption and many unburnt gases will pass through. If you want to be cheap you can either pre-burn the gases or add a filtering system to collect the unburnt gases and reuse them." +/datum/aas_config_entry/engineering_turbine_failure + name = "Engineering Alert: Turbine Failure" + announcement_lines_map = list( + "Message" = "Warning, turbine at %LOCATION taking damage, current integrity at %INTEGRITY%!", + ) + vars_and_tooltips_map = list( + "LOCATION" = "will be replaced with location of the turbine.", + "INTEGRITY" = "with the current integrity of the turbine.", + ) + #undef PRESSURE_MAX #undef MINIMUM_TURBINE_PRESSURE diff --git a/code/modules/research/techweb/_techweb_node.dm b/code/modules/research/techweb/_techweb_node.dm index 79b8b27986c..2a6eb8b7b7d 100644 --- a/code/modules/research/techweb/_techweb_node.dm +++ b/code/modules/research/techweb/_techweb_node.dm @@ -125,15 +125,5 @@ return if(board.obj_flags & EMAGGED) channels_to_use = list(RADIO_CHANNEL_COMMON) - if(!length(channels_to_use) || starting_node) - return - var/obj/machinery/announcement_system/system - var/list/available_machines = list() - for(var/obj/machinery/announcement_system/announce as anything in GLOB.announcement_systems) - if(announce.announce_research_node) - available_machines += announce - break - if(!length(available_machines)) - return - system = pick(available_machines) - system.announce(AUTO_ANNOUNCE_NODE, display_name, channels = channels_to_use) + if(length(channels_to_use) && !starting_node) + aas_config_announce(/datum/aas_config_entry/researched_node, list("NODE" = display_name), null, channels_to_use) diff --git a/code/modules/shuttle/mobile_port/variants/arrivals.dm b/code/modules/shuttle/mobile_port/variants/arrivals.dm index bdfe0da403c..416c300233d 100644 --- a/code/modules/shuttle/mobile_port/variants/arrivals.dm +++ b/code/modules/shuttle/mobile_port/variants/arrivals.dm @@ -80,10 +80,7 @@ damaged = TRUE if(console) console.say("Alert, hull breach detected!") - if (length(GLOB.announcement_systems)) - var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems) - if(!QDELETED(announcer)) - announcer.announce("ARRIVALS_BROKEN", channels = list()) + aas_config_announce(/datum/aas_config_entry/arrivals_broken, list(), command_span=TRUE) if(mode != SHUTTLE_CALL) sound_played = FALSE mode = SHUTTLE_IDLE diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 9c875b974ab..659a41eb8e2 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -217,9 +217,6 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) ///Name of lighting mask for the vending machine var/light_mask - /// used for narcing on underages - var/obj/item/radio/sec_radio - //the path of the fish_source datum to use for the fishing_spot component var/fish_source_path = /datum/fish_source/vending @@ -288,7 +285,6 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) /obj/machinery/vending/Destroy() QDEL_NULL(coin) QDEL_NULL(bill) - QDEL_NULL(sec_radio) GLOB.vending_machines_to_restock -= src return ..() @@ -1441,11 +1437,12 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) if(age_restrictions && item_record.age_restricted && (!card_used.registered_age || card_used.registered_age < AGE_MINOR)) speak("You are not of legal age to purchase [item_record.name].") if(!(usr in GLOB.narcd_underages)) - if (isnull(sec_radio)) - sec_radio = new (src) - sec_radio.set_listening(FALSE) - sec_radio.set_frequency(FREQ_SECURITY) - sec_radio.talk_into(src, "SECURITY ALERT: [usr] recorded attempting to purchase [item_record.name] in [get_area(src)]. Please watch for substance abuse.", FREQ_SECURITY) // SKYRAT EDIT CHANGE + aas_config_announce(/datum/aas_config_entry/vendomat_age_control, list( + "PERSON" = usr.name, + "LOCATION" = get_area_name(src), + "VENDOR" = name, + "PRODUCT" = item_record.name + ), src, list(RADIO_CHANNEL_SECURITY)) GLOB.narcd_underages += usr flick(icon_deny,src) vend_ready = TRUE @@ -1918,4 +1915,16 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) add_filter("vending_outline", 9, list("type" = "outline", "color" = COLOR_VERY_SOFT_YELLOW)) add_filter("vending_rays", 10, list("type" = "rays", "size" = 35, "color" = COLOR_VIVID_YELLOW)) +/datum/aas_config_entry/vendomat_age_control + name = "Security Alert: Underaged Substance Abuse" + announcement_lines_map = list( + "Message" = "SECURITY ALERT: Underaged crewmember %PERSON recorded attempting to purchase %PRODUCT in %LOCATION by %VENDOR. Please watch for substance abuse." + ) + vars_and_tooltips_map = list( + "PERSON" = "will be replaced with the name of the crewmember", + "PRODUCT" = "with the product, he attempted to purchase", + "LOCATION" = "with place of purchase", + "VENDOR" = "with the vending machine" + ) + #undef MAX_VENDING_INPUT_AMOUNT diff --git a/tgui/packages/tgui/interfaces/AutomatedAnnouncement.tsx b/tgui/packages/tgui/interfaces/AutomatedAnnouncement.tsx index f88b2da67f4..b794e63a636 100644 --- a/tgui/packages/tgui/interfaces/AutomatedAnnouncement.tsx +++ b/tgui/packages/tgui/interfaces/AutomatedAnnouncement.tsx @@ -1,142 +1,158 @@ -import { Button, Input, LabeledList, Section } from 'tgui-core/components'; +import { filter, sortBy } from 'common/collections'; +import { useState } from 'react'; +import { + Button, + Input, + LabeledList, + NoticeBox, + Section, + Stack, + Table, +} from 'tgui-core/components'; import { BooleanLike } from 'tgui-core/react'; import { useBackend } from '../backend'; import { Window } from '../layouts'; -const TOOLTIP_TEXT = ` - %PERSON will be replaced with their name. - %RANK with their job. -`; - -const TOOLTIP_NODE = ` - %NODE will be replaced with the researched node. -`; +type AASConfigEntry = { + name: string; + entryRef: string; + enabled: BooleanLike; + modifiable: BooleanLike; + announcementLinesMap: Record; + generalTooltip: string; + varsAndTooltipsMap: Record; +}; type Data = { - arrivalToggle: BooleanLike; - arrival: string; - newheadToggle: BooleanLike; - newhead: string; - node_toggle: BooleanLike; - node_message: string; + config_entries: AASConfigEntry[]; }; export const AutomatedAnnouncement = (props) => { const { act, data } = useBackend(); - const { - arrivalToggle, - arrival, - newheadToggle, - newhead, - node_toggle, - node_message, - } = data; + const { config_entries = [] } = data; + + const [search, setSearch] = useState(''); + + const isEntryMatch = (entry: AASConfigEntry, search: string) => { + if (!search) return true; + const { name, announcementLinesMap = [], varsAndTooltipsMap = [] } = entry; + switch (true) { + case name?.toLowerCase().includes(search?.toLowerCase()): + case Object.keys(announcementLinesMap).some((lineKey) => + lineKey.toLowerCase().includes(search?.toLowerCase()), + ): + case Object.values(announcementLinesMap).some((line) => + line.toLowerCase().includes(search?.toLowerCase()), + ): + case Object.keys(varsAndTooltipsMap).some((varName) => + varName.toLowerCase().includes(search?.toLowerCase()), + ): + return true; + default: + return false; + } + }; + + const errorMessage = !config_entries.length + ? 'No configurable options detected! Please report this to Nanotrasen.' + : 'No match. Refine your search.'; + + const sorted = sortBy( + filter(config_entries, (entry) => isEntryMatch(entry, search)), + (entry) => entry.name, + ); return ( - + -
act('ArrivalToggle')} - /> - } - > - - + + + + setSearch(value)} /> - } - > - - act('ArrivalText', { - newText: value, - }) - } - /> - - -
-
act('NewheadToggle')} - /> - } - > - - - } - > - - act('NewheadText', { - newText: value, - }) - } - /> - - -
-
act('node_toggle')} - /> - } - > - - - } - > - - act('node_message', { - newText: value, - }) - } - /> - - -
+ + + + + {!sorted.length ? ( + {errorMessage} + ) : ( +
+ {sorted.map((entry, index) => ( +
+ + + } + > + + {Object.entries(entry.announcementLinesMap).map( + ([lineKey, announcementLine]) => ( + + + {lineKey}: + + + + act('Text', { + entryRef: entry.entryRef, + lineKey, + newText: value, + }) + } + /> + + + ), + )} +
+
+ ))} +
+ )} +
+
);