diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index 5f8b10fd31..0f392a47da 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -84,7 +84,7 @@ if(..()) return var/mob/living/silicon/ai/AI = usr - AI.announcement() + AI.ai_announcement() /atom/movable/screen/ai/call_shuttle name = "Call Emergency Shuttle" diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 738a7ab17b..77cba9ec01 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -104,6 +104,8 @@ // TODO: Currently unused, needs port from TG. /// Station alert datum for showing alerts UI var/datum/station_alert/alert_control + /// Announcement UI + var/datum/ai_announcement/ai_announcement /// Lists possible spoken words for announcements var/datum/announcement_help/announcement_help ///remember AI's last location @@ -206,6 +208,7 @@ QDEL_NULL(doomsday_device) QDEL_NULL(robot_control) // QDEL_NULL(alert_control) + QDEL_NULL(ai_announcement) QDEL_NULL(announcement_help) QDEL_NULL(aiMulti) QDEL_NULL(aiPDA) diff --git a/code/modules/mob/living/silicon/ai/ai_announcement.dm b/code/modules/mob/living/silicon/ai/ai_announcement.dm new file mode 100644 index 0000000000..123e6687f6 --- /dev/null +++ b/code/modules/mob/living/silicon/ai/ai_announcement.dm @@ -0,0 +1,55 @@ +#ifdef AI_VOX +/mob/living/silicon/ai/verb/ai_announcement() + set name = "Vox Announcement" + set desc = "Make an audible announcement!" + set category = "AI Commands" + + if(incapacitated()) + return + + if(!ai_announcement) + ai_announcement = new(src) + + ai_announcement.ui_interact(src) + +/datum/ai_announcement + var/mob/living/silicon/ai/owner + +/datum/ai_announcement/New(mob/living/silicon/ai/new_owner) + if(!istype(new_owner)) + qdel(src) + owner = new_owner + +/datum/ai_announcement/ui_status(mob/living/silicon/ai/user) + if(owner == user && !owner.incapacitated()) + return ..() + return UI_CLOSE + +/datum/ai_announcement/ui_state(mob/user) + return GLOB.always_state + +/datum/ai_announcement/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AIAnnouncement") + ui.open() + +/datum/ai_announcement/ui_static_data(mob/user) + var/list/data = ..() + data["last_announcement"] = owner.last_announcement + data["vox_types"] = GLOB.vox_types + return data + +/datum/ai_announcement/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + switch(action) + if("announce") + var/vox_type = params["vox_type"] + if(!vox_type) + return + var/to_speak = params["to_speak"] + if(!to_speak) + return + owner.announcement(vox_type, to_speak) + ui.close() +#endif diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 6cb3af5363..2a05266587 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -79,27 +79,23 @@ // Make sure that the code compiles with AI_VOX undefined #ifdef AI_VOX -#define VOX_DELAY 600 -/mob/living/silicon/ai/proc/announcement() +#define VOX_DELAY 1 MINUTES +/mob/living/silicon/ai/proc/announcement(voxType = "Female", message) var/static/announcing_vox = 0 // Stores the time of the last announcement if(announcing_vox > world.time) - to_chat(src, "Please wait [DisplayTimeText(announcing_vox - world.time)].") + to_chat(src, span_notice("Please wait [DisplayTimeText(announcing_vox - world.time)].")) return - var/message = input(src, "WARNING: Misuse of this verb can result in you being job banned. More help is available in 'Announcement Help'", "Announcement", src.last_announcement) as text - last_announcement = message - var/voxType = input(src, "Male or female VOX?", "VOX-gender") in GLOB.vox_types - - if(!message || announcing_vox > world.time) + if(!message || !GLOB.vox_types[voxType]) return if(incapacitated()) return if(control_disabled) - to_chat(src, "Wireless interface disabled, unable to interact with announcement PA.") + to_chat(src, span_warning("Wireless interface disabled, unable to interact with announcement PA.")) return var/list/words = splittext(trim(message), " ") @@ -117,7 +113,7 @@ incorrect_words += word if(incorrect_words.len) - to_chat(src, "These words are not available on the announcement system: [english_list(incorrect_words)].") + to_chat(src, span_notice("These words are not available on the announcement system: [english_list(incorrect_words)].")) return announcing_vox = world.time + VOX_DELAY diff --git a/tgstation.dme b/tgstation.dme index f0a2de7c7d..e604aa4b4d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2830,6 +2830,7 @@ #include "code\modules\mob\living\silicon\silicon_defense.dm" #include "code\modules\mob\living\silicon\silicon_movement.dm" #include "code\modules\mob\living\silicon\ai\ai.dm" +#include "code\modules\mob\living\silicon\ai\ai_announcement.dm" #include "code\modules\mob\living\silicon\ai\ai_defense.dm" #include "code\modules\mob\living\silicon\ai\ai_portrait_picker.dm" #include "code\modules\mob\living\silicon\ai\announcement_help.dm" diff --git a/tgui/packages/tgui/interfaces/AIAnnouncement.js b/tgui/packages/tgui/interfaces/AIAnnouncement.js new file mode 100644 index 0000000000..9545a677d1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AIAnnouncement.js @@ -0,0 +1,88 @@ +import { filter } from 'common/collections'; +import { useBackend } from '../backend'; +import { Button, Icon, Input, Section, Stack, Tabs } from '../components'; +import { useLocalState } from '../backend'; +import { Window } from '../layouts'; + +export const AIAnnouncement = (props, context) => { + const { act, data } = useBackend(context); + const { + last_announcement, + vox_types = {}, + } = data; + + const [ + current_page, + set_page, + ] = useLocalState(context, 'current_page', 0); + + const [ + announcement_input, + set_announcement_input, + ] = useLocalState(context, 'announcement_input', last_announcement); + + // I love `Object`s!! + const words_filtered = Object.keys(vox_types[Object.keys(vox_types)[current_page]]); + + const search_split = announcement_input.split(" ").filter(element => element); + + const missing_words = search_split.map(element => words_filtered.includes(element) ? null : element).filter(element => element); + + return ( + + +
+ + + WARNING: Misuse of this verb can result in you being job banned. More help is available in 'Announcement Help' + + + + { + Object.keys(vox_types).map((vox_type, index) => ( + set_page(index)} + selected={current_page === index}> + {vox_type} + + )) + } + + + + + + set_announcement_input(value)} + onEnter={(e, value) => act("announce", { + "vox_type": Object.keys(vox_types)[current_page], + "to_speak": value, + })} + /> + + +
+
+
+ ); +};