From 912ca1e374c8c3394257403888384b48cc1b5404 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Tue, 21 May 2024 00:06:26 -0300 Subject: [PATCH 1/4] poosh --- code/modules/client/client_procs.dm | 9 +- .../integrated_electronics/subtypes/output.dm | 13 ++- code/modules/mob/living/silicon/ai/ai.dm | 3 + .../living/silicon/ai/announcement_help.dm | 54 ++++++++++++ code/modules/mob/living/silicon/ai/say.dm | 50 ++--------- .../mob/living/silicon/ai/vox_sounds.dm | 21 +++-- tgstation.dme | 1 + .../tgui/interfaces/AnnouncementHelp.js | 88 +++++++++++++++++++ 8 files changed, 178 insertions(+), 61 deletions(-) create mode 100644 code/modules/mob/living/silicon/ai/announcement_help.dm create mode 100644 tgui/packages/tgui/interfaces/AnnouncementHelp.js diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 0413502edc..4eb836dab5 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -974,10 +974,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( addtimer(CALLBACK(SSassets.transport, TYPE_PROC_REF(/datum/asset_transport, send_assets_slow), src, SSassets.transport.preload), 5 SECONDS) #if (PRELOAD_RSC == 0) - for (var/name in GLOB.vox_sounds) - var/file = GLOB.vox_sounds[name] - Export("##action=load_rsc", file) - stoplag() + for (var/type in GLOB.vox_types) + for(var/word in GLOB.vox_types[type]) + var/file = GLOB.vox_types[type][word] + Export("##action=load_rsc", file) + stoplag() #endif diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index 9fa816e0d0..518672aed1 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -226,15 +226,22 @@ spawn_flags = IC_SPAWN_RESEARCH /obj/item/integrated_circuit/output/sound/vox - name = "ai vox sound circuit" + name = "Female ai vox sound circuit" desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit is often found in AI announcement systems." spawn_flags = IC_SPAWN_RESEARCH + var/voice_type = "Female" /obj/item/integrated_circuit/output/sound/vox/Initialize(mapload) - .= ..() - sounds = GLOB.vox_sounds + . = ..() + sounds = GLOB.vox_types[voice_type] extended_desc = "The first input pin determines which sound is used. It uses the AI Vox Broadcast word list. So either experiment to find words that work, or ask the AI to help in figuring them out. The second pin determines the volume of sound that is played, and the third determines if the frequency of the sound will vary with each activation." +/obj/item/integrated_circuit/output/sound/vox/male + name = "Male ai vox sound circuit" + desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit is often found in AI announcement systems." + spawn_flags = IC_SPAWN_RESEARCH + voice_type = "Male" + /obj/item/integrated_circuit/output/text_to_speech name = "text-to-speech circuit" desc = "Takes any string as an input and will make the device say the string when pulsed." diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 2c8e3164a9..738a7ab17b 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 + /// Lists possible spoken words for announcements + var/datum/announcement_help/announcement_help ///remember AI's last location var/atom/lastloc interaction_range = INFINITY @@ -204,6 +206,7 @@ QDEL_NULL(doomsday_device) QDEL_NULL(robot_control) // QDEL_NULL(alert_control) + QDEL_NULL(announcement_help) QDEL_NULL(aiMulti) QDEL_NULL(aiPDA) malfhack = null diff --git a/code/modules/mob/living/silicon/ai/announcement_help.dm b/code/modules/mob/living/silicon/ai/announcement_help.dm new file mode 100644 index 0000000000..f22b3ce4cb --- /dev/null +++ b/code/modules/mob/living/silicon/ai/announcement_help.dm @@ -0,0 +1,54 @@ +#ifdef AI_VOX +/mob/living/silicon/ai/verb/announcement_help() + + set name = "Announcement Help" + set desc = "Display a list of vocal words to announce to the crew." + set category = "AI Commands" + + if(incapacitated()) + return + + if(!announcement_help) + announcement_help = new(src) + + announcement_help.ui_interact(src) + +/datum/announcement_help + var/mob/living/silicon/ai/owner + +/datum/announcement_help/New(mob/living/silicon/ai/new_owner) + if(!istype(new_owner)) + qdel(src) + owner = new_owner + +/datum/announcement_help/ui_status(mob/living/silicon/ai/user) + if(owner == user && !owner.incapacitated()) + return ..() + return UI_CLOSE + +/datum/announcement_help/ui_state(mob/user) + return GLOB.always_state + +/datum/announcement_help/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AnnouncementHelp") + ui.open() + +/datum/announcement_help/ui_static_data(mob/user) + var/list/data = ..() + data["vox_types"] = GLOB.vox_types + return data + +/datum/announcement_help/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + switch(action) + if("say_word") + var/vox_type = params["vox_type"] + if(!vox_type) + return + var/to_speak = params["to_speak"] + if(!to_speak) + return + play_vox_word(to_speak, null, owner, vox_type) +#endif diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index e23eb842ea..6cb3af5363 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -80,38 +80,6 @@ // Make sure that the code compiles with AI_VOX undefined #ifdef AI_VOX #define VOX_DELAY 600 -/mob/living/silicon/ai/verb/announcement_help() - - set name = "Announcement Help" - set desc = "Display a list of vocal words to announce to the crew." - set category = "AI Commands" - - if(incapacitated()) - return - - var/dat = {" - WARNING: Misuse of the announcement system will get you job banned.

- Here is a list of words you can type into the 'Announcement' button to create sentences to vocally announce to everyone on the same level at you.
-
- "} - - var/index = 0 - for(var/word in GLOB.vox_sounds) - index++ - dat += "[capitalize(word)]" - if(index != GLOB.vox_sounds.len) - dat += " / " - - var/datum/browser/popup = new(src, "announce_help", "Announcement Help", 500, 400) - popup.set_content(dat) - popup.open() - - /mob/living/silicon/ai/proc/announcement() var/static/announcing_vox = 0 // Stores the time of the last announcement if(announcing_vox > world.time) @@ -122,7 +90,7 @@ last_announcement = message - var/voxType = input(src, "Male or female VOX?", "VOX-gender") in list("male", "female") + var/voxType = input(src, "Male or female VOX?", "VOX-gender") in GLOB.vox_types if(!message || announcing_vox > world.time) return @@ -145,9 +113,7 @@ if(!word) words -= word continue - if(!GLOB.vox_sounds[word] && voxType == "female") - incorrect_words += word - if(!GLOB.vox_sounds_male[word] && voxType == "male") + if(!GLOB.vox_types[voxType][word]) incorrect_words += word if(incorrect_words.len) @@ -162,18 +128,12 @@ play_vox_word(word, src.z, null, voxType) -/proc/play_vox_word(word, z_level, mob/only_listener, voxType = "female") +/proc/play_vox_word(word, z_level, mob/only_listener, voxType = "Female") word = lowertext(word) - if( (GLOB.vox_sounds[word] && voxType == "female") || (GLOB.vox_sounds_male[word] && voxType == "male") ) - - var/sound_file - - if(voxType == "female") - sound_file = GLOB.vox_sounds[word] - else - sound_file = GLOB.vox_sounds_male[word] + var/sound_file = LAZYACCESSASSOC(GLOB.vox_types, voxType, word) + if(sound_file) var/sound/voice = sound(sound_file, wait = 1, channel = CHANNEL_VOX) voice.status = SOUND_STREAM diff --git a/code/modules/mob/living/silicon/ai/vox_sounds.dm b/code/modules/mob/living/silicon/ai/vox_sounds.dm index 057561562b..8959d49e9a 100644 --- a/code/modules/mob/living/silicon/ai/vox_sounds.dm +++ b/code/modules/mob/living/silicon/ai/vox_sounds.dm @@ -5,9 +5,11 @@ // Regex for collecting a list of ogg files // (([a-zA-Z,.]+)\.ogg) -// For vim -// :%s/\(\(.*\)\.ogg\)/"\2" = 'sound\/vox_fem\/\1',/g -GLOBAL_LIST_INIT(vox_sounds, list("abduction" = 'sound/vox_fem/abduction.ogg', +GLOBAL_LIST_INIT(vox_types, list( + // For vim + // :%s/\(\(.*\)\.ogg\)/"\2" = 'sound\/vox_fem\/\1',/g + "Female" = list( +"abduction" = 'sound/vox_fem/abduction.ogg', "abortions" = 'sound/vox_fem/abortions.ogg', "above" = 'sound/vox_fem/above.ogg', "abstain" = 'sound/vox_fem/abstain.ogg', @@ -974,11 +976,11 @@ GLOBAL_LIST_INIT(vox_sounds, list("abduction" = 'sound/vox_fem/abduction.ogg', "z" = 'sound/vox_fem/z.ogg', "zombie" = 'sound/vox_fem/zombie.ogg', "zone" = 'sound/vox_fem/zone.ogg', -"zulu" = 'sound/vox_fem/zulu.ogg')) - -//for vim -// :%s/\(\(.*\)\.ogg\)/"\2" = 'sound\/vox\/\1',/g -GLOBAL_LIST_INIT(vox_sounds_male, list("," = 'sound/vox/_comma.ogg', +"zulu" = 'sound/vox_fem/zulu.ogg'), + //for vim + // :%s/\(\(.*\)\.ogg\)/"\2" = 'sound\/vox\/\1',/g + "Male" = list( +"," = 'sound/vox/_comma.ogg', "." = 'sound/vox/_period.ogg', "a" = 'sound/vox/a.ogg', "accelerating" = 'sound/vox/accelerating.ogg', @@ -1605,5 +1607,6 @@ GLOBAL_LIST_INIT(vox_sounds_male, list("," = 'sound/vox/_comma.ogg', "yourself" = 'sound/vox/yourself.ogg', "zero" = 'sound/vox/zero.ogg', "zone" = 'sound/vox/zone.ogg', -"zulu" = 'sound/vox/zulu.ogg',)) +"zulu" = 'sound/vox/zulu.ogg', +))) #endif diff --git a/tgstation.dme b/tgstation.dme index a201f09456..f0a2de7c7d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2832,6 +2832,7 @@ #include "code\modules\mob\living\silicon\ai\ai.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" #include "code\modules\mob\living\silicon\ai\death.dm" #include "code\modules\mob\living\silicon\ai\emote.dm" #include "code\modules\mob\living\silicon\ai\examine.dm" diff --git a/tgui/packages/tgui/interfaces/AnnouncementHelp.js b/tgui/packages/tgui/interfaces/AnnouncementHelp.js new file mode 100644 index 0000000000..671e1f2b7a --- /dev/null +++ b/tgui/packages/tgui/interfaces/AnnouncementHelp.js @@ -0,0 +1,88 @@ +import { filter } from 'common/collections'; +import { flow } from 'common/fp'; +import { createSearch } from 'common/string'; +import { useBackend } from '../backend'; +import { Button, Icon, Input, Section, Stack, Tabs } from '../components'; +import { useLocalState } from '../backend'; +import { Window } from '../layouts'; + +export const AnnouncementHelp = (props, context) => { + const { act, data } = useBackend(context); + const { vox_types = {} } = data; + + const [ + current_page, + set_page, + ] = useLocalState(context, 'current_page', 0); + + const [ + search_text, + set_search_text, + ] = useLocalState(context, 'search_text', ''); + + // I love `Object`s!! + const words_filtered = prepare_search(Object.keys(vox_types[Object.keys(vox_types)[current_page]]), search_text); + + return ( + + +
+ + + + { + Object.keys(vox_types).map((vox_type, index) => ( + set_page(index)} + selected={current_page === index}> + {vox_type} + + )) + } + + + + + + + + + set_search_text(value)} + /> + + + + + {words_filtered.map(nestedKey => ( + + ))} + + +
+
+
+ ); +}; + +export const prepare_search = (words_filtered, search_text = '') => { + const search = createSearch(search_text, + word_soup => word_soup); + return flow([ + search_text && filter(search), + ])(words_filtered); +}; From dcc2126b919383855a8ee70a75ae073c850a3c5f Mon Sep 17 00:00:00 2001 From: SandPoot Date: Tue, 21 May 2024 15:18:00 -0300 Subject: [PATCH 2/4] include help text --- tgui/packages/tgui/interfaces/AnnouncementHelp.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tgui/packages/tgui/interfaces/AnnouncementHelp.js b/tgui/packages/tgui/interfaces/AnnouncementHelp.js index 671e1f2b7a..1869a21eab 100644 --- a/tgui/packages/tgui/interfaces/AnnouncementHelp.js +++ b/tgui/packages/tgui/interfaces/AnnouncementHelp.js @@ -2,7 +2,7 @@ import { filter } from 'common/collections'; import { flow } from 'common/fp'; import { createSearch } from 'common/string'; import { useBackend } from '../backend'; -import { Button, Icon, Input, Section, Stack, Tabs } from '../components'; +import { Button, Collapsible, Icon, Input, Section, Stack, Tabs } from '../components'; import { useLocalState } from '../backend'; import { Window } from '../layouts'; @@ -32,6 +32,18 @@ export const AnnouncementHelp = (props, context) => {
+ + + WARNING: Misuse of the announcement system will get you job banned.

+ Here is a list of words you can type into the 'Announcement' button to create sentences to vocally announce to everyone on the same level at you.
+
  • You can also click on the word to PREVIEW it.
  • +
  • You can only say 30 words for every announcement.
  • +
  • Do not use punctuation as you would normally, if you want a pause you can use the full stop and comma characters by separating them with spaces, like so: 'Alpha . Test , Bravo'.
  • +
  • Numbers are in word format, e.g. eight, sixty, etc
  • +
  • Sound effects begin with an 's' before the actual word, e.g. scensor
  • +

+
+
{ From 04c8919e75400517f05706e0b3d4632b532d3487 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Tue, 21 May 2024 18:25:43 -0300 Subject: [PATCH 3/4] announcement side --- code/_onclick/hud/ai.dm | 2 +- code/modules/mob/living/silicon/ai/ai.dm | 3 + .../mob/living/silicon/ai/ai_announcement.dm | 55 ++++++++++++ code/modules/mob/living/silicon/ai/say.dm | 16 ++-- tgstation.dme | 1 + .../tgui/interfaces/AIAnnouncement.js | 88 +++++++++++++++++++ 6 files changed, 154 insertions(+), 11 deletions(-) create mode 100644 code/modules/mob/living/silicon/ai/ai_announcement.dm create mode 100644 tgui/packages/tgui/interfaces/AIAnnouncement.js 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, + })} + /> + + +
+
+
+ ); +}; From d73f32f51aa8a82a47f1d41a132a819fda931296 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Tue, 21 May 2024 18:31:40 -0300 Subject: [PATCH 4/4] NOT static --- code/modules/mob/living/silicon/ai/ai_announcement.dm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/living/silicon/ai/ai_announcement.dm b/code/modules/mob/living/silicon/ai/ai_announcement.dm index 123e6687f6..4165dec6bd 100644 --- a/code/modules/mob/living/silicon/ai/ai_announcement.dm +++ b/code/modules/mob/living/silicon/ai/ai_announcement.dm @@ -34,9 +34,13 @@ ui = new(user, src, "AIAnnouncement") ui.open() -/datum/ai_announcement/ui_static_data(mob/user) +/datum/ai_announcement/ui_data(mob/user) var/list/data = ..() data["last_announcement"] = owner.last_announcement + return data + +/datum/ai_announcement/ui_static_data(mob/user) + var/list/data = ..() data["vox_types"] = GLOB.vox_types return data