diff --git a/config/bandastation/bandastation_config.txt b/config/bandastation/bandastation_config.txt index bb67611772896..eeb290cb866c2 100644 --- a/config/bandastation/bandastation_config.txt +++ b/config/bandastation/bandastation_config.txt @@ -1,4 +1,5 @@ ## Text-to-speech #TTS_TOKEN_SILERO mytoken #TTS_ENABLED -#TTS_CACHE +#TTS_CACHE_ENABLED +#TTS_API_URL_SILERO diff --git a/config/bandastation/tts_replacements.json b/config/bandastation/tts_replacements.json new file mode 100644 index 0000000000000..d62e9b1a7be9d --- /dev/null +++ b/config/bandastation/tts_replacements.json @@ -0,0 +1,180 @@ +{ + "tts_acronym_replacements": { + "нт": "Эн Тэ", + "смо": "Эс Мэ О", + "гп": "Гэ Пэ", + "рд": "Эр Дэ", + "гсб": "Гэ Эс Бэ", + "срп": "Эс Эр Пэ", + "цк": "Цэ Каа", + "рнд": "Эр Эн Дэ", + "сб": "Эс Бэ", + "рцд": "Эр Цэ Дэ", + "брпд": "Бэ Эр Пэ Дэ", + "рпд": "Эр Пэ Дэ", + "рпед": "Эр Пед", + "тсф": "Тэ Эс Эф", + "срт": "Эс Эр Тэ", + "обр": "О Бэ Эр", + "кпк": "Кэ Пэ Каа", + "пда": "Пэ Дэ А", + "id": "Ай Ди", + "мщ": "Эм Ще", + "вт": "Вэ Тэ", + "ерп": "Йе Эр Пэ", + "се": "Эс Йе", + "апц": "А Пэ Цэ", + "лкп": "Эл Ка Пэ", + "см": "Эс Эм", + "ека": "Йе Ка", + "ка": "Кэ А", + "бса": "Бэ Эс Аа", + "днк": "Дэ Эн Ка", + "тк": "Тэ Ка", + "бфл": "Бэ Эф Эл", + "бщ": "Бэ Щэ", + "кк": "Кэ Ка", + "ск": "Эс Ка", + "зк": "Зэ Ка", + "ерт": "Йе Эр Тэ", + "вкд": "Вэ Ка Дэ", + "нтр": "Эн Тэ Эр", + "пнт": "Пэ Эн Тэ", + "авд": "А Вэ Дэ", + "пнв": "Пэ Эн Вэ", + "ссд": "Эс Эс Дэ", + "кпб": "Кэ Пэ Бэ", + "сссп": "Эс Эс Эс Пэ", + "крб": "Ка Эр Бэ", + "бд": "Бэ Дэ", + "сст": "Эс Эс Тэ", + "скс": "Эс Ка Эс", + "икн": "И Ка Эн", + "нсс": "Эн Эс Эс", + "емп": "Йе Эм Пэ", + "бс": "Бэ Эс", + "цкс": "Цэ Ка Эс", + "срд": "Эс Эр Дэ", + "жпс": "Джи Пи Эс", + "gps": "Джи Пи Эс", + "ннксс": "Эн Эн Ка Эс Эс", + "ss": "Эс Эс", + "сс": "Эс Эс", + "тесла": "тэсла", + "трейзен": "трэйзэн", + "нанотрейзен": "нанотрэйзэн", + "мед": "м ед", + "меде": "м еде", + "кз": "Кэ Зэ", + "гбс": "Гэ Бэ Эс", + "цпсс": "Цэ Пэ Эс Эс", + "гкк": "Гэ Кэ Ка" + }, + "tts_job_replacements": { + "nanotrasen navy field officer": "Полевой офицер флота Нанотрэйзен", + "nanotrasen navy officer": "Офицер флота nanotrasen", + "supreme commander": "Верховный главнокомандующий", + "solar federation general": "Генерал Солнечной Федерации", + "special operations officer": "Офицер специальных операций", + "civilian": "Гражданский", + "tourist": "Турист", + "businessman": "Бизнэсмэн", + "trader": "Торговец", + "assistant": "Ассистент", + "chief engineer": "Главный Инженер", + "station engineer": "Станционный инженер", + "trainee engineer": "Инженер-стажер", + "Engineer Assistant": "Инженерный Ассистент", + "Technical Assistant": "Технический Ассистент", + "Engineer Student": "Инженер-практикант", + "Technical Student": "Техник-практикант", + "Technical Trainee": "Техник-стажер", + "maintenance technician": "Техник по обслуживанию", + "engine technician": "Техник по двигателям", + "electrician": "Электрик", + "life support specialist": "Специалист по жизнеобеспечению", + "atmospheric technician": "Атмосферный техник", + "mechanic": "Механик", + "chief medical officer": "Главный врач", + "medical doctor": "Врач", + "Intern": "Интерн", + "Student Medical Doctor": "Врач-практикант", + "Medical Assistant": "Ассистирующий врач", + "surgeon": "Хирург", + "nurse": "Медсестра", + "coroner": "К+оронэр", + "chemist": "Химик", + "pharmacist": "Фармацевт", + "pharmacologist": "Фармаколог", + "geneticist": "Генетик", + "virologist": "Вирусолог", + "pathologist": "Патологоанатом", + "microbiologist": "Микробиолог", + "psychiatrist": "Психиатр", + "psychologist": "Психолог", + "therapist": "Терапевт", + "paramedic": "Парамедик", + "research director": "Директор исследований", + "scientist": "Учёный", + "student scientist": "Учёный-практикант", + "Scientist Assistant": "Научный Ассистент", + "Scientist Pregraduate": "Учёный-бакалавр", + "Scientist Graduate": "Научный выпускник", + "Scientist Postgraduate": "Учёный-аспирант", + "anomalist": "Аномалист", + "plasma researcher": "Исследователь плазмы", + "xenobiologist": "Ксенобиолог", + "chemical researcher": "Химик-исследователь", + "roboticist": "Робототехник", + "student robotist": "Студент-робототехник", + "biomechanical engineer": "Биомеханический инженер", + "mechatronic engineer": "Инженер мехатроники", + "head of security": "Глава службы безопасности", + "warden": "Смотритель", + "detective": "Детектив", + "forensic technician": "Криминалист", + "junior security officer": "Младший офицер службы безопасности", + "security officer": "Офицер службы безопасности", + "security trainer": "Тренер службы безопасности", + "security cadet": "Кадет службы безопасности", + "Security Assistant": "Ассистент службы безопасности", + "Security Graduate": "Выпускник кадетской академии", + "brig physician": "Врач брига", + "security pod pilot": "Пилот пода службы безопасности", + "captain": "Капитан", + "ai": "И И", + "cyborg": "Киборг", + "robot": "Робот", + "head of personnel": "Глава персонала", + "nanotrasen representative": "Представитель Нанотрэйзен", + "blueshield": "Блюшилд", + "magistrate": "Магистрат", + "internal affairs agent": "Агент внутренних дел", + "human resources agent": "Агент по персоналу", + "bartender": "Бармэн", + "chef": "Повар", + "cook": "Кук", + "culinary artist": "Кулинар", + "butcher": "Мясник", + "botanist": "Ботаник", + "hydroponicist": "Гидропонист", + "botanical researcher": "Ботаник-исследователь", + "quartermaster": "Квартирмейстер", + "cargo technician": "Карго техник", + "shaft miner": "Шахтёр", + "spelunker": "Спелеолог", + "clown": "Клоун", + "mime": "Мим", + "janitor": "Уборщик", + "custodial technician": "Техник по уходу за помещениями", + "librarian": "Библиотекарь", + "journalist": "Журналист", + "barber": "Парикмахер", + "hair stylist": "Стилист", + "beautician": "Косметолог", + "explorer": "Исследователь", + "chaplain": "Священник", + "syndicate officer": "Офицер синдиката", + "visitor": "посетитель" + } +} diff --git a/dependencies.sh b/dependencies.sh index 087c337a0461f..fcb48eefcc31b 100644 --- a/dependencies.sh +++ b/dependencies.sh @@ -9,6 +9,7 @@ export BYOND_MINOR=1633 #rust_g git tag export RUST_G_VERSION=3.1.0 +export RUST_G_VERSION_SS220=3.0.0-ss220 #node version export NODE_VERSION=20 diff --git a/modular_bandastation/_defines220/_defines220.dme b/modular_bandastation/_defines220/_defines220.dme index 2e4cf7f6c8a23..8bc961b39df80 100644 --- a/modular_bandastation/_defines220/_defines220.dme +++ b/modular_bandastation/_defines220/_defines220.dme @@ -1,7 +1,10 @@ #include "_defines220.dm" +#include "code/signals_atom.dm" #include "code/defines/keybindings.dm" +#include "code/defines/misc.dm" #include "code/defines/spans.dm" +#include "code/defines/text_to_speech.dm" #include "code/signals_mob/signals_mob_ai.dm" #include "code/signals_mob/signals_mob_carbon.dm" #include "code/signals_mob/signals_mob_living.dm" diff --git a/modular_bandastation/_defines220/code/defines/misc.dm b/modular_bandastation/_defines220/code/defines/misc.dm new file mode 100644 index 0000000000000..84efa510030c4 --- /dev/null +++ b/modular_bandastation/_defines220/code/defines/misc.dm @@ -0,0 +1,6 @@ +///Do (almost) nothing - indev placeholder for switch case implementations etc +#define NOOP (.=.) +/// Copies the L from element START to elememt END if L is initialized, otherwise returns an empty list. +#define LAZYCOPY_RANGE(L, START, END) ( L ? L.Copy(START, END) : list() ) +/// Cuts the L from element START to elememt END if L is initialized, otherwise returns an empty list. +#define LAZYCUT(L, START, END) ( L ? L.Cut(START, END) : NOOP ) diff --git a/modular_bandastation/tts/code/_tts_defines.dm b/modular_bandastation/_defines220/code/defines/text_to_speech.dm similarity index 64% rename from modular_bandastation/tts/code/_tts_defines.dm rename to modular_bandastation/_defines220/code/defines/text_to_speech.dm index 9d358f17ae50d..8d56e1734a006 100644 --- a/modular_bandastation/tts/code/_tts_defines.dm +++ b/modular_bandastation/_defines220/code/defines/text_to_speech.dm @@ -1,18 +1,11 @@ #define VV_HK_SELECT_TTS_VOICE "select_tts_voice" -#define SOUND_EFFECT_NONE 0 -#define SOUND_EFFECT_RADIO 1 -#define SOUND_EFFECT_ROBOT 2 -#define SOUND_EFFECT_RADIO_ROBOT 3 -#define SOUND_EFFECT_MEGAPHONE 4 -#define SOUND_EFFECT_MEGAPHONE_ROBOT 5 - -#define CHANNEL_TTS_RADIO 1008 - #define TTS_TRAIT_PITCH_WHISPER (1<<1) #define TTS_TRAIT_RATE_FASTER (1<<2) #define TTS_TRAIT_RATE_MEDIUM (1<<3) +#define TTS_TRAIT_ROBOTIZE "tts_trait_robotize" + #define TTS_CATEGORY_OTHER "Другое" #define TTS_CATEGORY_WARCRAFT3 "WarCraft 3" #define TTS_CATEGORY_HALFLIFE2 "Half-Life 2" @@ -34,6 +27,25 @@ #define TTS_CATEGORY_HEARTHSTONE "Hearthstone" #define TTS_CATEGORY_VALORANT "Valorant" #define TTS_CATEGORY_EVILISLANDS "Evil Islands" +#define TTS_CATEGORY_WITCHER "Witcher" +#define TTS_CATEGORY_LEFT4DEAD "Left 4 Dead" +#define TTS_CATEGORY_SPONGEBOB "SpongeBob" +#define TTS_CATEGORY_TINYBUNNY "Tiny Bunny" +#define TTS_CATEGORY_BALDURS_GATE_3 "Baldur's gate 3" +#define TTS_CATEGORY_PORTAL "Portal" +#define TTS_CATEGORY_TMNT "Teenage mutant ninja turtle" +#define TTS_CATEGORY_STAR_WARS "Star Wars" +#define TTS_CATEGORY_TRANSFORMERS "Transformers" +#define TTS_CATEGORY_LOTR "The Lord of the rings" +#define TTS_CATEGORY_SHREK "Shrek" +#define TTS_CATEGORY_POTC "Pirates of the Caribbean" +#define TTS_CATEGORY_HARRY_POTTER "Harry Potter" +#define TTS_CATEGORY_X3 "X3" +#define TTS_CATEGORY_OVERLORD2 "The Overlord 2" +#define TTS_CATEGORY_MARVEL "Marvel" +#define TTS_CATEGORY_WOW "World of Warcraft" +#define TTS_CATEGORY_TREASURE_ISLAND "Treasure Island" +#define TTS_CATEGORY_BOYS_WORD "Слово пацана" #define TTS_GENDER_ANY "Любой" #define TTS_GENDER_MALE "Мужской" @@ -55,10 +67,9 @@ "Капитан, вы уверены что хотите назначить клоуна на должность главы персонала?",\ ) -#define LOCAL_TTS_VOLUME(mob) mob.client.prefs.read_preference(/datum/preference/numeric/sound_tts_local) -#define RADIO_TTS_VOLUME(mob) mob.client.prefs.read_preference(/datum/preference/numeric/sound_tts_radio) -#define LOCAL_TTS_ENABLED(mob) LOCAL_TTS_VOLUME(mob) -#define RADIO_TTS_ENABLED(mob) RADIO_TTS_VOLUME(mob) +#define BIG_WORKER_TIER 220 +#define LITTLE_WORKER_TIER 110 -/proc/error(msg) - log_world("## ERROR: [msg]") +#define BIG_WORKER_TTS_LEVEL 3 +#define LITTLE_WORKER_TTS_LEVEL 1 +#define DONATOR_LEVEL_MAX 5 diff --git a/modular_bandastation/_defines220/code/signals_atom.dm b/modular_bandastation/_defines220/code/signals_atom.dm new file mode 100644 index 0000000000000..6cb23c4e1f485 --- /dev/null +++ b/modular_bandastation/_defines220/code/signals_atom.dm @@ -0,0 +1,8 @@ +///from base of atom/change_tts_seed(): (mob/chooser, override, new_traits) +#define COMSIG_ATOM_TTS_SEED_CHANGE "atom_tts_seed_change" +///from base of atom/cast_tts: (mob/listener, message, atom/location, is_local, effect, traits, preSFX, postSFX) +#define COMSIG_ATOM_TTS_CAST "atom_tts_cast" +///from base of atom/tts_trait_add(): (trait) +#define COMSIG_ATOM_TTS_TRAIT_ADD "atom_tts_trait_add" +///from base of atom/tts_trait_remove(): (trait) +#define COMSIG_ATOM_TTS_TRAIT_REMOVE "atom_tts_trait_remove" diff --git a/modular_bandastation/_helpers220/code/unsorted.dm b/modular_bandastation/_helpers220/code/unsorted.dm index efb9c8a55e788..e69de29bb2d1d 100644 --- a/modular_bandastation/_helpers220/code/unsorted.dm +++ b/modular_bandastation/_helpers220/code/unsorted.dm @@ -1,32 +0,0 @@ -/** Get all hearers in range, ignores walls and such. Code stolen from `/proc/get_hearers_in_view()` - * Much faster and less expensive than range() -*/ -/proc/get_hearers_in_range(range_radius, atom/source) - var/turf/center_turf = get_turf(source) - if(!center_turf) - return - - . = list() - var/old_luminosity = center_turf.luminosity - if(range_radius <= 0) //special case for if only source cares - for(var/atom/movable/target as anything in center_turf) - var/list/recursive_contents = target.important_recursive_contents?[RECURSIVE_CONTENTS_HEARING_SENSITIVE] - if(recursive_contents) - . += recursive_contents - return . - - var/list/hearables_from_grid = SSspatial_grid.orthogonal_range_search(source, RECURSIVE_CONTENTS_HEARING_SENSITIVE, range_radius) - - if(!length(hearables_from_grid))//we know that something is returned by the grid, but we dont know if we need to actually filter down the output - return . - - var/list/assigned_oranges_ears = SSspatial_grid.assign_oranges_ears(hearables_from_grid) - - for(var/mob/oranges_ear/ear in range(range_radius, center_turf)) - . += ear.references - - for(var/mob/oranges_ear/remaining_ear as anything in assigned_oranges_ears) //we need to clean up our mess - remaining_ear.unassign() - - center_turf.luminosity = old_luminosity - return . diff --git a/modular_bandastation/_modpacks.dm b/modular_bandastation/_modpacks.dm index 347fcd019b6b9..68a24b2f48e0a 100644 --- a/modular_bandastation/_modpacks.dm +++ b/modular_bandastation/_modpacks.dm @@ -30,6 +30,8 @@ SUBSYSTEM_DEF(modpacks) var/fail_msg = package.post_initialize() if(fail_msg) CRASH("Modpack [(istype(package) && package.name) || "Unknown"] failed to post-initialize: [fail_msg]") + + return SS_INIT_SUCCESS /client/verb/modpacks_list() set name = "Modpacks List" diff --git a/modular_bandastation/_singletons/_singletons.dm b/modular_bandastation/_singletons/_singletons.dm new file mode 100644 index 0000000000000..d8b23d11f1a63 --- /dev/null +++ b/modular_bandastation/_singletons/_singletons.dm @@ -0,0 +1,4 @@ +/datum/modpack/singletons + name = "Singletons" + desc = "Ports Singletons from Sierra" + author = "larentoun" diff --git a/modular_bandastation/_singletons/_singletons.dme b/modular_bandastation/_singletons/_singletons.dme new file mode 100644 index 0000000000000..324fefed37667 --- /dev/null +++ b/modular_bandastation/_singletons/_singletons.dme @@ -0,0 +1,5 @@ +#include "_singletons.dm" + +#include "code/_defines.dm" +#include "code/repository.dm" +#include "code/singletons.dm" diff --git a/modular_bandastation/_singletons/code/_defines.dm b/modular_bandastation/_singletons/code/_defines.dm new file mode 100644 index 0000000000000..fd4cc7e73d1d0 --- /dev/null +++ b/modular_bandastation/_singletons/code/_defines.dm @@ -0,0 +1,23 @@ +/* +* Performance behaviors for avoiding calling procs unecessarily on the Singletons global. +*/ + +/// Get a singleton instance according to path P. Creates it if necessary. Null if abstract or not a singleton. +#define GET_SINGLETON(P)\ + (ispath(P, /datum/singleton) ? (GLOB.Singletons.resolved_instances[P] ? GLOB.Singletons.instances[P] : GLOB.Singletons.GetInstance(P)) : null) + +/// Get a (path = instance) map of valid singletons according to typesof(P). +#define GET_SINGLETON_TYPE_MAP(P)\ + (ispath(P, /datum/singleton) ? (GLOB.Singletons.resolved_type_maps[P] ? GLOB.Singletons.type_maps[P] : GLOB.Singletons.GetTypeMap(P)) : list()) + +/// Get a (path = instance) map of valid singletons according to subtypesof(P). +#define GET_SINGLETON_SUBTYPE_MAP(P)\ + (ispath(P, /datum/singleton) ? (GLOB.Singletons.resolved_subtype_maps[P] ? GLOB.Singletons.subtype_maps[P] : GLOB.Singletons.GetSubtypeMap(P)) : list()) + +/// Get a list of valid singletons according to typesof(path). +#define GET_SINGLETON_TYPE_LIST(P)\ + (ispath(P, /datum/singleton) ? (GLOB.Singletons.resolved_type_lists[P] ? GLOB.Singletons.type_lists[P] : GLOB.Singletons.GetTypeList(P)) : list()) + +/// Get a list of valid singletons according to subtypesof(path). +#define GET_SINGLETON_SUBTYPE_LIST(P)\ + (ispath(P, /datum/singleton) ? (GLOB.Singletons.resolved_subtype_lists[P] ? GLOB.Singletons.subtype_lists[P] : GLOB.Singletons.GetSubtypeList(P)) : list()) diff --git a/modular_bandastation/_singletons/code/repository.dm b/modular_bandastation/_singletons/code/repository.dm new file mode 100644 index 0000000000000..007155342f2bc --- /dev/null +++ b/modular_bandastation/_singletons/code/repository.dm @@ -0,0 +1,147 @@ +/repository/New() + return + +/* +/datum/cache_entry + var/timestamp + var/data + +/datum/cache_entry/New() + timestamp = world.time + +/datum/cache_entry/proc/is_valid() + return FALSE + +/datum/cache_entry/valid_until/New(valid_duration) + ..() + timestamp += valid_duration + +/datum/cache_entry/valid_until/is_valid() + return world.time < timestamp +*/ + + +GLOBAL_DATUM_INIT(Singletons, /repository/singletons, new) + +/repository/singletons + /// A cache of individual singletons as (/singleton/path = Instance, ...) + var/list/instances = list() + + /// A map of (/singleton/path = TRUE, ...). Indicates whether a path has been tried for instances. + var/list/resolved_instances = list() + + /// A cache of singleton types according to a parent type as (/singleton/path = list(/singleton/path = Instance, /singleton/path/foo = Instance, ...)) + var/list/type_maps = list() + + /// A map of (/singleton/path = TRUE, ...). Indicates whether a path has been tried for type_maps. + var/list/resolved_type_maps = list() + + /// A cache of singleton subtypes according to a parent type as (/singleton/path = list(/singleton/path/foo = Instance, ...)) + var/list/subtype_maps = list() + + /// A map of (/singleton/path = TRUE, ...). Indicates whether a path has been tried for subtype_maps. + var/list/resolved_subtype_maps = list() + + /// A cache of singleton types according to a parent type as (/singleton/path = list(Parent Instance, Subtype Instance, ...)) + var/list/type_lists = list() + + /// A map of (/singleton/path = TRUE, ...). Indicates whether a path has been tried for type_lists. + var/list/resolved_type_lists = list() + + /// A cache of singleton subtypes according to a parent type as (/singleton/path = list(Subtype Instance, Subtype Instance, ...)) + var/list/subtype_lists = list() + + /// A map of (/singleton/path = TRUE, ...). Indicates whether a path has been tried for subtype_lists. + var/list/resolved_subtype_lists = list() + + +/** +* Get a singleton instance according to path. Creates it if necessary. Null if abstract or not a singleton. +* Prefer the GET_SINGLETON macro to minimize proc calls. +*/ +/repository/singletons/proc/GetInstance(datum/singleton/path) + if(!ispath(path, /datum/singleton)) + return + if(resolved_instances[path]) + return instances[path] + resolved_instances[path] = TRUE + if(path == initial(path.abstract_type)) + return + var/datum/singleton/result = new path + instances[path] = result + result.Initialize() + return result + + +/// Get a (path = instance) map of valid singletons according to paths. +/repository/singletons/proc/GetMap(list/datum/singleton/paths) + var/list/result = list() + for(var/path in paths) + var/datum/singleton/instance = GetInstance(path) + if (!instance) + continue + result[path] = instance + return result + + +/// Get a list of valid singletons according to paths. +/repository/singletons/proc/GetList(list/datum/singleton/paths) + var/list/result = list() + for(var/path in paths) + var/datum/singleton/instance = GetInstance(path) + if(!instance) + continue + result += instance + return result + + +/** +* Get a (path = instance) map of valid singletons according to typesof(path). +* Prefer the GET_SINGLETON_TYPE_MAP macro to minimize proc calls. +*/ +/repository/singletons/proc/GetTypeMap(datum/singleton/path) + if(resolved_type_maps[path]) + return type_maps[path] || list() + resolved_type_maps[path] = TRUE + var/result = GetMap(typesof(path)) + type_maps[path] = result + return result + + +/** +* Get a (path = instance) map of valid singletons according to subtypesof(path). +* Prefer the GET_SINGLETON_TYPE_MAP macro to minimize proc calls. +*/ +/repository/singletons/proc/GetSubtypeMap(datum/singleton/path) + if(resolved_subtype_maps[path]) + return subtype_maps[path] || list() + resolved_subtype_maps[path] = TRUE + var/result = GetMap(subtypesof(path)) + subtype_maps[path] = result + return result + + +/** +* Get a list of valid singletons according to typesof(path). +* Prefer the GET_SINGLETON_TYPE_LIST macro to minimize proc calls. +*/ +/repository/singletons/proc/GetTypeList(datum/singleton/path) + if(resolved_type_lists[path]) + return type_lists[path] || list() + resolved_type_lists[path] = TRUE + var/result = GetList(typesof(path)) + type_lists[path] = result + return result + + +/** +* Get a list of valid singletons according to subtypesof(path). +* Prefer the GET_SINGLETON_SUBTYPE_LIST macro to minimize proc calls. +*/ +/repository/singletons/proc/GetSubtypeList(datum/singleton/path) + if(resolved_subtype_lists[path]) + return subtype_lists[path] || list() + resolved_subtype_lists[path] = TRUE + var/result = GetList(subtypesof(path)) + subtype_lists[path] = result + return result diff --git a/modular_bandastation/_singletons/code/singletons.dm b/modular_bandastation/_singletons/code/singletons.dm new file mode 100644 index 0000000000000..ee0bf41fcfe57 --- /dev/null +++ b/modular_bandastation/_singletons/code/singletons.dm @@ -0,0 +1,11 @@ +/datum/singleton + var/abstract_type = /datum/singleton + +/datum/singleton/proc/Initialize() + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + +/datum/singleton/Destroy() + SHOULD_CALL_PARENT(FALSE) + . = QDEL_HINT_LETMELIVE + CRASH("Prevented attempt to delete a singleton instance: [src]") diff --git a/modular_bandastation/modular_bandastation.dme b/modular_bandastation/modular_bandastation.dme index 7540a5b77fb82..06823277ce3f3 100644 --- a/modular_bandastation/modular_bandastation.dme +++ b/modular_bandastation/modular_bandastation.dme @@ -2,7 +2,9 @@ #include "_modpacks.dm" #include "_defines220/_defines220.dme" +#include "_helpers220/_helpers220.dme" #include "_signals220/_signals220.dme" +#include "_singletons/_singletons.dme" #include "aesthetics/_aesthetics.dme" #include "ai_laws/_ai_laws.dme" #include "barsigns/_barsigns.dme" diff --git a/modular_bandastation/translations/code/restaurant_customer.dm b/modular_bandastation/translations/code/restaurant_customer.dm index 1d6e3ec1728f7..e27766263e818 100644 --- a/modular_bandastation/translations/code/restaurant_customer.dm +++ b/modular_bandastation/translations/code/restaurant_customer.dm @@ -1,6 +1,6 @@ /datum/customer_data speech_sound = null - var/list/tts_seeds = list("Arthas") + var/list/tts_seeds = /datum/tts_seed/silero/angel /datum/customer_data/american found_seat_lines = list("Я надеюсь тут найдется местечко, которое выдержит мой вес.", "Надеюсь, я могу проносить сюда оружие.", "Я надеюсь здесь подают тройной жир-бургер делюкс.", "Люблю я здешнюю культуру.") @@ -12,8 +12,7 @@ first_warning_line = "Не стоит со мной шутить!" second_warning_line = "Последнее предупреждение, чувак! Отвали!" self_defense_line = "РЕЖИМ ЖИРОМЕНТАЛЯ АКТИВИРОВАН!" - tts_seeds = list("Braum", "Malfurion", "Medivh", "Ozara", "Pudge") - + tts_seeds = list(/datum/tts_seed/silero/braum, /datum/tts_seed/silero/malfurion, /datum/tts_seed/silero/medivh, /datum/tts_seed/silero/ozara, /datum/tts_seed/silero/pudge) /datum/customer_data/italian prefix_file = "strings/names/italian_prefix.txt" @@ -28,8 +27,7 @@ first_warning_line = "Не трогай меня, мамма-мия!" second_warning_line = "Последнее предупреждение! Не прикасайся к моим спагетти!" self_defense_line = "Я замешу тебя так, как моя мамма замешивала свои фирменные фрикадельки!" - tts_seeds = list("Barbas", "Clockwerk", "Muradin", "Rasil") - + tts_seeds = list(/datum/tts_seed/silero/barbas, /datum/tts_seed/silero/clockwerk, /datum/tts_seed/silero/muradin, /datum/tts_seed/silero/rasil) /datum/customer_data/french prefix_file = "strings/names/french_prefix.txt" @@ -45,8 +43,7 @@ second_warning_line = "Не трогай меня, ты, грязное животное! Последнее предупреждение!" self_defense_line = "Я сломаю тебя словно багет!" speech_sound = null - tts_seeds = list("Archmage", "Awilo", "Belloc", "Priest") - + tts_seeds = list(/datum/tts_seed/silero/archmage, /datum/tts_seed/silero/awilo, /datum/tts_seed/silero/belloc, /datum/tts_seed/silero/priest) /datum/customer_data/japanese prefix_file = "strings/names/japanese_prefix.txt" @@ -62,8 +59,7 @@ second_warning_line = "Я стану супер-сайяном, если ты снова прикоснешься ко мне! Последнее предупреждение!" self_defense_line = "OMAE WA MO, SHINDEROU!" speech_sound = null - tts_seeds = list("Ahri", "Chromie", "Eudora", "Luna", "Qiyana") - + tts_seeds = list(/datum/tts_seed/silero/ahri, /datum/tts_seed/silero/chromie, /datum/tts_seed/silero/eudora, /datum/tts_seed/silero/luna, /datum/tts_seed/silero/qiyana) /datum/customer_data/japanese/salaryman clothing_sets = list("japanese_salary") @@ -77,8 +73,7 @@ second_warning_line = "Отстань от меня, я пытаюсь сконцентрироваться. Последнее предупреждение!" self_defense_line = "Я не хотел, чтобы это закончилось вот так." speech_sound = null - tts_seeds = list("Malkoran", "Narrator", "Overseer", "Rhombus") - + tts_seeds = list(/datum/tts_seed/silero/malkoran, /datum/tts_seed/silero/narrator, /datum/tts_seed/silero/overseer, /datum/tts_seed/silero/rhombus) /datum/customer_data/moth prefix_file = "strings/names/moth_prefix.txt" @@ -93,8 +88,7 @@ second_warning_line = "Последнее предупреждение! Я уничтожу тебя!" self_defense_line = "Крылатая атака!" speech_sound = null - tts_seeds = list("Ahri", "Chromie", "Eudora", "Luna", "Qiyana") - + tts_seeds = list(/datum/tts_seed/silero/ahri, /datum/tts_seed/silero/chromie, /datum/tts_seed/silero/eudora, /datum/tts_seed/silero/luna, /datum/tts_seed/silero/qiyana) /datum/customer_data/mexican base_icon_state = "mexican" @@ -110,8 +104,7 @@ second_warning_line = "Комрад, хватит значит хватит! Последнее предупреждение!" self_defense_line = "Пришло время тебе узнать, какой из меня робот. Готов?" speech_sound = null - tts_seeds = list("Barney", "Batrider", "Putricide", "Soldier") - + tts_seeds = list(/datum/tts_seed/silero/barney, /datum/tts_seed/silero/batrider, /datum/tts_seed/silero/putricide, /datum/tts_seed/silero/soldier) /datum/customer_data/british base_icon_state = "british" @@ -121,8 +114,7 @@ second_warning_line = "И мирно вернуться в свои жилища или к своим законным делам, приложив усилия, содержащиеся в акте, изданном королём Георгом в первый год своего правления для предотвращения бунтов и мятежей. Более предупреждений не будет." self_defense_line = "Боже, храни Королеву." speech_sound = null - tts_seeds = list("Ebony", "Ekko", "Emperor", "Loxley") - + tts_seeds = list(/datum/tts_seed/silero/ebony, /datum/tts_seed/silero/ekko, /datum/tts_seed/silero/emperor, /datum/tts_seed/silero/loxley) /datum/customer_data/british/gent clothing_sets = list("british_gentleman") @@ -156,7 +148,7 @@ second_warning_line = "Сломать-ты-так-сильно-ты-вспомнить-дни-до-этот-момент.exe: запуск..." self_defense_line = "Я был создан, чтобы делать две вещи: заказывать еду и ломать каждую кость в твоем теле." speech_sound = null - tts_seeds = list("Glados") + tts_seeds = list(/datum/tts_seed/silero/glados) /datum/venue/restaurant/order_food_line(order) var/obj/item/object_to_order = order @@ -182,4 +174,4 @@ . = ..() var/datum/customer_data/customer_info = SSrestaurant.all_customers[customer_data] if(customer_info) - tts_seed = pick(customer_info.tts_seeds) + AddComponent(/datum/component/tts_component, pick(customer_info.tts_seeds)) diff --git a/modular_bandastation/tts/_tts.dm b/modular_bandastation/tts/_tts.dm index bb5b70a3afb72..a230df0e0a4d0 100644 --- a/modular_bandastation/tts/_tts.dm +++ b/modular_bandastation/tts/_tts.dm @@ -1,4 +1,4 @@ /datum/modpack/tts - name = "TTS Silero" - desc = "Добавляет ТТС Силеро" + name = "Text-To-Speech" + desc = "2д космонавтики говорят вслух." author = "furior" diff --git a/modular_bandastation/tts/_tts.dme b/modular_bandastation/tts/_tts.dme index 64c2f24a682d4..a75ac9b353f48 100644 --- a/modular_bandastation/tts/_tts.dme +++ b/modular_bandastation/tts/_tts.dme @@ -1,25 +1,28 @@ #include "_tts.dm" -#include "code\_tts_defines.dm" -#include "code\_tts_rust_g_ss220.dm" -#include "code\tts_configuration.dm" -#include "code\tts_mob_Hear.dm" -#include "code\tts_numbers.dm" -#include "code\tts_preferences.dm" -#include "code\tts_provider.dm" -#include "code\tts_seed.dm" -#include "code\tts_sound.dm" -#include "code\tts_sound_TEMPORARY.dm" -#include "code\tts_subsystem.dm" -#include "code\base_seeds\mobs\_base.dm" -#include "code\base_seeds\mobs\alien.dm" -#include "code\base_seeds\mobs\friendly.dm" -#include "code\base_seeds\mobs\guardian.dm" -#include "code\base_seeds\mobs\hostile.dm" -#include "code\base_seeds\mobs\lavaland.dm" -#include "code\base_seeds\mobs\other.dm" -#include "code\base_seeds\mobs\pets.dm" -#include "code\base_seeds\objs\objs.dm" -#include "code\providers\silero.dm" -#include "code\seeds\silero.dm" -#include "code\~undefs\~tts_undefs.dm" +#include "code/_tts_rust_g_ss220.dm" +#include "code/hear.dm" +#include "code/numbers.dm" +#include "code/shell.dm" +#include "code/SSHttp.dm" +#include "code/tts_component.dm" +#include "code/tts_configuration.dm" +#include "code/tts_preferences.dm" +#include "code/tts_provider.dm" +#include "code/tts_seed.dm" +#include "code/tts_subsystem.dm" +#include "code/tts_vv.dm" +#include "code/base_seeds/mobs/_base.dm" +#include "code/base_seeds/mobs/alien.dm" +#include "code/base_seeds/mobs/friendly.dm" +#include "code/base_seeds/mobs/guardian.dm" +#include "code/base_seeds/mobs/hostile.dm" +#include "code/base_seeds/mobs/lavaland.dm" +#include "code/base_seeds/mobs/other.dm" +#include "code/base_seeds/mobs/pets.dm" +#include "code/base_seeds/objs/objs.dm" +#include "code/providers/silero.dm" +#include "code/seeds/base.dm" +#include "code/seeds/silero.dm" + +#include "code/~undefs/~undefs.dm" diff --git a/modular_bandastation/tts/code/SSHttp.dm b/modular_bandastation/tts/code/SSHttp.dm new file mode 100644 index 0000000000000..5cb9cb7564c28 --- /dev/null +++ b/modular_bandastation/tts/code/SSHttp.dm @@ -0,0 +1,86 @@ +SUBSYSTEM_DEF(http) + name = "HTTP" + flags = SS_TICKER | SS_BACKGROUND | SS_NO_INIT // Measure in ticks, but also only run if we have the spare CPU. + wait = 1 + runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY // All the time + // Assuming for the worst, since only discord is hooked into this for now, but that may change + /// List of all async HTTP requests in the processing chain + var/list/datum/http_request/active_async_requests = list() + /// Variable to define if logging is enabled or not. Disabled by default since we know the requests the server is making. Enable with VV if you need to debug requests + var/logging_enabled = FALSE + /// Total requests the SS has processed in a round + var/total_requests + +/datum/controller/subsystem/http/PreInit() + . = ..() + rustgss220_create_async_http_client() // Open the door + +/datum/controller/subsystem/http/fire(resumed) + for(var/r in active_async_requests) + var/datum/http_request/req = r + // Check if we are complete + if(req.is_complete()) + // If so, take it out the processing list + active_async_requests -= req + var/datum/http_response/res = req.into_response() + + // If the request has a callback, invoke it.Async of course to avoid choking the SS + if(req.cb) + req.cb.InvokeAsync(res) + + // And log the result + if(logging_enabled) + var/list/log_data = list() + log_data += "BEGIN ASYNC RESPONSE (ID: [req.id])" + if(res.errored) + log_data += "\t ----- RESPONSE ERRROR -----" + log_data += "\t [res.error]" + else + log_data += "\tResponse status code: [res.status_code]" + log_data += "\tResponse body: [res.body]" + log_data += "\tResponse headers: [json_encode(res.headers)]" + log_data += "END ASYNC RESPONSE (ID: [req.id])" + logger.Log(LOG_CATEGORY_DEBUG, log_data.Join("\n")) + +/** + * Async request creator + * + * Generates an async request, and adds it to the subsystem's processing list + * These should be used as they do not lock the entire DD process up as they execute inside their own thread pool inside RUSTG + */ +/datum/controller/subsystem/http/proc/create_async_request(method, url, body = "", list/headers, datum/callback/proc_callback) + var/datum/http_request/req = new() + req.prepare(method, url, body, headers) + if(proc_callback) + req.cb = proc_callback + + // Begin it and add it to the SS active list + req.begin_async() + active_async_requests += req + total_requests++ + + if(logging_enabled) + // Create a log holder + var/list/log_data = list() + log_data += "BEGIN ASYNC REQUEST (ID: [req.id])" + log_data += "\t[uppertext(req.method)] [req.url]" + log_data += "\tRequest body: [req.body]" + log_data += "\tRequest headers: [req.headers]" + log_data += "END ASYNC REQUEST (ID: [req.id])" + + // Write the log data + + logger.Log(LOG_CATEGORY_DEBUG, log_data.Join("\n")) + +/** + * Blocking request creator + * + * Generates a blocking request, executes it, logs the info then cleanly returns the response + * Exists as a proof of concept, and should never be used + */ +/datum/controller/subsystem/http/proc/make_blocking_request(method, url, body = "", list/headers) + CRASH("Attempted use of a blocking HTTP request") + +/datum/http_request + /// Callback for executing after async requests. Will be called with an argument of [/datum/http_response] as first argument + var/datum/callback/cb diff --git a/modular_bandastation/tts/code/_tts_rust_g_ss220.dm b/modular_bandastation/tts/code/_tts_rust_g_ss220.dm index 5a0cd3647b25d..54599608e3cb6 100644 --- a/modular_bandastation/tts/code/_tts_rust_g_ss220.dm +++ b/modular_bandastation/tts/code/_tts_rust_g_ss220.dm @@ -56,3 +56,6 @@ // Text Operations // #define rustgss220_cyrillic_to_latin(text) RUSTG_CALL(RUST_G_SS220, "cyrillic_to_latin")("[text]") #define rustgss220_latin_to_cyrillic(text) RUSTG_CALL(RUST_G_SS220, "latin_to_cyrillic")("[text]") + +/proc/rustgss220_create_async_http_client() return RUSTG_CALL(RUST_G_SS220, "start_http_client")() +/proc/rustgss220_close_async_http_client() return RUSTG_CALL(RUST_G_SS220, "shutdown_http_client")() diff --git a/modular_bandastation/tts/code/base_seeds/mobs/_base.dm b/modular_bandastation/tts/code/base_seeds/mobs/_base.dm index 8be816e8f22c3..cf230fb4ba01a 100644 --- a/modular_bandastation/tts/code/base_seeds/mobs/_base.dm +++ b/modular_bandastation/tts/code/base_seeds/mobs/_base.dm @@ -1,8 +1,13 @@ -//Fallback values for TTS voices +// Fallback values for TTS voices -/mob/living - tts_seed = "Kleiner" +/mob/living/add_tts_component() + AddComponent(/datum/component/tts_component) -// /mob/living/basic +/mob/living/basic/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/angel) -// /mob/living/simple_animal +/mob/living/simple_animal/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/angel) + +/mob/living/silicon/add_tts_component() + AddComponent(/datum/component/tts_component, null, TTS_TRAIT_ROBOTIZE) diff --git a/modular_bandastation/tts/code/base_seeds/mobs/alien.dm b/modular_bandastation/tts/code/base_seeds/mobs/alien.dm index e961a000fac1e..273c6364a82ef 100644 --- a/modular_bandastation/tts/code/base_seeds/mobs/alien.dm +++ b/modular_bandastation/tts/code/base_seeds/mobs/alien.dm @@ -1,11 +1,11 @@ //Aliens! -/mob/living/carbon/alien - tts_seed = "Ladyvashj" +/mob/living/carbon/alien/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/ladyvashj) -/mob/living/carbon/alien/larva - tts_seed = "Templar" +/mob/living/carbon/alien/larva/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/templar) -/mob/living/carbon/alien/adult/royal/queen - tts_seed = "Queen" +/mob/living/carbon/alien/adult/royal/queen/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/queen) diff --git a/modular_bandastation/tts/code/base_seeds/mobs/friendly.dm b/modular_bandastation/tts/code/base_seeds/mobs/friendly.dm index 847713b532150..9d6c2aab0986d 100644 --- a/modular_bandastation/tts/code/base_seeds/mobs/friendly.dm +++ b/modular_bandastation/tts/code/base_seeds/mobs/friendly.dm @@ -1,61 +1,61 @@ //Farm and friendly creatures. -/mob/living/basic/parrot - tts_seed = "Sniper" +/mob/living/basic/parrot/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/sniper) -/mob/living/basic/pet/cat - tts_seed = "Valerian" +/mob/living/basic/pet/cat/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/valerian) -/mob/living/basic/cockroach - tts_seed = "Villagerm" +/mob/living/basic/cockroach/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/villagerm) -/mob/living/basic/crab - tts_seed = "Riki" +/mob/living/basic/crab/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/riki) -/mob/living/basic/pet/dog - tts_seed = "Stetmann" +/mob/living/basic/pet/dog/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/stetmann) -/mob/living/basic/goat - tts_seed = "Muradin" +/mob/living/basic/goat/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/muradin) -/mob/living/basic/cow - tts_seed = "Cairne" +/mob/living/basic/cow/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/cairne) -/mob/living/basic/chick - tts_seed = "Meepo" +/mob/living/basic/chick/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/meepo) -/mob/living/basic/chicken - tts_seed = "Windranger" +/mob/living/basic/chicken/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/windranger) -/mob/living/basic/pig - tts_seed = "Anubarak" +/mob/living/basic/pig/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/anubarak) -/mob/living/simple_animal/hostile/retaliate/goose - tts_seed = "pantheon" +/mob/living/simple_animal/hostile/retaliate/goose/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/pantheon) -/mob/living/basic/pet/fox - tts_seed = "Barney" +/mob/living/basic/pet/fox/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/barney) -/mob/living/basic/frog - tts_seed = "pantheon" +/mob/living/basic/frog/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/pantheon) -/mob/living/basic/lizard - tts_seed = "Shaker" +/mob/living/basic/lizard/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/shaker) -/mob/living/basic/mouse - tts_seed = "Gyro" +/mob/living/basic/mouse/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/gyro) -/mob/living/basic/mouse/white - tts_seed = "Meepo" +/mob/living/basic/mouse/white/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/meepo) -/mob/living/basic/mouse/brown - tts_seed = "Clockwerk" +/mob/living/basic/mouse/brown/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/clockwerk) -/mob/living/basic/pet/penguin - tts_seed = "Narrator" +/mob/living/basic/pet/penguin/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/narrator) -/mob/living/basic/sloth - tts_seed = "Peon" +/mob/living/basic/sloth/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/peon) -/mob/living/basic/butterfly - tts_seed = "Meepo" +/mob/living/basic/butterfly/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/meepo) diff --git a/modular_bandastation/tts/code/base_seeds/mobs/guardian.dm b/modular_bandastation/tts/code/base_seeds/mobs/guardian.dm index f313ce7fbff43..1d0c28c6fb87e 100644 --- a/modular_bandastation/tts/code/base_seeds/mobs/guardian.dm +++ b/modular_bandastation/tts/code/base_seeds/mobs/guardian.dm @@ -1,16 +1,16 @@ //Guardians -/mob/living/basic/guardian - tts_seed = "Earth" +/mob/living/basic/guardian/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/earth) -/mob/living/basic/guardian/assassin - tts_seed = "Spy" +/mob/living/basic/guardian/assassin/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/spy) -/mob/living/basic/guardian/lightning - tts_seed = "Archmage" +/mob/living/basic/guardian/lightning/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/archmage) -/mob/living/basic/guardian/protector - tts_seed = "Caime" +/mob/living/basic/guardian/protector/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/cairne) -/mob/living/basic/guardian/standard - tts_seed = "Heavy" +/mob/living/basic/guardian/standard/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/heavy) diff --git a/modular_bandastation/tts/code/base_seeds/mobs/hostile.dm b/modular_bandastation/tts/code/base_seeds/mobs/hostile.dm index 8a151180fcd1f..f0798bf83a647 100644 --- a/modular_bandastation/tts/code/base_seeds/mobs/hostile.dm +++ b/modular_bandastation/tts/code/base_seeds/mobs/hostile.dm @@ -1,46 +1,46 @@ //Hostile mobs -/mob/living/basic/blob_minion - tts_seed = "Earth" +/mob/living/basic/blob_minion/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/earth) -/mob/living/basic/clockwork_marauder - tts_seed = "Earth" +/mob/living/basic/clockwork_marauder/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/earth) -/mob/living/basic/cortical_borer - tts_seed = "Gman_e2" +/mob/living/basic/cortical_borer/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/gman_e2) -/mob/living/basic/morph - tts_seed = "Treant" +/mob/living/basic/morph/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/treant) -/mob/living/basic/revenant - tts_seed = "Sylvanas" +/mob/living/basic/revenant/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/sylvanas) -/mob/living/basic/demon - tts_seed = "Mannoroth" +/mob/living/basic/demon/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/mannoroth) -/mob/living/basic/construct - tts_seed = "Acolyte" +/mob/living/basic/construct/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/acolyte) -/mob/living/basic/construct/wraith - tts_seed = "Kelthuzad" +/mob/living/basic/construct/wraith/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/kelthuzad) -/mob/living/basic/snake - tts_seed = "Ladyvashj" +/mob/living/basic/snake/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/ladyvashj) -/mob/living/basic/bear - tts_seed = "Shaker" +/mob/living/basic/bear/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/shaker) -/mob/living/basic/carp - tts_seed = "Peon" +/mob/living/basic/carp/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/peon) -/mob/living/basic/carp/mega - tts_seed = "Shaker" +/mob/living/basic/carp/mega/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/shaker) -/mob/living/basic/creature - tts_seed = "Earth" +/mob/living/basic/creature/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/earth) -/mob/living/basic/giant_spider - tts_seed = "Anubarak" +/mob/living/basic/giant_spider/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/anubarak) -/mob/living/simple_animal/hostile - tts_seed = "Vort_e2" +/mob/living/simple_animal/hostile/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/vort_e2) diff --git a/modular_bandastation/tts/code/base_seeds/mobs/lavaland.dm b/modular_bandastation/tts/code/base_seeds/mobs/lavaland.dm index 72118bd8eb424..3ce943172e402 100644 --- a/modular_bandastation/tts/code/base_seeds/mobs/lavaland.dm +++ b/modular_bandastation/tts/code/base_seeds/mobs/lavaland.dm @@ -1,34 +1,34 @@ //Lavaland mobs and megafauna -/mob/living/simple_animal/hostile/megafauna - tts_seed = "Mannoroth" +/mob/living/simple_animal/hostile/megafauna/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/mannoroth) -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner - tts_seed = "Chen" +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/chen) -/mob/living/basic/mining/basilisk - tts_seed = "Antimage" +/mob/living/basic/mining/basilisk/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/antimage) -/mob/living/basic/mining/goliath/ancient - tts_seed = "Bloodseeker" +/mob/living/basic/mining/goliath/ancient/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/bloodseeker) -/mob/living/basic/mining/hivelord - tts_seed = "Ladyvashj" +/mob/living/basic/mining/hivelord/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/ladyvashj) -/mob/living/basic/mining/legion - tts_seed = "Bloodseeker" +/mob/living/basic/mining/legion/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/bloodseeker) -/mob/living/basic/mining/legion/big - tts_seed = "Mannoroth" +/mob/living/basic/mining/legion/big/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/mannoroth) -/mob/living/simple_animal/hostile/asteroid/elite/broodmother - tts_seed = "Azalina" +/mob/living/simple_animal/hostile/asteroid/elite/broodmother/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/azalina) -/mob/living/simple_animal/hostile/asteroid/elite/herald - tts_seed = "Abathur" +/mob/living/simple_animal/hostile/asteroid/elite/herald/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/abathur) -/mob/living/simple_animal/hostile/asteroid/elite/legionnaire - tts_seed = "Volibear" +/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/volibear) -/mob/living/simple_animal/hostile/asteroid/elite/pandora - tts_seed = "Zyra" +/mob/living/simple_animal/hostile/asteroid/elite/pandora/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/zyra) diff --git a/modular_bandastation/tts/code/base_seeds/mobs/other.dm b/modular_bandastation/tts/code/base_seeds/mobs/other.dm index 98abf06f165b1..66fd6367032c3 100644 --- a/modular_bandastation/tts/code/base_seeds/mobs/other.dm +++ b/modular_bandastation/tts/code/base_seeds/mobs/other.dm @@ -1,22 +1,22 @@ //Uncategorized mobs -/mob/living/silicon/ai - tts_seed = "Glados" +/mob/living/silicon/ai/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/glados, TTS_TRAIT_ROBOTIZE) -/obj/item/nullrod/scythe/talking - tts_seed = "Sylvanas" +/obj/item/nullrod/scythe/talking/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/sylvanas) -/mob/living/basic/shade - tts_seed = "Kelthuzad" +/mob/living/basic/shade/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/kelthuzad) -/mob/living/simple_animal/bot - tts_seed = null +/mob/living/simple_animal/bot/add_tts_component() + return -/mob/living/basic/slime - tts_seed = "Chen" +/mob/living/basic/slime/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/chen) -/mob/living/carbon/human/species/monkey - tts_seed = "Sniper" +/mob/living/carbon/human/species/monkey/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/sniper) -/mob/living/carbon/human/species/monkey/punpun - tts_seed = "Chen" +/mob/living/carbon/human/species/monkey/punpun/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/chen) diff --git a/modular_bandastation/tts/code/base_seeds/mobs/pets.dm b/modular_bandastation/tts/code/base_seeds/mobs/pets.dm index 5b8551cfc9618..738082880ceb0 100644 --- a/modular_bandastation/tts/code/base_seeds/mobs/pets.dm +++ b/modular_bandastation/tts/code/base_seeds/mobs/pets.dm @@ -1,7 +1,7 @@ //All named pets -/mob/living/basic/giant_spider/sgt_araneus - tts_seed = "Anubarak" +/mob/living/basic/giant_spider/sgt_araneus/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/anubarak) -/mob/living/basic/parrot/poly - tts_seed = "Gyro" +/mob/living/basic/parrot/poly/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/gyro) diff --git a/modular_bandastation/tts/code/base_seeds/objs/objs.dm b/modular_bandastation/tts/code/base_seeds/objs/objs.dm index bf110d0326075..8dbea91cf248b 100644 --- a/modular_bandastation/tts/code/base_seeds/objs/objs.dm +++ b/modular_bandastation/tts/code/base_seeds/objs/objs.dm @@ -1,26 +1,23 @@ -/obj/machinery - tts_seed = "Glados" +/obj/machinery/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/glados) -/obj/machinery/computer - tts_seed = null +/obj/machinery/computer/add_tts_component() + return -/obj/machinery/modular_computer - tts_seed = null +/obj/machinery/modular_computer/add_tts_component() + return -/obj/machinery/rnd - tts_seed = null +/obj/machinery/rnd/add_tts_component() + return -/obj/machinery/autolathe - tts_seed = null +/obj/machinery/autolathe/add_tts_component() + return -/obj/machinery/mecha_part_fabricator - tts_seed = null +/obj/machinery/mecha_part_fabricator/add_tts_component() + return -/obj/item/modular_computer - tts_seed = null +/obj/item/modular_computer/add_tts_component() + return -/obj/item/taperecorder - tts_seed = "Xenia" - -/obj/item/ttsdevice - tts_seed = "Xenia" +/obj/item/taperecorder/add_tts_component() + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/xenia) diff --git a/modular_bandastation/tts/code/hear.dm b/modular_bandastation/tts/code/hear.dm new file mode 100644 index 0000000000000..bdb5e768ad446 --- /dev/null +++ b/modular_bandastation/tts/code/hear.dm @@ -0,0 +1,11 @@ +/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range) + . = ..() + if(!.) + return + speaker.cast_tts(src, raw_message, effect = radio_freq ? /datum/singleton/sound_effect/radio : null) + +/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range) + . = ..() + if(!.) + return + speaker.cast_tts(src, raw_message, effect = radio_freq ? /datum/singleton/sound_effect/radio : null) diff --git a/modular_bandastation/tts/code/tts_numbers.dm b/modular_bandastation/tts/code/numbers.dm similarity index 94% rename from modular_bandastation/tts/code/tts_numbers.dm rename to modular_bandastation/tts/code/numbers.dm index c330ce48a285a..ed41e245dfcf3 100644 --- a/modular_bandastation/tts/code/tts_numbers.dm +++ b/modular_bandastation/tts/code/numbers.dm @@ -12,11 +12,7 @@ if(num.cache["[n]"]) return num.cache["[n]"] - var/result - if(decimal) - result = num.decimal2words(n) - else - result = num.int2words(n) + var/result = decimal ? num.decimal2words(n) : num.int2words(n) result = " [result] " num.cache["[n]"] = result @@ -84,12 +80,7 @@ var/plural = 3 var/list/name = list() var/use_teens = (rest % 100 >= 10) && (rest % 100 <= 19) - var/list/data = list() - - if(!use_teens) - data = list( list(units, 10), list(tens, 100), list(hundreds, 1000) ) - else - data = list( list(teens, 10), list(hundreds, 1000) ) + var/list/data = use_teens ? list(list(teens, 10), list(hundreds, 1000)) : list(list(units, 10), list(tens, 100), list(hundreds, 1000)) for(var/list in data) var/names = list[1] diff --git a/modular_bandastation/tts/code/providers/silero.dm b/modular_bandastation/tts/code/providers/silero.dm index bfd611760b7ee..3c5a46406a0b1 100644 --- a/modular_bandastation/tts/code/providers/silero.dm +++ b/modular_bandastation/tts/code/providers/silero.dm @@ -6,10 +6,10 @@ if(throttle_check()) return FALSE - var/api_url = "http://s2.ss220.club:9999/voice" var/ssml_text = {"[text]"} var/list/req_body = list() + req_body["api_token"] = CONFIG_GET(string/tts_token_silero) req_body["text"] = ssml_text req_body["sample_rate"] = 24000 @@ -22,16 +22,8 @@ req_body["symbol_durs"] = list() req_body["format"] = "ogg" req_body["word_ts"] = FALSE - // var/json_body = json_encode(req_body) - // log_debug(json_body) - - var/datum/http_request/request = new() - request.prepare(RUSTG_HTTP_METHOD_POST, api_url, json_encode(req_body), list("content-type" = "application/json")) - spawn(0) - request.begin_async() - UNTIL(request.is_complete()) - var/datum/http_response/response = request.into_response() - proc_callback.Invoke(response) + + SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, CONFIG_GET(string/tts_api_url_silero), json_encode(req_body), list("content-type" = "application/json"), proc_callback) return TRUE diff --git a/modular_bandastation/tts/code/seeds/base.dm b/modular_bandastation/tts/code/seeds/base.dm new file mode 100644 index 0000000000000..dbfe0e6af47d6 --- /dev/null +++ b/modular_bandastation/tts/code/seeds/base.dm @@ -0,0 +1,7 @@ +/datum/tts_seed + var/name = "STUB" + var/value = "STUB" + var/category = TTS_CATEGORY_OTHER + var/gender = TTS_GENDER_ANY + var/datum/tts_provider/provider = /datum/tts_provider + var/required_donator_level = 0 diff --git a/modular_bandastation/tts/code/seeds/silero.dm b/modular_bandastation/tts/code/seeds/silero.dm index 5f83cb4078cd6..d85746ec8fa6b 100644 --- a/modular_bandastation/tts/code/seeds/silero.dm +++ b/modular_bandastation/tts/code/seeds/silero.dm @@ -6,48 +6,56 @@ value = "arthas" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/kelthuzad name = "Kelthuzad" value = "kelthuzad" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/anubarak name = "Anubarak" value = "anubarak" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 2 /datum/tts_seed/silero/thrall name = "Thrall" value = "thrall" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/grunt name = "Grunt" value = "grunt" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/cairne name = "Cairne" value = "cairne" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 3 /datum/tts_seed/silero/rexxar name = "Rexxar" value = "rexxar" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/uther name = "Uther" value = "uther" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/jaina name = "Jaina" @@ -66,6 +74,7 @@ value = "garithos" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/maiev name = "Maiev" @@ -96,6 +105,7 @@ value = "illidan" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/ladyvashj name = "Ladyvashj" @@ -120,6 +130,7 @@ value = "villagerm" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/xenia name = "Xenia" @@ -132,24 +143,28 @@ value = "illidan_f" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/peon name = "Peon" value = "peon" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/chen name = "Chen" value = "chen" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/dread_bm name = "Dread_bm" value = "dread_bm" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/sylvanas name = "Sylvanas" @@ -162,12 +177,14 @@ value = "priest" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/acolyte name = "Acolyte" value = "acolyte" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/muradin name = "Muradin" @@ -186,6 +203,7 @@ value = "mannoroth" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/sorceress name = "Sorceress" @@ -198,6 +216,7 @@ value = "peasant" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/alyx name = "Alyx" @@ -234,7 +253,7 @@ value = "raynor" category = TTS_CATEGORY_STARCRAFT gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/kerrigan name = "Kerrigan" @@ -247,24 +266,28 @@ value = "tusk" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/earth name = "Earth" value = "earth" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/wraith name = "Wraith" value = "wraith" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/meepo name = "Meepo" value = "meepo" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_ANY + required_donator_level = 1 /datum/tts_seed/silero/lina name = "Lina" @@ -277,6 +300,7 @@ value = "bristle" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/gyro name = "Gyro" @@ -289,30 +313,35 @@ value = "treant" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/lancer name = "Lancer" value = "lancer" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/clockwerk name = "Clockwerk" value = "clockwerk" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/batrider name = "Batrider" value = "batrider" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/kotl name = "Kotl" value = "kotl" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/kunkka name = "Kunkka" @@ -325,19 +354,21 @@ value = "pudge" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/juggernaut name = "Juggernaut" value = "juggernaut" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/vort_e2 name = "Vort_e2" value = "vort_e2" category = TTS_CATEGORY_HALFLIFE2 gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/luna name = "Luna" @@ -350,30 +381,35 @@ value = "omni" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/sniper name = "Sniper" value = "sniper" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 2 /datum/tts_seed/silero/skywrath name = "Skywrath" value = "skywrath" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/bounty name = "Bounty" value = "bounty" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_ANY + required_donator_level = 1 /datum/tts_seed/silero/huskar name = "Huskar" value = "huskar" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/windranger name = "Windranger" @@ -386,6 +422,7 @@ value = "bloodseeker" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/templar name = "Templar" @@ -398,12 +435,14 @@ value = "ranger" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_FEMALE + required_donator_level = 1 /datum/tts_seed/silero/shaker name = "Shaker" value = "shaker" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/mortred name = "Mortred" @@ -422,43 +461,49 @@ value = "storm" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/tide name = "Tide" value = "tide" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/evelynn name = "Evelynn" value = "evelynn" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/riki name = "Riki" value = "riki" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/antimage name = "Antimage" value = "antimage" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_ANY + required_donator_level = 1 /datum/tts_seed/silero/witchdoctor name = "Witchdoctor" value = "witchdoctor" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/doom name = "Doom" value = "doom" category = TTS_CATEGORY_DOTA2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/yuumi name = "Yuumi" @@ -471,26 +516,28 @@ value = "bandit" category = TTS_CATEGORY_STALKER gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/pantheon name = "pantheon" value = "pantheon" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/tychus name = "Tychus" value = "tychus" category = TTS_CATEGORY_STARCRAFT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/breen name = "Breen" value = "breen" category = TTS_CATEGORY_HALFLIFE2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/kleiner name = "Kleiner" @@ -503,12 +550,14 @@ value = "father" category = TTS_CATEGORY_HALFLIFE2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/tosh name = "Tosh" value = "tosh" category = TTS_CATEGORY_STARCRAFT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/stetmann name = "Stetmann" @@ -527,19 +576,21 @@ value = "swann" category = TTS_CATEGORY_STARCRAFT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/hill name = "Hill" value = "hill" category = TTS_CATEGORY_STARCRAFT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/gman_e2 name = "Gman_e2" value = "gman_e2" category = TTS_CATEGORY_HALFLIFE2 gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/valerian name = "Valerian" @@ -552,6 +603,7 @@ value = "gman" category = TTS_CATEGORY_HALFLIFE2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/vort name = "Vort" @@ -570,6 +622,7 @@ value = "dornan" category = TTS_CATEGORY_FALLOUT2 gender = TTS_GENDER_MALE + required_donator_level = 2 /datum/tts_seed/silero/elder name = "Elder" @@ -594,12 +647,14 @@ value = "decker" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/dick name = "Dick" value = "dick" category = TTS_CATEGORY_FALLOUT2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/officer name = "Officer" @@ -612,30 +667,35 @@ value = "frank" category = TTS_CATEGORY_FALLOUT2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/gizmo name = "Gizmo" value = "gizmo" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/hakunin name = "Hakunin" value = "hakunin" category = TTS_CATEGORY_FALLOUT2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/harold name = "Harold" value = "harold" category = TTS_CATEGORY_FALLOUT2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/harry name = "Harry" value = "harry" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/jain name = "Jain" @@ -648,6 +708,7 @@ value = "maxson" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/killian name = "Killian" @@ -666,12 +727,14 @@ value = "lieutenant" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/loxley name = "Loxley" value = "loxley" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/lynette name = "Lynette" @@ -684,24 +747,28 @@ value = "marcus" category = TTS_CATEGORY_FALLOUT2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/master name = "Master" value = "master" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/morpheus name = "Morpheus" value = "morpheus" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/myron name = "Myron" value = "myron" category = TTS_CATEGORY_FALLOUT2 gender = TTS_GENDER_ANY + required_donator_level = 1 /datum/tts_seed/silero/nicole name = "Nicole" @@ -714,6 +781,7 @@ value = "overseer" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/rhombus name = "Rhombus" @@ -726,12 +794,14 @@ value = "set" category = TTS_CATEGORY_FALLOUT gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/sulik name = "Sulik" value = "sulik" category = TTS_CATEGORY_FALLOUT2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/tandi name = "Tandi" @@ -750,18 +820,21 @@ value = "dude" category = TTS_CATEGORY_POSTAL2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/archmage name = "Archmage" value = "archmage" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/demoman name = "Demoman" value = "demoman" category = TTS_CATEGORY_TEAMFORTRESS2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/engineer name = "Engineer" @@ -780,6 +853,7 @@ value = "medic" category = TTS_CATEGORY_TEAMFORTRESS2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/scout name = "Scout" @@ -792,12 +866,14 @@ value = "sniper_tf" category = TTS_CATEGORY_TEAMFORTRESS2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/soldier name = "Soldier" value = "soldier" category = TTS_CATEGORY_TEAMFORTRESS2 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/spy name = "Spy" @@ -810,18 +886,21 @@ value = "admiral" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/alchemist name = "Alchemist" value = "alchemist" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 2 /datum/tts_seed/silero/archimonde name = "Archimonde" value = "archimonde" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 2 /datum/tts_seed/silero/breaker name = "Breaker" @@ -834,6 +913,7 @@ value = "captain" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/dryad name = "Dryad" @@ -846,24 +926,28 @@ value = "elf_eng" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/footman name = "Footman" value = "footman" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/grom name = "Grom" value = "grom" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/hh name = "Hh" value = "hh" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/huntress name = "Huntress" @@ -876,18 +960,21 @@ value = "keeper" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/naga_m name = "Naga_m" value = "naga_m" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/naga_rg name = "Naga_rg" value = "naga_rg" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/peasant_w name = "Peasant_w" @@ -900,6 +987,7 @@ value = "rifleman" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/satyr name = "Satyr" @@ -918,2413 +1006,2415 @@ value = "voljin" category = TTS_CATEGORY_WARCRAFT3 gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/sidorovich name = "Sidorovich" value = "sidorovich" category = TTS_CATEGORY_STALKER gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/p3 name = "P3" value = "p3" category = TTS_CATEGORY_ATOMIC_HEART gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/hraz name = "Hraz" value = "hraz" category = TTS_CATEGORY_ATOMIC_HEART gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/tereshkova name = "Tereshkova" value = "tereshkova" category = TTS_CATEGORY_ATOMIC_HEART gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/babazina name = "Babazina" value = "babazina" category = TTS_CATEGORY_ATOMIC_HEART gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/darius name = "Darius" value = "darius" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/trundle name = "Trundle" value = "trundle" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/garen name = "Garen" value = "garen" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/kled name = "Kled" value = "kled" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/ekko name = "Ekko" value = "ekko" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/volibear name = "Volibear" value = "volibear" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/samira name = "Samira" value = "samira" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/swain name = "Swain" value = "swain" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/udyr name = "Udyr" value = "udyr" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/dr_mundo name = "Dr_mundo" value = "dr_mundo" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/graves name = "Graves" value = "graves" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/rakan name = "Rakan" value = "rakan" category = TTS_CATEGORY_LOL - gender = TTS_GENDER_FEMALE - donator_level = 0 + gender = TTS_GENDER_MALE + required_donator_level = 1 /datum/tts_seed/silero/renata_glasc name = "Renata_glasc" value = "renata_glasc" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/gangplank name = "Gangplank" value = "gangplank" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/riven name = "Riven" value = "riven" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/katarina name = "Katarina" value = "katarina" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/ahri name = "Ahri" value = "ahri" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/ornn name = "Ornn" value = "ornn" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/braum name = "Braum" value = "braum" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/fizz name = "Fizz" value = "fizz" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/draven name = "Draven" value = "draven" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/qiyana name = "Qiyana" value = "qiyana" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/ksante name = "Ksante" value = "ksante" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/talon name = "Talon" value = "talon" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/shyvana name = "Shyvana" value = "shyvana" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/zenyatta name = "Zenyatta" value = "zenyatta" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/kiriko name = "Kiriko" value = "kiriko" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/hanzo name = "Hanzo" value = "hanzo" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/roadhog name = "Roadhog" value = "roadhog" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/sigma name = "Sigma" value = "sigma" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/soldier_76 name = "Soldier_76" value = "soldier_76" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/junkrat name = "Junkrat" value = "junkrat" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/tracer name = "Tracer" value = "tracer" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/genji name = "Genji" value = "genji" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/echo name = "Echo" value = "echo" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/sojourn name = "Sojourn" value = "sojourn" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/winston name = "Winston" value = "winston" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/reaper name = "Reaper" value = "reaper" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/training_robot name = "Training_robot" value = "training_robot" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_darkelf name = "M_darkelf" value = "m_darkelf" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/esbern name = "Esbern" value = "esbern" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_argo name = "M_argo" value = "m_argo" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_khajiit name = "M_khajiit" value = "m_khajiit" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_coward name = "M_coward" value = "m_coward" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/farkas name = "Farkas" value = "farkas" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_drunk name = "M_drunk" value = "m_drunk" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/f_khajiit name = "F_khajiit" value = "f_khajiit" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_citizen name = "M_citizen" value = "m_citizen" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_orc name = "M_orc" value = "m_orc" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/odahviing name = "Odahviing" value = "odahviing" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/kodlak name = "Kodlak" value = "kodlak" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/m_child name = "M_child" value = "m_child" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/emperor name = "Emperor" value = "emperor" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/hagraven name = "Hagraven" value = "hagraven" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/nazir name = "Nazir" value = "nazir" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/dremora name = "Dremora" value = "dremora" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/alduin name = "Alduin" value = "alduin" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/malkoran name = "Malkoran" value = "malkoran" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/barbas name = "Barbas" value = "barbas" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/hermaeus name = "Hermaeus" value = "hermaeus" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/hakon name = "Hakon" value = "hakon" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/rita name = "Rita" value = "rita" category = TTS_CATEGORY_RITA gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/barman name = "Barman" value = "barman" category = TTS_CATEGORY_STALKER gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/bridger2 name = "Bridger2" value = "bridger2" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/bridger3 name = "Bridger3" value = "bridger3" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/cannibal3 name = "Cannibal3" value = "cannibal3" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/bridger1 name = "Bridger1" value = "bridger1" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/cannibal2 name = "Cannibal2" value = "cannibal2" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/slave1 name = "Slave1" value = "slave1" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/slave3 name = "Slave3" value = "slave3" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/mira name = "Mira" value = "mira" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/valeera name = "Valeera" value = "valeera" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/rehgar name = "Rehgar" value = "rehgar" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/yrel name = "Yrel" value = "yrel" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/volskaya name = "Volskaya" value = "volskaya" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/necromancer name = "Necromancer" value = "necromancer" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/zuljin name = "Zuljin" value = "zuljin" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/samuro name = "Samuro" value = "samuro" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/tyrael name = "Tyrael" value = "tyrael" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/athena name = "Athena" value = "athena" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/default name = "Default" value = "default" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/chromie name = "Chromie" value = "chromie" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/orphea name = "Orphea" value = "orphea" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/adjutant name = "Adjutant" value = "adjutant" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/vanndara name = "Vanndara" value = "vanndara" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/mechatassadar name = "Mechatassadar" value = "mechatassadar" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/blackheart name = "Blackheart" value = "blackheart" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/olaf name = "Olaf" value = "olaf" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/alarak name = "Alarak" value = "alarak" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/dva name = "Dva" value = "dva" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/toy18 name = "Toy18" value = "toy18" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/witchdoctor_h name = "Witchdoctor_h" value = "witchdoctor_h" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/lucio name = "Lucio" value = "lucio" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/angel name = "Angel" value = "angel" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/thunderking name = "Thunderking" value = "thunderking" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/dr_boom name = "Dr_boom" value = "dr_boom" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/hooktusk name = "Hooktusk" value = "hooktusk" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/sinclari name = "Sinclari" value = "sinclari" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/kazakus name = "Kazakus" value = "kazakus" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/ol_toomba name = "Ol_toomba" value = "ol_toomba" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/moroes name = "Moroes" value = "moroes" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/maiev_hs name = "Maiev_hs" value = "maiev_hs" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/zentimo name = "Zentimo" value = "zentimo" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/rastakhan name = "Rastakhan" value = "rastakhan" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/innkeeper name = "Innkeeper" value = "innkeeper" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/togwaggle name = "Togwaggle" value = "togwaggle" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/biggs name = "Biggs" value = "biggs" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/brann name = "Brann" value = "brann" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/tekahn_boss name = "Tekahn_boss" value = "tekahn_boss" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/siamat name = "Siamat" value = "siamat" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/omnotron name = "Omnotron" value = "omnotron" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/putricide name = "Putricide" value = "putricide" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/khadgar name = "Khadgar" value = "khadgar" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/zoie name = "Zoie" value = "zoie" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/azalina name = "Azalina" value = "azalina" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/chu name = "Chu" value = "chu" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/tekahn name = "Tekahn" value = "tekahn" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/sthara name = "Sthara" value = "sthara" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/dovo name = "Dovo" value = "dovo" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/shaw name = "Shaw" value = "shaw" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/greymane name = "Greymane" value = "greymane" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/willow name = "Willow" value = "willow" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/haro name = "Haro" value = "haro" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/hagatha name = "Hagatha" value = "hagatha" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/reno name = "Reno" value = "reno" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/ozara name = "Ozara" value = "ozara" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/loti name = "Loti" value = "loti" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/tarkus name = "Tarkus" value = "tarkus" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/voone name = "Voone" value = "voone" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/tala name = "Tala" value = "tala" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/edra name = "Edra" value = "edra" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/myra name = "Myra" value = "myra" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/smiggs name = "Smiggs" value = "smiggs" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/timothy name = "Timothy" value = "timothy" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/wendy name = "Wendy" value = "wendy" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/hannigan name = "Hannigan" value = "hannigan" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/vargoth name = "Vargoth" value = "vargoth" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/jolene name = "Jolene" value = "jolene" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/kyriss name = "Kyriss" value = "kyriss" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/saurfang name = "Saurfang" value = "saurfang" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/kizi name = "Kizi" value = "kizi" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/slate name = "Slate" value = "slate" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/hesutu name = "Hesutu" value = "hesutu" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/hancho name = "Hancho" value = "hancho" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/gnomenapper name = "Gnomenapper" value = "gnomenapper" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/valdera name = "Valdera" value = "valdera" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/disidra name = "Disidra" value = "disidra" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/omu name = "Omu" value = "omu" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/floop name = "Floop" value = "floop" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/belloc name = "Belloc" value = "belloc" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/xurios name = "Xurios" value = "xurios" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/wagtoggle name = "Wagtoggle" value = "wagtoggle" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/belnaara name = "Belnaara" value = "belnaara" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/lilayell name = "Lilayell" value = "lilayell" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/candlebeard name = "Candlebeard" value = "candlebeard" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/awilo name = "Awilo" value = "awilo" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/marei name = "Marei" value = "marei" category = TTS_CATEGORY_HEARTHSTONE - gender = TTS_GENDER_FEMALE - donator_level = 0 + gender = TTS_GENDER_MALE + required_donator_level = 2 /datum/tts_seed/silero/applebough name = "Applebough" value = "applebough" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/lazul name = "Lazul" value = "lazul" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/arwyn name = "Arwyn" value = "arwyn" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/glowtron name = "Glowtron" value = "glowtron" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/cardish name = "Cardish" value = "cardish" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/robold name = "Robold" value = "robold" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/malfurion name = "Malfurion" value = "malfurion" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/deathwhisper name = "Deathwhisper" value = "deathwhisper" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/janna name = "Janna" value = "janna" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/cassiopeia name = "Cassiopeia" value = "cassiopeia" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/taliyah name = "Taliyah" value = "taliyah" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/neeko name = "Neeko" value = "neeko" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/taric name = "Taric" value = "taric" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/akshan name = "Akshan" value = "akshan" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/tristana name = "Tristana" value = "tristana" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/sylas name = "Sylas" value = "sylas" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/sejuani name = "Sejuani" value = "sejuani" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/anivia name = "Anivia" value = "anivia" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/vayne name = "Vayne" value = "vayne" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/karma name = "Karma" value = "karma" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/nilah name = "Nilah" value = "nilah" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/olaf_lol name = "Olaf_lol" value = "olaf_lol" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/quinn name = "Quinn" value = "quinn" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/lissandra name = "Lissandra" value = "lissandra" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/hecarim name = "Hecarim" value = "hecarim" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/vi name = "Vi" value = "vi" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/zyra name = "Zyra" value = "zyra" category = TTS_CATEGORY_LOL gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/zac name = "Zac" value = "zac" category = TTS_CATEGORY_LOL gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/moira name = "Moira" value = "moira" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/ashe name = "Ashe" value = "ashe" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/brigitte name = "Brigitte" value = "brigitte" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/mercy name = "Mercy" value = "mercy" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/lucio_ov name = "Lucio_ov" value = "lucio_ov" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/dva_ov name = "Dva_ov" value = "dva_ov" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/symmetra name = "Symmetra" value = "symmetra" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/zarya name = "Zarya" value = "zarya" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/cassidy name = "Cassidy" value = "cassidy" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/baptiste name = "Baptiste" value = "baptiste" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/junker_queen name = "Junker_queen" value = "junker_queen" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/doomfist name = "Doomfist" value = "doomfist" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/pharah name = "Pharah" value = "pharah" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/sombra name = "Sombra" value = "sombra" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/ana name = "Ana" value = "ana" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/widowmaker name = "Widowmaker" value = "widowmaker" category = TTS_CATEGORY_OVERWATCH gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/harbor name = "Harbor" value = "harbor" category = TTS_CATEGORY_VALORANT gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/sage name = "Sage" value = "sage" category = TTS_CATEGORY_VALORANT gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/brimstone name = "Brimstone" value = "brimstone" category = TTS_CATEGORY_VALORANT gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/sova name = "Sova" value = "sova" category = TTS_CATEGORY_VALORANT gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/f_shrill name = "F_shrill" value = "f_shrill" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_haughty name = "M_haughty" value = "m_haughty" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_soldier name = "M_soldier" value = "m_soldier" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/sven name = "Sven" value = "sven" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/f_sultry name = "F_sultry" value = "f_sultry" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/eorlund name = "Eorlund" value = "eorlund" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/m_commander name = "M_commander" value = "m_commander" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/f_nord name = "F_nord" value = "f_nord" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/lydia name = "Lydia" value = "lydia" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/motierre name = "Motierre" value = "motierre" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/f_haughty name = "F_haughty" value = "f_haughty" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/tullius name = "Tullius" value = "tullius" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/festus name = "Festus" value = "festus" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_nord name = "M_nord" value = "m_nord" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/olava name = "Olava" value = "olava" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/f_commander name = "F_commander" value = "f_commander" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/hadvar name = "Hadvar" value = "hadvar" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/f_argo name = "F_argo" value = "f_argo" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/arngeir name = "Arngeir" value = "arngeir" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/nazeem name = "Nazeem" value = "nazeem" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/falion name = "Falion" value = "falion" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/f_coward name = "F_coward" value = "f_coward" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_guard name = "M_guard" value = "m_guard" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_commoner name = "M_commoner" value = "m_commoner" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/elisif name = "Elisif" value = "elisif" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/paarthurnax name = "Paarthurnax" value = "paarthurnax" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/grelka name = "Grelka" value = "grelka" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/f_commoner name = "F_commoner" value = "f_commoner" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/ebony name = "Ebony" value = "ebony" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/ulfric name = "Ulfric" value = "ulfric" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/farengar name = "Farengar" value = "farengar" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/astrid name = "Astrid" value = "astrid" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/brynjolf name = "Brynjolf" value = "brynjolf" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/maven name = "Maven" value = "maven" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/f_child name = "F_child" value = "f_child" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/f_orc name = "F_orc" value = "f_orc" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/delphine name = "Delphine" value = "delphine" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/f_darkelf name = "F_darkelf" value = "f_darkelf" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/grelod name = "Grelod" value = "grelod" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/tolfdir name = "Tolfdir" value = "tolfdir" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/m_bandit name = "M_bandit" value = "m_bandit" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/m_forsworn name = "M_forsworn" value = "m_forsworn" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/karliah name = "Karliah" value = "karliah" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/felldir name = "Felldir" value = "felldir" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/ancano name = "Ancano" value = "ancano" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/mercer name = "Mercer" value = "mercer" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/vex name = "Vex" value = "vex" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/mirabelle name = "Mirabelle" value = "mirabelle" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/aventus name = "Aventus" value = "aventus" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/tsun name = "Tsun" value = "tsun" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/elenwen name = "Elenwen" value = "elenwen" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/gormlaith name = "Gormlaith" value = "gormlaith" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/dragon name = "Dragon" value = "dragon" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/overwatch name = "Overwatch" value = "overwatch" category = TTS_CATEGORY_HALFLIFE2 gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/zak name = "Zak" value = "zak" category = TTS_CATEGORY_EVILISLANDS gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/merc2 name = "Merc2" value = "merc2" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/forest1 name = "Forest1" value = "forest1" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/bandit3 name = "Bandit3" value = "bandit3" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/forest2 name = "Forest2" value = "forest2" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/merc1 name = "Merc1" value = "merc1" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/bandit2 name = "Bandit2" value = "bandit2" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/forest3 name = "Forest3" value = "forest3" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/tribal3 name = "Tribal3" value = "tribal3" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/slave2 name = "Slave2" value = "slave2" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/miller name = "Miller" value = "miller" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/krest name = "Krest" value = "krest" category = TTS_CATEGORY_METRO gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/tribal1 name = "Tribal1" value = "tribal1" category = TTS_CATEGORY_METRO gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/abathur name = "Abathur" value = "abathur" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/erik name = "Erik" value = "erik" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/varian name = "Varian" value = "varian" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/anduin name = "Anduin" value = "anduin" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/deckard name = "Deckard" value = "deckard" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/malfurion_h name = "Malfurion_h" value = "malfurion_h" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/demonhunter name = "Demonhunter" value = "demonhunter" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/demon name = "Demon" value = "demon" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/kerrigan_h name = "Kerrigan_h" value = "kerrigan_h" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/ladyofthorns name = "Ladyofthorns" value = "ladyofthorns" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/barbarian name = "Barbarian" value = "barbarian" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/crusader name = "Crusader" value = "crusader" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/whitemane name = "Whitemane" value = "whitemane" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/nexushunter name = "Nexushunter" value = "nexushunter" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/greymane_h name = "Greymane_h" value = "greymane_h" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/gardensdayannouncer name = "Gardensdayannouncer" value = "gardensdayannouncer" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/drekthar name = "Drekthar" value = "drekthar" category = TTS_CATEGORY_HEROESOFTHESTORM gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/squeamlish name = "Squeamlish" value = "squeamlish" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/dagg name = "Dagg" value = "dagg" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/brukan name = "Brukan" value = "brukan" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/bolan name = "Bolan" value = "bolan" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/goya name = "Goya" value = "goya" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/stargazer name = "Stargazer" value = "stargazer" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/eudora name = "Eudora" value = "eudora" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/mozaki name = "Mozaki" value = "mozaki" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/katrana name = "Katrana" value = "katrana" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/valeera_hs name = "Valeera_hs" value = "valeera_hs" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/malacrass name = "Malacrass" value = "malacrass" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/elise name = "Elise" value = "elise" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/flark name = "Flark" value = "flark" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/rhogi name = "Rhogi" value = "rhogi" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/gallywix name = "Gallywix" value = "gallywix" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/talanji name = "Talanji" value = "talanji" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/dr_sezavo name = "Dr_sezavo" value = "dr_sezavo" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/tierra name = "Tierra" value = "tierra" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/zenda name = "Zenda" value = "zenda" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/baechao name = "Baechao" value = "baechao" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/lilian name = "Lilian" value = "lilian" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/aranna name = "Aranna" value = "aranna" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/oshi name = "Oshi" value = "oshi" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/norroa name = "Norroa" value = "norroa" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/turalyon name = "Turalyon" value = "turalyon" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/aki name = "Aki" value = "aki" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/lunara name = "Lunara" value = "lunara" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/bob name = "Bob" value = "bob" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/illucia name = "Illucia" value = "illucia" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/yrel_hs name = "Yrel_hs" value = "yrel_hs" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/fireheart name = "Fireheart" value = "fireheart" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/lanathel name = "Lanathel" value = "lanathel" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/tyrande_hs name = "Tyrande_hs" value = "tyrande_hs" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/draemus name = "Draemus" value = "draemus" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/rasil name = "Rasil" value = "rasil" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/kalec name = "Kalec" value = "kalec" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/karastamper name = "Karastamper" value = "karastamper" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/george name = "George" value = "george" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/pollark name = "Pollark" value = "pollark" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/stelina name = "Stelina" value = "stelina" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/kasa name = "Kasa" value = "kasa" category = TTS_CATEGORY_HEARTHSTONE - gender = TTS_GENDER_FEMALE - donator_level = 0 + gender = TTS_GENDER_ANY + required_donator_level = 1 /datum/tts_seed/silero/whirt name = "Whirt" value = "whirt" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/anarii name = "Anarii" value = "anarii" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/ilza name = "Ilza" value = "ilza" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/avozu name = "Avozu" value = "avozu" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/jeklik name = "Jeklik" value = "jeklik" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/zibb name = "Zibb" value = "zibb" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/thrud name = "Thrud" value = "thrud" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_MALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/isiset name = "Isiset" value = "isiset" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_FEMALE - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/akazamzarak name = "Akazamzarak" value = "akazamzarak" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/arha name = "Arha" @@ -3361,221 +3451,1413 @@ value = "senna" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/nunu name = "Nunu" value = "nunu" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/ryze name = "Ryze" value = "ryze" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/yone name = "Yone" value = "yone" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/sett name = "Sett" value = "sett" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/camille name = "Camille" value = "camille" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/lee_sin name = "Lee_sin" value = "lee_sin" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/kayle name = "Kayle" value = "kayle" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/azir name = "Azir" value = "azir" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/tryndamere name = "Tryndamere" value = "tryndamere" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/nami name = "Nami" value = "nami" category = TTS_CATEGORY_LOL gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/delvin name = "Delvin" value = "delvin" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/cicero name = "Cicero" value = "cicero" category = TTS_CATEGORY_SKYRIM gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/linzi name = "Linzi" value = "linzi" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/cache name = "Cache" value = "cache" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/cravitz name = "Cravitz" value = "cravitz" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 3 /datum/tts_seed/silero/lady_vashj name = "Lady_vashj" value = "lady_vashj" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/dendrologist name = "Dendrologist" value = "dendrologist" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/jythiros name = "Jythiros" value = "jythiros" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/draan name = "Draan" value = "draan" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/rikkar name = "Rikkar" value = "rikkar" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/splintergraft name = "Splintergraft" value = "splintergraft" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/malchezaar name = "Malchezaar" value = "malchezaar" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/taskmaster name = "Taskmaster" value = "taskmaster" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/oxana name = "Oxana" value = "oxana" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/inara name = "Inara" value = "inara" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/ivan name = "Ivan" value = "ivan" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/kazamon name = "Kazamon" value = "kazamon" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/albin name = "Albin" value = "albin" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/ammunae name = "Ammunae" value = "ammunae" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 /datum/tts_seed/silero/illidara name = "Illidara" value = "illidara" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 2 /datum/tts_seed/silero/nici name = "Nici" value = "nici" category = TTS_CATEGORY_HEARTHSTONE gender = TTS_GENDER_ANY - donator_level = 0 + required_donator_level = 1 + +/datum/tts_seed/silero/byasha + name = "Byasha" + value = "byasha" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/cerys + name = "Cerys" + value = "cerys" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_FEMALE + required_donator_level = 2 + +/datum/tts_seed/silero/philippa + name = "Philippa" + value = "philippa" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_FEMALE + required_donator_level = 2 + +/datum/tts_seed/silero/oldnekro + name = "Oldnekro" + value = "oldnekro" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/lambert + name = "Lambert" + value = "lambert" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/shani + name = "Shani" + value = "shani" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/anton + name = "Anton" + value = "anton" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/dolg1 + name = "Dolg1" + value = "dolg1" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/guru + name = "Guru" + value = "guru" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/lugos + name = "Lugos" + value = "lugos" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/karina + name = "Karina" + value = "karina" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/ewald + name = "Ewald" + value = "ewald" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/mirror + name = "Mirror" + value = "mirror" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/noble + name = "Noble" + value = "noble" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/huber + name = "Huber" + value = "huber" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/wywern + name = "Wywern" + value = "wywern" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + required_donator_level = 2 + +/datum/tts_seed/silero/avallach + name = "Avallach" + value = "avallach" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/semen + name = "Semen" + value = "semen" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/all_elder + name = "All_elder" + value = "all_elder" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/nsheriff + name = "Nsheriff" + value = "nsheriff" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/orcc + name = "Orcc" + value = "orcc" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/clerk + name = "Clerk" + value = "clerk" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/witch + name = "Witch" + value = "witch" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/deva + name = "Deva" + value = "deva" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/coach + name = "Coach" + value = "coach" + category = TTS_CATEGORY_LEFT4DEAD + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/dictor + name = "Dictor" + value = "dictor" + category = TTS_CATEGORY_PORTAL2 + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/monolith2 + name = "Monolith2" + value = "monolith2" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/invoker + name = "Invoker" + value = "invoker" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/goblin + name = "Goblin" + value = "goblin" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/annah + name = "Annah" + value = "annah" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/patrick + name = "Patrick" + value = "patrick" + category = TTS_CATEGORY_SPONGEBOB + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/spongebob + name = "Spongebob" + value = "spongebob" + category = TTS_CATEGORY_SPONGEBOB + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/kapitan + name = "Kapitan" + value = "kapitan" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/karh + name = "Karh" + value = "karh" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/lydia_tb + name = "Lydia_tb" + value = "lydia_tb" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/silencer + name = "Silencer" + value = "silencer" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/sheriff + name = "Sheriff" + value = "sheriff" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/lycan + name = "Lycan" + value = "lycan" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/cirilla + name = "Cirilla" + value = "cirilla" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_FEMALE + required_donator_level = 2 + +/datum/tts_seed/silero/legends + name = "Legends" + value = "legends" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/monolith1 + name = "Monolith1" + value = "monolith1" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/trapper + name = "Trapper" + value = "trapper" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/mirana + name = "Mirana" + value = "mirana" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + required_donator_level = 3 + +/datum/tts_seed/silero/glav + name = "Glav" + value = "glav" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/syanna + name = "Syanna" + value = "syanna" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/regis + name = "Regis" + value = "regis" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/dazzle + name = "Dazzle" + value = "dazzle" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/mthief + name = "Mthief" + value = "mthief" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/guillaume + name = "Guillaume" + value = "guillaume" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/vivienne + name = "Vivienne" + value = "vivienne" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/plankton + name = "Plankton" + value = "plankton" + category = TTS_CATEGORY_SPONGEBOB + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/rochelle + name = "Rochelle" + value = "rochelle" + category = TTS_CATEGORY_LEFT4DEAD + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/vor + name = "Vor" + value = "vor" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/grandmother + name = "Grandmother" + value = "grandmother" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_FEMALE + required_donator_level = 2 + +/datum/tts_seed/silero/dolg2 + name = "Dolg2" + value = "dolg2" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/junboy + name = "Junboy" + value = "junboy" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/shopper + name = "Shopper" + value = "shopper" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/papillon + name = "Papillon" + value = "papillon" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/cm + name = "Cm" + value = "cm" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/vesemir + name = "Vesemir" + value = "vesemir" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/kate + name = "Kate" + value = "kate" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/polina + name = "Polina" + value = "polina" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/crach + name = "Crach" + value = "crach" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/gryphon + name = "Gryphon" + value = "gryphon" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/zeus + name = "Zeus" + value = "zeus" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/iz + name = "Iz" + value = "iz" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/geralt + name = "Geralt" + value = "geralt" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 3 + +/datum/tts_seed/silero/stories + name = "Stories" + value = "stories" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/nekro + name = "Nekro" + value = "nekro" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/hwleader + name = "Hwleader" + value = "hwleader" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/yennefer + name = "Yennefer" + value = "yennefer" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/hero + name = "Hero" + value = "hero" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/baratrum + name = "Baratrum" + value = "baratrum" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/ellis + name = "Ellis" + value = "ellis" + category = TTS_CATEGORY_LEFT4DEAD + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/udalryk + name = "Udalryk" + value = "udalryk" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/dad + name = "Dad" + value = "dad" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/smith + name = "Smith" + value = "smith" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/romka + name = "Romka" + value = "romka" + category = TTS_CATEGORY_TINYBUNNY + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/abaddon + name = "Abaddon" + value = "abaddon" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/eskel + name = "Eskel" + value = "eskel" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/freedom + name = "Freedom" + value = "freedom" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/magess + name = "Magess" + value = "magess" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_ANY + required_donator_level = 1 + +/datum/tts_seed/silero/nalo + name = "Nalo" + value = "nalo" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_FEMALE + required_donator_level = 2 + +/datum/tts_seed/silero/dandelion + name = "Dandelion" + value = "dandelion" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 3 + +/datum/tts_seed/silero/palmerin + name = "Palmerin" + value = "palmerin" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/olgierd + name = "Olgierd" + value = "olgierd" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/d_sven + name = "D_sven" + value = "d_sven" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 3 + +/datum/tts_seed/silero/triss + name = "Triss" + value = "triss" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_FEMALE + required_donator_level = 1 + +/datum/tts_seed/silero/monkey + name = "Monkey" + value = "monkey" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/squidward + name = "Squidward" + value = "squidward" + category = TTS_CATEGORY_SPONGEBOB + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/ember + name = "Ember" + value = "ember" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/ycf + name = "Ycf" + value = "ycf" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + required_donator_level = 2 + +/datum/tts_seed/silero/nick + name = "Nick" + value = "nick" + category = TTS_CATEGORY_LEFT4DEAD + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/hjalmar + name = "Hjalmar" + value = "hjalmar" + category = TTS_CATEGORY_WITCHER + gender = TTS_GENDER_MALE + required_donator_level = 1 + +/datum/tts_seed/silero/gale + name = "Gale" + value = "en_Gale" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/jaheira + name = "Jaheira" + value = "en_Jaheira" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/laezel + name = "Laezel" + value = "en_Laezel" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/karlach + name = "Karlach" + value = "en_Karlach" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/shadowheart + name = "Shadowheart" + value = "en_Shadowheart" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/wyll + name = "Wyll" + value = "en_Wyll" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/minthara + name = "Minthara" + value = "en_Minthara" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/minsc + name = "Minsc" + value = "en_Minsc" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/astarion + name = "Astarion" + value = "en_Astarion" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/halsin + name = "Halsin" + value = "en_Halsin" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/emperor_bg3 + name = "Mind_Flayer_Emperor" + value = "en_Emperor" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/ketheric + name = "Ketheric" + value = "en_Ketheric" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/gortash + name = "Gortash" + value = "en_Gortash" + category = TTS_CATEGORY_BALDURS_GATE_3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/cave_johnson + name = "Cave_Johnson" + value = "portal_cave_johnson" + category = TTS_CATEGORY_PORTAL + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/shredder + name = "Shredder" + value = "Ninja_Turtles_shredder" + category = TTS_CATEGORY_TMNT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/han_solo + name = "Han_Solo" + value = "Star_Wars_Han_Solo" + category = TTS_CATEGORY_STAR_WARS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/darth_sidious + name = "Darth_Sidious" + value = "Star_Wars_Darth_Sidious" + category = TTS_CATEGORY_STAR_WARS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/luke_skywalker + name = "Luke_Skywalker" + value = "Star_Wars_Luke_Skywalker" + category = TTS_CATEGORY_STAR_WARS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/yoda + name = "Yoda" + value = "Star_Wars_Yoda" + category = TTS_CATEGORY_STAR_WARS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/darth_vader + name = "Darth_Vader" + value = "Star_Wars_Darth_Vader" + category = TTS_CATEGORY_STAR_WARS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/obiwan_kenobi + name = "Obi-wan_Kenobi" + value = "Star_Wars_Obi-Wan_Kenobi" + category = TTS_CATEGORY_STAR_WARS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/anakin_skywalker + name = "Anakin_Skywalker" + value = "Star_Wars_Anakin_Skywalker" + category = TTS_CATEGORY_STAR_WARS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/optimus_prime + name = "Optimus_Prime" + value = "Transformers_War_of_Cybertron_optimusprime" + category = TTS_CATEGORY_TRANSFORMERS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/megatron + name = "Megatron" + value = "Transformers_War_of_Cybertron_megatron" + category = TTS_CATEGORY_TRANSFORMERS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/soundwave + name = "Soundwave" + value = "Transformers_War_of_Cybertron_soundwave" + category = TTS_CATEGORY_TRANSFORMERS + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/aragorn + name = "Aragorn" + value = "The_Lord_of_the_Rings_Aragorn" + category = TTS_CATEGORY_LOTR + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/elrond + name = "Elrond" + value = "The_Lord_of_the_Rings_Elrond" + category = TTS_CATEGORY_LOTR + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/gandalf + name = "Gandalf" + value = "The_Lord_of_the_Rings_Gandalf" + category = TTS_CATEGORY_LOTR + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/gimli + name = "Gimli" + value = "The_Lord_of_the_Rings_Gimli" + category = TTS_CATEGORY_LOTR + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/gollum + name = "Gollum" + value = "The_Lord_of_the_Rings_Gollum" + category = TTS_CATEGORY_LOTR + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/legolas + name = "Legolas" + value = "The_Lord_of_the_Rings_Legolas" + category = TTS_CATEGORY_LOTR + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/gingerbread_man + name = "Gingerbread_Man" + value = "Srek_Gingerbread_Man" + category = TTS_CATEGORY_SHREK + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/fiona + name = "Fiona" + value = "Srek_Fiona" + category = TTS_CATEGORY_SHREK + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/donkey + name = "Donkey" + value = "Srek_Donkey" + category = TTS_CATEGORY_SHREK + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/fairy_godmother + name = "Fairy_Godmother" + value = "Srek_Fairy_Godmother" + category = TTS_CATEGORY_SHREK + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/srek_king + name = "King" + value = "Srek_King" + category = TTS_CATEGORY_SHREK + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/srek_narrator + name = "Shrek_Narrator" + value = "Srek_Narrator" + category = TTS_CATEGORY_SHREK + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/puss_in_boots + name = "Puss_in_Boots" + value = "Srek_Puss_in_Boots" + category = TTS_CATEGORY_SHREK + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/shrek + name = "Shrek" + value = "Srek_Shrek" + category = TTS_CATEGORY_SHREK + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/jack_sparrow + name = "Jack_Sparrow" + value = "Pirats_of_the_caribbean_Jack_Sparrow" + category = TTS_CATEGORY_POTC + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/barbossa + name = "Barbossa" + value = "Pirats_of_the_caribbean_Barbossa" + category = TTS_CATEGORY_POTC + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/tiadalma + name = "Tiadalma" + value = "Pirats_of_the_caribbean_Tiadalma" + category = TTS_CATEGORY_POTC + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/davy_jones + name = "Davy_Jones" + value = "Pirats_of_the_caribbean_Davy_Jones" + category = TTS_CATEGORY_POTC + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sirius_black + name = "Sirius_Black" + value = "Harry_Potter_Sirius_Black" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/dobby + name = "Dobby" + value = "Harry_Potter_Dobby" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/severus_snape_film + name = "Severus_snape_film" + value = "Harry_Potter_Severus_Snape_film" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/harry_potter + name = "Harry_Potter" + value = "Harry_Potter_Harry_Potter" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/dumbledore + name = "Albus_Dumbledore" + value = "Harry_Potter_Albus_Dumbledore" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/voldemort + name = "Voldemort" + value = "Harry_Potter_Lord_Voldemort" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/severus_snape + name = "Severus_Snape" + value = "Harry_Potter_Severus_Snape" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/flitwick + name = "Filius_Flitwick" + value = "Harry_Potter_Filius_Flitwick" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/minerva_mcgonagall + name = "Minnerva_McGonagall" + value = "Harry_Potter_Minerva_McGonagall" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/horace_slughorn + name = "Horace_Slughorn" + value = "Harry_Potter_Horace_Slughorn" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/cedric + name = "Cedric" + value = "Harry_Potter_Cedric" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/alastor + name = "Alastor_Mad-eye_Moody" + value = "Harry_Potter_Alastor_Mad-Eye_Moody" + category = TTS_CATEGORY_HARRY_POTTER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/x3_betty + name = "Betty" + value = "X3_reunion_Betty" + category = TTS_CATEGORY_X3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/overlord_gnarl + name = "Gnarl" + value = "Overlord_2_Gnarl" + category = TTS_CATEGORY_OVERLORD2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/tony_stark + name = "Tony_Stark" + value = "Marvel_Tony_Stark" + category = TTS_CATEGORY_MARVEL + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sabellian + name = "Sabellian" + value = "Dragons_Sabellian" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/ysera + name = "Ysera" + value = "Dragons_Ysera" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/malygos_wotlk + name = "Malygos_wotlk" + value = "Dragons_MalygosWrath_of_the_Lich_King" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/ebyssian + name = "Ebyssian" + value = "Dragons_Ebyssian" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/deathwing + name = "Deathwing" + value = "Dragons_Deathwing" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/nozdormu + name = "Nozdormu" + value = "Dragons_Nozdormu" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/malygos + name = "Malygos" + value = "Dragons_Malygos" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/calderax + name = "Calderax" + value = "Draconids_Calderax" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/bazentus + name = "Bazentus" + value = "Draconids_Bazentus" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/kazra + name = "Kazra" + value = "Draconids_Kazra" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/seltherex + name = "Seltherex" + value = "Draconids_Seltherex" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/sendrax + name = "Sendrax" + value = "Draconids_Sendrax" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/evantkis + name = "Evantkis" + value = "Draconids_Evantkis" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/drine + name = "Drine" + value = "Draconids_Drine" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/lethanak + name = "Lethanak" + value = "Draconids_Lethanak" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/wrathion_echo + name = "Wrathion_echo" + value = "Dragons2_Wrathion_echo" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/alexstraza + name = "Alexstraza" + value = "Dragons2_Alexstraza" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/kalecgos + name = "Kalecgos" + value = "Dragons2_Kalecgos" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/wrathion + name = "Wrathion" + value = "Dragons2_Wrathion" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/kalechos_echo + name = "Kalecgos_echo" + value = "Dragons2_Kalecgos_echo" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/alextraza_echo + name = "Alextraza_echo" + value = "Dragons2_Alextraza_echo" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/neltharion_echo + name = "Neltharion_echo" + value = "Dragons2_Neltharion_echo" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/wrathion_deathwing + name = "Wration_Deathwing" + value = "Dragons2_Wrathion_Deathwing" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/neltharion + name = "Neltharion" + value = "Dragons2_Neltharion" + category = TTS_CATEGORY_WOW + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/livsy + name = "Livsy" + value = "Treasure_Island_Livsy" + category = TTS_CATEGORY_TREASURE_ISLAND + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sp_brother + name = "sp_brother" + value = "slovo_patsana_brother" + category = TTS_CATEGORY_BOYS_WORD + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sp_koschei + name = "sp_koschei" + value = "slovo_patsana_koschei" + category = TTS_CATEGORY_BOYS_WORD + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sp_marat + name = "sp_marat" + value = "slovo_patsana_marat" + category = TTS_CATEGORY_BOYS_WORD + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sp_angry_cop + name = "sp_angry_cop" + value = "slovo_patsana_angry_cop" + category = TTS_CATEGORY_BOYS_WORD + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sp_cop + name = "sp_cop" + value = "slovo_patsana_cop" + category = TTS_CATEGORY_BOYS_WORD + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/sp_main + name = "sp_main" + value = "slovo_patsana_main" + category = TTS_CATEGORY_BOYS_WORD + gender = TTS_GENDER_MALE diff --git a/modular_bandastation/tts/code/shell.dm b/modular_bandastation/tts/code/shell.dm new file mode 100644 index 0000000000000..fb6ffeb98da39 --- /dev/null +++ b/modular_bandastation/tts/code/shell.dm @@ -0,0 +1,61 @@ +#define SHELLEO_ERRORLEVEL 1 +#define SHELLEO_STDOUT 2 +#define SHELLEO_STDERR 3 + +#define SHELLEO_NAME "data/shelleo." +#define SHELLEO_ERR ".err" +#define SHELLEO_OUT ".out" + +/proc/apply_sound_effect(datum/singleton/sound_effect/effect, filename_input, filename_output) + if(!effect) + CRASH("Invalid sound effect chosen.") + + var/taskset + CONFIG_GET(string/ffmpeg_cpuaffinity) + if(CONFIG_GET(string/ffmpeg_cpuaffinity)) + taskset = "taskset -ac [CONFIG_GET(string/ffmpeg_cpuaffinity)]" + + var/command = {"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "[effect.ffmpeg_arguments]" [filename_output]"} + var/list/output = world.shelleo(command) + + var/errorlevel = output[SHELLEO_ERRORLEVEL] + var/stdout = output[SHELLEO_STDOUT] + var/stderr = output[SHELLEO_STDERR] + if(errorlevel) + log_runtime("Error: apply_sound_effect([effect.suffix], [filename_input], [filename_output]) - See debug logs.") + logger.Log(LOG_CATEGORY_DEBUG, "apply_sound_effect([effect.suffix], [filename_input], [filename_output]) STDOUT: [stdout]") + logger.Log(LOG_CATEGORY_DEBUG, "apply_sound_effect([effect.suffix], [filename_input], [filename_output]) STDERR: [stderr]") + return FALSE + return TRUE + +/datum/singleton/sound_effect + var/suffix + var/ffmpeg_arguments + +/datum/singleton/sound_effect/radio + suffix = "_radio" + ffmpeg_arguments = "highpass=f=1000, lowpass=f=3000, acrusher=1:1:50:0:log" + +/datum/singleton/sound_effect/robot + suffix = "_robot" + ffmpeg_arguments = "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, volume=volume=1.5" + +/datum/singleton/sound_effect/radio_robot + suffix = "_radio_robot" + ffmpeg_arguments = "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, volume=volume=1.5, highpass=f=1000, lowpass=f=3000, acrusher=1:1:50:0:log" + +/datum/singleton/sound_effect/megaphone + suffix = "_megaphone" + ffmpeg_arguments = "highpass=f=500, lowpass=f=4000, volume=volume=10, acrusher=1:1:45:0:log" + +/datum/singleton/sound_effect/megaphone_robot + suffix = "_megaphone_robot" + ffmpeg_arguments = "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, highpass=f=500, lowpass=f=4000, volume=volume=10, acrusher=1:1:45:0:log" + +#undef SHELLEO_ERRORLEVEL +#undef SHELLEO_STDOUT +#undef SHELLEO_STDERR + +#undef SHELLEO_NAME +#undef SHELLEO_ERR +#undef SHELLEO_OUT diff --git a/modular_bandastation/tts/code/sound/radio_chatter.ogg b/modular_bandastation/tts/code/sound/radio_chatter.ogg new file mode 100644 index 0000000000000..6e5b3ecfbfe24 Binary files /dev/null and b/modular_bandastation/tts/code/sound/radio_chatter.ogg differ diff --git a/modular_bandastation/tts/code/tts_component.dm b/modular_bandastation/tts/code/tts_component.dm new file mode 100644 index 0000000000000..28edbfdd6d17a --- /dev/null +++ b/modular_bandastation/tts/code/tts_component.dm @@ -0,0 +1,158 @@ +/datum/component/tts_component + var/datum/tts_seed/tts_seed + var/list/traits = list() + +/datum/component/tts_component/RegisterWithParent() + RegisterSignal(parent, COMSIG_ATOM_TTS_SEED_CHANGE, PROC_REF(tts_seed_change)) + RegisterSignal(parent, COMSIG_ATOM_TTS_CAST, PROC_REF(cast_tts)) + RegisterSignal(parent, COMSIG_ATOM_TTS_TRAIT_ADD, PROC_REF(tts_trait_add)) + RegisterSignal(parent, COMSIG_ATOM_TTS_TRAIT_REMOVE, PROC_REF(tts_trait_remove)) + +/datum/component/tts_component/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_ATOM_TTS_SEED_CHANGE) + UnregisterSignal(parent, COMSIG_ATOM_TTS_CAST) + UnregisterSignal(parent, COMSIG_ATOM_TTS_TRAIT_ADD) + UnregisterSignal(parent, COMSIG_ATOM_TTS_TRAIT_REMOVE) + +/datum/component/tts_component/Initialize(datum/tts_seed/new_tts_seed, ...) + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + if(ispath(new_tts_seed) && SStts220.tts_seeds[initial(new_tts_seed.name)]) + new_tts_seed = SStts220.tts_seeds[initial(new_tts_seed.name)] + if(istype(new_tts_seed)) + tts_seed = new_tts_seed + if(!tts_seed) + tts_seed = get_random_tts_seed_by_gender() + if(!tts_seed) // Something went terribly wrong + return COMPONENT_INCOMPATIBLE + if(length(args) > 1) + for(var/trait in 2 to length(args)) + traits += args[trait] + +/datum/component/tts_component/proc/return_tts_seed() + SIGNAL_HANDLER + return tts_seed + +/datum/component/tts_component/proc/select_tts_seed(mob/chooser, silent_target = FALSE, override = FALSE, list/new_traits = null) + if(!chooser) + if(ismob(parent)) + chooser = parent + else + return null + + var/atom/being_changed = parent + var/static/tts_test_str = "Так звучит мой голос." + var/datum/tts_seed/new_tts_seed + + if(chooser == being_changed) + var/datum/preferences/prefs = chooser.client.prefs + var/prefs_tts_seed = prefs?.read_preference(/datum/preference/text/tts_seed) + if(being_changed.gender == prefs?.read_preference(/datum/preference/choiced/gender)) + if(tgui_alert(chooser, "Оставляем голос вашего персонажа [prefs?.read_preference(/datum/preference/name/real_name)] - [prefs_tts_seed]?", "Выбор голоса", "Нет", "Да") == "Да") + if(!SStts220.tts_seeds[prefs_tts_seed]) + to_chat(chooser, span_warning("Отсутствует tts_seed для значения \"[prefs_tts_seed]\". Текущий голос - [tts_seed]")) + return null + new_tts_seed = SStts220.tts_seeds[prefs_tts_seed] + if(new_traits) + traits = new_traits + INVOKE_ASYNC(SStts220, TYPE_PROC_REF(/datum/controller/subsystem/tts220, get_tts), null, chooser, tts_test_str, new_tts_seed, FALSE, get_effect()) + return new_tts_seed + + var/tts_seeds + var/list/tts_seeds_by_gender = SStts220.get_tts_by_gender(being_changed.gender) + if(!length(tts_seeds_by_gender)) + to_chat(chooser, span_warning("Не удалось найти пол для голоса! Текущий голос - [tts_seed.name]")) + return null + if(check_rights(R_ADMIN, FALSE, chooser) || override || !ismob(being_changed)) + tts_seeds = tts_seeds_by_gender + else + tts_seeds = tts_seeds_by_gender && SStts220.get_available_seeds(being_changed) // && for lists means intersection + + var/new_tts_seed_key + new_tts_seed_key = tgui_input_list(chooser, "Выберите голос персонажа", "Преобразуем голос", tts_seeds, tts_seed.name) + if(!new_tts_seed_key || !SStts220.tts_seeds[new_tts_seed_key]) + to_chat(chooser, span_warning("Что-то пошло не так с выбором голоса. Текущий голос - [tts_seed.name]")) + return null + + new_tts_seed = SStts220.tts_seeds[new_tts_seed_key] + if(new_traits) + traits = new_traits + + if(!silent_target && being_changed != chooser && ismob(being_changed)) + INVOKE_ASYNC(SStts220, TYPE_PROC_REF(/datum/controller/subsystem/tts220, get_tts), null, being_changed, tts_test_str, new_tts_seed, FALSE, get_effect()) + + if(chooser) + INVOKE_ASYNC(SStts220, TYPE_PROC_REF(/datum/controller/subsystem/tts220, get_tts), null, chooser, tts_test_str, new_tts_seed, FALSE, get_effect()) + + return new_tts_seed + +/datum/component/tts_component/proc/tts_seed_change(atom/being_changed, mob/chooser, override = FALSE, list/new_traits = null) + set waitfor = FALSE + var/datum/tts_seed/new_tts_seed = select_tts_seed(chooser = chooser, override = override, new_traits = new_traits) + if(!new_tts_seed) + return null + tts_seed = new_tts_seed + +/datum/component/tts_component/proc/get_random_tts_seed_by_gender() + var/atom/being_changed = parent + var/tts_choice = SStts220.pick_tts_seed_by_gender(being_changed.gender) + var/datum/tts_seed/seed = SStts220.tts_seeds[tts_choice] + if(!seed) + return null + return seed + +/datum/component/tts_component/proc/get_effect(effect) + . = effect + switch(.) + if(null) + if(TTS_TRAIT_ROBOTIZE in traits) + return /datum/singleton/sound_effect/robot + if(/datum/singleton/sound_effect/radio) + if(TTS_TRAIT_ROBOTIZE in traits) + return /datum/singleton/sound_effect/radio_robot + if(/datum/singleton/sound_effect/megaphone) + if(TTS_TRAIT_ROBOTIZE in traits) + return /datum/singleton/sound_effect/megaphone_robot + return . + +/datum/component/tts_component/proc/cast_tts(atom/speaker, mob/listener, message, atom/location, is_local = TRUE, effect = null, traits = TTS_TRAIT_RATE_FASTER, preSFX, postSFX) + SIGNAL_HANDLER + + if(!message) + return + var/datum/preferences/prefs = listener?.client?.prefs + if(prefs?.read_preference(/datum/preference/choiced/sound_tts) != TTS_SOUND_ENABLED || prefs?.read_preference(/datum/preference/numeric/sound_tts_volume) == 0) + return + if(HAS_TRAIT(listener, TRAIT_DEAF)) + return + if(!speaker) + speaker = parent + if(!location) + location = parent + if(effect == /datum/singleton/sound_effect/radio) + if(listener == speaker && !issilicon(parent)) // don't hear both radio and whisper from yourself + return + + effect = get_effect(effect) + + INVOKE_ASYNC(SStts220, TYPE_PROC_REF(/datum/controller/subsystem/tts220, get_tts), location, listener, message, tts_seed, is_local, effect, traits, preSFX, postSFX) + +/datum/component/tts_component/proc/tts_trait_add(atom/user, trait) + SIGNAL_HANDLER + + if(!isnull(trait) && !(trait in traits)) + traits += trait + +/datum/component/tts_component/proc/tts_trait_remove(atom/user, trait) + SIGNAL_HANDLER + + if(!isnull(trait) && (trait in traits)) + traits -= trait + +// Component usage + +/mob/living/silicon/verb/synth_change_voice() + set name = "Смена голоса" + set desc = "Express yourself!" + set category = "Silicon Commands" + change_tts_seed(src, new_traits = list(TTS_TRAIT_ROBOTIZE)) diff --git a/modular_bandastation/tts/code/tts_configuration.dm b/modular_bandastation/tts/code/tts_configuration.dm index 7f8d628e23674..5d199f9ccc0ef 100644 --- a/modular_bandastation/tts/code/tts_configuration.dm +++ b/modular_bandastation/tts/code/tts_configuration.dm @@ -6,9 +6,14 @@ default = "" protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN -/datum/config_entry/flag/tts_cache +/datum/config_entry/flag/tts_cache_enabled default = FALSE protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN /datum/config_entry/string/ffmpeg_cpuaffinity + default = "" + protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN + +/datum/config_entry/string/tts_api_url_silero + default = "" protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN diff --git a/modular_bandastation/tts/code/tts_mob_Hear.dm b/modular_bandastation/tts/code/tts_mob_Hear.dm deleted file mode 100644 index ded3d492a8457..0000000000000 --- a/modular_bandastation/tts/code/tts_mob_Hear.dm +++ /dev/null @@ -1,67 +0,0 @@ -/mob/proc/Hear_tts(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range) - if(!SStts220.is_enabled) - return - - if(!isliving(src) && !isobserver(src)) - return - - if(!client) - return - - if(HAS_TRAIT(speaker, TRAIT_SIGN_LANG)) - return - - if(!message_language) - return - - var/is_custom_say_emote_without_message = (MODE_CUSTOM_SAY_ERASE_INPUT in message_mods) - if(is_custom_say_emote_without_message) - return - - if(stat == UNCONSCIOUS || stat == HARD_CRIT) - return - - if(!radio_freq && !LOCAL_TTS_ENABLED(src) || radio_freq && !RADIO_TTS_ENABLED(src)) - return - - var/atom/movable/virtualspeaker/virtual_speaker = speaker - var/atom/movable/real_speaker = istype(virtual_speaker) ? virtual_speaker.source : speaker - - var/self_radio = radio_freq && src == real_speaker - if(self_radio) - return - - var/is_speaker_whispering = (WHISPER_MODE in message_mods) - var/can_hear_whisper = get_dist(speaker, src) <= message_range || isobserver(src) - if(is_speaker_whispering && !can_hear_whisper) - return - - var/effect = issilicon(real_speaker) ? SOUND_EFFECT_ROBOT : SOUND_EFFECT_NONE - if(radio_freq) - effect = issilicon(real_speaker) ? SOUND_EFFECT_RADIO_ROBOT : SOUND_EFFECT_RADIO - else if(SPAN_COMMAND in spans) - effect = issilicon(real_speaker) ? SOUND_EFFECT_MEGAPHONE_ROBOT : SOUND_EFFECT_MEGAPHONE - - var/traits = TTS_TRAIT_RATE_MEDIUM - if(is_speaker_whispering) - traits &= TTS_TRAIT_PITCH_WHISPER - - var/message_tts = translate_language(language = message_language, raw_message = raw_message) - - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), speaker, src, message_tts, real_speaker.tts_seed, !radio_freq, effect, traits) - -/mob/living/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range) - var/static/regex/plus_sign_replace = new(@"\+", "g") - var/plussless_message = plus_sign_replace.Replace(raw_message, "") - - . = ..(message, speaker, message_language, plussless_message, radio_freq, spans, message_mods, message_range) - - Hear_tts(message, speaker, message_language, raw_message, radio_freq, spans, message_mods, message_range) - -/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range) - var/static/regex/plus_sign_replace = new(@"\+", "g") - var/plussless_message = plus_sign_replace.Replace(raw_message, "") - - . = ..(message, speaker, message_language, plussless_message, radio_freq, spans, message_mods, message_range) - - Hear_tts(message, speaker, message_language, raw_message, radio_freq, spans, message_mods, message_range) diff --git a/modular_bandastation/tts/code/tts_preferences.dm b/modular_bandastation/tts/code/tts_preferences.dm index bfcd0a61d8c9c..e747d7a18b1f3 100644 --- a/modular_bandastation/tts/code/tts_preferences.dm +++ b/modular_bandastation/tts/code/tts_preferences.dm @@ -1,8 +1,6 @@ /datum/preferences/ui_static_data(mob/user) var/list/data = ..() - data["tts_enabled"] = CONFIG_GET(flag/tts_enabled) - var/list/providers = list() for(var/_provider in SStts220.tts_providers) var/datum/tts_provider/provider = SStts220.tts_providers[_provider] @@ -21,53 +19,37 @@ "category" = seed.category, "gender" = seed.gender, "provider" = initial(seed.provider.name), - "donator_level" = seed.donator_level, + "donator_level" = seed.required_donator_level, )) data["seeds"] = seeds - data["phrases"] = TTS_PHRASES - return data +/datum/preferences/ui_data(mob/user) + var/list/data = ..() + data["tts_seed"] = read_preference(/datum/preference/text/tts_seed) + return data /datum/preferences/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() - if (.) + if(.) return - - switch (action) + switch(action) if("listen") var/phrase = params["phrase"] var/seed_name = params["seed"] - if((phrase in TTS_PHRASES) && (seed_name in SStts220.tts_seeds)) - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), null, usr, phrase, seed_name, FALSE) + INVOKE_ASYNC(SStts220, TYPE_PROC_REF(/datum/controller/subsystem/tts220, get_tts), null, usr, phrase, SStts220.tts_seeds[seed_name], FALSE) return FALSE - if("select_voice") var/seed_name = params["seed"] - var/datum/preference/tts_seed = GLOB.preference_entries_by_key["tts_seed"] - write_preference(tts_seed, seed_name) + if(!isnull(seed_name) && (seed_name in SStts220.tts_seeds)) + write_preference(GLOB.preference_entries[/datum/preference/text/tts_seed], seed_name) return TRUE -/datum/preference/numeric/sound_tts_local - category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "sound_tts_local" - savefile_identifier = PREFERENCE_PLAYER - - minimum = 0 - maximum = 100 - -/datum/preference/numeric/sound_tts_local/create_default_value() - return 100 - -/datum/preference/numeric/sound_tts_radio - category = PREFERENCE_CATEGORY_GAME_PREFERENCES - savefile_key = "sound_tts_radio" - savefile_identifier = PREFERENCE_PLAYER - - minimum = 0 - maximum = 100 +/datum/preference/text/tts_seed + savefile_key = "tts_seed" + savefile_identifier = PREFERENCE_CHARACTER -/datum/preference/numeric/sound_tts_radio/create_default_value() - return 50 +/datum/preference/text/tts_seed/apply_to_human(mob/living/carbon/human/target, value) + target.AddComponent(/datum/component/tts_component, SStts220.tts_seeds[value]) diff --git a/modular_bandastation/tts/code/tts_provider.dm b/modular_bandastation/tts/code/tts_provider.dm index aa159eef07cde..65d2e484fdbc3 100644 --- a/modular_bandastation/tts/code/tts_provider.dm +++ b/modular_bandastation/tts/code/tts_provider.dm @@ -1,18 +1,19 @@ /datum/tts_provider var/name = "STUB" var/is_enabled = TRUE + var/api_url - // Throttling var/is_throttled = FALSE var/throttled_until = 0 + var/timed_out_requests = 0 var/failed_requests = 0 var/failed_requests_limit = 10 /datum/tts_provider/proc/request(text, datum/tts_seed/seed, datum/callback/proc_callback) return TRUE -/datum/tts_provider/proc/process_response(datum/http_response/response) +/datum/tts_provider/proc/process_response(list/response) return null /datum/tts_provider/proc/throttle_check() diff --git a/modular_bandastation/tts/code/tts_seed.dm b/modular_bandastation/tts/code/tts_seed.dm index 627dd3f35f615..23c005e0a660b 100644 --- a/modular_bandastation/tts/code/tts_seed.dm +++ b/modular_bandastation/tts/code/tts_seed.dm @@ -1,24 +1,42 @@ -/datum/tts_seed - var/name = "STUB" - var/value = "STUB" - var/category = TTS_CATEGORY_OTHER - var/gender = TTS_GENDER_ANY - var/datum/tts_provider/provider = /datum/tts_provider - var/donator_level = 0 - -/datum/tts_seed/vv_edit_var(var_name, var_value) - return FALSE - -/datum/preference/text/tts_seed - savefile_key = "tts_seed" - savefile_identifier = PREFERENCE_CHARACTER - -/datum/preference/text/tts_seed/create_default_value() - return "Arthas" - -/// Any movable atom -/atom/movable - var/tts_seed - -/datum/preference/text/tts_seed/apply_to_human(mob/living/carbon/human/target, value) - target.tts_seed = value +/datum/dna + var/datum/tts_seed/tts_seed_dna + +/datum/dna/transfer_identity(mob/living/carbon/destination, transfer_SE, transfer_species) + if(!istype(destination)) + return + . = ..() + destination.dna.tts_seed_dna = tts_seed_dna + destination.AddComponent(/datum/component/tts_component, tts_seed_dna) + +/datum/dna/copy_dna(datum/dna/new_dna) + . = ..() + new_dna.tts_seed_dna = tts_seed_dna + +/atom/proc/add_tts_component() + return + +/atom/Initialize(mapload, ...) + . = ..() + add_tts_component() + +/atom/proc/cast_tts(mob/listener, message, atom/location, is_local = TRUE, effect = null, traits = TTS_TRAIT_RATE_FASTER, preSFX, postSFX) + SEND_SIGNAL(src, COMSIG_ATOM_TTS_CAST, listener, message, location, is_local, effect, traits, preSFX, postSFX) + +// TODO: Do it better? +/atom/proc/get_tts_seed() + var/datum/component/tts_component/tts_component = GetComponent(/datum/component/tts_component) + if(tts_component) + return tts_component.tts_seed + +/atom/proc/change_tts_seed(mob/chooser, override, list/new_traits = null) + if(!get_tts_seed()) + if(alert(chooser, "Отсутствует TTS компонент. Создать?", "Изменение TTS", "Да", "Нет") == "Нет") + return + AddComponent(/datum/component/tts_component, /datum/tts_seed/silero/angel) + SEND_SIGNAL(src, COMSIG_ATOM_TTS_SEED_CHANGE, chooser, override, new_traits) + +/atom/proc/tts_trait_add(trait) + SEND_SIGNAL(src, COMSIG_ATOM_TTS_TRAIT_ADD, trait) + +/atom/proc/tts_trait_remove(trait) + SEND_SIGNAL(src, COMSIG_ATOM_TTS_TRAIT_REMOVE, trait) diff --git a/modular_bandastation/tts/code/tts_sound.dm b/modular_bandastation/tts/code/tts_sound.dm deleted file mode 100644 index f9271e3f399cc..0000000000000 --- a/modular_bandastation/tts/code/tts_sound.dm +++ /dev/null @@ -1,44 +0,0 @@ -// TODO: SS220-TTS to delete -//world/proc/shelleo -#define SHELLEO_ERRORLEVEL 1 -#define SHELLEO_STDOUT 2 -#define SHELLEO_STDERR 3 - -/proc/apply_sound_effect(effect, filename_input, filename_output) - if(!effect) - CRASH("Invalid sound effect chosen.") - - var/taskset - // TODO: SS220-TTS - if(CONFIG_GET(string/ffmpeg_cpuaffinity)) - taskset = "taskset -ac [CONFIG_GET(string/ffmpeg_cpuaffinity)]" - - var/list/output - switch(effect) - if(SOUND_EFFECT_RADIO) - output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "highpass=f=1000, lowpass=f=3000, acrusher=1:1:50:0:log" [filename_output]"}) - if(SOUND_EFFECT_ROBOT) - output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, volume=volume=1.5" [filename_output]"}) - if(SOUND_EFFECT_RADIO_ROBOT) - output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, volume=volume=1.5, highpass=f=1000, lowpass=f=3000, acrusher=1:1:50:0:log" [filename_output]"}) - if(SOUND_EFFECT_MEGAPHONE) - output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "highpass=f=500, lowpass=f=4000, volume=volume=10, acrusher=1:1:45:0:log" [filename_output]"}) - if(SOUND_EFFECT_MEGAPHONE_ROBOT) - output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, highpass=f=500, lowpass=f=4000, volume=volume=10, acrusher=1:1:45:0:log" [filename_output]"}) - else - CRASH("Invalid sound effect chosen.") - var/errorlevel = output[SHELLEO_ERRORLEVEL] - var/stdout = output[SHELLEO_STDOUT] - var/stderr = output[SHELLEO_STDERR] - if(errorlevel) - error("Error: apply_sound_effect([effect], [filename_input], [filename_output]) - See debug logs.") - // TODO: SS220-TTS log_debug -> debug_world_log - debug_world_log("apply_sound_effect([effect], [filename_input], [filename_output]) STDOUT: [stdout]") - debug_world_log("apply_sound_effect([effect], [filename_input], [filename_output]) STDERR: [stderr]") - return FALSE - return TRUE - -//world/proc/shelleo -#undef SHELLEO_ERRORLEVEL -#undef SHELLEO_STDOUT -#undef SHELLEO_STDERR diff --git a/modular_bandastation/tts/code/tts_sound_TEMPORARY.dm b/modular_bandastation/tts/code/tts_sound_TEMPORARY.dm deleted file mode 100644 index a8bae493691ec..0000000000000 --- a/modular_bandastation/tts/code/tts_sound_TEMPORARY.dm +++ /dev/null @@ -1,77 +0,0 @@ -// TODO: SS220-TTS Remove this file when upstream `/mob/proc/playsound_local` supports passing `wait` as parameter. -// Copypasted `/mob/proc/playsound_local` method with `wait` support. -/mob/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/sound_to_use, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, use_reverb = TRUE, wait = FALSE) - if(!wait) - return ..() - - if(!client || !can_hear()) - return - - if(!sound_to_use) - sound_to_use = sound(get_sfx(soundin)) - - sound_to_use.wait = wait - sound_to_use.channel = channel || SSsounds.random_available_channel() - sound_to_use.volume = vol - - if(vary) - if(frequency) - sound_to_use.frequency = frequency - else - sound_to_use.frequency = get_rand_frequency() - - if(isturf(turf_source)) - var/turf/turf_loc = get_turf(src) - - //sound volume falloff with distance - var/distance = get_dist(turf_loc, turf_source) * distance_multiplier - - if(max_distance) //If theres no max_distance we're not a 3D sound, so no falloff. - sound_to_use.volume -= (max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * sound_to_use.volume - //https://www.desmos.com/calculator/sqdfl8ipgf - - if(pressure_affected) - //Atmosphere affects sound - var/pressure_factor = 1 - var/datum/gas_mixture/hearer_env = turf_loc.return_air() - var/datum/gas_mixture/source_env = turf_source.return_air() - - if(hearer_env && source_env) - var/pressure = min(hearer_env.return_pressure(), source_env.return_pressure()) - if(pressure < ONE_ATMOSPHERE) - pressure_factor = max((pressure - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 0) - else //space - pressure_factor = 0 - - if(distance <= 1) - pressure_factor = max(pressure_factor, 0.15) //touching the source of the sound - - sound_to_use.volume *= pressure_factor - //End Atmosphere affecting sound - - if(sound_to_use.volume <= 0) - return //No sound - - var/dx = turf_source.x - turf_loc.x // Hearing from the right/left - sound_to_use.x = dx * distance_multiplier - var/dz = turf_source.y - turf_loc.y // Hearing from infront/behind - sound_to_use.z = dz * distance_multiplier - var/dy = (turf_source.z - turf_loc.z) * 5 * distance_multiplier // Hearing from above / below, multiplied by 5 because we assume height is further along coords. - sound_to_use.y = dy - - sound_to_use.falloff = max_distance || 1 //use max_distance, else just use 1 as we are a direct sound so falloff isnt relevant. - - // Sounds can't have their own environment. A sound's environment will be: - // 1. the mob's - // 2. the area's (defaults to SOUND_ENVRIONMENT_NONE) - if(sound_environment_override != SOUND_ENVIRONMENT_NONE) - sound_to_use.environment = sound_environment_override - else - var/area/A = get_area(src) - sound_to_use.environment = A.sound_environment - - if(use_reverb && sound_to_use.environment != SOUND_ENVIRONMENT_NONE) //We have reverb, reset our echo setting - sound_to_use.echo[3] = -1300 //Room setting, 0 means normal reverb //SKYRAT EDIT CHANGE - sound_to_use.echo[4] = -1300 //RoomHF setting, 0 means normal reverb. //SKYRAT EDIT CHANGE - - SEND_SOUND(src, sound_to_use) diff --git a/modular_bandastation/tts/code/tts_subsystem.dm b/modular_bandastation/tts/code/tts_subsystem.dm index 2eb40ae067265..df429adbe378e 100644 --- a/modular_bandastation/tts/code/tts_subsystem.dm +++ b/modular_bandastation/tts/code/tts_subsystem.dm @@ -1,161 +1,93 @@ +#define TTS_REPLACEMENTS_FILE_PATH "config/bandastation/tts_replacements.json" +#define TTS_ACRONYM_REPLACEMENTS "tts_acronym_replacements" +#define TTS_JOB_REPLACEMENTS "tts_job_replacements" + +#define FILE_CLEANUP_DELAY 30 SECONDS + SUBSYSTEM_DEF(tts220) name = "Text-to-Speech 220" init_order = INIT_ORDER_DEFAULT - wait = 1 SECONDS + wait = 0.5 SECONDS runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - var/tts_wanted = 0 - var/tts_request_failed = 0 - var/tts_request_succeeded = 0 - var/tts_reused = 0 - var/list/tts_errors = list() - var/tts_error_raw = "" + /// All time tts uses + VAR_PRIVATE/tts_wanted = 0 + /// Amount of errored requests to providers + VAR_PRIVATE/tts_request_failed = 0 + /// Amount of successfull requests to providers + VAR_PRIVATE/tts_request_succeeded = 0 + /// Amount of cache hits + VAR_PRIVATE/tts_reused = 0 + /// Assoc list of request error codes + VAR_PRIVATE/list/tts_errors = list() + /// Last errored requests' contents + VAR_PRIVATE/tts_error_raw = "" // Simple Moving Average RPS - var/list/tts_rps_list = list() - var/tts_sma_rps = 0 + VAR_PRIVATE/list/tts_rps_list = list() + VAR_PRIVATE/tts_sma_rps = 0 - // Requests per Second (RPS), only real API requests - var/tts_rps = 0 - var/tts_rps_counter = 0 + /// Requests per Second (RPS), only real API requests + VAR_PRIVATE/tts_rps = 0 + VAR_PRIVATE/tts_rps_counter = 0 - // Total Requests per Second (TRPS), all TTS request, even reused - var/tts_trps = 0 - var/tts_trps_counter = 0 + /// Total Requests per Second (TRPS), all TTS request, even reused + VAR_PRIVATE/tts_trps = 0 + VAR_PRIVATE/tts_trps_counter = 0 - // Reused Requests per Second (RRPS), only reused requests - var/tts_rrps = 0 - var/tts_rrps_counter = 0 - - var/is_enabled = TRUE + /// Reused Requests per Second (RRPS), only reused requests + VAR_PRIVATE/tts_rrps = 0 + VAR_PRIVATE/tts_rrps_counter = 0 + VAR_PRIVATE/is_enabled = TRUE + /// List of all available TTS seeds var/list/datum/tts_seed/tts_seeds = list() - var/list/tts_seeds_names = list() - var/list/tts_seeds_names_by_donator_levels = list() + /// List of all available TTS providers var/list/datum/tts_provider/tts_providers = list() - var/list/tts_local_channels_by_owner = list() - - var/list/tts_requests_queue = list() - var/tts_requests_queue_limit = 100 - var/tts_rps_limit = 5 - - var/list/tts_queue = list() - var/list/tts_effects_queue = list() - - var/sanitized_messages_caching = TRUE - var/list/sanitized_messages_cache = list() - var/sanitized_messages_cache_hit = 0 - var/sanitized_messages_cache_miss = 0 - - var/debug_mode_enabled = FALSE - - var/static/list/tts_job_replacements = list( - "nanotrasen navy field officer" = "Полевой офицер флота Нанотрэйзен", - "nanotrasen navy officer" = "Офицер флота nanotrasen", - "supreme commander" = "Верховный главнокомандующий", - "solar federation general" = "Генерал Солнечной Федерации", - "special operations officer" = "Офицер специальных операций", - "civilian" = "Гражданский", - "tourist" = "Турист", - "businessman" = "Бизнэсмэн", - "trader" = "Торговец", - "assistant" = "Ассистент", - "chief engineer" = "Главный Инженер", - "station engineer" = "Станционный инженер", - "trainee engineer" = "Инженер-стажер", - "Engineer Assistant" = "Инженерный Ассистент", - "Technical Assistant" = "Технический Ассистент", - "Engineer Student" = "Инженер-практикант", - "Technical Student" = "Техник-практикант", - "Technical Trainee" = "Техник-стажер", - "maintenance technician" = "Техник по обслуживанию", - "engine technician" = "Техник по двигателям", - "electrician" = "Электрик", - "life support specialist" = "Специалист по жизнеобеспечению", - "atmospheric technician" = "Атмосферный техник", - "mechanic" = "Механик", - "chief medical officer" = "Главный врач", - "medical doctor" = "Врач", - "Intern" = "Интерн", - "Student Medical Doctor" = "Врач-практикант", - "Medical Assistant" = "Ассистирующий врач", - "surgeon" = "Хирург", - "nurse" = "Медсестра", - "coroner" = "К+оронэр", - "chemist" = "Химик", - "pharmacist" = "Фармацевт", - "pharmacologist" = "Фармаколог", - "geneticist" = "Генетик", - "virologist" = "Вирусолог", - "pathologist" = "Патологоанатом", - "microbiologist" = "Микробиолог", - "psychiatrist" = "Психиатр", - "psychologist" = "Психолог", - "therapist" = "Терапевт", - "paramedic" = "Парамедик", - "research director" = "Директор исследований", - "scientist" = "Учёный", - "student scientist" = "Учёный-практикант", - "Scientist Assistant" = "Научный Ассистент", - "Scientist Pregraduate" = "Учёный-бакалавр", - "Scientist Graduate" = "Научный выпускник", - "Scientist Postgraduate" = "Учёный-аспирант", - "anomalist" = "Аномалист", - "plasma researcher" = "Исследователь плазмы", - "xenobiologist" = "Ксенобиолог", - "chemical researcher" = "Химик-исследователь", - "roboticist" = "Робототехник", - "student robotist" = "Студент-робототехник", - "biomechanical engineer" = "Биомеханический инженер", - "mechatronic engineer" = "Инженер мехатроники", - "head of security" = "Глава службы безопасности", - "warden" = "Смотритель", - "detective" = "Детектив", - "forensic technician" = "Криминалист", - "security officer" = "Офицер службы безопасности", - "security cadet" = "Кадет службы безопасности", - "Security Assistant" = "Ассистент службы безопасности", - "Security Graduate" = "Выпускник кадетской академии", - "brig physician" = "Врач брига", - "security pod pilot" = "Пилот пода службы безопасности", - "ai" = "И И", - "cyborg" = "Киборг", - "robot" = "Робот", - "captain" = "Капитан", - "head of personnel" = "Глава персонала", - "nanotrasen representative" = "Представитель Нанотрэйзен", - "blueshield" = "Блюшилд", - "magistrate" = "Магистрат", - "internal affairs agent" = "Агент внутренних дел", - "human resources agent" = "Агент по персоналу", - "bartender" = "Бармэн", - "chef" = "Повар", - "cook" = "Кук", - "culinary artist" = "Кулинар", - "butcher" = "Мясник", - "botanist" = "Ботаник", - "hydroponicist" = "Гидропонист", - "botanical researcher" = "Ботаник-исследователь", - "quartermaster" = "Квартирмейстер", - "cargo technician" = "Карго техник", - "shaft miner" = "Шахтёр", - "spelunker" = "Спелеолог", - "clown" = "Клоун", - "mime" = "Мим", - "janitor" = "Уборщик", - "custodial technician" = "Техник по уходу за помещениями", - "librarian" = "Библиотекарь", - "journalist" = "Журналист", - "barber" = "Парикмахер", - "hair stylist" = "Стилист", - "beautician" = "Косметолог", - "explorer" = "Исследователь", - "chaplain" = "Священник", - "syndicate officer" = "Офицер синдиката", - "visitor" = "посетитель", + VAR_PRIVATE/tts_requests_queue_limit = 100 + VAR_PRIVATE/tts_rps_limit = 11 + VAR_PRIVATE/last_network_fire = 0 + + /// General request queue + VAR_PRIVATE/list/tts_queue = list() + /// Ffmpeg queue. Is an assoc list. Each entry is a filename mapped to the list of sound processing requests which require it. + VAR_PRIVATE/list/tts_effects_queue = list() + /// Lazy list of request that need to performed to TTS provider API + VAR_PRIVATE/list/tts_requests_queue + + /// List of currently existing binding of atom and sound channel: `atom` => `sound_channel`. SS220 TODO: free channel when atom is detroyed and may be on some other circumstances + VAR_PRIVATE/list/tts_local_channels_by_owner = list() + + /// Mapping of BYOND gender to TTS gender + VAR_PRIVATE/list/gender_table = list( + NEUTER = TTS_GENDER_ANY, + PLURAL = TTS_GENDER_ANY, + MALE = TTS_GENDER_MALE, + FEMALE = TTS_GENDER_FEMALE ) - + /// Is debug mode enabled or not. Information about `sanitized_messages_cache_hit` and `sanitized_messages_cache_miss` is printed to debug logs each SS fire + VAR_PRIVATE/debug_mode_enabled = FALSE + /// Whether or not caching of sanitized messages is performed + VAR_PRIVATE/sanitized_messages_caching = TRUE + /// Amount of message duplicates that were sanitized current SS fire. Debug purpose only + VAR_PRIVATE/sanitized_messages_cache_hit = 0 + /// Amount of unique messages that were sanitized current SS fire. Debug purpose only + VAR_PRIVATE/sanitized_messages_cache_miss = 0 + /// List of all messages that were sanitized as: `meesage md5 hash` => `message` + VAR_PRIVATE/list/sanitized_messages_cache = list() + + /// List of all available TTS seed names + VAR_PRIVATE/list/tts_seeds_names = list() + /// List of all available TTS seed names, mapped by donator level for faster access + VAR_PRIVATE/list/tts_seeds_names_by_donator_levels = list() + + /// List of all tts seeds mapped by TTS gender: `tts gender` => `list of seeds` + VAR_PRIVATE/list/tts_seeds_by_gender + /// Replacement map for acronyms for proper TTS spelling. Not private because `replacetext` can use only global procs + var/list/tts_acronym_replacements + /// Replacement map for jobs for proper TTS spelling + VAR_PRIVATE/list/tts_job_replacements /datum/controller/subsystem/tts220/stat_entry(msg) msg += "tRPS:[tts_trps] " msg += "rRPS:[tts_rrps] " @@ -172,6 +104,7 @@ SUBSYSTEM_DEF(tts220) for(var/path in subtypesof(/datum/tts_provider)) var/datum/tts_provider/provider = new path tts_providers[provider.name] += provider + for(var/path in subtypesof(/datum/tts_seed)) var/datum/tts_seed/seed = new path if(seed.value == "STUB") @@ -179,17 +112,31 @@ SUBSYSTEM_DEF(tts220) seed.provider = tts_providers[initial(seed.provider.name)] tts_seeds[seed.name] = seed tts_seeds_names += seed.name - tts_seeds_names_by_donator_levels["[seed.donator_level]"] += list(seed.name) - tts_seeds_names = sortTim(tts_seeds_names, /proc/cmp_text_asc) + tts_seeds_names_by_donator_levels["[seed.required_donator_level]"] += list(seed.name) + LAZYADDASSOCLIST(tts_seeds_by_gender, seed.gender, seed.name) + tts_seeds_names = sortTim(tts_seeds_names, GLOBAL_PROC_REF(cmp_text_asc)) /datum/controller/subsystem/tts220/Initialize(start_timeofday) - is_enabled = CONFIG_GET(flag/tts_enabled) - if(!is_enabled) - flags |= SS_NO_FIRE + if(!CONFIG_GET(flag/tts_enabled)) + is_enabled = FALSE + return SS_INIT_NO_NEED + + load_replacements() return SS_INIT_SUCCESS +/datum/controller/subsystem/tts220/pause() + . = ..() + is_enabled = FALSE + /datum/controller/subsystem/tts220/fire() + if(last_network_fire + 1 SECONDS <= world.time) + fire_networking() + fire_sound_processing() + +/datum/controller/subsystem/tts220/proc/fire_networking() + last_network_fire = world.time + tts_rps = tts_rps_counter tts_rps_counter = 0 tts_trps = tts_trps_counter @@ -198,16 +145,16 @@ SUBSYSTEM_DEF(tts220) tts_rrps_counter = 0 tts_rps_list += tts_rps - if(tts_rps_list.len > 15) + if(length(tts_rps_list) > 15) tts_rps_list.Cut(1,2) var/rps_sum = 0 for(var/rps in tts_rps_list) rps_sum += rps - tts_sma_rps = round(rps_sum / tts_rps_list.len, 0.1) + tts_sma_rps = round(rps_sum / length(tts_rps_list), 0.1) var/free_rps = clamp(tts_rps_limit - tts_rps, 0, tts_rps_limit) - var/requests = tts_requests_queue.Copy(1, clamp(LAZYLEN(tts_requests_queue), 0, free_rps) + 1) + var/requests = LAZYCOPY_RANGE(tts_requests_queue, 1, clamp(LAZYLEN(tts_requests_queue), 0, free_rps) + 1) for(var/request in requests) var/text = request[1] var/datum/tts_seed/seed = request[2] @@ -215,26 +162,63 @@ SUBSYSTEM_DEF(tts220) var/datum/tts_provider/provider = seed.provider provider.request(text, seed, proc_callback) tts_rps_counter++ - tts_requests_queue.Cut(1, clamp(LAZYLEN(tts_requests_queue), 0, free_rps) + 1) + LAZYCUT(tts_requests_queue, 1, clamp(LAZYLEN(tts_requests_queue), 0, free_rps) + 1) if(sanitized_messages_caching) sanitized_messages_cache.Cut() if(debug_mode_enabled) - world.log << "sanitized_messages_cache: HIT=[sanitized_messages_cache_hit] / MISS=[sanitized_messages_cache_miss]" + logger.Log(LOG_CATEGORY_DEBUG, "sanitized_messages_cache: HIT=[sanitized_messages_cache_hit] / MISS=[sanitized_messages_cache_miss]") sanitized_messages_cache_hit = 0 sanitized_messages_cache_miss = 0 +/datum/controller/subsystem/tts220/proc/fire_sound_processing() + var/queue_position = 1 + while(LAZYLEN(tts_effects_queue) >= queue_position) + var/filename = tts_effects_queue[queue_position++] + INVOKE_ASYNC(src, PROC_REF(process_filename_sound_effect_requests), filename) + + if(MC_TICK_CHECK) + break + + LAZYCUT(tts_effects_queue, 1, queue_position) + +/datum/controller/subsystem/tts220/proc/process_filename_sound_effect_requests(filename) + var/list/filename_requests = tts_effects_queue[filename] + var/datum/sound_effect_request/request = filename_requests[1] + + if(!apply_sound_effect(request.effect, request.original_filename, request.output_filename)) + return + + for(var/datum/sound_effect_request/adjacent_request as anything in filename_requests) + adjacent_request.cb.InvokeAsync() + /datum/controller/subsystem/tts220/Recover() is_enabled = SStts220.is_enabled tts_wanted = SStts220.tts_wanted tts_request_failed = SStts220.tts_request_failed tts_request_succeeded = SStts220.tts_request_succeeded tts_reused = SStts220.tts_reused + tts_acronym_replacements = SStts220.tts_acronym_replacements + tts_job_replacements = SStts220.tts_job_replacements + +/datum/controller/subsystem/tts220/proc/load_replacements() + if(!fexists(TTS_REPLACEMENTS_FILE_PATH)) + logger.Log(LOG_CATEGORY_DEBUG, "No file for TTS replacements located at: [TTS_REPLACEMENTS_FILE_PATH]. No replacements will be applied for TTS.") + return + + var/tts_replacements_json = file2text(TTS_REPLACEMENTS_FILE_PATH) + if(!length(tts_replacements_json)) + logger.Log(LOG_CATEGORY_DEBUG, "TTS replacements file is empty at: [TTS_REPLACEMENTS_FILE_PATH].") + return + + var/list/replacements = json_decode(tts_replacements_json) + tts_acronym_replacements = replacements[TTS_ACRONYM_REPLACEMENTS] + tts_job_replacements = replacements[TTS_JOB_REPLACEMENTS] /datum/controller/subsystem/tts220/proc/queue_request(text, datum/tts_seed/seed, datum/callback/proc_callback) if(LAZYLEN(tts_requests_queue) > tts_requests_queue_limit) is_enabled = FALSE - to_chat(world, span_announce("SERVER: очередь запросов превысила лимит, подсистема SStts принудительно отключена!")) + to_chat(world, span_info("SERVER: очередь запросов превысила лимит, подсистема [src] принудительно отключена!")) return FALSE if(tts_rps_counter < tts_rps_limit) @@ -243,24 +227,25 @@ SUBSYSTEM_DEF(tts220) tts_rps_counter++ return TRUE - tts_requests_queue += list(list(text, seed, proc_callback)) + LAZYADD(tts_requests_queue, list(list(text, seed, proc_callback))) return TRUE -/datum/controller/subsystem/tts220/proc/get_tts(atom/speaker, mob/listener, message, seed_name, is_local = TRUE, effect = SOUND_EFFECT_NONE, traits = TTS_TRAIT_RATE_FASTER, preSFX = null, postSFX = null) +/datum/controller/subsystem/tts220/proc/get_tts(atom/speaker, mob/listener, message, datum/tts_seed/tts_seed, is_local = TRUE, datum/singleton/sound_effect/effect = null, traits = TTS_TRAIT_RATE_FASTER, preSFX = null, postSFX = null) if(!is_enabled) return if(!message) return if(isnull(listener) || !listener.client) return - if(isnull(seed_name) || !(seed_name in tts_seeds)) + if(ispath(tts_seed) && SStts220.tts_seeds[initial(tts_seed.name)]) + tts_seed = SStts220.tts_seeds[initial(tts_seed.name)] + if(!istype(tts_seed)) return - var/datum/tts_seed/seed = tts_seeds[seed_name] tts_wanted++ tts_trps_counter++ - var/datum/tts_provider/provider = seed.provider + var/datum/tts_provider/provider = tts_seed.provider if(!provider.is_enabled) return if(provider.throttle_check()) @@ -281,25 +266,27 @@ SUBSYSTEM_DEF(tts220) if(traits & TTS_TRAIT_PITCH_WHISPER) text = provider.pitch_whisper(text) - var/hash = rustgss220_hash_string(RUSTG_HASH_MD5, text) - var/filename = "sound/tts_cache/[seed.name]/[hash]" + var/hash = md5(lowertext(text)) - var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect, preSFX, postSFX) + var/filename = "data/tts_cache/[tts_seed.name]/[hash]" + var/datum/singleton/sound_effect/effect_singleton = GET_SINGLETON(effect) if(fexists("[filename].ogg")) tts_reused++ tts_rrps_counter++ - play_tts(speaker, listener, filename, is_local, effect, preSFX, postSFX) + play_tts(speaker, listener, filename, is_local, effect_singleton, preSFX, postSFX) return + var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect_singleton, preSFX, postSFX) + if(LAZYLEN(tts_queue[filename])) tts_reused++ tts_rrps_counter++ LAZYADD(tts_queue[filename], play_tts_cb) return - var/datum/callback/cb = CALLBACK(src, PROC_REF(get_tts_callback), speaker, listener, filename, seed, is_local, effect, preSFX, postSFX) - queue_request(text, seed, cb) + queue_request(text, tts_seed, CALLBACK(src, PROC_REF(get_tts_callback), speaker, listener, filename, tts_seed, is_local, effect_singleton, preSFX, postSFX)) + LAZYADD(tts_queue[filename], play_tts_cb) /datum/controller/subsystem/tts220/proc/get_tts_callback(atom/speaker, mob/listener, filename, datum/tts_seed/seed, is_local, effect, preSFX, postSFX, datum/http_response/response) @@ -307,17 +294,15 @@ SUBSYSTEM_DEF(tts220) // Bail if it errored if(response.errored) - provider.failed_requests++ - if(provider.failed_requests >= provider.failed_requests_limit) - provider.is_enabled = FALSE - message_admins("Error connecting to [provider.name] TTS API. Please inform a maintainer or server host.") + provider.timed_out_requests++ + log_game(span_warning("Error connecting to [provider.name] TTS API. Please inform a maintainer or server host.")) + message_admins(span_warning("Error connecting to [provider.name] TTS API. Please inform a maintainer or server host.")) return if(response.status_code != 200) provider.failed_requests++ - if(provider.failed_requests >= provider.failed_requests_limit) - provider.is_enabled = FALSE - message_admins("Error performing [provider.name] TTS API request (Code: [response.status_code])") + log_game(span_warning("Error performing [provider.name] TTS API request (Code: [response.status_code])")) + message_admins(span_warning("Error performing [provider.name] TTS API request (Code: [response.status_code])")) tts_request_failed++ if(response.status_code) if(tts_errors["[response.status_code]"]) @@ -336,8 +321,8 @@ SUBSYSTEM_DEF(tts220) rustgss220_file_write_b64decode(voice, "[filename].ogg") - if (!CONFIG_GET(flag/tts_cache)) - addtimer(CALLBACK(src, PROC_REF(cleanup_tts_file), "[filename].ogg"), 30 SECONDS) + if(!CONFIG_GET(flag/tts_cache_enabled)) + addtimer(CALLBACK(src, PROC_REF(cleanup_tts_file), "[filename].ogg"), FILE_CLEANUP_DELAY) for(var/datum/callback/cb in tts_queue[filename]) cb.InvokeAsync() @@ -345,99 +330,71 @@ SUBSYSTEM_DEF(tts220) tts_queue -= filename -/datum/controller/subsystem/tts220/proc/play_tts(atom/speaker, mob/listener, filename, is_local = TRUE, effect = SOUND_EFFECT_NONE, preSFX = null, postSFX = null) +/datum/controller/subsystem/tts220/proc/queue_sound_effect_processing(pure_filename, effect, processed_filename, datum/callback/output_tts_cb) + var/datum/sound_effect_request/request = new + request.original_filename = "[pure_filename].ogg" + request.output_filename = processed_filename + request.effect = effect + request.cb = output_tts_cb + LAZYADD(tts_effects_queue[processed_filename], request) + +/datum/controller/subsystem/tts220/proc/play_tts(atom/speaker, mob/listener, pure_filename, is_local = TRUE, datum/singleton/sound_effect/effect = null, preSFX = null, postSFX = null) if(isnull(listener) || !listener.client) return - var/voice - switch(effect) - if(SOUND_EFFECT_NONE) - voice = "[filename].ogg" - if(SOUND_EFFECT_RADIO) - voice = "[filename]_radio.ogg" - if(SOUND_EFFECT_ROBOT) - voice = "[filename]_robot.ogg" - if(SOUND_EFFECT_RADIO_ROBOT) - voice = "[filename]_radio_robot.ogg" - if(SOUND_EFFECT_MEGAPHONE) - voice = "[filename]_megaphone.ogg" - if(SOUND_EFFECT_MEGAPHONE_ROBOT) - voice = "[filename]_megaphone_robot.ogg" - else - CRASH("Invalid sound effect chosen.") - if(effect != SOUND_EFFECT_NONE) - if(!fexists(voice)) - var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect, preSFX, postSFX) - if(LAZYLEN(tts_effects_queue[voice])) - LAZYADD(tts_effects_queue[voice], play_tts_cb) - return - LAZYADD(tts_effects_queue[voice], play_tts_cb) - apply_sound_effect(effect, "[filename].ogg", voice) - for(var/datum/callback/cb in tts_effects_queue[voice]) - tts_effects_queue[voice] -= cb - if(cb == play_tts_cb) - continue - cb.InvokeAsync() - tts_effects_queue -= voice + var/filename2play = "[pure_filename][effect?.suffix].ogg" - var/turf/turf_source = get_turf(speaker) + if(isnull(effect) || fexists(filename2play)) + output_tts(speaker, listener, filename2play, is_local, preSFX, postSFX) + return - var/volume - var/channel - if(is_local) - volume = LOCAL_TTS_VOLUME(listener) - channel = get_local_channel_by_owner(speaker) - else - volume = RADIO_TTS_VOLUME(listener) - channel = CHANNEL_TTS_RADIO + var/datum/callback/output_tts_cb = CALLBACK(src, PROC_REF(output_tts), speaker, listener, filename2play, is_local, preSFX, postSFX) + queue_sound_effect_processing(pure_filename, effect, filename2play, output_tts_cb) - var/sound/output = sound(voice) - output.status = SOUND_STREAM +/datum/controller/subsystem/tts220/proc/output_tts(atom/speaker, mob/listener, filename2play, is_local = TRUE, preSFX = null, postSFX = null) + var/volume = listener?.client?.prefs?.read_preference(/datum/preference/numeric/sound_tts_volume) + if(!volume) + return - if(isnull(speaker)) + var/turf/turf_source = get_turf(speaker) + + var/sound/output = sound(filename2play) + output.status = SOUND_STREAM + if(!is_local || isnull(speaker)) output.wait = TRUE - output.channel = channel - // TODO: SS220-TTS - // output.volume = volume * listener.client.prefs.get_channel_volume(CHANNEL_GENERAL) * listener.client.prefs.get_channel_volume(channel) - output.volume = volume - output.environment = -1 + output.volume = volume * 0.75 // non-local is slightly less loud // TODO220: Make volume different + output.environment = SOUND_ENVIRONMENT_NONE if(output.volume <= 0) return - if(preSFX) - play_sfx(listener, preSFX, output.channel, output.volume, output.environment) - + play_sfx_if_exists(listener, preSFX, output) SEND_SOUND(listener, output) + play_sfx_if_exists(listener, postSFX, output) + return - if(preSFX) - play_sfx(listener, preSFX, output.channel, output.volume, output.environment) + play_sfx_if_exists(listener, preSFX, output) - listener.playsound_local(turf_source, output, volume, sound_to_use = output, channel = channel, wait = TRUE) + output = listener.playsound_local(turf_source, output, volume) if(!output || output.volume <= 0) return - if(postSFX) - play_sfx(listener, postSFX, output.channel, output.volume, output.environment) + play_sfx_if_exists(listener, postSFX, output) + +/datum/controller/subsystem/tts220/proc/play_sfx_if_exists(mob/listener, sfx, sound/output) + if(sfx) + play_sfx(listener, sfx, output.volume, output.environment) -/datum/controller/subsystem/tts220/proc/play_sfx(mob/listener, sfx, channel, volume, environment) +/datum/controller/subsystem/tts220/proc/play_sfx(mob/listener, sfx, volume, environment) var/sound/output = sound(sfx) output.status = SOUND_STREAM output.wait = TRUE - output.channel = channel output.volume = volume output.environment = environment SEND_SOUND(listener, output) -/datum/controller/subsystem/tts220/proc/get_local_channel_by_owner(owner) - var/channel = tts_local_channels_by_owner[owner] - if(isnull(channel)) - channel = SSsounds.reserve_sound_channel_datumless() - tts_local_channels_by_owner[owner] = channel - return channel - /datum/controller/subsystem/tts220/proc/cleanup_tts_file(filename) fdel(filename) @@ -453,10 +410,6 @@ SUBSYSTEM_DEF(tts220) if(!M.client) return _tts_seeds_names - // TODO: SS220-TTS - // for(var/donator_level in 0 to DONATOR_LEVEL_MAX) - // if(M.client.donator_level < donator_level) - // _tts_seeds_names -= tts_seeds_names_by_donator_levels["[donator_level]"] return _tts_seeds_names /datum/controller/subsystem/tts220/proc/get_random_seed(owner) @@ -465,113 +418,73 @@ SUBSYSTEM_DEF(tts220) /datum/controller/subsystem/tts220/proc/sanitize_tts_input(message) var/hash if(sanitized_messages_caching) - hash = rustgss220_hash_string(RUSTG_HASH_MD5, message) + hash = md5(lowertext(message)) if(sanitized_messages_cache[hash]) sanitized_messages_cache_hit++ return sanitized_messages_cache[hash] sanitized_messages_cache_miss++ . = message . = trim(.) - var/static/regex/punctuation_check = new(@"[.,?!]\Z") if(!punctuation_check.Find(.)) . += "." - var/static/regex/html_tags = new(@"<[^>]*>", "g") . = html_tags.Replace(., "") . = html_decode(.) - var/static/regex/forbidden_symbols = new(@"[^a-zA-Z0-9а-яА-ЯёЁ,!?+./ \r\n\t:—()-]", "g") . = forbidden_symbols.Replace(., "") + var/static/regex/acronyms = new(@"(? { const { act, data } = useBackend(); - const { - providers, - seeds, - phrases, - character_preferences: { - misc: { tts_seed }, - }, - } = data; + const { providers, seeds, phrases, tts_seed } = data; const donator_level = 5; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts index b8867f0878d21..2a6e6c3cfc72d 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts @@ -147,7 +147,6 @@ export type PreferencesMenuData = { gender: Gender; joblessrole: JoblessRole; species: string; - tts_seed: string; // BANDASTATION EDIT ADD - TTS }; randomization: Record; @@ -177,7 +176,10 @@ export type PreferencesMenuData = { active_slot: number; name_to_use: string; + window: Window; + // BANDASTATION EDIT START - TTS + tts_seed: string; tts_enabled: BooleanLike; providers: Array<{ name: string; @@ -193,8 +195,6 @@ export type PreferencesMenuData = { }>; phrases: string[]; // BANDASTATION EDIT END - - window: Window; }; export type ServerData = { diff --git a/tools/ci/install_rust_g.sh b/tools/ci/install_rust_g.sh index c237162d8faa2..582e80536f5d0 100755 --- a/tools/ci/install_rust_g.sh +++ b/tools/ci/install_rust_g.sh @@ -7,3 +7,7 @@ mkdir -p ~/.byond/bin wget -nv -O ~/.byond/bin/librust_g.so "https://github.com/tgstation/rust-g/releases/download/$RUST_G_VERSION/librust_g.so" chmod +x ~/.byond/bin/librust_g.so ldd ~/.byond/bin/librust_g.so + +wget -nv -O ~/.byond/bin/librust_g_ss220.so "https://github.com/ss220club/rust-g-tg/releases/download/$RUST_G_VERSION_SS220/librust_g.so" +chmod +x ~/.byond/bin/librust_g_ss220.so +ldd ~/.byond/bin/librust_g_ss220.so diff --git a/tools/tgs_scripts/InstallDeps.sh b/tools/tgs_scripts/InstallDeps.sh index e66053ab89c7c..cc38087096cda 100755 --- a/tools/tgs_scripts/InstallDeps.sh +++ b/tools/tgs_scripts/InstallDeps.sh @@ -8,20 +8,21 @@ has_cargo="$(command -v ~/.cargo/bin/cargo)" has_sudo="$(command -v sudo)" has_youtubedl="$(command -v youtube-dl)" has_pip3="$(command -v pip3)" +has_cmake="$(command -v cmake)" set -e set -x # apt packages, libssl needed by rust-g but not included in TGS barebones install -if ! ( [ -x "$has_git" ] && [ -x "$has_curl" ] && [ -f "/usr/lib/i386-linux-gnu/libssl.so" ] ); then +if ! ( [ -x "$has_git" ] && [ -x "$has_curl" ] && [ -x "$has_cmake" ] && [ -f "/usr/lib/i386-linux-gnu/libssl.so" ] ); then echo "Installing apt dependencies..." if ! [ -x "$has_sudo" ]; then dpkg --add-architecture i386 apt-get update - apt-get install -y lib32z1 git pkg-config libssl-dev:i386 libssl-dev zlib1g-dev:i386 curl + apt-get install -y lib32z1 git pkg-config libssl-dev:i386 libssl-dev zlib1g-dev:i386 curl cmake else sudo dpkg --add-architecture i386 sudo apt-get update - sudo apt-get install -y lib32z1 git pkg-config libssl-dev:i386 libssl-dev zlib1g-dev:i386 curl + sudo apt-get install -y lib32z1 git pkg-config libssl-dev:i386 libssl-dev zlib1g-dev:i386 curl cmake fi fi diff --git a/tools/tgs_scripts/PreCompile.sh b/tools/tgs_scripts/PreCompile.sh index ae8cba7a45b96..b33f821942e54 100755 --- a/tools/tgs_scripts/PreCompile.sh +++ b/tools/tgs_scripts/PreCompile.sh @@ -32,6 +32,25 @@ env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --ignore-rust-version --re mv target/i686-unknown-linux-gnu/release/librust_g.so "$1/librust_g.so" cd .. +# update rust-g-tg s220 +if [ ! -d "rust-g-tg" ]; then + echo "Cloning rust-g ss220..." + git clone https://github.com/ss220club/rust-g-tg + cd rust-g-tg + ~/.cargo/bin/rustup target add i686-unknown-linux-gnu +else + echo "Fetching rust-g ss220..." + cd rust-g-tg + git fetch + ~/.cargo/bin/rustup target add i686-unknown-linux-gnu +fi + +echo "Deploying rust-g ss220..." +git checkout master +env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --ignore-rust-version --release --target=i686-unknown-linux-gnu +mv target/i686-unknown-linux-gnu/release/librust_g.so "$1/librust_g_ss220.so" +cd .. + # compile tgui echo "Compiling tgui..." cd "$1"